mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 01:08:26 +00:00 
			
		
		
		
	refactor ACL for easy voter. Apply on TaskVoter
This commit is contained in:
		| @@ -23,6 +23,7 @@ use Chill\MainBundle\Security\Authorization\AbstractChillVoter; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; | ||||
| use Chill\DocStoreBundle\Entity\PersonDocument; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\PersonBundle\Security\Authorization\PersonVoter; | ||||
| @@ -42,30 +43,25 @@ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHiera | ||||
|     const UPDATE = 'CHILL_PERSON_DOCUMENT_UPDATE'; | ||||
|     const DELETE = 'CHILL_PERSON_DOCUMENT_DELETE'; | ||||
|  | ||||
|     /** | ||||
|      * @var AuthorizationHelper | ||||
|      */ | ||||
|     protected $authorizationHelper; | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|  | ||||
|     /** | ||||
|      * @var AccessDecisionManagerInterface | ||||
|      */ | ||||
|     protected $accessDecisionManager; | ||||
|     protected AccessDecisionManagerInterface $accessDecisionManager; | ||||
|  | ||||
|     /** | ||||
|      * @var LoggerInterface | ||||
|      */ | ||||
|     protected $logger; | ||||
|     protected LoggerInterface $logger; | ||||
|  | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     public function __construct( | ||||
|         AccessDecisionManagerInterface $accessDecisionManager, | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         LoggerInterface $logger | ||||
|         LoggerInterface $logger//, | ||||
|         //CenterResolverDispatcher $centerResolverDispatcher | ||||
|     ) | ||||
|     { | ||||
|         $this->accessDecisionManager = $accessDecisionManager; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->logger = $logger; | ||||
|         //$this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|     } | ||||
|  | ||||
|     public function getRoles() | ||||
| @@ -78,17 +74,18 @@ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHiera | ||||
|             self::DELETE | ||||
|         ]; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     protected function supports($attribute, $subject) | ||||
|     { | ||||
|         if (\in_array($attribute, $this->getRoles()) && $subject instanceof PersonDocument) { | ||||
|             return true; | ||||
|         } | ||||
|          | ||||
|         if ($subject instanceof Person && $attribute === self::CREATE) { | ||||
|  | ||||
|         if ($subject instanceof Person | ||||
|             && \in_array($attribute, [self::CREATE, self::SEE])) { | ||||
|             return true; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @@ -107,6 +104,8 @@ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHiera | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $center = $this->centerResolverDispatcher->resolveCenter($subject); | ||||
|  | ||||
|         if ($subject instanceof PersonDocument) { | ||||
|             return $this->authorizationHelper->userHasAccess($token->getUser(), $subject, $attribute); | ||||
|  | ||||
|   | ||||
							
								
								
									
										36
									
								
								src/Bundle/ChillMainBundle/Form/Event/CustomizeFormEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/Bundle/ChillMainBundle/Form/Event/CustomizeFormEvent.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Form\Event; | ||||
|  | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
|  | ||||
| class CustomizeFormEvent extends \Symfony\Component\EventDispatcher\Event | ||||
| { | ||||
|     const NAME = 'chill_main.customize_form'; | ||||
|  | ||||
|     protected string $type; | ||||
|  | ||||
|     protected FormBuilderInterface $builder; | ||||
|  | ||||
|     public function __construct(string $type, FormBuilderInterface $builder) | ||||
|     { | ||||
|         $this->type = $type; | ||||
|         $this->builder = $builder; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getType(): string | ||||
|     { | ||||
|         return $this->type; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return FormBuilderInterface | ||||
|      */ | ||||
|     public function getBuilder(): FormBuilderInterface | ||||
|     { | ||||
|         return $this->builder; | ||||
|     } | ||||
| } | ||||
| @@ -23,8 +23,8 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter; | ||||
| use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | ||||
|  | ||||
| /** | ||||
|  * Voter for Chill software.  | ||||
|  *  | ||||
|  * Voter for Chill software. | ||||
|  * | ||||
|  * This abstract Voter provide generic methods to handle object specific to Chill | ||||
|  * | ||||
|  * | ||||
| @@ -36,20 +36,20 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface | ||||
|     { | ||||
|         @trigger_error('This voter should implements the new `supports` ' | ||||
|             . 'methods introduced by Symfony 3.0, and do not rely on ' | ||||
|             . 'getSupportedAttributes and getSupportedClasses methods.',  | ||||
|             . 'getSupportedAttributes and getSupportedClasses methods.', | ||||
|             E_USER_DEPRECATED); | ||||
|  | ||||
|         return \in_array($attribute, $this->getSupportedAttributes($attribute)) | ||||
|             && \in_array(\get_class($subject), $this->getSupportedClasses()); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     protected function voteOnAttribute($attribute, $subject, TokenInterface $token) | ||||
|     { | ||||
|         @trigger_error('This voter should implements the new `voteOnAttribute` ' | ||||
|             . 'methods introduced by Symfony 3.0, and do not rely on ' | ||||
|             . 'isGranted method', E_USER_DEPRECATED); | ||||
|          | ||||
|  | ||||
|         return $this->isGranted($attribute, $subject, $token->getUser()); | ||||
|     } | ||||
|      | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,8 @@ use Chill\MainBundle\Entity\User; | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Chill\MainBundle\Entity\HasCenterInterface; | ||||
| use Chill\MainBundle\Entity\HasScopeInterface; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
| use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | ||||
| use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; | ||||
| use Symfony\Component\Security\Core\Role\Role; | ||||
| use Chill\MainBundle\Entity\Scope; | ||||
| @@ -32,87 +34,97 @@ use Chill\MainBundle\Entity\GroupCenter; | ||||
| use Chill\MainBundle\Entity\RoleScope; | ||||
|  | ||||
| /** | ||||
|  * Helper for authorizations.  | ||||
|  *  | ||||
|  * Helper for authorizations. | ||||
|  * | ||||
|  * Provides methods for user and entities information. | ||||
|  * | ||||
|  * @author Julien Fastré <julien.fastre@champs-libres.coop> | ||||
|  */ | ||||
| class AuthorizationHelper | ||||
| { | ||||
|     protected RoleHierarchyInterface $roleHierarchy; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var RoleHierarchyInterface | ||||
|      */ | ||||
|     protected $roleHierarchy; | ||||
|      | ||||
|     /** | ||||
|      * The role in a hierarchy, given by the parameter  | ||||
|      * The role in a hierarchy, given by the parameter | ||||
|      * `security.role_hierarchy.roles` from the container. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     protected $hierarchy; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var EntityManagerInterface | ||||
|      */ | ||||
|     protected $em; | ||||
|      | ||||
|     protected array $hierarchy; | ||||
|  | ||||
|     protected EntityManagerInterface $em; | ||||
|  | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     public function __construct( | ||||
|         RoleHierarchyInterface $roleHierarchy, | ||||
|         $hierarchy, | ||||
|         EntityManagerInterface $em | ||||
|         ParameterBagInterface $parameterBag, | ||||
|         EntityManagerInterface $em, | ||||
|         CenterResolverDispatcher $centerResolverDispatcher | ||||
|     ) { | ||||
|         $this->roleHierarchy = $roleHierarchy; | ||||
|         $this->hierarchy     = $hierarchy; | ||||
|         $this->hierarchy     = $parameterBag->get('security.role_hierarchy.roles'); | ||||
|         $this->em = $em; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Determines if a user is active on this center | ||||
|      *  | ||||
|      * | ||||
|      * If | ||||
|      * | ||||
|      * @param User $user | ||||
|      * @param Center $center | ||||
|      * @param Center|Center[] $center May be an array of center | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function userCanReachCenter(User $user, Center $center) | ||||
|     public function userCanReachCenter(User $user, $center) | ||||
|     { | ||||
|         foreach ($user->getGroupCenters() as $groupCenter) { | ||||
|             if ($center->getId() === $groupCenter->getCenter()->getId()) { | ||||
|                  | ||||
|                 return true; | ||||
|         if ($center instanceof \Traversable) { | ||||
|             foreach ($center as $c) { | ||||
|                 if ($c->userCanReachCenter($user, $c)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         } elseif ($center instanceof Center) { | ||||
|             foreach ($user->getGroupCenters() as $groupCenter) { | ||||
|                 if ($center->getId() === $groupCenter->getCenter()->getId()) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|         return false; | ||||
|  | ||||
|         throw new \UnexpectedValueException(sprintf("The entity given is not an ". | ||||
|             "instance of %s, %s given", Center::class, get_class($center))); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      *  | ||||
|      * | ||||
|      * Determines if the user has access to the given entity. | ||||
|      *  | ||||
|      * | ||||
|      * if the entity implements Chill\MainBundle\Entity\HasScopeInterface, | ||||
|      * the scope is taken into account. | ||||
|      *  | ||||
|      * | ||||
|      * @param User $user | ||||
|      * @param HasCenterInterface $entity the entity may also implement HasScopeInterface | ||||
|      * @param mixed $entity the entity may also implement HasScopeInterface | ||||
|      * @param string|Role $attribute | ||||
|      * @return boolean true if the user has access | ||||
|      */ | ||||
|     public function userHasAccess(User $user, HasCenterInterface $entity, $attribute) | ||||
|     public function userHasAccess(User $user, $entity, $attribute) | ||||
|     { | ||||
|          | ||||
|         $center = $entity->getCenter(); | ||||
|          | ||||
|         if (NULL === $center = $this->centerResolverDispatcher->resolveCenter($entity)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!$this->userCanReachCenter($user, $center)) { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         foreach ($user->getGroupCenters() as $groupCenter){ | ||||
|             //filter on center | ||||
|             if ($groupCenter->getCenter()->getId() === $entity->getCenter()->getId()) { | ||||
|             if ($groupCenter->getCenter() === $center) { | ||||
|                 $permissionGroup = $groupCenter->getPermissionsGroup(); | ||||
|                 //iterate on roleScopes | ||||
|                 foreach($permissionGroup->getRoleScopes() as $roleScope) { | ||||
| @@ -134,17 +146,17 @@ class AuthorizationHelper | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|  | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Get reachable Centers for the given user, role, | ||||
|      * and optionnaly Scope | ||||
|      *  | ||||
|      * | ||||
|      * @param User $user | ||||
|      * @param string|Role $role | ||||
|      * @param null|Scope $scope | ||||
| @@ -156,7 +168,7 @@ class AuthorizationHelper | ||||
|             $role = $role->getRole(); | ||||
|         } | ||||
|         $centers = array(); | ||||
|          | ||||
|  | ||||
|         foreach ($user->getGroupCenters() as $groupCenter){ | ||||
|             $permissionGroup = $groupCenter->getPermissionsGroup(); | ||||
|             //iterate on roleScopes | ||||
| @@ -170,13 +182,13 @@ class AuthorizationHelper | ||||
|                         if ($scope->getId() == $roleScope->getScope()->getId()){ | ||||
|                             $centers[] = $groupCenter->getCenter(); | ||||
|                             break 1; | ||||
|                         }       | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|  | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return $centers; | ||||
|     } | ||||
|  | ||||
| @@ -203,10 +215,10 @@ class AuthorizationHelper | ||||
|  | ||||
|         return $results; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Return all reachable scope for a given user, center and role | ||||
|      *  | ||||
|      * | ||||
|      * @deprecated Use getReachableCircles | ||||
|      * | ||||
|      * @param User $user | ||||
| @@ -222,10 +234,10 @@ class AuthorizationHelper | ||||
|  | ||||
|         return $this->getReachableCircles($user, $role, $center); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Return all reachable circle for a given user, center and role | ||||
|      *  | ||||
|      * | ||||
|      * @param User $user | ||||
|      * @param string|Role $role | ||||
|      * @param Center $center | ||||
| @@ -237,7 +249,7 @@ class AuthorizationHelper | ||||
|             $role = $role->getRole(); | ||||
|         } | ||||
|         $scopes = array(); | ||||
|          | ||||
|  | ||||
|         foreach ($user->getGroupCenters() as $groupCenter){ | ||||
|             if ($center->getId() === $groupCenter->getCenter()->getId()) { | ||||
|                 //iterate on permissionGroup | ||||
| @@ -251,12 +263,12 @@ class AuthorizationHelper | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return $scopes; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      *  | ||||
|      * | ||||
|      * @param Role $role | ||||
|      * @param Center $center | ||||
|      * @param Scope $circle | ||||
| @@ -267,7 +279,7 @@ class AuthorizationHelper | ||||
|         $parents = $this->getParentRoles($role); | ||||
|         $parents[] = $role; | ||||
|         $parentRolesString = \array_map(function(Role $r) { return $r->getRole(); }, $parents); | ||||
|          | ||||
|  | ||||
|         $qb = $this->em->createQueryBuilder(); | ||||
|         $qb | ||||
|             ->select('u') | ||||
| @@ -278,21 +290,21 @@ class AuthorizationHelper | ||||
|             ->where('gc.center = :center') | ||||
|             ->andWhere($qb->expr()->in('rs.role', $parentRolesString)) | ||||
|             ; | ||||
|          | ||||
|  | ||||
|         $qb->setParameter('center', $center); | ||||
|          | ||||
|  | ||||
|         if ($circle !== null) { | ||||
|             $qb->andWhere('rs.scope = :circle') | ||||
|                 ->setParameter('circle', $circle) | ||||
|                 ; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return $qb->getQuery()->getResult(); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Test if a parent role may give access to a given child role | ||||
|      *  | ||||
|      * | ||||
|      * @param Role $childRole The role we want to test if he is reachable | ||||
|      * @param Role $parentRole The role which should give access to $childRole | ||||
|      * @return boolean true if the child role is granted by parent role | ||||
| @@ -301,14 +313,14 @@ class AuthorizationHelper | ||||
|     { | ||||
|         $reachableRoles = $this->roleHierarchy | ||||
|                 ->getReachableRoleNames([$parentRole]); | ||||
|          | ||||
|  | ||||
|         return in_array($childRole, $reachableRoles); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Return all the role which give access to the given role. Only the role  | ||||
|      * Return all the role which give access to the given role. Only the role | ||||
|      * which are registered into Chill are taken into account. | ||||
|      *  | ||||
|      * | ||||
|      * @param Role $role | ||||
|      * @return Role[] the role which give access to the given $role | ||||
|      */ | ||||
| @@ -319,18 +331,18 @@ class AuthorizationHelper | ||||
|         $roles = \array_map( | ||||
|                 function($string) { | ||||
|                     return new Role($string); | ||||
|                 },  | ||||
|                 }, | ||||
|                 \array_keys($this->hierarchy) | ||||
|                 ); | ||||
|          | ||||
|  | ||||
|         foreach ($roles as $r) { | ||||
|             $childRoles = $this->roleHierarchy->getReachableRoleNames([$r->getRole()]); | ||||
|              | ||||
|  | ||||
|             if (\in_array($role, $childRoles)) { | ||||
|                 $parentRoles[] = $r; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         return $parentRoles; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,69 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
|  | ||||
| class DefaultVoter implements VoterInterface | ||||
| { | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|  | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     protected array $configuration = []; | ||||
|  | ||||
|     /** | ||||
|      * @param AuthorizationHelper $authorizationHelper | ||||
|      * @param CenterResolverDispatcher $centerResolverDispatcher | ||||
|      * @param array $configuration | ||||
|      */ | ||||
|     public function __construct( | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         CenterResolverDispatcher $centerResolverDispatcher, | ||||
|         array $configuration | ||||
|     ) { | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|         $this->configuration = $configuration; | ||||
|     } | ||||
|  | ||||
|     public function supports($attribute, $subject): bool | ||||
|     { | ||||
|         foreach ($this->configuration as list($attributes, $subj)) { | ||||
|             if ($subj === null) { | ||||
|                 if ($subject === null && \in_array($attribute, $attributes)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } elseif ($subject instanceof $subj) { | ||||
|                 return \in_array($attribute, $attributes); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public function voteOnAttribute($attribute, $subject, $token): bool | ||||
|     { | ||||
|         if (!$token->getUser() instanceof User) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (NULL === $subject) { | ||||
|            if (NULL === $center = $this->centerResolverDispatcher | ||||
|                ->resolveCenter($subject)) { | ||||
|                return false; | ||||
|            } | ||||
|            return $this->authorizationHelper->userCanReachCenter( | ||||
|                $token->getUser(), | ||||
|                $center | ||||
|            ); | ||||
|         } | ||||
|  | ||||
|         return $this->authorizationHelper->userHasAccess( | ||||
|             $token->getUser(), | ||||
|             $subject, | ||||
|             $attribute | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
|  | ||||
| class DefaultVoterFactory implements VoterFactoryInterface | ||||
| { | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     public function __construct( | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         CenterResolverDispatcher $centerResolverDispatcher | ||||
|     ) { | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|     } | ||||
|  | ||||
|     public function generate($context): VoterGeneratorInterface | ||||
|     { | ||||
|         return new VoterGenerator( | ||||
|             $this->authorizationHelper, | ||||
|             $this->centerResolverDispatcher | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| interface VoterFactoryInterface | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
|  | ||||
| final class VoterGenerator implements VoterGeneratorInterface | ||||
| { | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|     protected array $configuration = []; | ||||
|  | ||||
|     public function __construct( | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         CenterResolverDispatcher $centerResolverDispatcher | ||||
|     ) { | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|     } | ||||
|  | ||||
|     public function addCheckFor($subject, $attributes): self | ||||
|     { | ||||
|        $this->configuration[] = [$attributes, $subject]; | ||||
|  | ||||
|        return $this; | ||||
|     } | ||||
|  | ||||
|     public function build(): VoterInterface | ||||
|     { | ||||
|         return new DefaultVoter( | ||||
|             $this->authorizationHelper, | ||||
|             $this->centerResolverDispatcher, | ||||
|             $this->configuration | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| interface VoterGeneratorInterface | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Security\Authorization; | ||||
|  | ||||
| interface VoterInterface | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Chill\MainBundle\Tests\Security\Resolver; | ||||
|  | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
| use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; | ||||
|  | ||||
| class CenterResolverDispatcherTest extends KernelTestCase | ||||
| { | ||||
|     private CenterResolverDispatcher $dispatcher; | ||||
|  | ||||
|     protected function setUp() | ||||
|     { | ||||
|         self::bootKernel(); | ||||
|         $this->dispatcher = self::$container->get(CenterResolverDispatcher::class); | ||||
|     } | ||||
|  | ||||
|     public function testResolveCenter() | ||||
|     { | ||||
|        $center = new Center(); | ||||
|  | ||||
|        $resolved = $this->dispatcher->resolveCenter($center); | ||||
|  | ||||
|        $this->assertSame($center, $resolved); | ||||
|     } | ||||
| } | ||||
| @@ -3,12 +3,38 @@ services: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     Chill\MainBundle\Security\Resolver\CenterResolverDispatcher: | ||||
|         arguments: | ||||
|             - !tagged_iterator chill_main.center_resolver | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     _instanceof: | ||||
|         Chill\MainBundle\Security\Resolver\CenterResolverInterface: | ||||
|             tags: | ||||
|                 - chill_main.center_resolver | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     Chill\MainBundle\Security\Resolver\DefaultCenterResolver: | ||||
|         autoconfigure: true | ||||
|         autowire: true | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     Chill\MainBundle\Security\Resolver\ResolverTwigExtension: | ||||
|         autoconfigure: true | ||||
|         autowire: true | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     Chill\MainBundle\Security\Authorization\DefaultVoterFactory: | ||||
|         autowire: true | ||||
|  | ||||
|     # do not autowire the directory Security/Resolver | ||||
|     Chill\MainBundle\Security\Authorization\VoterFactoryInterface: '@Chill\MainBundle\Security\Authorization\DefaultVoterFactory' | ||||
|  | ||||
|     chill.main.security.authorization.helper: | ||||
|         class: Chill\MainBundle\Security\Authorization\AuthorizationHelper | ||||
|         arguments: | ||||
|             $roleHierarchy: "@security.role_hierarchy" | ||||
|             $hierarchy: "%security.role_hierarchy.roles%" | ||||
|             $em: '@Doctrine\ORM\EntityManagerInterface' | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|     Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper' | ||||
|  | ||||
|     chill.main.role_provider: | ||||
|   | ||||
| @@ -18,7 +18,12 @@ | ||||
|  | ||||
| namespace Chill\TaskBundle\Security\Authorization; | ||||
|  | ||||
| use Chill\EventBundle\Entity\Event; | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Chill\MainBundle\Security\Authorization\AbstractChillVoter; | ||||
| use Chill\MainBundle\Security\Authorization\VoterFactoryInterface; | ||||
| use Chill\MainBundle\Security\Authorization\VoterInterface; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
| use Chill\TaskBundle\Entity\AbstractTask; | ||||
| use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| @@ -32,12 +37,7 @@ use Symfony\Component\Security\Core\Role\Role; | ||||
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||||
| use Chill\TaskBundle\Security\Authorization\AuthorizationEvent; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * | ||||
|  * @author Julien Fastré <julien.fastre@champs-libres.coop> | ||||
|  */ | ||||
| class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface | ||||
| final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface | ||||
| { | ||||
|     const CREATE = 'CHILL_TASK_TASK_CREATE'; | ||||
|     const UPDATE = 'CHILL_TASK_TASK_UPDATE'; | ||||
| @@ -51,50 +51,52 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf | ||||
|         self::DELETE | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var AuthorizationHelper | ||||
|      */ | ||||
|     protected $authorizationHelper; | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var AccessDecisionManagerInterface | ||||
|      */ | ||||
|     protected $accessDecisionManager; | ||||
|     protected AccessDecisionManagerInterface $accessDecisionManager; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var LoggerInterface | ||||
|      */ | ||||
|     protected $logger; | ||||
|      | ||||
|     /** | ||||
|      * | ||||
|      * @var EventDispatcherInterface | ||||
|      */ | ||||
|     protected $eventDispatcher; | ||||
|     protected LoggerInterface $logger; | ||||
|  | ||||
|     protected EventDispatcherInterface $eventDispatcher; | ||||
|  | ||||
|     protected CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     protected VoterInterface $voter; | ||||
|  | ||||
|     public function __construct( | ||||
|         AccessDecisionManagerInterface $accessDecisionManager, | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         EventDispatcherInterface $eventDispatcher, | ||||
|         LoggerInterface $logger | ||||
|         LoggerInterface $logger, | ||||
|         CenterResolverDispatcher $centerResolverDispatcher, | ||||
|         VoterFactoryInterface $voterFactory | ||||
|     ) { | ||||
|         $this->accessDecisionManager = $accessDecisionManager; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->eventDispatcher = $eventDispatcher; | ||||
|         $this->logger = $logger; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|  | ||||
|         $this->voter = $voterFactory | ||||
|             ->generate(AbstractTask::class) | ||||
|             ->addCheckFor(AbstractTask::class, self::ROLES) | ||||
|             ->addCheckFor(Person::class, [self::SHOW]) | ||||
|             ->addCheckFor(null, [self::SHOW]) | ||||
|             ->build() | ||||
|             ; | ||||
|     } | ||||
|  | ||||
|     public function supports($attribute, $subject) | ||||
|     { | ||||
|         return $this->voter->supports($attribute, $subject); | ||||
|        /* | ||||
|         return ($subject instanceof AbstractTask && in_array($attribute, self::ROLES)) | ||||
|             || | ||||
|             ($subject instanceof Person && \in_array($attribute, [ self::CREATE, self::SHOW ])) | ||||
|             || | ||||
|             (NULL === $subject && $attribute === self::SHOW ) | ||||
|             ; | ||||
|        */ | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -111,40 +113,60 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf | ||||
|         if (!$token->getUser() instanceof User) { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         $event = new AuthorizationEvent($subject, $attribute, $token); | ||||
|          | ||||
|  | ||||
|         $this->eventDispatcher->dispatch(AuthorizationEvent::VOTE, $event); | ||||
|          | ||||
|  | ||||
|         if ($event->hasVote()) { | ||||
|              | ||||
|  | ||||
|             $this->logger->debug("The TaskVoter is overriding by " | ||||
|                 .AuthorizationEvent::VOTE, [ | ||||
|                     'vote' => $event->getVote(), | ||||
|                     'task_id' => $subject->getId() | ||||
|                 ]); | ||||
|              | ||||
|  | ||||
|             return $event->getVote(); | ||||
|         } | ||||
|  | ||||
|         // do pre-flight check, relying on other decision manager | ||||
|         // those pre-flight check concern associated entities | ||||
|         if ($subject instanceof AbstractTask) { | ||||
|             if (NULL !== $person = $subject->getPerson()) { | ||||
|                 if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } elseif (false) { | ||||
|                 // here will come the test if the task is associated to an accompanying course | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // do regular check. | ||||
|         return $this->voter->voteOnAttribute($attribute, $subject, $token); | ||||
|  | ||||
|        /* | ||||
|         if ($subject instanceof AbstractTask) { | ||||
|             if ($subject->getPerson() === null) { | ||||
|                 throw new \LogicException("You should associate a person with task " | ||||
|                     . "in order to check autorizations"); | ||||
|             } | ||||
|              | ||||
|  | ||||
|             $person = $subject->getPerson(); | ||||
|         } elseif ($subject instanceof Person) { | ||||
|             $person = $subject; | ||||
|         } else { | ||||
|             // subject is null. We check  that at least one center is reachable | ||||
|             $centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute)); | ||||
|              | ||||
|  | ||||
|             return count($centers) > 0; | ||||
|         } | ||||
|  | ||||
|         if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { | ||||
|             return false; | ||||
|         } | ||||
|         $center = $this->centerResolverDispatcher->resolveCenter($subject); | ||||
|  | ||||
|         if (NULL === $center) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| @@ -153,6 +175,7 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf | ||||
|             $subject, | ||||
|             $attribute | ||||
|             ); | ||||
|        */ | ||||
|     } | ||||
|  | ||||
|     public function getRoles() | ||||
|   | ||||
| @@ -1,11 +1,7 @@ | ||||
| services: | ||||
|     chill_task.task_voter: | ||||
|         class: Chill\TaskBundle\Security\Authorization\TaskVoter | ||||
|         arguments: | ||||
|             - "@security.access.decision_manager" | ||||
|             - "@chill.main.security.authorization.helper" | ||||
|             - '@Symfony\Component\EventDispatcher\EventDispatcherInterface' | ||||
|             - "@logger" | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|         tags: | ||||
|             - { name: security.voter } | ||||
|             - { name: chill.role } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user