Symfony 2 acl isFieldGranted выдает исключение

Я занят реализацией ACL на уровне класса + поля (classFieldAce) в своем приложении. Кажется, все работает нормально, но я получаю неожиданное поведение от метода isFieldGranted() из ACL. Вот мой код:

// setup ACL
$className       = 'Acme\Model\Junk';
$oid             = new ObjectIdentity('class', $className);

try {
    $acl  = $aclProvider->findAcl($oid);
} catch (Exception $e) {
    $acl  = $aclProvider->createAcl($oid);

    $roleUser  = new RoleSecurityIdentity('ROLE_USER');
    $mask      = new MaskBuilder(4); // 4 = EDIT

    $acl->insertclassFieldAce('name', $roleUser, $mask->get());
}

$aclProvider->updateAcl($acl);

Все идет нормально. Я назначаю своему пользователю роль "ROLE_USER". Теперь я хочу запустить некоторые проверки (вручную, когда я создаю сервис). Вот код проверки (пока внутри контроллера):

// check the ACL
$className       = 'Acme\Model\Junk';
$oid             = new ObjectIdentity('class', $className);
$aclProvider     = $this->get('security.acl.provider');

try {
    $acl             = $aclProvider->findAcl($oid);
} catch (...)

все хорошо. Теперь то, что я хочу проверить:

$sids = array();
foreach ($this->getUser()->getRoles() as $role) {
    $sids[] = new RoleSecurityIdentity($role);
}
$masks = array();
$masks[] = MaskBuilder::MASK_EDIT;

if ($acl->isFieldGranted('name', $masks, $sids)) {
    echo "OK";
} else {
    echo "NOT ALLOWED";
}

На данный момент все работает, я получаю вывод «ОК». Единственное, в чем я немного не уверен, так это в очень окольном способе отправки $sids на проверку, поэтому мой первый вопрос будет заключаться в том, есть ли более быстрый способ, кроме ручного создания списка.

Все начинает идти «не так» (как неожиданно), когда я пытаюсь проверить более высокую маску:

$sids = array();
foreach ($this->getUser()->getRoles() as $role) {
    $sids[] = new RoleSecurityIdentity($role);
}
$masks = array();
$masks[] = MaskBuilder::MASK_OWNER;

if ($acl->isFieldGranted('name', $masks, $sids)) {
    echo "OK";
} else {
    echo "NOT ALLOWED";
}

вместо возврата false $acl->isFieldGranted генерирует исключение NoAceFoundException.

Я неправильно проверяю поле, или я должен просто ловить исключения здесь?

Обновление: добавлены журналы:

код проверки изменен на:

    $this->get('logger')->debug('XX: ACL retrieved, checking field grants');
    try {
        if ($acl->isFieldGranted('name', $masks, $sids, true)) {
            $this->get('logger')->debug('XX: ACL OK for field :name:');
        }
    } catch (NoAceFoundException $e) {
        $this->get('logger')->debug('XX: ACL NOT OK for field :name: NoAceFoundException thrown');
    }

При успешной проверке журналы выглядят следующим образом:

DEBUG - SELECT o.id as acl_id, o.object_identifier, o.parent_object_identity_id, 
o.entries_inheriting, c.class_type, e.id as ace_id, e.object_identity_id, e.field_name, 
e.ace_order, e.mask, e.granting, e.granting_strategy, e.audit_success, e.audit_failure, 
s.username, s.identifier as security_identifier FROM acl_object_identities o INNER JOIN 
acl_classes c ON c.id = o.class_id LEFT JOIN acl_entries e ON ( e.class_id = o.class_id AND 
(e.object_identity_id = o.id OR e.object_identity_id IS NULL) ) 
LEFT JOIN acl_security_identities s ON ( s.id = e.security_identity_id ) WHERE (o.id =16)

DEBUG - SELECT t0.name AS name1, t0.roles AS roles2, t0.id AS id3 FROM staff_group t0 
INNER JOIN rel_staff_staff_group ON t0.id = rel_staff_staff_group.group_id 
WHERE rel_staff_staff_group.staff_id = ?  Context: ["242"]

DEBUG - XX: ACL retrieved, checking field grants

DEBUG - XX: ACL OK for field :name:

При неудачной проверке меняется только эта строчка (я сравнивал логи в двух вкладках браузера)

DEBUG - XX: ACL NOT OK for field :name: NoAceFoundException thrown

Нет никакой разницы в регистрации между

$acl->isFieldGranted('name', $masks, $sids)

а также

$acl->isFieldGranted('name', $masks, $sids, true)

Обновление 2:

Я открыл отчет об ошибке в Symfony, и он был исправлен https://github.com/symfony/symfony/issues/9433


person mogoman    schedule 15.10.2013    source источник
comment
@freetrace спасибо за предложение; обновленный вопрос, но мало что получил из журналов   -  person mogoman    schedule 16.10.2013
comment
Ok. Попробуйте вставить это var_dump($acl->getClassFieldAces('name')); перед вашим $acl->isFieldGranted('name', $masks, $sids).   -  person Serge Kvashnin    schedule 16.10.2013
comment
вот вам pastebin.com/RPddrX4P .   -  person mogoman    schedule 16.10.2013
comment
Может ли это быть ошибкой в ​​системе ACL?   -  person mogoman    schedule 16.10.2013
comment
Действительно ошибка, я обновил вопрос   -  person mogoman    schedule 12.12.2013


Ответы (1)


Похоже, вы должны поймать NoAceFoundException в своем коде и выбросить AccessDeniedException:

try{
    if (!$acl->isFieldGranted('name', $masks, $sids)) {
        throw new Symfony\Component\Security\Core\Exception\AccessDeniedException();
    } 
} catch (NoAceFoundException $e) {
    throw new Symfony\Component\Security\Core\Exception\AccessDeniedException();
}

Потому что:

Первый подходящий ACE примет окончательное решение относительно комбинации
разрешение/удостоверение. Если он разрешает, этот метод вернет true, если он отрицает, метод продолжит проверку следующей комбинации разрешения/удостоверения. Этот процесс повторяется до тех пор, пока либо не будет найден ACE, предоставляющий доступ, либо пока не останется комбинаций разрешений и идентификаторов. Наконец, мы либо сгенерируем исключение NoAceFoundException, либо откажем в доступе.

Таким образом, если MaskBuilder::MASK_OWNER/RoleSecurityIdentity($role) комбинация не найдена, будет выброшена NoAceFoundException. Подробнее см. это строка 137.

person Serge Kvashnin    schedule 16.10.2013
comment
Спасибо за исследование. Честно говоря, я нахожу это странным, потому что логическое значение TRUE/FALSE проверяется гораздо быстрее, чем блок TRY/CATCH. - person mogoman; 16.10.2013
comment
@mogoman Я согласен с тобой. Это странно, и я не понимаю, почему они выдают исключение вместо простого FALSE. - person Serge Kvashnin; 16.10.2013