Loading config/unicaen-perimetre.global.php.dist +8 −3 Original line number Diff line number Diff line Loading @@ -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, ], ], Loading src/UnicaenPrivilege/Assertion/AbstractAssertion.php +16 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,11 @@ abstract class AbstractAssertion implements AssertionInterface */ private $mvcEvent; /** * @var bool */ protected $isPerimetreAssertion = false; /** Loading Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -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() { Loading src/UnicaenPrivilege/Assertion/Perimetre/AbstractPerimetreAssertion.php 0 → 100644 +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 src/UnicaenPrivilege/Assertion/Perimetre/AbstractPerimetreAssertionFactory.php 0 → 100644 +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 src/UnicaenPrivilege/Assertion/Perimetre/StructurePerimetreAssertion.php 0 → 100644 +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
config/unicaen-perimetre.global.php.dist +8 −3 Original line number Diff line number Diff line Loading @@ -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, ], ], Loading
src/UnicaenPrivilege/Assertion/AbstractAssertion.php +16 −4 Original line number Diff line number Diff line Loading @@ -36,6 +36,11 @@ abstract class AbstractAssertion implements AssertionInterface */ private $mvcEvent; /** * @var bool */ protected $isPerimetreAssertion = false; /** Loading Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -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() { Loading
src/UnicaenPrivilege/Assertion/Perimetre/AbstractPerimetreAssertion.php 0 → 100644 +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
src/UnicaenPrivilege/Assertion/Perimetre/AbstractPerimetreAssertionFactory.php 0 → 100644 +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
src/UnicaenPrivilege/Assertion/Perimetre/StructurePerimetreAssertion.php 0 → 100644 +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