Модель расчета динамических ролей(рантайм опредение связей)

docker

Новичок
Модель расчета динамических ролей(рантайм опредение связей)

Планируется оптимальная архитектура разграничения прав для некоторого подобия ERP-системы. А именно такой специфический security-level, который был бы универсальным для различного рода объектов системы и который сам бы проверял все права, вытекающие как из статических, так и динамических ролей пользователя(свя).

Статическая роль – то что принято считать: роль, которая обеспечивает для ее пользователей повсеместный доступ для объектов определенного класса ВНЕ зависимости от свойств этих объектов. Например, роль «Администратор заказов» — значит то, что пользователи этой роли имеют доступ к любому объекту заказ.

Динамическая роль – здесь доступ к объекту рассчитывается исходя из свойств самих объектов(рантайм определение связей между объектами-субъектами). Например: роль «Ответственный за заказы» — будет иметь доступ не ко всем заказам, а только к тем, у которых он поставлен ответственным, т.е. к тем объектам-заказам у которых в свойстве «ответственный» указан id этого пользователя.

Обычно права вот этих динамических ролей разруливаются в различного рода менеджерах объектов, т.е. в дополнительном слое, вышестоящем над DAL-ом и security-level. Т.е. для расчета доступа к объекту(ам) сначала стандартным образом security-level проверяет статичные роли, если там нет доступа, то делает запросы к БД для выяснения того, должен ли у этого пользователя быть доступ к данному объекту(ам) на основании каких-либо динамических ролей. Например для того же «Ответственного за заказы» — это будет дополнительный запрос в БД, который даст нам: совпадает ли id ответственного у данного заказа с id текущего пользователя.

Так вот, это же получается то, что вот эти вот права динамических ролей, их расчет мы выносим из security-level и уже в другом, более высокоуровневом слое, вручную проверяем дополнительными запросами к БД.

И вот вопрос в том, как спланировать архитектуру этого security-level, чтобы это все инкапсулировать в нем. Т.е. мы говорим ему просто: «От имени текущего пользователя – выбрать объекты такого-то класса». Он сам смотрит сначала статические права, потом каким-то образом проверяет нет ли у пользователя доступа к объекту по каким-то его динамическим ролям – и выдает результат. Например, для админа заказов – этот запрос должен будет выдать все объекты-заказы, для Ответсвенных за заказы – все заказы, у которых ответственный == текущему пользователю и т.д.

Вся проблема в том, как построить такую архитектуру, которая позволит легко и универсально добавлять, удалять динамические роли вместе со стратегиями их вычисления в этот security level.

Можно например, иметь в базе справочник динамических ролей и к каждому id этой роли жестко привязать код правила который будет определять доступность объектов по этой роли.

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

DexizeR

Новичок
Честно говоря, никогда не пытался запихнуть т.н. динамические роли в security level(т.к. он д.б. низкоуровневым), потому что это изначально разные уровни и есть.

Поясняю, определение доступа к функционалу(управление заказами в нашем случае) и есть статическая роль. А динамическая роль это уже просто определение того, какую именно информацию необходимо отобразить. Можно провести аналогию с поиском в системе, предположим что к поиску имеют доступ только пользователи с определёнными правами, но мы ведь не определяем здесь за них какую именно информацию они будут искать, эти данные приходят из вне(вводятся пользователем), аналогично с управлением заказами, только данные берутся не от пользователя, а из сессии(например), но для функции которая в качестве аргументов принимает искомые данные для поиска не имеет значения откуда эти данные пришли(будь то user_id из сессии или search_string из запроса). Поэтому заносить динамические роли в security level, на мой взгляд, нельзя, т.к. security level - низкоуровневый и по хорошему он должен быть универсальным, а т.н. динамические роли - это уровень бизнес логики(самый верхний) и записывая его в более низкий уровень, ты теряешь универсальность.

Приблизительный код в наших проектах на конечной странице:
PHP:
<?
require 'prepend_file_name';
// подключить начальный файл(там фильтруются входящие данные,
// подключаются глобальные функции и базовые классы, конфиги,
// инициируется сессия, доступ к БД и т.п.)

getObject("User","User","classes");
// создать объект $User класса User из директории classes в глобальной
// области видимости, если объект уже существует то возвращается ссылка
// на него(но здесь ловить эту ссылку не надо, т.к. мы в глобальной
области и находимся)

getObject("Order","Order","classes","area",$_REQUEST['same_arg']);
// аргумент "area" - первый аргумент(нужен для
// шаблонизатора(root_dir)) класса Order, $_REQUEST['same_arg'] -
// второй аргумент

getObject("Base","Base","classes","area");
// класс содержащий базовые функции для большинства страниц

$User->Init('functional_name');
//если доступа нет к 'functional_name', то редирект на
// логин или предыдующую страницу (если есть), с установкой
// сообщения об отказе в доступе

$Base->ShowHeader();
// показать заголовок

$Order->ShowOrdersList($User->GetAuthUserId());
// показать список заказов для текущего пользователя

$Base->ShowFooter();
// показать концевик
?>

Аналогично для страницы поиска, только там будет свой 'functional_name' и вместо Order будет другой класс с другим аргументом.

-~{}~ 20.02.06 20:15:

Блин, надо было комментарии разбить на энтеры, а то вся страница разъехалась, а редактировать уже нельзя
 

docker

Новичок
Автор оригинала: DexizeR
Честно говоря, никогда не пытался запихнуть т.н. динамические роли в security level(т.к. он д.б. низкоуровневым), потому что это изначально разные уровни и есть.

Поясняю, определение доступа к функционалу(управление заказами в нашем случае) и есть статическая роль. А динамическая роль это уже просто определение того, какую именно информацию необходимо отобразить. Можно провести аналогию с поиском в системе, предположим что к поиску имеют доступ только пользователи с определёнными правами, но мы ведь не определяем здесь за них какую именно информацию они будут искать, эти данные приходят из вне(вводятся пользователем), аналогично с управлением заказами, только данные берутся не от пользователя, а из сессии(например), но для функции которая в качестве аргументов принимает искомые данные для поиска не имеет значения откуда эти данные пришли(будь то user_id из сессии или search_string из запроса).
В этом случае мы не решим проблему универсальности: для выборки объектов заказов для роли ответственного мы пишем фукнцию выборки, которая будет выбирать их по id ответственного этих заказов и id текущего юзера в сессии.

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

Т.е. в таком случае, мы даже повторно код использовать не сможем, т.к. для каждого статического права(право на редактирование заказов, право на редактирование сотрудников менеджером) надо будет писать кастомную функцию выборки, которая будет добавлять там свои фильтры в зависимости от user_id в сессии...

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


Поэтому заносить динамические роли в security level, на мой взгляд, нельзя, т.к. security level - низкоуровневый и по хорошему он должен быть универсальным, а т.н. динамические роли - это уровень бизнес логики(самый верхний) и записывая его в более низкий уровень, ты теряешь универсальность.
Разрешение динамических прав - это тот же security-level. И размазывать его по другим различным более высоким уровням - ну это как минимум не красиво с точки зрения архитектуры.
Лучше когда эти самые высокие уровни ничего не знают ни о каких динамически рассчитываемых правах, а получают уже готовые, проверенные объекты из security-level и с ними выполняют СВОИ специфические действия, свою дальнейшую бизнес логику....
 

DexizeR

Новичок
Я не до конца понял многие фразы из твоего последнего поста, например, что значит - "объектов заказов" ? ну и пр. Поэтому попробую пример привести.

Есть страница - список ордеров для авторизованного пользователя. За это отвечает функция
$Order->ShowOrdersList($User->GetAuthUserId());

Выглядит она примерно так:
PHP:
function ShowOrdersList($user_id) 
{ 
	$this->tpl->Define('tpl_name'); 

	$User = &getObject("User"); 

	$q  = "SELECT DISTINCT users.user_id as `user_id`"; 
	$q .= ", users.user_name as `user_name`"; 
	$q .= ", orders.order_id as `order_id`"; 
	$q .= ", orders.amount as `amount`"; 
	$q .= " FROM `orders`"; 
	$q .= " INNER JOIN `users` ON orders.user_id = users.user_id"; 

	if($User->GetAuthGroup() != $User->admin_group_name) 
		$q .= " WHERE orders.owner_id =".$user_id; 

	$q .= " ORDER BY orders.cr_date DESC"; 

	$this->db->query($q); 

	while($this->db->NextRecord()) 
	     { 
			$this->tpl->parse("order_list",array( 
			'ORDER_ID' => $this->db->f("order_id"), 
			'AMOUNT' => $this->db->f("amount"), 
			'USER_ID' => $this->db->f("user_id"), 
			'USER_NAME' => $this->db->f("user_name") 
			)); 
	     } 

	$this->tpl->PrintTemplate('tpl_name'); 
}
Т.е. никакой проблемы и не возникает(в этом примере), опиши пожалуйста подробнее случай когда возникает проблема и теряется универсальность(или ещё что нибудь).

-~{}~ 21.02.06 00:08:

А ещё лучше приведи таблицы и расскажи задачи, чтобы проблему можно было увидеть воочию, так сказать.
 

docker

Новичок
DexizeR да я тебя понял, но вот именно от таких кастомных функций и есть цель уйти...

Лучше когда эти самые высокие уровни ничего не знают ни о каких динамически рассчитываемых правах, а получают уже готовые, проверенные объекты из security-level и с ними выполняют СВОИ специфические действия, свою дальнейшую бизнес логику....
Другие мнения будут?
 

atv

Новичок
Почему бы не взять за основу доступ к файловой системе, в той же Windows NT. Очень гибкая вещь. Вместо файлов подразумеваем любые другие ресурсы организованные в дерево.

Я сам использую следующие понятия:
Ресурсы - всё что угодно к чему требуется доступ, организованы ввиде дерева, как частный случай, дерево может состоять из одного элемента.

Группы - это понятно, включают в себя пользователей.

Пользователи - тоже понятно, могут быть участниками нескольких групп, должны быть участником хотя бы одной группы (например "ВСЕ")

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

Проверка доступа:
Все объекты в системе действуют от имени авторизовавшегося пользователя. Права доступа для групп и пользователей хранятся в базе. Объект AccessRights (нарпимер) обслуживает все остальные объекты, предоставляя им информацию о правах доступа. Когда один объект обращается к методу другого объекта (отвечающего за доступ к ресурсу) тот запрашивает у AccessRights разрешено ли указанное действие над указанным ресурсом для текущего пользователя. AccessRights возвращает ответ ДА или НЕТ. Проверка принадлежности пользователя к группам и проверка его прав производиться самим AccessRights.

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

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

Чтобы решить проблему с "Динамической ролью", достаточно ввести абстракцию "Владелец", и точно так же выставлять для неё права.

Такая система может быть встроена на любом уровне приложения - от низкоуровневых операций с БД или файлом до макрообъектов таких как страница или раздел сайта. Достаточно поместить в объекте, отвечающем за доступ к ресурсу, вызов метода AccessRights::checkAccess().
 

whirlwind

TDD infected, paranoid
Сначала нужно абстрагироваться до уровня объектов. Когда будет стабильный ORM, контроль доступа легко влезет слоем между ORM и источником данных и в контексте ORM превращается в элементарные фильтры, при условии что каждый объект это не просто ActiveRecord, но и итератор. Отталкиваться нужно от абстрактной модели объекта. А там уже какие хочешь права: статические - условие из таблиц ACL в виде фильтра в базовом, динамические - переопределяешь onSelect,selectObjects,etc конечного...
 

DexizeR

Новичок
2 atv, по смыслу тоже самое, что писал я, только у меня вместо действий функционал(одно и тоже в данном случае) и ресурсы - плоский список, а не дерево.

А к теме того, что верхний уровнь не знает о расчитываемости прав, то может примерно следующее подойдёт:
PHP:
if($AccessRights->checkAccess($resource, $action))
{
	list($object,$method) = $AccessRights->GetMethod($resource,$action);

	$object->$method();
}
Соответственно для каждого действия и ресурса(если отличаются) описаны методы в соответствующих классах. А в методе GetMethod каким либо образом(хоть в БД) храняться имена объектов и их методов для каждого действия над каждым ресурсом(если ресурсы разного типа, например).
 

atv

Новичок
Я это представляю себе по другому:
PHP:
class FileAccess
{
    function read()
    {
        if (! $AccessRights->checkAccess($resource, $action = 'read')) {
            throw new AccessDenied();
        }
        // ...code
    }

    function write()
    {
        if (! $AccessRights->checkAccess($resource, $action = 'write')) {
            throw new AccessDenied();
        }
        // ...code
    }
}
Т.е. действие неразрывно связано с методом объекта, отвечающего за доступ к ресурсу.
 
Сверху