Commit 16101a3c authored by David Surville's avatar David Surville
Browse files

Mise en place du contrôle du périmètre au niveau des guards

parent 5491000e
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
<?php

use UnicaenPrivilege\Guard\PrivilegeController;
use UnicaenPrivilege\Provider\Perimetre\Rules\StructureRuleProvider;
use UnicaenPrivilege\Provider\Perimetre\Rules\StructureRuleProviderFactory;
use UnicaenPrivilege\Service\Perimetre\PerimetreCategorieService;
use UnicaenPrivilege\Service\Perimetre\PerimetreCategorieServiceFactory;
use UnicaenPrivilege\Service\Perimetre\PerimetreService;
use UnicaenPrivilege\Service\Perimetre\PerimetreServiceFactory;

return [
    '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é.
         */
        'rule_providers'     => [
            'structure' => StructureRuleProvider::class,
        ],
    ],

    'bjyauthorize' => [
        'guards' => [
            PrivilegeController::class => [
@@ -66,6 +78,7 @@ return [
        'factories' => [
            PerimetreService::class => PerimetreServiceFactory::class,
            PerimetreCategorieService::class => PerimetreCategorieServiceFactory::class,
            StructureRuleProvider::class => StructureRuleProviderFactory::class
        ],
    ],

+60 −0
Original line number Diff line number Diff line
<?php

use UnicaenPrivilege\Entity\Db\Perimetre;
use UnicaenPrivilege\Entity\Db\Privilege;
use UnicaenPrivilege\Provider\Privilege\PrivilegePrivileges;
use UnicaenPrivilege\Provider\Rule\PrivilegeRuleProvider;
use UnicaenPrivilege\Service\Privilege\PrivilegeService;


$settings = [
    /**
     * L'entité associée aux périmètres peut être spécifiée via la clef de configuration ['unicaen_auth']['perimetre_entity_class']
     * Si elle est manquante alors la classe @see \UnicaenPrivilege\Entity\Db\Perimetre est utilisée
     * NB: la classe spécifiée doit hériter de @see \UnicaenPrivilege\Entity\Db\AbstractPerimetre
     */
    'perimetre_entity_class' => Perimetre::class,

    'enable_perimetres' => true,
];

$config = [
    'unicaen-auth' => $settings,
];

if ($settings['enable_perimetres']) {
    $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é.
             */
            'rule_providers'     => [
            ],
        ],

        'navigation' => [
            'default' => [
                'home' => [
                    'pages' => [
                        'droits' => [
                            'pages' => [
                                [
                                    'label' => "Gérer les périmètres",
                                    'title' => "Gérer les périmetres",
                                    'route' => 'unicaen-perimetre',
                                    'resource' => PrivilegePrivileges::getResourceId(PrivilegePrivileges::PRIVILEGE_VOIR),
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ];
} else {
    $perimetres = [];
}

return array_merge_recursive($config, $perimetres);
 No newline at end of file
+11 −14
Original line number Diff line number Diff line
@@ -8,14 +8,6 @@ use UnicaenPrivilege\Service\Privilege\PrivilegeService;


$settings = [
    'unicaen-auth' => [
        'enable_privileges' => true,
    ],
];

if ($settings['unicaen-auth']['enable_privileges']) {
    $privileges = [
        'unicaen-auth' => [
    /**
     * L'entité associée aux privilèges peut être spécifiée via la clef de configuration ['unicaen_auth']['privilege_entity_class']
     * Si elle est manquante alors la classe @see \UnicaenPrivilege\Entity\Db\Privilege est utilisée
@@ -24,12 +16,17 @@ if ($settings['unicaen-auth']['enable_privileges']) {
    'privilege_entity_class' => Privilege::class,

    /**
             * L'entité associée aux périmètres peut être spécifiée via la clef de configuration ['unicaen_auth']['perimetre_entity_class']
             * Si elle est manquante alors la classe @see \UnicaenPrivilege\Entity\Db\Perimetre est utilisée
             * NB: la classe spécifiée doit hériter de @see \UnicaenPrivilege\Entity\Db\AbstractPerimetre
     * Activation des privilèges
     */
            'perimetre_entity_class' => Perimetre::class
        ],
    'enable_privileges' => true,
];

$config = [
    'unicaen-auth' => $settings,
];

if ($settings['enable_privileges']) {
    $privileges = [

        'bjyauthorize' => [

@@ -69,4 +66,4 @@ if ($settings['unicaen-auth']['enable_privileges']) {
    $privileges = [];
}

return array_merge_recursive($settings, $privileges);
 No newline at end of file
return array_merge_recursive($config, $privileges);
 No newline at end of file
+92 −4
Original line number Diff line number Diff line
@@ -2,9 +2,16 @@

namespace UnicaenPrivilege\Guard;

use Application\Entity\Db\UtilisateurRoleLinker;
use BjyAuthorize\Guard\Controller;
use UnicaenAuthentification\Service\Traits\UserContextServiceAwareTrait;
use UnicaenPrivilege\Exception\RuntimeException;
use UnicaenPrivilege\Provider\Perimetre\PerimetreProviderAwareTrait;
use UnicaenPrivilege\Provider\Perimetre\Rules\PerimetreRuleInterface;
use UnicaenPrivilege\Provider\Privilege\PrivilegeProviderAwareTrait;
use UnicaenApp\Traits\SessionContainerTrait;
use UnicaenUtilisateur\Entity\Db\UserInterface;
use UnicaenUtilisateur\Entity\Db\UserRoleInterface;
use UnicaenUtilisateur\Service\RechercheIndividu\RechercheIndividuServiceInterface;

/**
 * Description of PrivilegeController
@@ -14,14 +21,35 @@ use UnicaenApp\Traits\SessionContainerTrait;
class PrivilegeController extends Controller
{
    use PrivilegeProviderAwareTrait;
    use SessionContainerTrait;
    use PerimetreProviderAwareTrait;
    use UserContextServiceAwareTrait;

    /**
     * @var PerimetreRuleInterface[]
     */
    public $perimetresRules;


    /**
     * @param PerimetreRuleInterface[] $perimetresRules
     * @return self
     */
    public function setPerimetresRules($perimetresRules)
    {
        $this->perimetresRules = $perimetresRules;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function processConfig()
    {
        // filtrage des rôles en fonction des privilèges
        $this->config = $this->privilegesToRoles($this->config);
        // filtrage des rôles en fonction des périmètres
        $this->config = $this->perimetresToRoles($this->config);

        parent::processConfig();
    }
@@ -36,12 +64,10 @@ class PrivilegeController extends Controller

        foreach ($rules as $index => $rule) {
            if (isset($rule['privileges'])) {
                $rolesCount    = 0;
                $privileges    = (array)$rule['privileges'];
                $rule['roles'] = isset($rule['roles']) ? (array)$rule['roles'] : [];
                foreach ($pr as $privilege => $roles) {
                    if (in_array($privilege, $privileges)) {
                        $rolesCount += count($roles);
                        $rule['roles'] = array_unique(array_merge($rule['roles'], $roles));
                    }
                }
@@ -57,6 +83,68 @@ class PrivilegeController extends Controller
        return $rules;
    }

    protected function perimetresToRoles(array $rules): array
    {
        /** @var UserInterface $user */
        $user = $this->serviceUserContext->getDbUser();
        // liste des périmètres et des rôles associés
        $perimetresRoles = $this->getPerimetreProvider()->getPerimetresRoles();

        // parcours de la liste des règles "bjyauthorize/guards"
        foreach ($rules as $index => $rule) {
            if(isset($rule['perimetres']) && $user) {
                $rulePerimetres = (array)$rule['perimetres'];
                // parcours de toutes les périmètres (catégories)
                foreach ($perimetresRoles as $perimetreCateg => $pr) {
                    // vérification que le périmètre est appelé dans la règle
                    if(in_array($perimetreCateg, $rulePerimetres)) {
                        // parcours des rôles autorisés pour la règle
                        foreach($rule['roles'] as $role) {
                            // vérification que le rôle fait partie des rôles associés au périmètre
                            if(in_array($role, $pr)) {
                                // récupération des droits de l'utilisateur pour le rôle et le périmètre
                                $userExactRole = $user->getFullRoles()->filter(function ($r) use ($role, $perimetreCateg) {
                                    /** @var UserRoleInterface $r */
                                    $pc = $r->getPerimetreCategorie();
                                    return $r->getRole()->getRoleId() === $role && ($pc ? $pc->getCode() : null) === $perimetreCateg;
                                });
                                // si une règle existe
                                if($userExactRole->count() === 1) {
                                    $ur = $userExactRole->current();
                                    /** vérification que le périmètre a un service implémentant @see PerimetreRuleInterface associé dans la config */
                                    if(array_key_exists($perimetreCateg, $this->perimetresRules)) {
                                        /** @var PerimetreRuleInterface $service */
                                        $service = $this->perimetresRules[$perimetreCateg];
                                        // vérification de la règle
                                        if($service->checkRule($ur->getPerimetre())) {
                                            continue;
                                        }
                                    }
                                    else {
                                        throw new RuntimeException(sprintf('Le périmètre "%s" n\'a aucun service associé défini dans la configuration : "unicaen-perimetre" > "rule_providers"', $perimetreCateg));
                                    }
                                }

                                // suppression du rôle des rôles autorisés pour cette règle
                                $key = array_search($role, $rule['roles']);
                                unset($rule['roles'][$key]);
                            }
                        }
                    }
                }

                unset($rule['perimetres']);
                if (0 < count($rule['roles'])) {
                    $rules[$index] = $rule;
                } else {
                    unset($rules[$index]);
                }
            }
        }

        return $rules;
    }

    public static function getResourceId($controller, $action = null) : string
    {
        if (isset($action)) {
+26 −1
Original line number Diff line number Diff line
@@ -3,20 +3,45 @@
namespace UnicaenPrivilege\Guard;

use Interop\Container\ContainerInterface;
use UnicaenAuthentification\Service\UserContext;
use UnicaenPrivilege\Provider\Perimetre\PerimetreProviderInterface;
use UnicaenPrivilege\Provider\Perimetre\Rules\PerimetreRuleInterface;
use UnicaenPrivilege\Provider\Privilege\PrivilegeProviderInterface;
use UnicaenPrivilege\Service\Perimetre\PerimetreCategorieService;
use UnicaenPrivilege\Service\Privilege\PrivilegeService;

class PrivilegeControllerFactory
{
    public function __invoke(ContainerInterface $container) : PrivilegeController
    {
        /** @var PrivilegeProviderInterface $privilegeService */
        /**
         * @var PrivilegeProviderInterface $privilegeProvider
         * @var PerimetreProviderInterface $perimetreProvider
         * @var UserContext $userContextService
         */
        $config = $container->get('Config');
        $perimetreConfig = $config['unicaen-perimetre'];
        $privilegeProvider = $container->get(PrivilegeService::class);
        $perimetreProvider = $container->get(PerimetreCategorieService::class);
        $userContextService = $container->get(UserContext::class);

        $rules = []; // NB: l'injection des vraies rules est faite par \BjyAuthorize\Service\BaseProvidersServiceFactory

        $perimetresRules = [];
        if(array_key_exists('rule_providers', $perimetreConfig)) {
            foreach($perimetreConfig['rule_providers'] as $key => $provider) {
                $service = $container->get($provider);
                if($service instanceof PerimetreRuleInterface) {
                    $perimetresRules[$key] = $service;
                }
            }
        }

        $instance = new PrivilegeController($rules, $container);
        $instance->setPrivilegeProvider($privilegeProvider);
        $instance->setPerimetreProvider($perimetreProvider);
        $instance->setPerimetresRules($perimetresRules);
        $instance->setServiceUserContext($userContextService);

        return $instance;
    }
Loading