Как работать с ACL

Modjo

Новичок
Как работать с ACL

добрых времён суток.
пытаюсь разобраться с системой ACL, появилось несколько вопросов по реализации. насколько я понимаю суть сводится к тому, что у нас есть два дерева - ресурсы и роли. если некая роль А хочет получить доступ к ресурсу Б, то нам нужно проблежатся по дереву ресурсов от ресурса Б вверх в поисках первого правила разрешающего или запрещающего доступ. если правила не оказалась, то бегим вверх по дереву ролей.
первое что не понятно - как хранить это всё в базе? как в системе унифицировать ресурсы (новости, статьи, форум, каталог)?
 

korchasa

LIMB infected
Modjo
Эрм, нифига не понял. Своими словами: есть дерево ролей:
- Местный барин
- Петя (наследник Местного барина)
дерево ресурсов:
- ходить в лес
- гулять с девушками
и таблица(массив) правил:
- Васе можно "ходить в лес"
- Местному барину можно "гулять с девушками"
Когда нам нужно проверить какое-то правило:
можно ли Пете "гулять с девушками"
, то мы ищем его в массиве правил, и не находим. Но так как Петя наследник Местного Барина, то мы ищем правило для местного барина и находим.

Насчет хранения деревьев - в поиск по форуму.

Готовые можно посмотреть Zend_Acl или limb acl
 

Modjo

Новичок
смотрел именно в зенде. там описан механизм работы с правилами, но нет механизма хранения. пытаюсь сформулировать то что я хочу узнать, но получается коряво.
приземлимся от барина с васей к сайту. бонально, у меня есть два раздела, новости и форум. по идеи сущности разные, хранятся они по разному, в зенде, на сколько я понял, предлагается под каждую сущность написать потомка, которая могла бы идентифицировать себя в списке ресурсов. что именно хранить в базе? в качестве идентифицирующего ключа. конкретно. форум, раздел "Общее" что спрашивать у ACL (имеет ли роль к ресурсу форум, подраздел Общее право на чтение), так? как это типизировать? или опять сумбурно получилось?
 

korchasa

LIMB infected
Modjo
Выстроить цепочку наследования, к которой приводить:
- весь форум - категория "БД" - подкатегория "MySQL" - топик #42

Есть другой подход. Позволяет уменьшать количество правил. Мы, в итоге, их вообще в базе не храним.
 

Modjo

Новичок
посмотрел этот другой подход... на самом деле подход абсолютно тот же что и в зенде, даже немного упрощенее. в целом идея понятна - идентифицировать узлы дерева ресурсов предлагается по стринговым алиасам. хранить в базе как дерево. во время работы приложения вытягивать оба дерева и создавать соответствующие деревья обьектов ACL.
 

korchasa

LIMB infected
Автор оригинала: Modjo
посмотрел этот другой подход... на самом деле подход абсолютно тот же что и в зенде, даже немного упрощенее.
В зенде нет динамического определения роли объекта в контексте ресурса, это приходится делать своим кодом. В вашем примере мне не понадобиться вообще хранить дерево в базе, мне хватит правильного определения ролей. Пост может редактировать его автор, а так же автор топика, к которому относится этот пост:
PHP:
$acl->addRole('owner');

$acl->allow('owner', 'post', 'edit');

class Post implements ...
{
   function getRoleFor($member)
   {
      if($this->owner_id == $member->id)
        return 'owner';
      if($this->getTopic()->owner_id == $member->id)
        return 'owner';
   }
}
 

Modjo

Новичок
взять хотя бы мой пример - помимо авторов, допустим, править могут ещё и модераторы, а ещё какой нибудь Вася Пупкин... без дерева в общем случае не обойтись таки.
по поводу динамического определения роли - так в зенде для этого можно Zend_Acl_Assert_Interface (условные правила) заточить.

по поводу хранения решил завести 3 таблицы
Ресурсы
CREATE TABLE `acl_resorces` (
`id` INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`pid` INTEGER(11) UNSIGNED NOT NULL, // указатель на родителя
`alias` VARCHAR(32) NOT NULL DEFAULT '' // строковый идентификатор
)

Роли
CREATE TABLE `lenin_acl_role` (
`id` INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`pid` INTEGER(11) UNSIGNED NOT NULL,
`alias` VARCHAR(32) NOT NULL DEFAULT ''
)

Привелегии
CREATE TABLE `lenin_acl_rule` (
`id` INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`role_id` INTEGER(11) UNSIGNED NOT NULL,
`resource_id` INTEGER(11) UNSIGNED NOT NULL,
`privilege` VARCHAR(16), // алиас привилегии
`allow` TINYINT(1) NOT NULL, // разрешено или запрещено
`assert` TEXT // сериализованное зендовское условие
)

оптимальна ли будет такая структура базы?
 

korchasa

LIMB infected
Автор оригинала: Modjo
взять хотя бы мой пример - помимо авторов, допустим, править могут ещё и модераторы,
$acl->addRole('moderator');
$acl->allow('moderator');

а ещё какой нибудь Вася Пупкин...
Что значит "какой-нибудь"? Модератор ветки? Администратор?

без дерева в общем случае не обойтись таки.
Без дерева нет, а без дерева в БД - вполне. Какой смысл? Правила меняются редко, хитрые выборки по ним не нужны.

так в зенде для этого можно Zend_Acl_Assert_Interface (условные правила) заточить
Ассерт это фильтр на отсечение не нужного, он к резолвингу вообщен никаким боком. Да и логичнее, на мой взгляд, этот код держать в самих ресурсах, а не "где-то снаружи".
 

iceman

говнокодер
PHP:
    // Загрузка Ролей
    $roles = $db->select()
                ->from('acl_roles', array('role'=>'LOWER(role)'));
    $roles = $db->fetchAll($roles);

    // Загрузка Ресурсов
    $resources = $db->select()
                    ->from('acl_resources', array('resource'=>'LOWER(resource)'));
    $resources = $db->fetchAll($resources);

    // Загрузка связи Роль+Ресурс
    $fkAcl = $db->select()
                ->from('acl', array('role.role'=>'LOWER(role)',
                                    'res.resource'=>'LOWER(resource)'))
                ->join(array('role'=>'acl_roles'), 'role.id = acl.fk_role', array())
                ->join(array('res' =>'acl_resources'),
                             'res.id = acl.fk_resource', array());

    $fkAcl = $db->fetchAll($fkAcl);

    // Добавляем Роли
    foreach ($roles as $key=>$value){
        $acl->addRole(new Zend_Acl_Role($value['role']));
    }

    // Добавляем Ресурсы
    foreach ($resources as $key=>$value){
       $acl->add(new Zend_Acl_Resource($value['resource']));
    }

    foreach ($fkAcl AS $key=>$value){
        $acl->allow($value['role.role'], $value['res.resource']);
    }
у меня лично эту связь загружает Служба ACL, выше приведенный код из нее...

каждая страничка, либо группа страничек - это ресурс...

вот проверка в самой службе
PHP:
if(!$acl->isAllowed($userProfile->role, $router->config->var->resource)){
	    	die('Access Denied');
	    }
 

Modjo

Новичок
2korchasa
Вася Пупки, это на случай, если администратор захочет дать какие то привилегии отдельному человеку не из группы модераторов. или, если для форума нужен модератор, который отвечает только за один из подразделов.

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

2iceman
ну насколько я понимаю у тебя почти такая же структура в бд что я и привел выше. у меня к тебе вопрос:
допустим ты задаешь привилегии отдельному юзеру, что ты пишешь в acl_roles.role? его id?
и другой вопрос по ресурсам, допустим, есть форум : ) тебе нужно задать привилегии для одного из его разделов, что в этом случае записываешь в acl_resources.resource? если просто айди туда записать, то ведь оно может конфликтовать с привилегиями для других разделов

****************

а вот ещё как бы мысля, как думаете - а стоит ли обьеденить таблицу ролей с таблицей пользователей?
 

iceman

говнокодер
Modjo
допустим ты задаешь привилегии отдельному юзеру, что ты пишешь в acl_roles.role? его id?
нет, создам отдельную группу и дам ее одному пользователю

тебе нужно задать привилегии для одного из его разделов, что в этом случае записываешь в acl_resources.resource?
данная реализация будет проходить уже в коде форума.
Либо написать более продуманную службу ACL +) но так и так сквозной код будет присутствовать скрипте форума. +)

-~{}~ 09.01.09 14:10:

а вот ещё как бы мысля, как думаете - а стоит ли обьеденить таблицу ролей с таблицей пользователей?
у пользователей должна быть принадлежность к группе, а роль в моем случае - это группа. (это самый примитивный пример)
 
Сверху