2012-04-27 4 views
3

Вообще у меня есть следующая бизнес-модель:symfony2 acl groups

Есть пользователи и группы. Каждый пользователь принадлежит только к одной группе, и количество групп не определяется заранее (а также количество пользователей для большинства сайтов). Также существует несколько различных объектов занятости, которые могут принадлежать пользователю.

Группы не являются отдельными объектами, которые должны контролироваться самими ACL, но они должны влиять на то, как другие объекты должны контролироваться так же, как группы unix.

Существует 3 основные роли: SUPERADMIN, ADMIN и USER.

  • SUPERADMIN способен делать что угодно с любым объектом.
  • ПОЛЬЗОВАТЕЛЬ, как правило, может читать/писать собственные объекты (включая его/ее) и читать из своей группы.
  • ADMIN должен иметь полный контроль над объектами внутри своей группы , но не из других групп. Я не понимаю, как применить наследование ACL здесь (и может ли это вообще можно использовать ).

Также меня интересует, как отказ в доступе может быть применен в ACL. Подобно тому, как пользователь имеет доступ на чтение и запись ко всем своим полям, кроме имени пользователя. Пользователь должен только читать свой логин. I.e. логично предоставить доступ для чтения/записи к его собственному профилю, но запретить запись для входа в систему, а не напрямую определять доступ для чтения/записи ко всем его полям (кроме имени входа).

+0

Ok, я решил его без использования ACL , но с возможностью интеграции ACL: я зарегистрировал свою собственную услугу голосования. – kirilloid

+0

@krilloid - У меня такой же вопрос, как и вы. Можете ли вы поделиться своим кодом обслуживания избирателя? Это будет высоко ценится. спасибо – Flukey

+0

kirilloid Если вы нашли подходящий дизайн, было бы неплохо разместить его здесь, отвечая на ваш собственный вопрос. Как @Flukey, я был бы признателен, поскольку я приближаюсь к аналогичной задаче. Благодарю. – mokagio

ответ

2

Хорошо, вот оно. Код вообще не идеален, но лучше, чем ничего.

Избирательное обслуживание.

<?php 
namespace Acme\AcmeBundle\Services\Security; 

use Symfony\Component\DependencyInjection\ContainerInterface; 
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Acl\Domain\ObjectIdentity; 

class GroupedConcernVoter implements VoterInterface { 

    public function __construct(ContainerInterface $container) 
    { 
     $this->container = $container; 
     $rc = $this->container->getParameter('grouped_concern_voter.config'); 
     // some config normalization performed 
     $this->rightsConfig = $rc; 
    } 

    // even though supportsAttribute and supportsClass methods are required by interface, 
    // services that I saw, leaves them empty and do not use them 

    public function supportsAttribute($attribute) 
    { 
     return in_array($attribute, array('OWNER', 'MASTER', 'OPERATOR', 'VIEW', 'EDIT', 'CREATE', 'DELETE', 'UNDELETE', 'DEPLOY')) 
      // hacky way to support per-attribute edit and even view rights. 
      or preg_match("/^(EDIT|VIEW)(_[A-Z]+)+$/", $attribute); 
    }   

    public function supportsClass($object) 
    { 
     $object = $object instanceof ObjectIdentity ? $object->getType() : $object; 
     // all our business object, which should be manageable by that code have common basic class. 
     // Actually it is a decorator over Propel objects with some php magic... nevermind. 
     // If one wants similar solution, interface like IOwnableByUserAndGroup with 
     // getUserId and getGroupId methods may be defined and used 
     return is_subclass_of($object, "Acme\\AcmeBundle\\CommonBusinessObject"); 
    }  

    function vote(TokenInterface $token, $object, array $attributes) 
    { 

     if (!$this->supportsClass($object)) { 
      return self::ACCESS_ABSTAIN; 
     } 
     if ($object instanceof ObjectIdentity) $object = $object->getType(); 

     if (is_string($object)) { 
      $scope = 'own'; 
      $entity = $object; 
     } else { 
      if ($object->getUserId() == $this->getUser()->getId()) { 
       $scope = 'own'; 
      } else if ($object->getGroupId() == $this->getUser()->getGroupId()) { 
       $scope = 'group'; 
      } else { 
       $scope = 'others'; 
      } 
      $entity = get_class($object); 
     } 

     $user = $token->getUser(); 
     $roles = $user->getRoles(); 
     $role = empty($roles) ? 'ROLE_USER' : $roles[0]; 

     $rights = $this->getRightsFor($role, $scope, $entity); 
     if ($rights === null) return self::ACCESS_ABSTAIN; 

     // some complicated logic for checking rights... 
     foreach ($attributes as $attr) { 
      $a = $attr; 
      $field = ''; 
      if (preg_match("/^(EDIT|VIEW)((?:_[A-Z]+)+)$/", $attr, $m)) list(, $a, $field) = $m; 
      if (!array_key_exists($a, $rights)) return self::ACCESS_DENIED; 
      if ($rights[$a]) { 
       if ($rights[$a] === true 
       or $field === '') 
        return self::ACCESS_GRANTED; 
      } 
      if (is_array($rights[$a])) { 
       if ($field == '') return self::ACCESS_GRANTED; 
       $rfield = ltrim(strtolower($field), '_'); 
       if (in_array($rfield, $rights[$a])) return self::ACCESS_GRANTED; 
      } 

      return self::ACCESS_DENIED; 
     } 
    } 

    private function getRightsFor($role, $scope, $entity) 
    { 
     if (array_key_exists($entity, $this->rightsConfig)) { 
      $rc = $this->rightsConfig[$entity]; 
     } else { 
      $rc = $this->rightsConfig['global']; 
     } 
     $rc = $rc[$role][$scope]; 
     $ret = array(); 
     foreach($rc as $k => $v) { 
      if (is_numeric($k)) $ret[$v] = true; 
      else $ret[$k] = $v; 
     } 
     // hacky way to emulate cumulative rights like in ACL 
     if (isset($ret['OWNER'])) $ret['MASTER'] = true; 
     if (isset($ret['MASTER'])) $ret['OPERATOR'] = true; 
     if (isset($ret['OPERATOR'])) 
      foreach(array('VIEW', 'EDIT', 'CREATE', 'DELETE', 'UNDELETE') as $r) $ret[$r] = true; 
     return $ret; 
    } 

    private function getUser() { 
     if (empty($this->user)) { 
      // Not sure, how this shortcut works. This is a service (?) returning current authorized user. 
      $this->user = $this->container->get('acme.user.shortcut'); 
     } 
     return $this->user; 
    } 

} 

И конфигурация ... на самом деле, она специфична для реализации и ее структура абсолютно произвольна.

grouped_concern_voter.config: 
    global: 
     ROLE_SUPERADMIN: 
      own: [MASTER] 
      group: [MASTER] 
      others: [MASTER] 
     ROLE_ADMIN: 
      own: [MASTER] 
      group: [MASTER] 
      others: [] 
     ROLE_USER: 
      own: [VIEW, EDIT, CREATE] 
      group: [VIEW] 
      others: [] 
    "Acme\\AcmeBundle\\User": 
     # rights for ROLE_SUPERADMIN are derived from 'global' 
     ROLE_ADMIN: 
      own: 
       VIEW: [login, email, real_name, properties, group_id] 
       EDIT: [login, password, email, real_name, properties] 
       CREATE: true 
      group: 
       VIEW: [login, email, real_name, properties] 
       EDIT: [login, password, email, real_name, properties] 
      # rights for ROLE_ADMIN/others are derived from 'global' 
     ROLE_USER: 
      own: 
       VIEW: [login, password, email, real_name, properties] 
       EDIT: [password, email, real_name, properties] 
      group: [] 
      # rights for ROLE_USER/others are derived from 'global' 
    "Acme\\AcmeBundle\\Cake": 
     # most rights are derived from global here. 
     ROLE_ADMIN: 
      others: [VIEW] 
     ROLE_USER: 
      own: [VIEW] 
      others: [VIEW] 

И, наконец, пример использования. Где-то в контроллере:

$cake = Acme\AcmeBundle\CakeFactory->produce('strawberry', '1.3kg'); 
$securityContext = $this->get('security.context'); 
if ($securityContext->isGranted('EAT', $cake)) { 
    die ("The cake is a lie"); 
} 
0

при создании группы, создать роль ROLE_GROUP_ (идентификатор группы), содействовать группе с этой ролью, и предоставление разрешения с rolesecurityidentity

+0

Я думал об этом, но в группе есть разные роли. Также я должен обновлять все роли, если пользователь меняет группу. – kirilloid

+0

@kirilloid вы всегда можете создать ROLE_GROUP_A_ (идентификатор группы), ROLE_GROUP_B_ (идентификатор группы) и т. Д., И я действительно не понимаю, что вы подразумеваете под «обновлением всех ролей, если пользователь меняет группу» (?), Когда пользователь меняет группа автоматически выполняет роли из этой новой группы и не имеет каких-либо ролей предыдущего –

+0

Для изменения «правил игры» требуется обновленная группа записей ACL. На днях я решаю, есть новый правильный ВКУС, и любой может попробовать любой пирог.В случае групп в качестве ролей я должен написать какой-то новый код и обновить текущий ACL. Inb в случае моей конфигурации я должен обновлять только одну строку в config: 'ROLE_USER: others: [TASTE]' – kirilloid

 Смежные вопросы

  • Нет связанных вопросов^_^