Skip to content
Snippets Groups Projects
Select Git revision
  • 1d43096e7e5a5a1289d76ef0d0d1819d6f9a66ac
  • main default protected
  • register-logging-channel
  • expr-lang
  • ci-82
  • attr-events
  • locale-wip
  • custom-routes
  • v0.1.85
  • v0.1.84
  • v0.1.83
  • v0.1.82
  • v0.1.81
  • v0.1.80
  • v0.1.79
  • v0.1.78
  • v0.1.77
  • v0.1.76
  • v0.1.75
  • v0.1.74
  • v0.1.73
  • v0.1.72
  • v0.1.71
  • v0.1.70
  • v0.1.69
  • v0.1.68
  • v0.1.67
  • v0.1.65
28 results

AuthorizationExpressionChecker.php

Blame
  • AuthorizationExpressionChecker.php 4.42 KiB
    <?php
    
    declare(strict_types=1);
    
    namespace Dbp\Relay\CoreBundle\Authorization;
    
    use Dbp\Relay\CoreBundle\ExpressionLanguage\ExpressionLanguage;
    
    /**
     * @internal
     */
    class AuthorizationExpressionChecker
    {
        public const ROLES_CONFIG_NODE = 'roles';
        public const ATTRIBUTES_CONFIG_NODE = 'attributes';
    
        private const USER_VARIBLE_NAME = 'user';
        private const DEFAULT_OBJECT_VARIBLE_NAME = 'object';
    
        /** @var ExpressionLanguage */
        private $expressionLanguage;
    
        /** @var array */
        private $rightExpressions;
    
        /** @var array */
        private $attributeExpressions;
    
        /** @var AuthorizationDataMuxer */
        private $dataMux;
    
        /** @var array */
        private $rightExpressionStack;
    
        /** @var array */
        private $attributeExpressionStack;
    
        public function __construct(AuthorizationDataMuxer $dataMux)
        {
            $this->expressionLanguage = new ExpressionLanguage();
            $this->rightExpressions = [];
            $this->attributeExpressions = [];
            $this->dataMux = $dataMux;
            $this->rightExpressionStack = [];
            $this->attributeExpressionStack = [];
        }
    
        public function setConfig(array $config)
        {
            $this->loadExpressions($config[self::ROLES_CONFIG_NODE] ?? [], $this->rightExpressions);
            $this->loadExpressions($config[self::ATTRIBUTES_CONFIG_NODE] ?? [], $this->attributeExpressions);
        }
    
        /**
         * @param mixed|null $defaultValue
         *
         * @return mixed|null
         */
        public function evalAttributeExpression(AuthorizationUser $currentAuthorizationUser, string $expressionName, $defaultValue = null)
        {
            if (in_array($expressionName, $this->attributeExpressionStack, true)) {
                throw new AuthorizationException(sprintf('infinite loop caused by authorization attribute expression %s detected', $expressionName), AuthorizationException::INFINITE_EXRPESSION_LOOP_DETECTED);
            }
            array_push($this->attributeExpressionStack, $expressionName);
    
            try {
                if (($expression = $this->attributeExpressions[$expressionName] ?? null) !== null) {
                    $result = $this->expressionLanguage->evaluate($expression, [
                        'user' => $currentAuthorizationUser,
                    ]);
                } else {
                    throw new AuthorizationException(sprintf('expression \'%s\' undefined', $expressionName), AuthorizationException::ATTRIBUTE_UNDEFINED);
                }
    
                return $result ?? $defaultValue;
            } finally {
                array_pop($this->attributeExpressionStack);
            }
        }
    
        /**
         * @param mixed|null $defaultValue
         *
         * @return mixed|null
         *
         * @throws AuthorizationException
         */
        public function getUserAttribute(AuthorizationUser $currentAuthorizationUser, string $attributeName, $defaultValue = null)
        {
            return $this->dataMux->getAttribute($currentAuthorizationUser->getIdentifier(), $attributeName, $defaultValue);
        }
    
        /**
         * Currently, there are no custom privileges. As opposed to roles and attributes, they can't be cached per privilege, since there values depend on the subject.
         * Might be a future requirement.
         *
         * @throws AuthorizationException
         */
        public function isGranted(AuthorizationUser $currentAuthorizationUser, string $rightName, $object, string $objectAlias = null): bool
        {
            if (in_array($rightName, $this->rightExpressionStack, true)) {
                throw new AuthorizationException(sprintf('infinite loop caused by authorization right expression %s detected', $rightName), AuthorizationException::INFINITE_EXRPESSION_LOOP_DETECTED);
            }
            array_push($this->rightExpressionStack, $rightName);
    
            try {
                $rightExpression = $this->rightExpressions[$rightName] ?? null;
                if ($rightExpression === null) {
                    throw new AuthorizationException(sprintf('right \'%s\' undefined', $rightName), AuthorizationException::PRIVILEGE_UNDEFINED);
                }
    
                return $this->expressionLanguage->evaluate($rightExpression, [
                    self::USER_VARIBLE_NAME => $currentAuthorizationUser,
                    $objectAlias ?? self::DEFAULT_OBJECT_VARIBLE_NAME => $object,
                ]);
            } finally {
                array_pop($this->rightExpressionStack);
            }
        }
    
        private function loadExpressions(array $expressions, array &$target): void
        {
            foreach ($expressions as $name => $expression) {
                $target[$name] = $expression;
            }
        }
    }