chill-bundles/Security/Authorization/AuthorizationHelper.php
Julien Fastré 8ac41a15b6 Merge branch add_acl ref #263
Squashed commit of the following:

commit e1236655e1514fd207818aeb57789eca0d949453
Merge: c0b349b fb15bd3
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Tue Jun 30 09:51:35 2015 +0200

    Merge remote-tracking branch 'origin/master' into add_acl

    In order to prepare merging of add_acl to master

    Conflicts:
    	composer.json

commit c0b349bb5f31fe79c84f82d4dd6658c9e90ef728
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Tue Jun 30 00:40:13 2015 +0200

    fix infos in composer.json

    [ci skip]

commit 106bbf56a5060efd2a89232f278692eeb57e3092
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Mon Jun 29 22:58:48 2015 +0200

    add username and password to client auth options

    [ci skip]

commit c4990972711850616aa1426394884223d63b504f
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Mon Jun 29 22:22:20 2015 +0200

    fix quoting in timelinebuilder

commit 1db7cbea5a0fb0e8d396f8c9d8dc01240b47e96f
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 25 22:43:46 2015 +0200

    remove data_class to allow edit form

commit 7c999279310b5e2b9ecef8a9b5001c71910a822d
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Jun 24 22:33:42 2015 +0200

    fix doc for AppendScopeChoiceTypeTrait

commit 839d4c43bf6f463e705b47d5dbc0cdf7853db0b6
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Jun 24 22:30:13 2015 +0200

    refactor: move scope field to a trait

    Example usage :

      ```
      class AbcType extends Symfony\Component\Form\AbstractType
      {
          use AppendScopeChoiceTypeTrait;
          protected $authorizationHelper;
          protected $translatableStringHelper;
          protected $user;

          public function __construct(AuthorizationHelper $helper,
            TokenStorageInterface $tokenStorage,
            TranslatableStringHelper $translatableStringHelper)
          {
              $this->authorizationHelper = $helper;
              $this->user = $tokenStorage->getToken()->getUser();
              $this->translatableStringHelper = $translatableStringHelper;
          }

          public function buildForm(FormBuilder $builder, array $options)
          {
                // ... add your form there

              // append the scope using FormEvents: PRE_SET_DATA
              $this->appendScopeChoices($builder, $options['role'],
                   $options['center'], $this->user,
                   $this->authorizationHelper,
                   $this->translatableStringHelper);
           }

           public function configureOptions(OptionsResolver $resolver)
           {
                   // ... add your options

              // add an option 'role' and 'center' to your form (optional)
              $this->appendScopeChoicesOptions($resolver);
           }

       }
      ```

    [ci skip]

commit a1ac530f343146eee12b5982e4e6fceb6dc1da66
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Jun 24 00:24:30 2015 +0200

    remove unused statements

commit 74f0a4ce5dfdfa4f8fc39ce1cfe726d945e9bdec
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Jun 24 00:09:47 2015 +0200

    add missing unused statement

commit b3a49f2de8758b51c57af6ff0437f30b2fdef72e
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Tue Jun 23 23:58:57 2015 +0200

    remove ScopeType strategy and fix autorization helper

commit aaa70b5eeae76b0950110f51b8274e08bef10576
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Tue Jun 23 21:35:06 2015 +0200

    create scope type

commit 8f5b2b23c9448b8c8e752e46bdd7f9f721054a98
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 19 18:13:54 2015 +0200

    add getREachableCenter method + test on Autho.Helper

commit ab2ccb8c287f9aef12912ea9b7f5dc4998209d77
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 19 17:27:22 2015 +0200

    remove debug information [ci skip]

commit 9d481c07966a5d769d1418bb366bd6b1ea9d4f76
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 19 17:24:57 2015 +0200

    fix role hierarchy

    Now we test effectively that a user has access, not that a role may grant access

commit f4b17d0ae398fd6c26d4907377761e9f15d36a90
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 18 02:08:55 2015 +0200

    fix trait conflicts

    traits does not share the same instance of prophet any more

commit baac8ce97ba0acd4c4fe6f03278f7a0d30da28ad
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 18 01:56:39 2015 +0200

    try to fix trait error in zend strict mode (used by travis)

commit 7b9fa4b14bde72c7036a29c03a0c6cca3c7e6c74
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 18 01:24:09 2015 +0200

    fix error on trait hierarchy (should be)

commit f8b3451089f7017653bf8d280b640356df6a1841
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 18 01:03:04 2015 +0200

    add userHasAccess method to AuthorizationHelper

    This method may be used in voter to check access.

    It supports both hasCenterInterface and HasScopeInterface and check all
    required permission.

commit 9ad9f624a0aa73ca2e639fd68fe4d5559da2bbd7
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Jun 18 01:01:16 2015 +0200

    add utilities to generate prophesized entities

    entities
    - User (with permissions)
    - Center
    - Scope
    may be  generated by trait/methods

    This ease test writing about acl

commit 16008b9e64bb7f551319abea494db1c1c5a12b82
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 12 22:42:46 2015 +0200

    add test to CenterType

commit 55e2c64aba9714caf09df8dbc9595980826b296a
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 12 20:50:05 2015 +0200

    first implementation of test on CenterType

    [TRANSFER][ci skip]

commit 548fb24927cc470794a51d81f1ba6af52237363f
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Jun 10 21:15:52 2015 +0200

    add center type

    the center type is hidden if the current user can reach only one center,
    and is `entity` type if the user can reach multiple centers

commit 024e3ef8d969d25560406864d86768a260ef4402
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Mon Jun 8 00:28:51 2015 +0200

    add first impl of Access Model

    - first classes and interfaces
    - authorizationHelper + test
    - rewrite loadUser to have multi-center

commit bc5ae70c83c39a0a738e78313e33020fc284f456
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Jun 7 22:11:13 2015 +0200

    make deprecations message not fail tests

commit ab9308ed62e45171e9d355ec033b0862b9274e07
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sat Jun 6 10:19:19 2015 +0200

    introducting phpunit-bridge to handle deprecation warnings

commit 5b7a43c4d058af58578c30120670c5be5b11cfd8
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 12:49:23 2015 +0200

    fix options resolver deprecation

commit a5b4e5743f790c16dfd04e5068c5ecca9c2b1583
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 12:36:17 2015 +0200

    remove warning about deprecation in phpunit

commit 56621767936df1ea1293c49ae06380d7735c8d6c
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 11:59:50 2015 +0200

    fix pattern deprecation in routing/test

    pattern=> path

commit 17d40fc5294b245d7377572a8c918abab2484985
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 11:40:59 2015 +0200

    fix deprecation of pattern in routing

    pattern => path in routing

commit 6a33752c6439bd9dbc707df010ce1d8765eeb5fe
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 11:21:18 2015 +0200

    fix twig.form.resources deprecation

    the new key is twig.form_themes

commit adf03eb819f2d443f87d7b744f59a5adf51d2b31
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Jun 5 10:56:51 2015 +0200

    switch to symfony 2.7 [ci-skip]
2015-06-30 14:38:08 +02:00

210 lines
7.1 KiB
PHP

<?php
/*
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Security\Authorization;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Entity\Scope;
/**
* Helper for authorizations.
*
* Provides methods for user and entities information.
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AuthorizationHelper
{
/**
*
* @var RoleHierarchyInterface
*/
protected $roleHierarchy;
protected $existingRoles = array('CHILL_MASTER_ROLE', 'CHILL_PERSON_SEE',
'CHILL_PERSON_UPDATE',);
public function __construct(RoleHierarchyInterface $roleHierarchy)
{
$this->roleHierarchy = $roleHierarchy;
}
/**
* Determines if a user is active on this center
*
* @param User $user
* @param Center $center
* @return bool
*/
public function userCanReachCenter(User $user, Center $center)
{
foreach ($user->getGroupCenters() as $groupCenter) {
if ($center->getId() === $groupCenter->getCenter()->getId()) {
return true;
}
}
return false;
}
/**
*
* 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 string|Role $attribute
* @return boolean true if the user has access
*/
public function userHasAccess(User $user, HasCenterInterface $entity, $attribute)
{
$center = $entity->getCenter();
if (!$this->userCanReachCenter($user, $center)) {
return false;
}
$role = ($attribute instanceof Role) ? $attribute : new Role($attribute);
foreach ($user->getGroupCenters() as $groupCenter){
//filter on center
if ($groupCenter->getCenter()->getId() === $entity->getCenter()->getId()) {
//iterate on permissionGroup
foreach($groupCenter->getPermissionGroups() as $permissionGroup) {
//iterate on roleScopes
foreach($permissionGroup->getRoleScopes() as $roleScope) {
//check that the role allow to reach the required role
if ($this->isRoleReached($role,
new Role($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->getId() === $roleScope
->getScope()->getId()) {
return true;
}
} else {
return true;
}
}
}
}
}
}
return false;
}
/**
* Get reachable Centers for the given user, role,
* and optionnaly Scope
*
* @param User $user
* @param Role $role
* @param null|Scope $scope
* @return Center[]
*/
public function getReachableCenters(User $user, Role $role, Scope $scope = null)
{
$centers = array();
foreach ($user->getGroupCenters() as $groupCenter){
//iterate on permissionGroup
foreach($groupCenter->getPermissionGroups() as $permissionGroup) {
//iterate on roleScopes
foreach($permissionGroup->getRoleScopes() as $roleScope) {
//check that the role is in the reachable roles
if ($this->isRoleReached($role,
new Role($roleScope->getRole()))) {
if ($scope === null) {
$centers[] = $groupCenter->getCenter();
break 2;
} else {
if ($scope->getId() == $roleScope->getScope()->getId()){
$centers[] = $groupCenter->getCenter();
break 2;
}
}
}
}
}
}
return $centers;
}
/**
* Return all reachable scope for a given user, center and role
*
* @param User $user
* @param Role $role
* @param Center $center
* @return Scope[]
*/
public function getReachableScopes(User $user, Role $role, Center $center)
{
$scopes = array();
foreach ($user->getGroupCenters() as $groupCenter){
if ($center->getId() === $groupCenter->getCenter()->getId()) {
//iterate on permissionGroup
foreach($groupCenter->getPermissionGroups() as $permissionGroup) {
//iterate on roleScopes
foreach($permissionGroup->getRoleScopes() as $roleScope) {
//check that the role is in the reachable roles
if ($this->isRoleReached($role,
new Role($roleScope->getRole()))) {
$scopes[] = $roleScope->getScope();
}
}
}
}
}
return $scopes;
}
/**
* 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
*/
protected function isRoleReached(Role $childRole, Role $parentRole)
{
$reachableRoles = $this->roleHierarchy
->getReachableRoles([$parentRole]);
return in_array($childRole, $reachableRoles);
}
}