Как сократить количество правил в ACL (Access Control List)?
Главная проблема ACL – ее размер
Из этой проблемы формируются две гадости: во-первых правила долго писать, во-вторых по ним искать сложно. Попробуем пойти нестандартным путем и решить проблему, сделав роли более селективными.
Стандартные способы борьбы
Для уменьшения количества правил, обычно применяют наследование ролей и ресурсов, а так же множественные роли на один инстанс объекта. В итоге имеем дерево, нелинейный алгоритм, и количество проходов по нему отличное от нуля
Обратная сторона – использование масок доступа. Проверка по маске это быстро, но маска не бесконечная. А как же “показывать только друзьям”?
В общем надо решать как-то иначе.
Как же?
Кто лучше всех знает кем для статьи является конкретный пользователь?
Правильно, сама статья.
На практике оказывается, что информация, которая необходима для определения роли конкретного объекта, в рамках конкретного субъекта, лежит либо в самом субъекте, либо “близко” к нему. Так пусть он и определяет, кто в данный момент перед ним.
В LIMB’е это выглядит так:
class Article implements lmbRolesResolverInterface, lmbResourceProviderInterface
{
function getRoleFor(Member $member)
{
if($this->getOwnerId() === $member->getId())
return 'owner';
if($this->getCategory()->getModeratorId() === $member->getId())
return 'moderator';
}
function getResource()
{
return 'article';
}
}
class Member implements lmbRoleProviderInterface
{
function getRole()
{
return 'member';
}
}
//простой пользователь сайта
$this->acl->addRole('member');
//наш контенто-писака, жаждущий кармы
$this->acl->addRole('owner', 'member');
//модер, уныло бдящий за порядком на сайте
$this->acl->addRole('moderator', 'owner');
//а я статья. Просто статья
$this->acl->addResource('article');
//простой пользователь может статью комментировать
$this->acl->allow('member', 'article', 'comment');
//автор ее редактировать
$this->acl->allow('owner', 'article', 'edit');
// модератор акцептировать
$this->acl->allow('moderator', 'article', 'accept');
/* создадим по объектику на каждую роль */
$just_member = new Member();
$just_member->save();
$owner = new Member();
$owner->save();
$moderator = new Member();
$moderator->save();
$category = new ArticleCategory();
$category->setTitle('About LIMB');
$category->setModerator($moderator);
$category->save();
$article = new Article();
$article->setCategory($category);
$article->setOwnerId($owner->getId());
$article->save();
/* проверим нашу "магию" */
var_dump($this->acl->isAllowed($member, $article, 'comment')); //bool(true)
var_dump($this->acl->isAllowed($member, $article, 'edit')); //bool(false)
var_dump($this->acl->isAllowed($member, $article, 'approve')); //bool(false)
var_dump($this->acl->isAllowed($owner, $article, 'comment')); //bool(true)
var_dump($this->acl->isAllowed($owner, $article, 'edit')); //bool(true)
var_dump($this->acl->isAllowed($owner, $article, 'approve')); //bool(false)
var_dump($this->acl->isAllowed($moderator, $article, 'comment')); //bool(true)
var_dump($this->acl->isAllowed($moderator, $article, 'edit')); //bool(false)
var_dump($this->acl->isAllowed($moderator, $article, 'accept')); //bool(true)
Данный подход впервые пришлось использовать при создании acl, для фреймворка Adept, а теперь подобная штука есть и в LIMB’е.
К сожалению нет – все проверки на лету.
А есть ли какой-нибудь способ при такой организции доступа искать объекты, к которым есть доступ? Например, найти все статьи, которые может модерировать текущий пользователь. Я правильно понимаю, что так не выйдет?
Ресурс не всегда может определить роль. Если он не возвращает ничего для предложенного объекта, то берется роль объекта. Там удобно резолвить общую для сайта логику (гости, пользователи, админы).
Не совсем понятно, зачем нужен Member::getRole(), ведь роли для текущего пользователя резолвятся самим объектом по известной логике (конкретно тут id + owner_id/moderator_id)