Commit 2d97b8e6 authored by David Surville's avatar David Surville
Browse files

Suppression du contrôle du périmètre au niveau des guards et utilisation des assertions

parent 26668853
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -27,10 +27,15 @@ if ($settings['enable_perimetres']) {

        'unicaen-perimetre' => [
            /**
             * Liste des périmètres (catégories) utilisés et du service associé fournissant la règle de gestion du périmètre.
             * NB: le service fournisseur doit implémenter l'interface @see PerimetreRuleInterface sinon il ne sera pas associé.
             * Liste des assertions utilisées et des services associés.
             *
             * Important :
             * - le nom de l'assertion au niveau des routes doit respecter le format 'Perimetre\\<nom_de_l_assertion>'
             *   (ex : 'assertion' => 'Perimetre\\Structure')
             * - le service associé doit hériter de @see \UnicaenPrivilege\Assertion\AbstractPerimetreAssertion
             */
            'rule_providers'     => [
            'assertions' => [
                // 'Structure' => StructureAssertion::class,
            ],
        ],

+16 −4
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ abstract class AbstractAssertion implements AssertionInterface
     */
    private $mvcEvent;

    /**
     * @var bool
     */
    protected $isPerimetreAssertion = false;



    /**
@@ -69,6 +74,7 @@ abstract class AbstractAssertion implements AssertionInterface
        $this->setAcl($acl);
        $this->setRole($role);
        $this->init();

        switch (true) {
            case $this->detectPrivilege($resource):
//                var_dump('assertPrivilege '.get_class($this).' '.$resource->getResourceId().' '.$privilege);
@@ -83,15 +89,21 @@ abstract class AbstractAssertion implements AssertionInterface
                $action     = substr($resource, $dpos);

//                var_dump('assertController '.get_class($this).' '.$controller.'.'.$action.' '.$privilege);
                return $this->assertController($controller, $action, $privilege);
                return $this->isPerimetreAssertion
                    ? $this->assertController($controller, $action, $privilege) && $this->assertPerimetre('controller', $resource)
                    : $this->assertController($controller, $action, $privilege);

            case $this->detectEntity($resource):
//                var_dump('assertEntity '.get_class($this).' '.$resource->getResourceId().' '.$privilege);
                return $this->assertEntity($resource, $privilege);
                return $this->isPerimetreAssertion
                    ? $this->assertEntity($resource, $privilege) && $this->assertPerimetre('entity', $resource)
                    : $this->assertEntity($resource, $privilege);

            default:
//                var_dump('assertOther '.get_class($this).' '.$resource->getResourceId().' '.$privilege);
                return $this->assertOther($resource, $privilege);
                return $this->isPerimetreAssertion
                    ? $this->assertOther($resource, $privilege) && $this->assertPerimetre('other', $resource)
                    : $this->assertOther($resource, $privilege);
        }
    }

@@ -211,7 +223,7 @@ abstract class AbstractAssertion implements AssertionInterface


    /**
     * Ititialisation des paramètres de l'assertion (si nécessaire)
     * Initialisation des paramètres de l'assertion (si nécessaire)
     */
    public function init()
    {
+180 −0
Original line number Diff line number Diff line
<?php

namespace UnicaenPrivilege\Assertion\Perimetre;

use UnicaenApp\Service\EntityManagerAwareTrait;
use UnicaenPrivilege\Assertion\AbstractAssertion;
use UnicaenPrivilege\Entity\Db\PerimetreCategorieInterface;
use UnicaenPrivilege\Entity\Db\PerimetreInterface;
use UnicaenPrivilege\Provider\Privilege\Privileges;
use UnicaenPrivilege\Service\Perimetre\PerimetreCategorieServiceAwareTrait;
use UnicaenUtilisateur\Entity\Db\UserInterface;
use UnicaenUtilisateur\Entity\Db\UserRolePerimetreInterface;
use Zend\Permissions\Acl\Exception\InvalidArgumentException;
use Zend\Permissions\Acl\Resource\ResourceInterface;
use Zend\Router\RouteMatch;

abstract class AbstractPerimetreAssertion extends AbstractAssertion
{
    use EntityManagerAwareTrait;
    use PerimetreCategorieServiceAwareTrait;

    const MODE_ALL = 'all';
    const MODE_AT_LEAST_ONE = 'at_least_one';


    /**
     * Liste des périmètres (categories) à vérifier selon le rôle de l'utilisateur
     *
     * @var array
     */
    private $perimetres;

    /**
     * @var string
     */
    protected $mode = self::MODE_ALL;


    /**
     * Nom des périmètres (catégories) associés à l'assertion
     *
     * @return string[]
     */
    abstract protected function getPerimetres() : array;

    /**
     * Mode de vérification des périmètres (catégories) associés à l'assertion @see getPerimetres()
     * - MODE_ALL : tous les périmètres doivent être validés
     * - MODE_AT_LEAST_ONE : au moins un des périmètres doit être validé
     *
     * @return string
     */
    abstract protected function getMode() : string;

    /**
     * @return void
     */
    public function init()
    {
        $this->isPerimetreAssertion = true;

        $mode = $this->getMode();
        if ($mode != self::MODE_ALL && $mode != self::MODE_AT_LEAST_ONE) {
            throw new InvalidArgumentException('Mode invalide');
        }

        // liste des catégories de périmètres et des rôles associés
        $perimetreRoles = $this->perimetreCategorieService->getPerimetresRoles();

        // récupération des périmètres associés au rôle de l'utilisateur à partir des périmètres (catégories) associés à l'assertion
        $this->perimetres = array_filter($this->getPerimetres(),
            function($p) use ($perimetreRoles) {
                return  array_key_exists($p, $perimetreRoles)
                &&  in_array($this->getRole()->getRoleId(), $perimetreRoles[$p]);
            });
    }

    protected function assertPerimetre(string $type, $resource) : bool
    {
        /** @var UserInterface $user */
        $user = $this->serviceUserContext->getDbUser();
        $role = $this->getRole();
        $fullRole = $user->getFullRole($role);

        if ($fullRole !== null) { // ça doit toujours être le cas, car on s'appuie sur le rôle sélectionné pour l'utilisateur connecté

            foreach ($this->perimetres as $perimetre) {
                // récupération des droits de l'utilisateur pour le rôle et le périmètre
                $userRights = $fullRole->getPerimetres()->filter(function ($p) use ($perimetre) {
                    /** @var UserRolePerimetreInterface $p */
                    return $p->getPerimetreCategorie()->getCode() == $perimetre;
                });

                /** @var UserRolePerimetreInterface $right */
                foreach($userRights as $right) {
                    // vérification de la règle
                    switch($type) {
                        case 'controller':
                            $result = $this->assertPerimetreController($right->getPerimetre());
                            break;
                        case 'entity':
                            $result = $this->assertPerimetreEntity($right->getPerimetre(), $resource);
                            break;
                        case 'other':
                            $result = $this->assertPerimetreOther($right->getPerimetre(), $resource);
                            break;
                    }

                    // si le périmètre est validé on sort de la boucle
                    if($result) { break; }
                }

                if ($this->getMode() == self::MODE_ALL && ! $result) {
                    return false;
                }

                if ($this->getMode() == self::MODE_AT_LEAST_ONE && $result) {
                    return true;
                }
            }

            // si aucun périmètre associé à l'assertion alors on renvoie true
            return $result ?? true;
        }

        return false;
    }

    /**
     * @return RouteMatch
     */
    private function getRouteMatch()
    {
        return $this->getMvcEvent()->getRouteMatch();
    }

    /**
     * @param string $name nom du paramètre
     * @return mixed
     */
    protected function getRouteParam($name)
    {
        return $this->getRouteMatch()->getParam($name);
    }

    /**
     * @return mixed
     */
    protected function getRouteController()
    {
        return $this->getRouteMatch()->getParam('controller');
    }

    /**
     * @return mixed
     */
    protected function getRouteAction()
    {
        return $this->getRouteMatch()->getParam('action');
    }

    /**
     * @param PerimetreInterface $perimetre
     * @param ResourceInterface $resource
     * @return bool
     */
    protected function assertPerimetreEntity(PerimetreInterface $perimetre, ResourceInterface $resource) : bool
    {
        return true;
    }

    /**
     * @param PerimetreInterface $perimetre
     * @return bool
     */
    protected function assertPerimetreController(PerimetreInterface $perimetre) : bool
    {
        return true;
    }
}
 No newline at end of file
+127 −0
Original line number Diff line number Diff line
<?php

namespace UnicaenPrivilege\Assertion\Perimetre;

use Doctrine\ORM\EntityManager;
use Interop\Container\ContainerInterface;
use UnicaenAuthentification\Service\UserContext;
use UnicaenPrivilege\Assertion\AbstractAssertion;
use UnicaenPrivilege\Service\AuthorizeService;
use UnicaenPrivilege\Service\Perimetre\PerimetreCategorieService;
use Zend\Mvc\MvcEvent;
use Zend\ServiceManager\Factory\AbstractFactoryInterface;

class AbstractPerimetreAssertionFactory implements AbstractFactoryInterface
{
    const START = 'Perimetre';

    /**
     * @var array
     */
    private $perimetreConfig = [];

    /**
     * Can the factory create an instance for the service
     *
     * @param ContainerInterface $container
     * @param string $requestedName
     * @return bool
     */
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        $parts = explode('\\', $requestedName);
        if (!$parts || $parts[0] !== self::START) {
            return false;
        }

        $config = $container->get('Config');
        $perimetreConfig = $config['unicaen-perimetre'];

        return $this->isPerimetreAssertionRequested($requestedName, $perimetreConfig);
    }

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $config = $container->get('Config');
        $perimetreConfig = $config['unicaen-perimetre'];

        $className = $this->getPerimetreAssertionClassName($requestedName,  $perimetreConfig);
        $assertion = new $className;
        $this->initAssertion($assertion, $container);

        return $assertion;
    }

    /**
     * Check assertion exists
     *
     * @param string $requestedName
     * @return bool
     */
    private function isPerimetreAssertionRequested(string $requestedName, array $config)
    {
        $parts = explode('\\', $requestedName);
        $isAssertion = count($parts) === 2;

        $className = $this->getPerimetreAssertionClassName($requestedName, $config);

        return
            $isAssertion &&
            class_exists($className) &&
            is_a($className, AbstractPerimetreAssertion::class, true);
    }

    /**
     * Initialize services
     *
     * @param AbstractPerimetreAssertion $assertion
     * @param ContainerInterface $container
     * @return AbstractPerimetreAssertion
     */
    private function initAssertion(AbstractPerimetreAssertion $assertion, ContainerInterface $container)
    {
        /**
         * @var MvcEvent $mvcEvent
         * @var AuthorizeService $authorizeService
         * @var UserContext $userContextService
         * @var EntityManager $entityManager
         * @var PerimetreCategorieService $perimetreCategorieService
         */
        $mvcEvent = $container->get('application')->getMvcEvent();
        $authorizeService = $container->get('BjyAuthorize\Service\Authorize');
        $userContextService = $container->get(UserContext::class);
        $entityManager = $container->get('Doctrine\ORM\EntityManager');
        $perimetreCategorieService = $container->get(PerimetreCategorieService::class);

        $assertion->setMvcEvent($mvcEvent);
        $assertion->setServiceAuthorize($authorizeService);
        $assertion->setServiceUserContext($userContextService);
        $assertion->setEntityManager($entityManager);
        $assertion->setPerimetreCategorieService($perimetreCategorieService);

        return $assertion;
    }

    /**
     * Get assertion class name
     *
     * @param $requestedName
     * @return string
     */
    private function getPerimetreAssertionClassName(string $requestedName, array $config)
    {
        $parts = explode('\\', $requestedName);
        $parts = array_slice($parts, 1);
        $name = $parts[0];

        $key = 'assertions';
        if(array_key_exists($key, $config)) {
            $assertions = $config[$key];
            if(array_key_exists($name,  $assertions)) {
                return $assertions[$name];
            }
        }

        return '';
    }
}
 No newline at end of file
+49 −0
Original line number Diff line number Diff line
<?php

namespace UnicaenPrivilege\Assertion\Perimetre;

use UnicaenPrivilege\Assertion\Perimetre\AbstractPerimetreAssertion;
use UnicaenPrivilege\Entity\Db\PerimetreCategorie;
use UnicaenPrivilege\Entity\Db\PerimetreInterface;
use Zend\Permissions\Acl\Resource\ResourceInterface;

class StructurePerimetreAssertion extends AbstractPerimetreAssertion
{
    protected function getPerimetres() : array
    {
        return [PerimetreCategorie::STRUCTURE];
    }

    protected function getMode(): string
    {
        return parent::MODE_ALL;
    }

    protected function assertPerimetreController(PerimetreInterface $perimetre) : bool
    {
        switch ($perimetre->getCategorie()->getCode()) {
            case 'structure':
                switch ($perimetre->getCode()) {
                    case 'all':
                        return true;
                    case 'c68':
                    default:
                        return false;
                }
        }
    }

    protected function assertPerimetreEntity(PerimetreInterface $perimetre, ResourceInterface $resource) : bool
    {
        switch ($perimetre->getCategorie()->getCode()) {
            case 'structure':
                switch ($perimetre->getCode()) {
                    case 'all':
                        return true;
                    case 'c68':
                    default:
                        return false;
                }
        }
    }
}
 No newline at end of file
Loading