mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-21 23:23:51 +00:00
refactor ACL for easy voter. Apply on TaskVoter
This commit is contained in:
@@ -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
|
||||
{
|
||||
}
|
Reference in New Issue
Block a user