mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-21 15:13:50 +00:00
Merge remote-tracking branch 'origin/master' into doc/authorizaton-documentation-update
This commit is contained in:
@@ -1,34 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* Voter for Chill software.
|
||||
*
|
||||
* This abstract Voter provide generic methods to handle object specific to Chill
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
{
|
||||
@@ -39,6 +22,8 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
. 'getSupportedAttributes and getSupportedClasses methods.',
|
||||
E_USER_DEPRECATED);
|
||||
|
||||
// @TODO: getSupportedAttributes() should be created in here and made abstract or in ChillVoterInterface.
|
||||
// @TODO: getSupportedClasses() should be created in here and made abstract or in ChillVoterInterface.
|
||||
return \in_array($attribute, $this->getSupportedAttributes($attribute))
|
||||
&& \in_array(\get_class($subject), $this->getSupportedClasses());
|
||||
}
|
||||
@@ -49,7 +34,7 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
. 'methods introduced by Symfony 3.0, and do not rely on '
|
||||
. 'isGranted method', E_USER_DEPRECATED);
|
||||
|
||||
// @TODO: isGranted() should be created in here and made abstract or in ChillVoterInterface.
|
||||
return $this->isGranted($attribute, $subject, $token->getUser());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,21 +1,6 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
@@ -38,12 +23,12 @@ use Chill\MainBundle\Security\RoleProvider;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Chill\MainBundle\Entity\GroupCenter;
|
||||
use Chill\MainBundle\Entity\RoleScope;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* Helper for authorizations.
|
||||
*
|
||||
* Provides methods for user and entities information.
|
||||
*
|
||||
*/
|
||||
class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
{
|
||||
@@ -74,11 +59,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
/**
|
||||
* Determines if a user is active on this center
|
||||
*
|
||||
* If
|
||||
*
|
||||
* @param User $user
|
||||
* @param Center|Center[] $center May be an array of center
|
||||
* @return bool
|
||||
*/
|
||||
public function userCanReachCenter(User $user, $center): bool
|
||||
{
|
||||
@@ -89,7 +70,9 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} elseif ($center instanceof Center) {
|
||||
}
|
||||
|
||||
if ($center instanceof Center) {
|
||||
foreach ($user->getGroupCenters() as $groupCenter) {
|
||||
if ($center->getId() === $groupCenter->getCenter()->getId()) {
|
||||
return true;
|
||||
@@ -99,12 +82,16 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException(sprintf("The entity given is not an ".
|
||||
"instance of %s, %s given", Center::class, get_class($center)));
|
||||
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,
|
||||
@@ -159,19 +146,21 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
if ($this->scopeResolverDispatcher->isConcerned($entity)) {
|
||||
$scope = $this->scopeResolverDispatcher->resolveScope($entity);
|
||||
|
||||
if (NULL === $scope) {
|
||||
return true;
|
||||
} elseif (is_iterable($scope)) {
|
||||
foreach ($scope as $s) {
|
||||
if ($s === $roleScope->getScope()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($scope === $roleScope->getScope()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (NULL === $scope) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_iterable($scope)) {
|
||||
foreach ($scope as $s) {
|
||||
if ($s === $roleScope->getScope()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($scope === $roleScope->getScope()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@@ -190,14 +179,11 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
|
||||
/**
|
||||
* Get reachable Centers for the given user, role,
|
||||
* and optionnaly Scope
|
||||
* and optionally Scope
|
||||
*
|
||||
* @param User $user
|
||||
* @param string|Role $role
|
||||
* @param null|Scope $scope
|
||||
* @return Center[]|array
|
||||
*/
|
||||
public function getReachableCenters(User $user, string $role, ?Scope $scope = null): array
|
||||
public function getReachableCenters(UserInterface $user, string $role, ?Scope $scope = null): array
|
||||
{
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
@@ -213,11 +199,11 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
if ($scope === null) {
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
break 1;
|
||||
} else {
|
||||
if ($scope->getId() == $roleScope->getScope()->getId()){
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
break 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope->getId() == $roleScope->getScope()->getId()){
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
break 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,7 +229,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
}
|
||||
|
||||
foreach ($centers as $center) {
|
||||
if ($this->userCanReachCenter($user, $center, $role)) {
|
||||
if ($this->userCanReachCenter($user, $center)) {
|
||||
$results[] = $center;
|
||||
}
|
||||
}
|
||||
@@ -256,12 +242,10 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
*
|
||||
* @deprecated Use getReachableCircles
|
||||
*
|
||||
* @param User $user
|
||||
* @param string role
|
||||
* @param Center|Center[] $center
|
||||
* @return Scope[]|array
|
||||
*/
|
||||
public function getReachableScopes(User $user, string $role, $center): array
|
||||
public function getReachableScopes(UserInterface $user, string $role, $center): array
|
||||
{
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
@@ -273,12 +257,11 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
/**
|
||||
* Return all reachable circle for a given user, center and role
|
||||
*
|
||||
* @param User $user
|
||||
* @param string|Role $role
|
||||
* @param Center|Center[] $center
|
||||
* @return Scope[]
|
||||
*/
|
||||
public function getReachableCircles(User $user, $role, $center)
|
||||
public function getReachableCircles(UserInterface $user, $role, $center)
|
||||
{
|
||||
$scopes = [];
|
||||
|
||||
|
@@ -4,29 +4,23 @@ namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
interface AuthorizationHelperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Get reachable Centers for the given user, role,
|
||||
* and optionnaly Scope
|
||||
*
|
||||
* @param User $user
|
||||
* @param string|Role $role
|
||||
* @param null|Scope $scope
|
||||
* @return Center[]
|
||||
*/
|
||||
public function getReachableCenters(User $user, string $role, ?Scope $scope = null): array;
|
||||
public function getReachableCenters(UserInterface $user, string $role, ?Scope $scope = null): array;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $role
|
||||
* @param Center|Center[]|array $center
|
||||
* @return array
|
||||
*/
|
||||
public function getReachableScopes(User $user, string $role, $center): array;
|
||||
public function getReachableScopes(UserInterface $user, string $role, $center): array;
|
||||
|
||||
}
|
||||
|
@@ -1,48 +1,24 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class ChillExportVoter extends Voter
|
||||
{
|
||||
const EXPORT = 'chill_export';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
public function __construct(AuthorizationHelper $authorizationHelper)
|
||||
public const EXPORT = 'chill_export';
|
||||
|
||||
protected AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
public function __construct(AuthorizationHelperInterface $authorizationHelper)
|
||||
{
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
{
|
||||
return $attribute === self::EXPORT;
|
||||
@@ -53,10 +29,7 @@ class ChillExportVoter extends Voter
|
||||
if (!$token->getUser() instanceof User) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($token->getUser(), new Role($attribute));
|
||||
|
||||
return count($centers) > 0;
|
||||
|
||||
return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute);
|
||||
}
|
||||
}
|
||||
|
@@ -98,5 +98,7 @@ class PasswordRecoverVoter extends Voter
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,27 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
class CenterResolverDispatcher
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
/**
|
||||
* @deprecated Use CenterResolverManager and its interface CenterResolverManagerInterface
|
||||
*/
|
||||
final class CenterResolverDispatcher
|
||||
{
|
||||
/**
|
||||
* @var iterabble|CenterResolverInterface[]
|
||||
* @var CenterResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers = [];
|
||||
private iterable $resolvers;
|
||||
|
||||
public function __construct(iterable $resolvers)
|
||||
public function __construct(iterable $resolvers = [])
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
* @param array|null $options
|
||||
* @param object $entity
|
||||
* @return null|Center|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
foreach($this->resolvers as $priority => $resolver) {
|
||||
trigger_deprecation(
|
||||
'ChillMainBundle',
|
||||
'dev-master',
|
||||
'
|
||||
Use the service CenterResolverManager through the interface CenterResolverManagerInterface.
|
||||
The new method "CenterResolverManagerInterface::resolveCenters(): array" is available and the typing
|
||||
has been improved in order to avoid mixing types.
|
||||
'
|
||||
);
|
||||
|
||||
foreach($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return $resolver->resolveCenter($entity, $options);
|
||||
}
|
||||
|
@@ -6,12 +6,14 @@ use Chill\MainBundle\Entity\Center;
|
||||
|
||||
interface CenterResolverInterface
|
||||
{
|
||||
/**
|
||||
* @param object $entity
|
||||
*/
|
||||
public function supports($entity, ?array $options = []): bool;
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @param array|null $options
|
||||
* @return Center|array|Center[]
|
||||
* @param object $entity
|
||||
* @return Center|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = []);
|
||||
|
||||
|
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
final class CenterResolverManager implements CenterResolverManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var CenterResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers;
|
||||
|
||||
public function __construct(iterable $resolvers = [])
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
public function resolveCenters($entity, ?array $options = []): array
|
||||
{
|
||||
foreach($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return (array) $resolver->resolveCenter($entity, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
interface CenterResolverManagerInterface
|
||||
{
|
||||
/**
|
||||
* @param object $entity
|
||||
* @return Center[]
|
||||
*/
|
||||
public function resolveCenters($entity, ?array $options = []): array;
|
||||
}
|
@@ -6,10 +6,10 @@ use Twig\TwigFilter;
|
||||
|
||||
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||
{
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
private ScopeResolverInterface $scopeResolverDispatcher;
|
||||
private CenterResolverManagerInterface $centerResolverDispatcher;
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
public function __construct(CenterResolverDispatcher $centerResolverDispatcher, ScopeResolverInterface $scopeResolverDispatcher)
|
||||
public function __construct(CenterResolverManagerInterface $centerResolverDispatcher, ScopeResolverDispatcher $scopeResolverDispatcher)
|
||||
{
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||
@@ -31,7 +31,7 @@ final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
return $this->centerResolverDispatcher->resolveCenter($entity, $options);
|
||||
return $this->centerResolverDispatcher->resolveCenters($entity, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,22 +1,10 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\UserProvider;
|
||||
|
||||
use Doctrine\ORM\NoResultException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -24,25 +12,15 @@ use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class UserProvider implements UserProviderInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
|
||||
public function loadUserByUsername($username): UserInterface
|
||||
{
|
||||
try {
|
||||
@@ -50,14 +28,18 @@ class UserProvider implements UserProviderInterface
|
||||
"SELECT u FROM %s u "
|
||||
. "WHERE u.usernameCanonical = UNACCENT(LOWER(:pattern)) "
|
||||
. "OR "
|
||||
. "u.emailCanonical = UNACCENT(LOWER(:pattern))",
|
||||
. "u.emailCanonical = UNACCENT(LOWER(:pattern))",
|
||||
User::class))
|
||||
->setParameter('pattern', $username)
|
||||
->getSingleResult();
|
||||
} catch (\Doctrine\ORM\NoResultException $e) {
|
||||
throw new UsernameNotFoundException(sprintf('Bad credentials.', $username));
|
||||
} catch (NoResultException $e) {
|
||||
throw new UsernameNotFoundException(
|
||||
sprintf('Bad credentials.'),
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
@@ -66,13 +48,13 @@ class UserProvider implements UserProviderInterface
|
||||
if (!$user instanceof User) {
|
||||
throw new UnsupportedUserException("Unsupported user class: cannot reload this user");
|
||||
}
|
||||
|
||||
|
||||
$reloadedUser = $this->em->getRepository(User::class)->find($user->getId());
|
||||
|
||||
|
||||
if (NULL === $reloadedUser) {
|
||||
throw new UsernameNotFoundException(sprintf('User with ID "%s" could not be reloaded.', $user->getId()));
|
||||
}
|
||||
|
||||
|
||||
return $reloadedUser;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user