adaptations for acl with tasks

This commit is contained in:
2021-10-26 18:05:06 +02:00
parent bae06fcc9c
commit 965ea528e3
22 changed files with 371 additions and 298 deletions

View File

@@ -90,6 +90,10 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
// replace all config with a main key:
$container->setParameter('chill_main', $config);
// legacy config
$container->setParameter('chill_main.installation_name',
$config['installation_name']);

View File

@@ -16,23 +16,23 @@ use Symfony\Component\HttpFoundation\Request;
*/
class Configuration implements ConfigurationInterface
{
use AddWidgetConfigurationTrait;
/**
*
* @var ContainerBuilder
*/
private $containerBuilder;
public function __construct(array $widgetFactories = array(),
public function __construct(array $widgetFactories = array(),
ContainerBuilder $containerBuilder)
{
$this->setWidgetFactories($widgetFactories);
$this->containerBuilder = $containerBuilder;
}
/**
* {@inheritDoc}
*/
@@ -97,6 +97,14 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end()
->arrayNode('acl')
->addDefaultsIfNotSet()
->children()
->booleanNode('form_show_scopes')
->defaultTrue()
->end()
->end()
->end()
->arrayNode('redis')
->children()
->scalarNode('host')
@@ -247,7 +255,7 @@ class Configuration implements ConfigurationInterface
->end() // end of root
;
return $treeBuilder;
}
}

View File

@@ -23,7 +23,9 @@ use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -36,6 +38,7 @@ use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Security;
/**
* Allow to pick amongst available scope for the current
@@ -46,14 +49,10 @@ use Symfony\Component\Security\Core\Role\Role;
* - `center`: the center of the entity
* - `role` : the role of the user
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ScopePickerType extends AbstractType
{
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
protected AuthorizationHelperInterface $authorizationHelper;
/**
* @var TokenStorageInterface
@@ -70,22 +69,26 @@ class ScopePickerType extends AbstractType
*/
protected $translatableStringHelper;
protected Security $security;
public function __construct(
AuthorizationHelper $authorizationHelper,
AuthorizationHelperInterface $authorizationHelper,
TokenStorageInterface $tokenStorage,
ScopeRepository $scopeRepository,
Security $security,
TranslatableStringHelper $translatableStringHelper
) {
$this->authorizationHelper = $authorizationHelper;
$this->tokenStorage = $tokenStorage;
$this->scopeRepository = $scopeRepository;
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$query = $this->buildAccessibleScopeQuery($options['center'], $options['role']);
$items = $query->getQuery()->execute();
$items = $this->authorizationHelper->getReachableScopes($this->security->getUser(),
$options['role'], $options['center']);
if (1 !== count($items)) {
$builder->add('scope', EntityType::class, [
@@ -94,9 +97,7 @@ class ScopePickerType extends AbstractType
'choice_label' => function (Scope $c) {
return $this->translatableStringHelper->localize($c->getName());
},
'query_builder' => function () use ($options) {
return $this->buildAccessibleScopeQuery($options['center'], $options['role']);
},
'choices' => $items,
]);
$builder->setDataMapper(new ScopePickerDataMapper());
} else {
@@ -121,19 +122,22 @@ class ScopePickerType extends AbstractType
$resolver
// create `center` option
->setRequired('center')
->setAllowedTypes('center', [Center::class])
->setAllowedTypes('center', [Center::class, 'array', 'null'])
// create ``role` option
->setRequired('role')
->setAllowedTypes('role', ['string', Role::class]);
}
/**
* @param Center|array|Center[] $center
* @param string $role
* @return \Doctrine\ORM\QueryBuilder
*/
protected function buildAccessibleScopeQuery(Center $center, Role $role)
protected function buildAccessibleScopeQuery($center, $role)
{
$roles = $this->authorizationHelper->getParentRoles($role);
$roles[] = $role;
$centers = $center instanceof Center ? [$center]: $center;
$qb = $this->scopeRepository->createQueryBuilder('s');
$qb
@@ -142,8 +146,8 @@ class ScopePickerType extends AbstractType
->join('rs.permissionsGroups', 'pg')
->join('pg.groupCenters', 'gc')
// add center constraint
->where($qb->expr()->eq('IDENTITY(gc.center)', ':center'))
->setParameter('center', $center->getId())
->where($qb->expr()->in('IDENTITY(gc.center)', ':centers'))
->setParameter('centers', \array_map(fn(Center $c) => $c->getId(), $centers))
// role constraints
->andWhere($qb->expr()->in('rs.role', ':roles'))
->setParameter('roles', $roles)

View File

@@ -17,6 +17,8 @@
*/
namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
use Symfony\Component\Form\AbstractType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Doctrine\ORM\EntityRepository;
@@ -56,14 +58,18 @@ class UserPickerType extends AbstractType
protected UserRepository $userRepository;
protected UserACLAwareRepositoryInterface $userACLAwareRepository;
public function __construct(
AuthorizationHelper $authorizationHelper,
TokenStorageInterface $tokenStorage,
UserRepository $userRepository
UserRepository $userRepository,
UserACLAwareRepositoryInterface $userACLAwareRepository
) {
$this->authorizationHelper = $authorizationHelper;
$this->tokenStorage = $tokenStorage;
$this->userRepository = $userRepository;
$this->userACLAwareRepository = $userACLAwareRepository;
}
@@ -72,7 +78,7 @@ class UserPickerType extends AbstractType
$resolver
// create `center` option
->setRequired('center')
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ])
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class, 'null', 'array' ])
// create ``role` option
->setRequired('role')
->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ])
@@ -86,17 +92,19 @@ class UserPickerType extends AbstractType
->setDefault('choice_label', function(User $u) {
return $u->getUsername();
})
->setDefault('scope', null)
->setAllowedTypes('scope', [Scope::class, 'array', 'null'])
->setNormalizer('choices', function(Options $options) {
$users = $this->authorizationHelper
->findUsersReaching($options['role'], $options['center']);
$users = $this->userACLAwareRepository
->findUsersByReachedACL($options['role'], $options['center'], $options['scope'], true);
if (NULL !== $options['having_permissions_group_flag']) {
return $this->userRepository
->findUsersHavingFlags($options['having_permissions_group_flag'], $users)
;
}
return $users;
})
;

View File

@@ -0,0 +1,54 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
class UserACLAwareRepository implements UserACLAwareRepositoryInterface
{
private AuthorizationHelper $authorizationHelper;
public function findUsersByReachedACL(string $role, $center, $scope = null, bool $onlyEnabled = true): array
{
$parents = $this->authorizationHelper->getParentRoles($role);
$parents[] = $role;
$qb = $this->em->createQueryBuilder();
$qb
->select('u')
->from(User::class, 'u')
->join('u.groupCenters', 'gc')
->join('gc.permissionsGroup', 'pg')
->join('pg.roleScopes', 'rs')
->where($qb->expr()->in('rs.role', $parents))
;
if ($onlyEnabled) {
$qb->andWhere($qb->expr()->eq('u.enabled', "'TRUE'"));
}
if (NULL !== $center) {
$centers = $center instanceof Center ? [$center] : $center;
$qb
->andWhere($qb->expr()->in('gc.center', ':centers'))
->setParameter('centers', $centers)
;
}
if (NULL !== $scope) {
$scopes = $scope instanceof Scope ? [$scope] : $scope;
$qb
->andWhere($qb->expr()->in('rs.scope', ':scopes'))
->setParameter('scopes', $scopes)
;
}
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
interface UserACLAwareRepositoryInterface
{
/**
* Find the users reaching the given center and scope, for the given role
*
* @param string $role
* @param Center|Center[]|array $center
* @param Scope|Scope[]|array|null $scope
* @param bool $onlyActive true if get only active users
*
* @return User[]
*/
public function findUsersByReachedACL(string $role, $center, $scope = null, bool $onlyActive = true): array;
}

View File

@@ -8,7 +8,7 @@
{{ form_start(form) }}
<ul class="record_actions">
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a href="{{ path(cancel_route, cancel_parameters|default( { } ) ) }}" class="btn btn-cancel">
{{ 'Cancel'|trans }}
@@ -18,5 +18,5 @@
{{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-delete" } } ) }}
</li>
</ul>
{{ form_end(form) }}
{{ form_end(form) }}

View File

@@ -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\Repository\UserACLAwareRepository;
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
@@ -62,6 +64,8 @@ class AuthorizationHelper implements AuthorizationHelperInterface
protected LoggerInterface $logger;
private UserACLAwareRepositoryInterface $userACLAwareRepository;
public function __construct(
RoleHierarchyInterface $roleHierarchy,
ParameterBagInterface $parameterBag,
@@ -320,33 +324,15 @@ class AuthorizationHelper implements AuthorizationHelperInterface
/**
*
* @deprecated use UserACLAwareRepositoryInterface::findUsersByReachedACL instead
* @param Center|Center[]|array $center
* @param Scope|Scope[]|array|null $scope
* @param bool $onlyActive true if get only active users
* @return User[]
*/
public function findUsersReaching(string $role, Center $center, Scope $circle = null): array
public function findUsersReaching(string $role, $center, $scope = null, bool $onlyEnabled = true): array
{
$parents = $this->getParentRoles($role);
$parents[] = $role;
$qb = $this->em->createQueryBuilder();
$qb
->select('u')
->from(User::class, 'u')
->join('u.groupCenters', 'gc')
->join('gc.permissionsGroup', 'pg')
->join('pg.roleScopes', 'rs')
->where('gc.center = :center')
->andWhere($qb->expr()->in('rs.role', $parents))
;
$qb->setParameter('center', $center);
if ($circle !== null) {
$qb->andWhere('rs.scope = :circle')
->setParameter('circle', $circle)
;
}
return $qb->getQuery()->getResult();
return $this->userACLAwareRepository->findUsersByReachedACL($role, $center, $scope, $onlyEnabled);
}
/**

View File

@@ -11,6 +11,8 @@ services:
autowire: true
autoconfigure: true
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
Chill\MainBundle\Serializer\Normalizer\:
resource: '../Serializer/Normalizer'
autoconfigure: true