mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'master' into person_renderbox_thirdparty_onthefly
This commit is contained in:
@@ -3,6 +3,11 @@
|
||||
namespace Chill\MainBundle;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Chill\MainBundle\Search\SearchInterface;
|
||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverInterface;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass;
|
||||
@@ -27,6 +32,12 @@ class ChillMainBundle extends Bundle
|
||||
|
||||
$container->registerForAutoconfiguration(LocalMenuBuilderInterface::class)
|
||||
->addTag('chill.menu_builder');
|
||||
$container->registerForAutoconfiguration(ProvideRoleInterface::class)
|
||||
->addTag('chill.role');
|
||||
$container->registerForAutoconfiguration(CenterResolverInterface::class)
|
||||
->addTag('chill_main.center_resolver');
|
||||
$container->registerForAutoconfiguration(ScopeResolverInterface::class)
|
||||
->addTag('chill_main.scope_resolver');
|
||||
|
||||
$container->addCompilerPass(new SearchableServicesCompilerPass());
|
||||
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
|
||||
|
@@ -19,6 +19,10 @@
|
||||
|
||||
namespace Chill\MainBundle\DependencyInjection;
|
||||
|
||||
use Chill\MainBundle\Doctrine\DQL\STContains;
|
||||
use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\UserJobType;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
@@ -183,6 +187,8 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
|
||||
'SIMILARITY' => Similarity::class,
|
||||
'OVERLAPSI' => OverlapsI::class,
|
||||
'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class,
|
||||
'ST_CONTAINS' => STContains::class,
|
||||
],
|
||||
],
|
||||
'hydrators' => [
|
||||
@@ -264,6 +270,27 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
protected function prependCruds(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'cruds' => [
|
||||
[
|
||||
'class' => UserJob::class,
|
||||
'name' => 'admin_user_job',
|
||||
'base_path' => '/admin/main/user-job',
|
||||
'base_role' => 'ROLE_ADMIN',
|
||||
'form_class' => UserJobType::class,
|
||||
'actions' => [
|
||||
'index' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillMain/UserJob/index.html.twig',
|
||||
],
|
||||
'new' => [
|
||||
'role' => 'ROLE_ADMIN'
|
||||
],
|
||||
'edit' => [
|
||||
'role' => 'ROLE_ADMIN'
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
'apis' => [
|
||||
[
|
||||
'class' => \Chill\MainBundle\Entity\Address::class,
|
||||
@@ -371,6 +398,26 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'class' => \Chill\MainBundle\Entity\Scope::class,
|
||||
'name' => 'scope',
|
||||
'base_path' => '/api/1.0/main/scope',
|
||||
'base_role' => 'ROLE_USER',
|
||||
'actions' => [
|
||||
'_index' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true
|
||||
],
|
||||
],
|
||||
'_entity' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
]
|
||||
],
|
||||
]
|
||||
],
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
52
src/Bundle/ChillMainBundle/Doctrine/DQL/STContains.php
Normal file
52
src/Bundle/ChillMainBundle/Doctrine/DQL/STContains.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
* Copyright (C) 2018 Champs-Libres Coopérative <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/>.
|
||||
*/
|
||||
namespace Chill\MainBundle\Doctrine\DQL;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
|
||||
/**
|
||||
* Geometry function 'ST_CONTAINS', added by postgis
|
||||
*/
|
||||
class STContains extends FunctionNode
|
||||
{
|
||||
private $firstPart;
|
||||
|
||||
private $secondPart;
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'ST_CONTAINS('.$this->firstPart->dispatch($sqlWalker).
|
||||
', ' . $this->secondPart->dispatch($sqlWalker) .")";
|
||||
}
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstPart = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
|
||||
$this->secondPart = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@@ -21,20 +21,15 @@ namespace Chill\MainBundle\Doctrine\DQL;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Similarity extends FunctionNode
|
||||
{
|
||||
private $firstPart;
|
||||
|
||||
|
||||
private $secondPart;
|
||||
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'SIMILARITY('.$this->firstPart->dispatch($sqlWalker).
|
||||
return 'SIMILARITY('.$this->firstPart->dispatch($sqlWalker).
|
||||
', ' . $this->secondPart->dispatch($sqlWalker) .")";
|
||||
}
|
||||
|
||||
@@ -42,13 +37,13 @@ class Similarity extends FunctionNode
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
|
||||
$this->firstPart = $parser->StringPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
|
||||
|
||||
$this->secondPart = $parser->StringPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Doctrine\DQL;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
class StrictWordSimilarityOPS extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
|
||||
{
|
||||
private $firstPart;
|
||||
|
||||
private $secondPart;
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return $this->firstPart->dispatch($sqlWalker).
|
||||
' <<% ' . $this->secondPart->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstPart = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
|
||||
$this->secondPart = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ class PointType extends Type {
|
||||
*
|
||||
* @param array $fieldDeclaration
|
||||
* @param AbstractPlatform $platform
|
||||
* @return type
|
||||
* @return string
|
||||
*/
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
@@ -32,7 +32,7 @@ class PointType extends Type {
|
||||
*
|
||||
* @param type $value
|
||||
* @param AbstractPlatform $platform
|
||||
* @return Point
|
||||
* @return ?Point
|
||||
*/
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
|
@@ -383,6 +383,16 @@ class Address
|
||||
;
|
||||
}
|
||||
|
||||
public static function createFromAddressReference(AddressReference $original): Address
|
||||
{
|
||||
return (new Address())
|
||||
->setPoint($original->getPoint())
|
||||
->setPostcode($original->getPostcode())
|
||||
->setStreet($original->getStreet())
|
||||
->setStreetNumber($original->getStreetNumber())
|
||||
;
|
||||
}
|
||||
|
||||
public function getStreet(): ?string
|
||||
{
|
||||
return $this->street;
|
||||
|
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
interface HasCentersInterface
|
||||
{
|
||||
public function getCenters(): ?iterable;
|
||||
}
|
11
src/Bundle/ChillMainBundle/Entity/HasScopesInterface.php
Normal file
11
src/Bundle/ChillMainBundle/Entity/HasScopesInterface.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
interface HasScopesInterface
|
||||
{
|
||||
/**
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function getScopes(): iterable;
|
||||
}
|
@@ -3,17 +3,17 @@
|
||||
/*
|
||||
* Chill is a suite of a modules, Chill is a software for social workers
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.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/>.
|
||||
*/
|
||||
@@ -46,17 +46,17 @@ class Scope
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private $id;
|
||||
|
||||
|
||||
/**
|
||||
* translatable names
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private $name = [];
|
||||
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*
|
||||
@@ -66,8 +66,8 @@ class Scope
|
||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
private $roleScopes;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scope constructor.
|
||||
*/
|
||||
@@ -75,7 +75,7 @@ class Scope
|
||||
{
|
||||
$this->roleScopes = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ class Scope
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return $this
|
||||
@@ -101,7 +101,7 @@ class Scope
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -109,7 +109,7 @@ class Scope
|
||||
{
|
||||
return $this->roleScopes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param RoleScope $roleScope
|
||||
*/
|
||||
|
@@ -5,6 +5,7 @@ namespace Chill\MainBundle\Entity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
@@ -20,7 +21,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
* })
|
||||
*/
|
||||
class User implements AdvancedUserInterface {
|
||||
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
@@ -36,24 +37,30 @@ class User implements AdvancedUserInterface {
|
||||
* @ORM\Column(type="string", length=80)
|
||||
*/
|
||||
private $username;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(
|
||||
* type="string",
|
||||
* length=80,
|
||||
* unique=true)
|
||||
* unique=true,
|
||||
* nullable=true)
|
||||
*/
|
||||
private $usernameCanonical;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=200)
|
||||
*/
|
||||
private string $label = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=150, nullable=true)
|
||||
*/
|
||||
private $email;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
@@ -64,14 +71,14 @@ class User implements AdvancedUserInterface {
|
||||
* unique=true)
|
||||
*/
|
||||
private $emailCanonical;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
private $password;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @internal must be set to null if we use bcrypt
|
||||
@@ -79,7 +86,7 @@ class User implements AdvancedUserInterface {
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $salt = null;
|
||||
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
@@ -87,14 +94,14 @@ class User implements AdvancedUserInterface {
|
||||
* sf4 check: in yml was false by default !?
|
||||
*/
|
||||
private $locked = true;
|
||||
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
private $enabled = true;
|
||||
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*
|
||||
@@ -112,7 +119,25 @@ class User implements AdvancedUserInterface {
|
||||
* @ORM\Column(type="json_array", nullable=true)
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
|
||||
/**
|
||||
* @var Center|null
|
||||
* @ORM\ManyToOne(targetEntity=Center::class)
|
||||
*/
|
||||
private ?Center $mainCenter = null;
|
||||
|
||||
/**
|
||||
* @var Scope|null
|
||||
* @ORM\ManyToOne(targetEntity=Scope::class)
|
||||
*/
|
||||
private ?Scope $mainScope = null;
|
||||
|
||||
/**
|
||||
* @var UserJob|null
|
||||
* @ORM\ManyToOne(targetEntity=UserJob::class)
|
||||
*/
|
||||
private ?UserJob $userJob = null;
|
||||
|
||||
/**
|
||||
* User constructor.
|
||||
*/
|
||||
@@ -120,13 +145,13 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
$this->groupCenters = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getUsername();
|
||||
return $this->getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,10 +173,14 @@ class User implements AdvancedUserInterface {
|
||||
public function setUsername($name)
|
||||
{
|
||||
$this->username = $name;
|
||||
|
||||
|
||||
if (empty($this->getLabel())) {
|
||||
$this->setLabel($name);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -159,11 +188,11 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
public function eraseCredentials() {}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@@ -171,7 +200,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return array('ROLE_USER');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -179,7 +208,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->salt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $usernameCanonical
|
||||
* @return $this
|
||||
@@ -187,10 +216,10 @@ class User implements AdvancedUserInterface {
|
||||
public function setUsernameCanonical($usernameCanonical)
|
||||
{
|
||||
$this->usernameCanonical = $usernameCanonical;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -198,7 +227,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->usernameCanonical;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
* @return $this
|
||||
@@ -206,10 +235,10 @@ class User implements AdvancedUserInterface {
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -217,7 +246,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $emailCanonical
|
||||
* @return $this
|
||||
@@ -225,10 +254,10 @@ class User implements AdvancedUserInterface {
|
||||
public function setEmailCanonical($emailCanonical)
|
||||
{
|
||||
$this->emailCanonical = $emailCanonical;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -236,7 +265,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->emailCanonical;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $password
|
||||
* @return $this
|
||||
@@ -244,7 +273,7 @@ class User implements AdvancedUserInterface {
|
||||
function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -255,7 +284,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $salt
|
||||
* @return $this
|
||||
@@ -265,7 +294,7 @@ class User implements AdvancedUserInterface {
|
||||
$this->salt = $salt;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -273,7 +302,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -281,7 +310,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -289,7 +318,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -297,17 +326,17 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled($enabled)
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return GroupCenter
|
||||
*/
|
||||
@@ -315,7 +344,7 @@ class User implements AdvancedUserInterface {
|
||||
{
|
||||
return $this->groupCenters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Chill\MainBundle\Entity\GroupCenter $groupCenter
|
||||
* @return \Chill\MainBundle\Entity\User
|
||||
@@ -325,7 +354,7 @@ class User implements AdvancedUserInterface {
|
||||
$this->groupCenters->add($groupCenter);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Chill\MainBundle\Entity\GroupCenter $groupCenter
|
||||
* @throws \RuntimeException if the groupCenter is not in the collection
|
||||
@@ -337,9 +366,9 @@ class User implements AdvancedUserInterface {
|
||||
. "it seems not to be associated with the user. Aborting."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function check that groupCenter are present only once. The validator
|
||||
* This function check that groupCenter are present only once. The validator
|
||||
* use this function to avoid a user to be associated to the same groupCenter
|
||||
* more than once.
|
||||
*/
|
||||
@@ -350,7 +379,7 @@ class User implements AdvancedUserInterface {
|
||||
if (in_array($groupCenter->getId(), $groupCentersIds)) {
|
||||
$context->buildViolation("The user has already those permissions")
|
||||
->addViolation();
|
||||
|
||||
|
||||
} else {
|
||||
$groupCentersIds[] = $groupCenter->getId();
|
||||
}
|
||||
@@ -384,4 +413,76 @@ class User implements AdvancedUserInterface {
|
||||
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
* @return User
|
||||
*/
|
||||
public function setLabel(string $label): User
|
||||
{
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Center|null
|
||||
*/
|
||||
public function getMainCenter(): ?Center
|
||||
{
|
||||
return $this->mainCenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Center|null $mainCenter
|
||||
* @return User
|
||||
*/
|
||||
public function setMainCenter(?Center $mainCenter): User
|
||||
{
|
||||
$this->mainCenter = $mainCenter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Scope|null
|
||||
*/
|
||||
public function getMainScope(): ?Scope
|
||||
{
|
||||
return $this->mainScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Scope|null $mainScope
|
||||
* @return User
|
||||
*/
|
||||
public function setMainScope(?Scope $mainScope): User
|
||||
{
|
||||
$this->mainScope = $mainScope;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UserJob|null
|
||||
*/
|
||||
public function getUserJob(): ?UserJob
|
||||
{
|
||||
return $this->userJob;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserJob|null $userJob
|
||||
* @return User
|
||||
*/
|
||||
public function setUserJob(?UserJob $userJob): User
|
||||
{
|
||||
$this->userJob = $userJob;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
76
src/Bundle/ChillMainBundle/Entity/UserJob.php
Normal file
76
src/Bundle/ChillMainBundle/Entity/UserJob.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("chill_main_user_job")
|
||||
*/
|
||||
class UserJob
|
||||
{
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var array|string[]A
|
||||
* @ORM\Column(name="label", type="json")
|
||||
*/
|
||||
protected array $label = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @ORM\Column(name="active", type="boolean")
|
||||
*/
|
||||
protected bool $active = true;
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string[] $label
|
||||
* @return UserJob
|
||||
*/
|
||||
public function setLabel(array $label): UserJob
|
||||
{
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $active
|
||||
* @return UserJob
|
||||
*/
|
||||
public function setActive(bool $active): UserJob
|
||||
{
|
||||
$this->active = $active;
|
||||
return $this;
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
@@ -95,9 +95,12 @@ class CenterType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
if (count($this->reachableCenters) > 1) {
|
||||
$resolver->setDefault('class', Center::class);
|
||||
$resolver->setDefault('choices', $this->reachableCenters);
|
||||
$resolver->setDefault('class', Center::class)
|
||||
->setDefault('choices', $this->reachableCenters)
|
||||
->setDefault('placeholder', 'Pick a center')
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -146,14 +146,7 @@ class ScopePickerType extends AbstractType
|
||||
->setParameter('center', $center->getId())
|
||||
// role constraints
|
||||
->andWhere($qb->expr()->in('rs.role', ':roles'))
|
||||
->setParameter(
|
||||
'roles', \array_map(
|
||||
function (Role $role) {
|
||||
return $role->getRole();
|
||||
},
|
||||
$roles
|
||||
)
|
||||
)
|
||||
->setParameter('roles', $roles)
|
||||
// user contraint
|
||||
->andWhere(':user MEMBER OF gc.users')
|
||||
->setParameter('user', $this->tokenStorage->getToken()->getUser());
|
||||
|
27
src/Bundle/ChillMainBundle/Form/UserJobType.php
Normal file
27
src/Bundle/ChillMainBundle/Form/UserJobType.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class UserJobType extends \Symfony\Component\Form\AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('label', TranslatableStringFormType::class, [
|
||||
'label' => 'Label',
|
||||
'required' => true
|
||||
])
|
||||
->add('active', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Active' => true,
|
||||
'Inactive' => false
|
||||
]
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
}
|
@@ -2,7 +2,15 @@
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
@@ -16,6 +24,16 @@ use Chill\MainBundle\Form\UserPasswordType;
|
||||
|
||||
class UserType extends AbstractType
|
||||
{
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
/**
|
||||
* @param TranslatableStringHelper $translatableStringHelper
|
||||
*/
|
||||
public function __construct(TranslatableStringHelper $translatableStringHelper)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param array $options
|
||||
@@ -24,7 +42,40 @@ class UserType extends AbstractType
|
||||
{
|
||||
$builder
|
||||
->add('username')
|
||||
->add('email')
|
||||
->add('email', EmailType::class, [
|
||||
'required' => true
|
||||
])
|
||||
->add('label', TextType::class)
|
||||
->add('mainCenter', EntityType::class, [
|
||||
'label' => 'main center',
|
||||
'required' => false,
|
||||
'placeholder' => 'choose a main center',
|
||||
'class' => Center::class,
|
||||
'query_builder' => function (EntityRepository $er) {
|
||||
$qb = $er->createQueryBuilder('c');
|
||||
$qb->addOrderBy('c.name');
|
||||
|
||||
return $qb;
|
||||
}
|
||||
])
|
||||
->add('mainScope', EntityType::class, [
|
||||
'label' => 'Choose a main scope',
|
||||
'required' => false,
|
||||
'placeholder' => 'choose a main scope',
|
||||
'class' => Scope::class,
|
||||
'choice_label' => function (Scope $c) {
|
||||
return $this->translatableStringHelper->localize($c->getName());
|
||||
},
|
||||
])
|
||||
->add('userJob', EntityType::class, [
|
||||
'label' => 'Choose a job',
|
||||
'required' => false,
|
||||
'placeholder' => 'choose a job',
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => function (UserJob $c) {
|
||||
return $this->translatableStringHelper->localize($c->getLabel());
|
||||
},
|
||||
])
|
||||
;
|
||||
if ($options['is_creation']) {
|
||||
$builder->add('plainPassword', RepeatedType::class, array(
|
||||
|
@@ -36,6 +36,14 @@ final class AddressReferenceRepository implements ObjectRepository
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function countAll(): int
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('ar');
|
||||
$qb->select('count(ar.id)');
|
||||
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AddressReference[]
|
||||
*/
|
||||
|
17
src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js
Normal file
17
src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const fetchScopes = () => {
|
||||
return window.fetch('/api/1.0/main/scope.json').then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
}).then(data => {
|
||||
console.log(data);
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(data);
|
||||
resolve(data.results);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
fetchScopes
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ ('crud.' ~ crud_name ~ '.index.title')|trans({'%crud_name%': crud_name}) }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
{% endembed %}
|
||||
{% endblock content %}
|
@@ -0,0 +1,26 @@
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>id</th>
|
||||
<th>label</th>
|
||||
<th> </th>
|
||||
{% endblock %}
|
||||
{% block table_entities_tbody %}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.id }}</td>
|
||||
<td>{{ entity.label|localize_translatable_string }}</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_crud_admin_user_job_edit', { 'id': entity.id}) }}" class="btn btn-sm btn-edit btn-mini"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock content %}
|
@@ -23,45 +23,44 @@ namespace Chill\MainBundle\Search;
|
||||
|
||||
/**
|
||||
* This interface must be implemented on services which provide search results.
|
||||
*
|
||||
*
|
||||
* @todo : write doc and add a link to documentation
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
*/
|
||||
interface SearchInterface
|
||||
{
|
||||
|
||||
const SEARCH_PREVIEW_OPTION = '_search_preview';
|
||||
|
||||
|
||||
/**
|
||||
* Request parameters contained inside the `add_q` parameters
|
||||
*/
|
||||
const REQUEST_QUERY_PARAMETERS = '_search_parameters';
|
||||
|
||||
|
||||
/**
|
||||
* Supplementary parameters to the query string
|
||||
*/
|
||||
const REQUEST_QUERY_KEY_ADD_PARAMETERS = 'add_q';
|
||||
|
||||
/**
|
||||
/**
|
||||
* return the result in a html string. The string will be inclued (as raw)
|
||||
* into a global view.
|
||||
*
|
||||
*
|
||||
* The global view may be :
|
||||
* {% for result as resultsFromDifferentSearchInterface %}
|
||||
* {{ result|raw }}
|
||||
* {% endfor %}
|
||||
*
|
||||
*
|
||||
* **available options** :
|
||||
* - SEARCH_PREVIEW_OPTION (boolean) : if renderResult should return a "preview" of
|
||||
* - SEARCH_PREVIEW_OPTION (boolean) : if renderResult should return a "preview" of
|
||||
* the results. In this case, a subset of results should be returned, and,
|
||||
* if the query return more results, a button "see all results" should be
|
||||
* displayed at the end of the list.
|
||||
*
|
||||
* **Interaction between `start` and `limit` and pagination : you should
|
||||
* take only the given parameters into account; the results from pagination
|
||||
* should be ignored. (Most of the time, it should be the same).
|
||||
*
|
||||
* **Interaction between `start` and `limit` and pagination : you should
|
||||
* take only the given parameters into account; the results from pagination
|
||||
* should be ignored. (Most of the time, it should be the same).
|
||||
*
|
||||
* @param array $terms the string to search
|
||||
* @param int $start the first result (for pagination)
|
||||
@@ -72,10 +71,10 @@ interface SearchInterface
|
||||
*/
|
||||
public function renderResult(array $terms, $start=0, $limit=50, array $options = array(), $format = 'html');
|
||||
|
||||
/**
|
||||
/**
|
||||
* we may desactive the search interface by default. in this case,
|
||||
* the search will be launch and rendered only with "advanced search"
|
||||
*
|
||||
* the search will be launch and rendered only with "advanced search"
|
||||
*
|
||||
* this may be activated/desactived from bundle definition in config.yml
|
||||
*
|
||||
* @return bool
|
||||
@@ -84,18 +83,18 @@ interface SearchInterface
|
||||
|
||||
/**
|
||||
* the order in which the results will appears in the global view
|
||||
*
|
||||
*
|
||||
* (this may be eventually defined in config.yml)
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOrder();
|
||||
|
||||
|
||||
/**
|
||||
* indicate if the implementation supports the given domain
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supports($domain, $format);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -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,11 @@ 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 Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
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 +37,126 @@ 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;
|
||||
|
||||
protected ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
RoleHierarchyInterface $roleHierarchy,
|
||||
$hierarchy,
|
||||
EntityManagerInterface $em
|
||||
ParameterBagInterface $parameterBag,
|
||||
EntityManagerInterface $em,
|
||||
CenterResolverDispatcher $centerResolverDispatcher,
|
||||
LoggerInterface $logger,
|
||||
ScopeResolverDispatcher $scopeResolverDispatcher
|
||||
) {
|
||||
$this->roleHierarchy = $roleHierarchy;
|
||||
$this->hierarchy = $hierarchy;
|
||||
$this->hierarchy = $parameterBag->get('security.role_hierarchy.roles');
|
||||
$this->em = $em;
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->logger = $logger;
|
||||
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 = $this->centerResolverDispatcher->resolveCenter($entity);
|
||||
|
||||
if (is_iterable($center)) {
|
||||
foreach ($center as $c) {
|
||||
if ($this->userHasAccessForCenter($user, $c, $entity, $attribute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} elseif ($center instanceof Center) {
|
||||
return $this->userHasAccessForCenter($user, $center, $entity, $attribute);
|
||||
} elseif (NULL === $center) {
|
||||
return false;
|
||||
} else {
|
||||
throw new \UnexpectedValueException("could not resolver a center");
|
||||
}
|
||||
}
|
||||
|
||||
private function userHasAccessForCenter(User $user, Center $center, $entity, $attribute): bool
|
||||
{
|
||||
|
||||
$center = $entity->getCenter();
|
||||
|
||||
if (!$this->userCanReachCenter($user, $center)) {
|
||||
$this->logger->debug("user cannot reach center of entity", [
|
||||
'center_name' => $center->getName(),
|
||||
'user' => $user->getUsername()
|
||||
]);
|
||||
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) {
|
||||
@@ -120,31 +164,42 @@ class AuthorizationHelper
|
||||
if ($this->isRoleReached($attribute, $roleScope->getRole())) {
|
||||
//if yes, we have a right on something...
|
||||
// perform check on scope if necessary
|
||||
if ($entity instanceof HasScopeInterface) {
|
||||
$scope = $entity->getScope();
|
||||
if ($scope === NULL) {
|
||||
return true;
|
||||
}
|
||||
if ($scope->getId() === $roleScope
|
||||
->getScope()->getId()) {
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->logger->debug("user can reach center entity, but not role", [
|
||||
'username' => $user->getUsername(),
|
||||
'center' => $center->getName()
|
||||
]);
|
||||
|
||||
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 +211,7 @@ class AuthorizationHelper
|
||||
$role = $role->getRole();
|
||||
}
|
||||
$centers = array();
|
||||
|
||||
|
||||
foreach ($user->getGroupCenters() as $groupCenter){
|
||||
$permissionGroup = $groupCenter->getPermissionsGroup();
|
||||
//iterate on roleScopes
|
||||
@@ -170,13 +225,13 @@ class AuthorizationHelper
|
||||
if ($scope->getId() == $roleScope->getScope()->getId()){
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
break 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $centers;
|
||||
}
|
||||
|
||||
@@ -203,18 +258,18 @@ class AuthorizationHelper
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return all reachable scope for a given user, center and role
|
||||
*
|
||||
*
|
||||
* @deprecated Use getReachableCircles
|
||||
*
|
||||
* @param User $user
|
||||
* @param Role $role
|
||||
* @param Center $center
|
||||
* @param string role
|
||||
* @param Center|Center[] $center
|
||||
* @return Scope[]
|
||||
*/
|
||||
public function getReachableScopes(User $user, $role, Center $center)
|
||||
public function getReachableScopes(User $user, $role, $center)
|
||||
{
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
@@ -222,22 +277,31 @@ 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
|
||||
* @param Center|Center[] $center
|
||||
* @return Scope[]
|
||||
*/
|
||||
public function getReachableCircles(User $user, $role, Center $center)
|
||||
public function getReachableCircles(User $user, $role, $center)
|
||||
{
|
||||
$scopes = [];
|
||||
|
||||
if (is_iterable($center)) {
|
||||
foreach ($center as $c) {
|
||||
$scopes = \array_merge($scopes, $this->getReachableCircles($user, $role, $c));
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
}
|
||||
$scopes = array();
|
||||
|
||||
|
||||
foreach ($user->getGroupCenters() as $groupCenter){
|
||||
if ($center->getId() === $groupCenter->getCenter()->getId()) {
|
||||
//iterate on permissionGroup
|
||||
@@ -251,23 +315,19 @@ class AuthorizationHelper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Role $role
|
||||
* @param Center $center
|
||||
* @param Scope $circle
|
||||
* @return Users
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
public function findUsersReaching(Role $role, Center $center, Scope $circle = null)
|
||||
public function findUsersReaching(string $role, Center $center, Scope $circle = null): array
|
||||
{
|
||||
$parents = $this->getParentRoles($role);
|
||||
$parents[] = $role;
|
||||
$parentRolesString = \array_map(function(Role $r) { return $r->getRole(); }, $parents);
|
||||
|
||||
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb
|
||||
->select('u')
|
||||
@@ -276,23 +336,23 @@ class AuthorizationHelper
|
||||
->join('gc.permissionsGroup', 'pg')
|
||||
->join('pg.roleScopes', 'rs')
|
||||
->where('gc.center = :center')
|
||||
->andWhere($qb->expr()->in('rs.role', $parentRolesString))
|
||||
->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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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,36 +361,31 @@ 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
|
||||
* @return string[] the role which give access to the given $role
|
||||
*/
|
||||
public function getParentRoles(Role $role)
|
||||
public function getParentRoles($role): array
|
||||
{
|
||||
$parentRoles = [];
|
||||
// transform the roles from role hierarchy from string to Role
|
||||
$roles = \array_map(
|
||||
function($string) {
|
||||
return new Role($string);
|
||||
},
|
||||
\array_keys($this->hierarchy)
|
||||
);
|
||||
|
||||
$roles = \array_keys($this->hierarchy);
|
||||
|
||||
foreach ($roles as $r) {
|
||||
$childRoles = $this->roleHierarchy->getReachableRoleNames([$r->getRole()]);
|
||||
|
||||
$childRoles = $this->roleHierarchy->getReachableRoleNames([$r]);
|
||||
|
||||
if (\in_array($role, $childRoles)) {
|
||||
$parentRoles[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $parentRoles;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
|
||||
final class DefaultVoterHelper implements VoterHelperInterface
|
||||
{
|
||||
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) {
|
||||
return 0 < count($this->authorizationHelper->getReachableCenters($token->getUser(), $attribute, null));
|
||||
}
|
||||
|
||||
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 DefaultVoterHelperFactory implements VoterHelperFactoryInterface
|
||||
{
|
||||
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 DefaultVoterHelperGenerator(
|
||||
$this->authorizationHelper,
|
||||
$this->centerResolverDispatcher
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
|
||||
final class DefaultVoterHelperGenerator 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(?string $subject, array $attributes): self
|
||||
{
|
||||
$this->configuration[] = [$attributes, $subject];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function build(): VoterHelperInterface
|
||||
{
|
||||
return new DefaultVoterHelper(
|
||||
$this->authorizationHelper,
|
||||
$this->centerResolverDispatcher,
|
||||
$this->configuration
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
interface VoterGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @param string $class The FQDN of a class
|
||||
* @param array $attributes an array of attributes
|
||||
* @return $this
|
||||
*/
|
||||
public function addCheckFor(?string $class, array $attributes): self;
|
||||
|
||||
public function build(): VoterHelperInterface;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
interface VoterHelperFactoryInterface
|
||||
{
|
||||
public function generate($context): VoterGeneratorInterface;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
interface VoterHelperInterface
|
||||
{
|
||||
public function supports($attribute, $subject): bool;
|
||||
|
||||
public function voteOnAttribute($attribute, $subject, TokenInterface $token);
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
class CenterResolverDispatcher
|
||||
{
|
||||
/**
|
||||
* @var iterabble|CenterResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers = [];
|
||||
|
||||
public function __construct(iterable $resolvers)
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
* @param array|null $options
|
||||
* @return null|Center|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
foreach($this->resolvers as $priority => $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return $resolver->resolveCenter($entity, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
interface CenterResolverInterface
|
||||
{
|
||||
public function supports($entity, ?array $options = []): bool;
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @param array|null $options
|
||||
* @return Center|array|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = []);
|
||||
|
||||
public static function getDefaultPriority(): int;
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\HasCentersInterface;
|
||||
|
||||
class DefaultCenterResolver implements CenterResolverInterface
|
||||
{
|
||||
public function supports($entity, ?array $options = []): bool
|
||||
{
|
||||
return $entity instanceof HasCenterInterface || $entity instanceof HasCentersInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param HasCenterInterface $entity
|
||||
* @param array $options
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
if ($entity instanceof HasCenterInterface) {
|
||||
return $entity->getCenter();
|
||||
} elseif ($entity instanceof HasCentersInterface) {
|
||||
return $entity->getCenters();
|
||||
} else {
|
||||
throw new \UnexpectedValueException("should be an instanceof");
|
||||
}
|
||||
}
|
||||
|
||||
public static function getDefaultPriority(): int
|
||||
{
|
||||
return -256;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\HasScopesInterface;
|
||||
|
||||
class DefaultScopeResolver implements ScopeResolverInterface
|
||||
{
|
||||
|
||||
public function supports($entity, ?array $options = []): bool
|
||||
{
|
||||
return $entity instanceof HasScopeInterface || $entity instanceof HasScopesInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param HasScopeInterface|HasScopesInterface $entity
|
||||
*/
|
||||
public function resolveScope($entity, ?array $options = [])
|
||||
{
|
||||
if ($entity instanceof HasScopeInterface) {
|
||||
return $entity->getScope();
|
||||
} elseif ($entity instanceof HasScopesInterface) {
|
||||
return $entity->getScopes();
|
||||
} else {
|
||||
throw new \UnexpectedValueException("should be an instanceof %s or %s",
|
||||
HasScopesInterface::class, HasScopeInterface::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function isConcerned($entity, ?array $options = []): bool
|
||||
{
|
||||
return $entity instanceof HasScopeInterface || $entity instanceof HasScopesInterface;
|
||||
}
|
||||
|
||||
public static function getDefaultPriority(): int
|
||||
{
|
||||
return -256;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Twig\TwigFilter;
|
||||
|
||||
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||
{
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
|
||||
/**
|
||||
* @param CenterResolverDispatcher $centerResolverDispatcher
|
||||
*/
|
||||
public function __construct(CenterResolverDispatcher $centerResolverDispatcher)
|
||||
{
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter'])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
* @param array|null $options
|
||||
* @return Center|Center[]|null
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
return $this->centerResolverDispatcher->resolveCenter($entity, $options);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
|
||||
final class ScopeResolverDispatcher
|
||||
{
|
||||
/**
|
||||
* @var iterable|ScopeResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers;
|
||||
|
||||
public function __construct(iterable $resolvers)
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @return Scope|Scope[]|iterable
|
||||
*/
|
||||
public function resolveScope($entity, ?array $options = [])
|
||||
{
|
||||
foreach ($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return $resolver->resolveScope($entity, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isConcerned($entity, ?array $options = []): bool
|
||||
{
|
||||
foreach ($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return $resolver->isConcerned($entity, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
|
||||
/**
|
||||
* Interface to implement to define a ScopeResolver.
|
||||
*/
|
||||
interface ScopeResolverInterface
|
||||
{
|
||||
/**
|
||||
* Return true if this resolve is able to decide "something" on this entity.
|
||||
*/
|
||||
public function supports($entity, ?array $options = []): bool;
|
||||
|
||||
/**
|
||||
* Will return the scope for the entity
|
||||
*
|
||||
* @return Scope|array|Scope[]
|
||||
*/
|
||||
public function resolveScope($entity, ?array $options = []);
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the entity is concerned by scope, false otherwise.
|
||||
*/
|
||||
public function isConcerned($entity, ?array $options = []): bool;
|
||||
|
||||
/**
|
||||
* get the default priority for this resolver. Resolver with an higher priority will be
|
||||
* queried first.
|
||||
*/
|
||||
public static function getDefaultPriority(): int;
|
||||
}
|
@@ -7,11 +7,11 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
class UserControllerTest extends WebTestCase
|
||||
{
|
||||
private $client;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
|
||||
$this->client = static::createClient(array(), array(
|
||||
'PHP_AUTH_USER' => 'admin',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
@@ -23,62 +23,64 @@ class UserControllerTest extends WebTestCase
|
||||
{
|
||||
// get the list
|
||||
$crawler = $this->client->request('GET', '/fr/admin/user/');
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
|
||||
"Unexpected HTTP status code for GET /admin/user/");
|
||||
|
||||
|
||||
$link = $crawler->selectLink('Ajouter un nouvel utilisateur')->link();
|
||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $link);
|
||||
$this->assertRegExp('|/fr/admin/user/new$|', $link->getUri());
|
||||
}
|
||||
|
||||
|
||||
public function testNew()
|
||||
{
|
||||
$crawler = $this->client->request('GET', '/fr/admin/user/new');
|
||||
|
||||
|
||||
$username = 'Test_user'. uniqid();
|
||||
$password = 'Password1234!';
|
||||
dump($crawler->text());
|
||||
// Fill in the form and submit it
|
||||
$form = $crawler->selectButton('Créer')->form(array(
|
||||
'chill_mainbundle_user[username]' => $username,
|
||||
'chill_mainbundle_user[plainPassword][first]' => $password,
|
||||
'chill_mainbundle_user[plainPassword][second]' => $password
|
||||
'chill_mainbundle_user[plainPassword][second]' => $password,
|
||||
'chill_mainbundle_user[email]' => $username.'@gmail.com',
|
||||
'chill_mainbundle_user[label]' => $username,
|
||||
|
||||
));
|
||||
|
||||
$this->client->submit($form);
|
||||
$crawler = $this->client->followRedirect();
|
||||
|
||||
// Check data in the show view
|
||||
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test_user")')->count(),
|
||||
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test_user")')->count(),
|
||||
'Missing element td:contains("Test user")');
|
||||
|
||||
|
||||
$update = $crawler->selectLink('Modifier')->link();
|
||||
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $update);
|
||||
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit$|', $update->getUri());
|
||||
|
||||
|
||||
//test the auth of the new client
|
||||
$this->isPasswordValid($username, $password);
|
||||
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
|
||||
protected function isPasswordValid($username, $password)
|
||||
{
|
||||
/* @var $passwordEncoder \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder */
|
||||
$passwordEncoder = self::$kernel->getContainer()
|
||||
->get('security.password_encoder');
|
||||
|
||||
|
||||
$user = self::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository('ChillMainBundle:User')
|
||||
->findOneBy(array('username' => $username));
|
||||
|
||||
|
||||
$this->assertTrue($passwordEncoder->isPasswordValid($user, $password));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param \Symfony\Component\DomCrawler\Link $update
|
||||
* @depends testNew
|
||||
*/
|
||||
@@ -90,24 +92,24 @@ class UserControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Mettre à jour')->form(array(
|
||||
'chill_mainbundle_user[username]' => $username,
|
||||
));
|
||||
|
||||
|
||||
$this->client->submit($form);
|
||||
$crawler = $this->client->followRedirect();
|
||||
// Check the element contains an attribute with value equals "Foo"
|
||||
$this->assertGreaterThan(0, $crawler->filter('[value="'.$username.'"]')->count(),
|
||||
$this->assertGreaterThan(0, $crawler->filter('[value="'.$username.'"]')->count(),
|
||||
'Missing element [value="Foo bar"]');
|
||||
|
||||
|
||||
$updatePassword = $crawler->selectLink('Modifier le mot de passe')->link();
|
||||
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\DomCrawler\Link', $updatePassword);
|
||||
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit_password$|',
|
||||
$this->assertRegExp('|/fr/admin/user/[0-9]{1,}/edit_password$|',
|
||||
$updatePassword->getUri());
|
||||
|
||||
|
||||
return array('link' => $updatePassword, 'username' => $username);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param \Symfony\Component\DomCrawler\Link $updatePassword
|
||||
* @depends testUpdate
|
||||
*/
|
||||
@@ -116,22 +118,22 @@ class UserControllerTest extends WebTestCase
|
||||
$link = $params['link'];
|
||||
$username = $params['username'];
|
||||
$newPassword = '1234Password!';
|
||||
|
||||
|
||||
$crawler = $this->client->click($link);
|
||||
|
||||
|
||||
$form = $crawler->selectButton('Changer le mot de passe')->form(array(
|
||||
'chill_mainbundle_user_password[new_password][first]' => $newPassword,
|
||||
'chill_mainbundle_user_password[new_password][second]' => $newPassword,
|
||||
));
|
||||
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isRedirect(),
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isRedirect(),
|
||||
"the response is a redirection");
|
||||
$this->client->followRedirect();
|
||||
|
||||
|
||||
$this->isPasswordValid($username, $newPassword);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -19,6 +19,10 @@
|
||||
|
||||
namespace Chill\MainBundle\Tests\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\HasCentersInterface;
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\HasScopesInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Chill\MainBundle\Test\PrepareUserTrait;
|
||||
use Chill\MainBundle\Test\PrepareCenterTrait;
|
||||
@@ -30,22 +34,22 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class AuthorizationHelperTest extends KernelTestCase
|
||||
{
|
||||
|
||||
|
||||
use PrepareUserTrait, PrepareCenterTrait, PrepareScopeTrait, ProphecyTrait;
|
||||
|
||||
public function setUp()
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
static::bootKernel();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return \Chill\MainBundle\Security\Authorization\AuthorizationHelper
|
||||
*/
|
||||
private function getAuthorizationHelper()
|
||||
@@ -54,13 +58,13 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
->get('chill.main.security.authorization.helper')
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test function userCanReach of helper.
|
||||
*
|
||||
*
|
||||
* A user can reach center => the function should return true.
|
||||
*/
|
||||
public function testUserCanReachCenter_UserShouldReach()
|
||||
public function testUserCanReachCenter_UserShouldReach()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
$scope = $this->prepareScope(1, 'default');
|
||||
@@ -72,16 +76,16 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
|
||||
|
||||
$this->assertTrue($helper->userCanReachCenter($user, $center));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test function userCanReach of helper
|
||||
*
|
||||
*
|
||||
* A user can not reachcenter =>W the function should return false
|
||||
*/
|
||||
public function testUserCanReachCenter_UserShouldNotReach()
|
||||
public function testUserCanReachCenter_UserShouldNotReach()
|
||||
{
|
||||
$centerA = $this->prepareCenter(1, 'center');
|
||||
$centerB = $this->prepareCenter(2, 'centerB');
|
||||
@@ -94,11 +98,11 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
|
||||
|
||||
$this->assertFalse($helper->userCanReachCenter($user, $centerB));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_shouldHaveAccess_EntityWithoutScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -114,11 +118,11 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasCenterInterface');
|
||||
$entity->getCenter()->willReturn($center);
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(),
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(),
|
||||
'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_ShouldHaveAccessWithInheritance_EntityWithoutScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -130,17 +134,17 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasCenterInterface');
|
||||
$entity->getCenter()->willReturn($center);
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(),
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(),
|
||||
'CHILL_INHERITED_ROLE_1'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function testuserHasAccess_UserHasNoRole_EntityWithoutScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -156,10 +160,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasCenterInterface');
|
||||
$entity->getCenter()->willReturn($center);
|
||||
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test that a user has no access on a entity, but is granted on the same role
|
||||
* on another center
|
||||
@@ -186,10 +190,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasCenterInterface');
|
||||
$entity->getCenter()->willReturn($centerA);
|
||||
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testtestUserHasAccess_UserShouldHaveAccess_EntityWithScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -207,10 +211,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasScopeInterface');
|
||||
$entity->getCenter()->willReturn($center);
|
||||
$entity->getScope()->willReturn($scope);
|
||||
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_UserHasNoRole_EntityWithScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -228,10 +232,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasScopeInterface');
|
||||
$entity->getCenter()->willReturn($center);
|
||||
$entity->getScope()->willReturn($scope);
|
||||
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'ANOTHER_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_UserHasNoCenter_EntityWithScope()
|
||||
{
|
||||
$centerA = $this->prepareCenter(1, 'center'); //the user will have this center
|
||||
@@ -250,10 +254,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasScopeInterface');
|
||||
$entity->getCenter()->willReturn($centerB);
|
||||
$entity->getScope()->willReturn($scope);
|
||||
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_UserHasNoScope_EntityWithScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
@@ -268,16 +272,106 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasCenterInterface');
|
||||
$entity->willImplement('\Chill\MainBundle\Entity\HasScopeInterface');
|
||||
$entity->willImplement(HasCenterInterface::class);
|
||||
$entity->willImplement(HasScopeInterface::class);
|
||||
$entity->getCenter()->willReturn($center);
|
||||
$entity->getScope()->willReturn($scopeA);
|
||||
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
public function testUserHasAccess_MultiCenter_EntityWithoutScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
$centerB = $this->prepareCenter(1, 'centerB');
|
||||
$scopeB = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$user = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $center, 'permissionsGroup' => array(
|
||||
['scope' => $scopeB, 'role' => 'CHILL_ROLE']
|
||||
)
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement(HasCentersInterface::class);
|
||||
$entity->getCenters()->willReturn([$center, $centerB]);
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
public function testUserHasNoAccess_MultiCenter_EntityWithoutScope()
|
||||
{
|
||||
$center = $this->prepareCenter(1, 'center');
|
||||
$centerB = $this->prepareCenter(1, 'centerB');
|
||||
$centerC = $this->prepareCenter(1, 'centerC');
|
||||
$scopeB = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$user = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $center, 'permissionsGroup' => array(
|
||||
['scope' => $scopeB, 'role' => 'CHILL_ROLE']
|
||||
)
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement(HasCentersInterface::class);
|
||||
$entity->getCenters()->willReturn([$centerB, $centerC]);
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
public function testUserHasNoAccess_EntityMultiScope()
|
||||
{
|
||||
$centerA = $this->prepareCenter(1, 'center');
|
||||
$centerB = $this->prepareCenter(1, 'centerB');
|
||||
$scopeA = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$scopeB = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$scopeC = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$user = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $centerA, 'permissionsGroup' => array(
|
||||
['scope' => $scopeA, 'role' => 'CHILL_ROLE']
|
||||
)
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement(HasCentersInterface::class);
|
||||
$entity->willImplement(HasScopesInterface::class);
|
||||
$entity->getCenters()->willReturn([$centerA, $centerB]);
|
||||
$entity->getScopes()->willReturn([$scopeB, $scopeC]);
|
||||
|
||||
$this->assertFalse($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
public function testUserHasAccess_EntityMultiScope()
|
||||
{
|
||||
$centerA = $this->prepareCenter(1, 'center');
|
||||
$centerB = $this->prepareCenter(1, 'centerB');
|
||||
$scopeA = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$scopeB = $this->prepareScope(2, 'other'); //the user will be granted this scope
|
||||
$user = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $centerA, 'permissionsGroup' => array(
|
||||
['scope' => $scopeA, 'role' => 'CHILL_ROLE']
|
||||
)
|
||||
)
|
||||
));
|
||||
$helper = $this->getAuthorizationHelper();
|
||||
$entity = $this->getProphet()->prophesize();
|
||||
$entity->willImplement(HasCentersInterface::class);
|
||||
$entity->willImplement(HasScopesInterface::class);
|
||||
$entity->getCenters()->willReturn([$centerA, $centerB]);
|
||||
$entity->getScopes()->willReturn([$scopeA, $scopeB]);
|
||||
|
||||
$this->assertTrue($helper->userHasAccess($user, $entity->reveal(), 'CHILL_ROLE'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @dataProvider dataProvider_getReachableCenters
|
||||
* @param Center $shouldHaveCenter
|
||||
* @param User $user
|
||||
@@ -288,7 +382,7 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
{
|
||||
$this->assertEquals($test, $result, $msg);
|
||||
}
|
||||
|
||||
|
||||
public function dataProvider_getReachableCenters()
|
||||
{
|
||||
$this->setUp();
|
||||
@@ -297,10 +391,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$scopeA = $this->prepareScope(1, 'scope default');
|
||||
$scopeB = $this->prepareScope(2, 'scope B');
|
||||
$scopeC = $this->prepareScope(3, 'scope C');
|
||||
|
||||
|
||||
$userA = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $centerA,
|
||||
'center' => $centerA,
|
||||
'permissionsGroup' => array(
|
||||
['scope' => $scopeB, 'role' => 'CHILL_ROLE_1'],
|
||||
['scope' => $scopeA, 'role' => 'CHILL_ROLE_2']
|
||||
@@ -313,62 +407,62 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
['scope' => $scopeC, 'role' => 'CHILL_ROLE_2']
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
));
|
||||
|
||||
|
||||
$ah = $this->getAuthorizationHelper();
|
||||
|
||||
|
||||
return array(
|
||||
// without scopes
|
||||
array(
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_1'), null)),
|
||||
'center A should be available for userA, with role 1 '
|
||||
),
|
||||
array(
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_2'), null)),
|
||||
'center A should be available for userA, with role 2 '
|
||||
),
|
||||
array(
|
||||
true,
|
||||
in_array($centerB, $ah->getReachableCenters($userA,
|
||||
true,
|
||||
in_array($centerB, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_2'), null)),
|
||||
'center A should be available for userA, with role 2 '
|
||||
),
|
||||
array(
|
||||
false,
|
||||
false,
|
||||
in_array($centerB, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_1'), null)),
|
||||
'center B should NOT be available for userA, with role 1 '
|
||||
),
|
||||
// with scope
|
||||
array(
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
true,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_1'), $scopeB)),
|
||||
'center A should be available for userA, with role 1, scopeC '
|
||||
),
|
||||
array(
|
||||
false,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
false,
|
||||
in_array($centerA, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_2'), $scopeC)),
|
||||
'center A should NOT be available for userA, with role 2, scopeA '
|
||||
),
|
||||
array(
|
||||
true,
|
||||
in_array($centerB, $ah->getReachableCenters($userA,
|
||||
true,
|
||||
in_array($centerB, $ah->getReachableCenters($userA,
|
||||
new Role('CHILL_ROLE_2'), $scopeA)),
|
||||
'center B should be available for userA, with role 2, scopeA '
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @dataProvider dataProvider_getReachableScopes
|
||||
* @param boolean $expectedResult
|
||||
* @param Scope $testedScope
|
||||
@@ -382,11 +476,11 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
{
|
||||
$reachableScopes = $this->getAuthorizationHelper()
|
||||
->getReachableScopes($user, $role, $center);
|
||||
|
||||
|
||||
$this->assertEquals($expectedResult, in_array($testedScope, $reachableScopes),
|
||||
$message);
|
||||
}
|
||||
|
||||
|
||||
public function dataProvider_getReachableScopes()
|
||||
{
|
||||
$centerA = $this->prepareCenter(1, 'center A');
|
||||
@@ -394,10 +488,10 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
$scopeA = $this->prepareScope(1, 'scope default');
|
||||
$scopeB = $this->prepareScope(2, 'scope B');
|
||||
$scopeC = $this->prepareScope(3, 'scope C');
|
||||
|
||||
|
||||
$userA = $this->prepareUser(array(
|
||||
array(
|
||||
'center' => $centerA,
|
||||
'center' => $centerA,
|
||||
'permissionsGroup' => array(
|
||||
['scope' => $scopeB, 'role' => 'CHILL_ROLE_1'],
|
||||
['scope' => $scopeA, 'role' => 'CHILL_ROLE_2']
|
||||
@@ -411,9 +505,9 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
['scope' => $scopeB, 'role' => 'CHILL_ROLE_2']
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
));
|
||||
|
||||
|
||||
return array(
|
||||
array(
|
||||
true,
|
||||
@@ -442,37 +536,30 @@ class AuthorizationHelperTest extends KernelTestCase
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testGetParentRoles()
|
||||
{
|
||||
$parentRoles = $this->getAuthorizationHelper()
|
||||
->getParentRoles(new Role('CHILL_INHERITED_ROLE_1'));
|
||||
|
||||
$this->assertContains(
|
||||
'CHILL_MASTER_ROLE',
|
||||
\array_map(
|
||||
function(Role $role) {
|
||||
return $role->getRole();
|
||||
},
|
||||
$parentRoles
|
||||
),
|
||||
->getParentRoles('CHILL_INHERITED_ROLE_1');
|
||||
|
||||
$this->assertContains('CHILL_MASTER_ROLE', $parentRoles,
|
||||
"Assert that `CHILL_MASTER_ROLE` is a parent of `CHILL_INHERITED_ROLE_1`");
|
||||
}
|
||||
|
||||
|
||||
public function testFindUsersReaching()
|
||||
{
|
||||
$centerA = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository(Center::class)
|
||||
->findOneByName('Center A');
|
||||
|
||||
|
||||
$users = $this->getAuthorizationHelper()
|
||||
->findUsersReaching(new Role('CHILL_PERSON_SEE'),
|
||||
->findUsersReaching(new Role('CHILL_PERSON_SEE'),
|
||||
$centerA);
|
||||
|
||||
|
||||
$usernames = \array_map(function(User $u) { return $u->getUsername(); }, $users);
|
||||
|
||||
|
||||
$this->assertContains('center a_social', $usernames);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Tests\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\HasScopesInterface;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Security\Resolver\DefaultScopeResolver;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DefaultScopeResolverTest extends TestCase
|
||||
{
|
||||
private DefaultScopeResolver $scopeResolver;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->scopeResolver = new DefaultScopeResolver();
|
||||
}
|
||||
|
||||
public function testHasScopeInterface()
|
||||
{
|
||||
$scope = new Scope();
|
||||
$entity = new class($scope) implements HasScopeInterface {
|
||||
|
||||
public function __construct(Scope $scope) {
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
public function getScope()
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$this->assertTrue($this->scopeResolver->supports($entity));
|
||||
$this->assertTrue($this->scopeResolver->isConcerned($entity));
|
||||
$this->assertSame($scope, $this->scopeResolver->resolveScope($entity));
|
||||
}
|
||||
|
||||
public function testHasScopesInterface()
|
||||
{
|
||||
$entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
|
||||
public function __construct(Scope $scopeA, Scope $scopeB) {
|
||||
$this->scopes = [$scopeA, $scopeB];
|
||||
}
|
||||
|
||||
public function getScopes(): iterable
|
||||
{
|
||||
return $this->scopes;
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertTrue($this->scopeResolver->supports($entity));
|
||||
$this->assertTrue($this->scopeResolver->isConcerned($entity));
|
||||
$this->assertIsArray($this->scopeResolver->resolveScope($entity));
|
||||
$this->assertSame($scopeA, $this->scopeResolver->resolveScope($entity)[0]);
|
||||
$this->assertSame($scopeB, $this->scopeResolver->resolveScope($entity)[1]);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Tests\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\HasScopesInterface;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Security\Resolver\DefaultScopeResolver;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DefaultScopeResolverDispatcherTest extends TestCase
|
||||
{
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->scopeResolverDispatcher = new ScopeResolverDispatcher([new DefaultScopeResolver()]);
|
||||
}
|
||||
|
||||
public function testHasScopeInterface()
|
||||
{
|
||||
$scope = new Scope();
|
||||
$entity = new class($scope) implements HasScopeInterface {
|
||||
|
||||
public function __construct(Scope $scope) {
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
public function getScope()
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$this->assertTrue($this->scopeResolverDispatcher->isConcerned($entity));
|
||||
$this->assertSame($scope, $this->scopeResolverDispatcher->resolveScope($entity));
|
||||
}
|
||||
|
||||
public function testHasScopesInterface()
|
||||
{
|
||||
$entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
|
||||
public function __construct(Scope $scopeA, Scope $scopeB) {
|
||||
$this->scopes = [$scopeA, $scopeB];
|
||||
}
|
||||
|
||||
public function getScopes(): iterable
|
||||
{
|
||||
return $this->scopes;
|
||||
}
|
||||
};
|
||||
|
||||
$this->assertTrue($this->scopeResolverDispatcher->isConcerned($entity));
|
||||
$this->assertIsArray($this->scopeResolverDispatcher->resolveScope($entity));
|
||||
$this->assertSame($scopeA, $this->scopeResolverDispatcher->resolveScope($entity)[0]);
|
||||
$this->assertSame($scopeB, $this->scopeResolverDispatcher->resolveScope($entity)[1]);
|
||||
}
|
||||
}
|
@@ -480,3 +480,34 @@ paths:
|
||||
description: "not found"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
|
||||
/1.0/main/scope.json:
|
||||
get:
|
||||
tags:
|
||||
- scope
|
||||
summary: return a list of scopes
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
|
||||
/1.0/main/scope/{id}.json:
|
||||
get:
|
||||
tags:
|
||||
- scope
|
||||
summary: return a list of scopes
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The scope id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
|
@@ -113,6 +113,10 @@ services:
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\MainBundle\Form\UserType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Form\PermissionsGroupType:
|
||||
tags:
|
||||
- { name: form.type }
|
||||
@@ -123,3 +127,4 @@ services:
|
||||
- "@security.token_storage"
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
|
@@ -3,16 +3,45 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverDispatcher:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.center_resolver
|
||||
|
||||
Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.scope_resolver
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Resolver\DefaultCenterResolver:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
Chill\MainBundle\Security\Resolver\DefaultScopeResolver:
|
||||
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\DefaultVoterHelperFactory:
|
||||
autowire: true
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface: '@Chill\MainBundle\Security\Authorization\DefaultVoterHelperFactory'
|
||||
|
||||
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:
|
||||
class: Chill\MainBundle\Security\RoleProvider
|
||||
Chill\MainBundle\Security\RoleProvider: '@chill.main.role_provider'
|
||||
|
||||
chill.main.user_provider:
|
||||
class: Chill\MainBundle\Security\UserProvider\UserProvider
|
||||
|
@@ -18,6 +18,8 @@ Chill\MainBundle\Entity\User:
|
||||
min: 3
|
||||
email:
|
||||
- Email: ~
|
||||
label:
|
||||
- NotBlank: ~
|
||||
constraints:
|
||||
- Callback:
|
||||
callback: isGroupCenterPresentOnce
|
||||
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add metadata on users
|
||||
*/
|
||||
final class Version20210903144853 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add metadata on users';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_main_user_job_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_main_user_job (id INT NOT NULL, label JSON NOT NULL, active BOOLEAN NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('ALTER TABLE users ADD label VARCHAR(200) NULL DEFAULT NULL');
|
||||
$this->addSql('UPDATE users SET label=username');
|
||||
$this->addSql('ALTER TABLE users ALTER label DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE users ALTER label SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD mainCenter_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD mainScope_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD userJob_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD CONSTRAINT FK_1483A5E92C2125C1 FOREIGN KEY (mainCenter_id) REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE users ADD CONSTRAINT FK_1483A5E9115E73F3 FOREIGN KEY (mainScope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE users ADD CONSTRAINT FK_1483A5E964B65C5B FOREIGN KEY (userJob_id) REFERENCES chill_main_user_job (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E92C2125C1 ON users (mainCenter_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9115E73F3 ON users (mainScope_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E964B65C5B ON users (userJob_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users DROP CONSTRAINT FK_1483A5E964B65C5B');
|
||||
$this->addSql('DROP SEQUENCE chill_main_user_job_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_main_user_job');
|
||||
$this->addSql('ALTER TABLE users DROP CONSTRAINT FK_1483A5E92C2125C1');
|
||||
$this->addSql('ALTER TABLE users DROP CONSTRAINT FK_1483A5E9115E73F3');
|
||||
$this->addSql('ALTER TABLE users DROP label');
|
||||
$this->addSql('ALTER TABLE users DROP mainCenter_id');
|
||||
$this->addSql('ALTER TABLE users DROP mainScope_id');
|
||||
$this->addSql('ALTER TABLE users DROP userJob_id');
|
||||
$this->addSql('ALTER TABLE users ALTER usernameCanonical DROP NOT NULL');
|
||||
}
|
||||
}
|
@@ -186,6 +186,7 @@ Exports list: Liste des exports
|
||||
Create an export: Créer un export
|
||||
#export creation step 'center' : pick a center
|
||||
Pick centers: Choisir les centres
|
||||
Pick a center: Choisir un centre
|
||||
The export will contains only data from the picked centers.: L'export ne contiendra que les données des centres choisis.
|
||||
This will eventually restrict your possibilities in filtering the data.: Les possibilités de filtrages seront adaptées aux droits de consultation pour les centres choisis.
|
||||
Go to export options: Vers la préparation de l'export
|
||||
|
Reference in New Issue
Block a user