Николай Ланец
30 июня 2013 г., 22:02

Глобальные и локальные политики доступов в MODX Revolution

В этот раз постараемся разобраться с методами проверки прав доступов ->hasPermission() и ->checkPolicy(); Я не буду в этом топике рассказывать как в принципе создавать Роли, группы пользователей, настраивать доступы к контекстам и группам ресурсов. Об этом написано не мало. Но так как многие настраивая эти политики (то есть зная где это делается) часто не получают желаемого результата (то есть не всегда понимают как что работает), то в этом топике я опишу принципы проверки прав к контекстам и ресурсам, чтобы каждый всегда мог проверить что и куда у него разрешено и где что работает или нет.
Итак, для начала повторно отметим, что для проверки прав есть два метода: 1. ->checkPolicy() 2. ->hasPermission() И здесь как раз и стоит отметить, что выполняют они на самом деле одно и то же, просто с разными объектами. ->checkPolicy() проверяется на конкретном объекте, к примеру $modx->resource->checkPolicy(), то есть в данном случае мы проверяем права доступов к конкретному объекту (ресурсу). Это мы условно назовем «Локальные политики». А $modx->hasPermission(), как я уже писал ранее, это на самом деле $modx->context->checkPolicy(), то есть проверка прав именно к контексту, так как все так или иначе работает в раках контекста. Это мы и назовем «Глобальные политики».
Сразу приведу пример, чтобы стало понятней. Дано: группа пользователей имеет права доступов load, list, view к контексту web, и к определенной группе ресурсов только load. У нас есть ресурс, который находится в этой группе ресурсов, на которую как мы и сказали, пользователь имеет только права load. Проверяя его права на данном ресурсе $resource->checkPolicy('view'), мы получим отрицательный результат. Все, к этому ресурсу у нас нет доступа. Но, проверяя его глобальные права $modx->hasPermission('view') (то есть $modx->context->checkPolicy('view')), мы получим положительный ответ, так как его права к данному контексту load, list, view.
Сразу отмечу, что в штатной работе фронтенда вам больше понадобятся именно политики локальные, то есть на группы ресурсов, так как именно за счет настроек доступов к различным группам ресурсов и происходит контроль что кому можно показывать, а кому нет. А вот в бэкенде больше проверяются контекстные политики, так как элементарно не имея прав доступа 'frames' к контексту, не получится войти в админку. Так же все меню и т.д. проверяют глобальные политики, и только работая с конкретными объектами (редактирование и просмотр ресурсов, сохранение и т.п.) будут уже проверять локальные политики доступов к конкретным объектам.
Итак, здесь немного разобрались. Теперь необходимо выучить пару правил. 1. «Если не настроено ни одного правила доступа, то разрешено все». 2. «Если настроено хоть одно правило хоть для кого-то, то все, что не разрешено, то запрещено».
Объясню. К примеру, вы создали новый контекст, к которому не настроили ни одного правила доступа, даже для anonymus. В таком случае чтобы вы ни проверяли методом $modx->hasPermission(), у вас всегда будет положительный результат, даже $modx->hasPermission('sdfsdfweeefwe wef we dfs'); Но если вы к этому контексту пропишите права для группы анонимусов load only, то все остальные группы пользователей, для которых не настроены права к этому контексту, потеряют все права к этому контексту, даже группа Админов (не забываем, что принципиально в MODX Revolution нет разницы между группами, есть только настройки прав. Даже анонимусы могут иметь больше прав, чем у админов, если так прописаны политики доступов). Так вот, если проверить права в данном контексте анонимусов $modx->hasPermission('load'), то мы получим положительный результат. Если пользователь авторизуется и для его группы не настроены политики доступов к данному контексту, то данный пользователь не будет иметь к контексту вообще никаких доступов.
Заметка: как раз по ходу экспериментов с доступами, удалил для группы Админов права на доступ к контексту web, и потерял на этот контекст все права, так как доступ к контексту все еще имели анонимусы. Вот если бы все права на контекст были удалены, то тогда бы доступ был. Пришлось переключаться в SUDO и восстанавливать доступ.
То же самое касается и ресурсов. Если ресурс не входит ни в одну группу ресурсов, или к этим группам ресурсов не настроено никаких политик доступов, то всем пользователям будет все разрешено.
Теперь научимся проверять, какие политики настроены, а какие нет (чтобы не гадать, все ли у нас ОК). Вариант 1: надо проверить настроены ли какие политики доступов к контексту. Пишем такой код:
print '<pre>'; $modx->switchContext($context_name); $policy = $modx->context->findPolicy(); print_R($policy);
Если вы проверяете доступы в текущем контексте, то можно без $modx->switchContext($context_name); Выполним его. Если политики не настроены для контекста вообще, то мы увидим пустой массив. Если настроены, то увидим типа этого:
Array( [modAccessContext] => Array ( [web] => Array ( [0] => Array ( [principal] => 0 [authority] => 9999 [policy] => Array ( [load] => 1 ) ) [1] => Array ( [principal] => 2 [authority] => 9999 [policy] => Array ( [load] => 1 [list] => 1 [view] => 1 [save] => 1 [remove] => 1 [copy] => 1 [view_unpublished] => 1 ) ) ) ) )
Здесь мы видим, что к контексту web прописаны следующие политики: 1. Для группы пользователей 0(анонимусы) (principal — группа пользователей) даны права (policy) только load. 2. Для группы пользователей 2 прописаны load, list, view и еще 4 политики. Таким образом только пользователи данных групп будут иметь соответствующие доступы к контексту web. Все остальные группы пользователей не будут иметь права вообще. Уточню, что это глобальные политики, проверяемые через $modx->hasPermission().
Так же давайте отметим, что корень этого массива имеет ключ modAccessContext. Это очень важно, так как именно в этом объекте (в таблице modx_access_context), а не в самом объекте modContext, прописаны политики доступов к контексту (точнее связь контекст — группа пользователей — ранг — политика). Прописано это в методе modContext::findPolicy()
public function findPolicy($context = '') { $policy = array(); $enabled = true; $context = !empty($context) ? $context : $this->xpdo->context->get('key'); if (!is_object($this->xpdo->context) || $context === $this->xpdo->context->get('key')) { $enabled = (boolean) $this->xpdo->getOption('access_context_enabled', null, true); } elseif ($this->xpdo->getContext($context)) { $enabled = (boolean) $this->xpdo->contexts[$context]->getOption('access_context_enabled', true); } if ($enabled) { if (empty($this->_policies) || !isset($this->_policies[$context])) { $c = $this->xpdo->newQuery('modAccessContext'); $c->leftJoin('modAccessPolicy','Policy'); $c->select(array( 'modAccessContext.id', 'modAccessContext.target', 'modAccessContext.principal', 'modAccessContext.authority', 'modAccessContext.policy', 'Policy.data', )); $c->where(array( 'modAccessContext.principal_class' => 'modUserGroup', 'modAccessContext.target' => $this->get('key'), )); $c->sortby('modAccessContext.target,modAccessContext.principal,modAccessContext.authority,modAccessContext.policy'); $acls = $this->xpdo->getCollection('modAccessContext',$c); foreach ($acls as $acl) { $policy['modAccessContext'][$acl->get('target')][] = array( 'principal' => $acl->get('principal'), 'authority' => $acl->get('authority'), 'policy' => $acl->get('data') ? $this->xpdo->fromJSON($acl->get('data'), true) : array(), ); } $this->_policies[$context] = $policy; } else { $policy = $this->_policies[$context]; } } return $policy; }
Кстати, очень интересный факт: как видите, в этой функции прописана проверка системной настройки access_context_enabled. То есть если в настройках системы указать access_context_enabled = false, то политики безопасности к контексту вообще не будут проверяться. Это может быть очень удобно тогда, когда надо дать полные права на контекст какому-нибудь отдельному пользователю (указав в его настройках этот параметр), или отключить временно проверки для определенного контекста (дебаг или типа того). Но интересно еще и описание данной настройки на русском языке:
Включает или отключает проверку прав доступа к контекстам. ВАЖНО: Если эта настройка установлена в «Нет», то все политики доступа к контекстам будут игнорироваться! Не отключайте проверку прав доступа к контекстам для всей системы или для контекста «mgr», иначе вы отключите доступ к интерфейсу управления сайтом.
То есть говорит, что не отключайте эту настройку особенно для контекста mgr, а то отключится доступ к этому контексту. На самом же деле не доступ отключится, а отключится проверка доступов, и наоборот, пользователь получит неограниченный доступ к этому контексту (если она авторизован). При чем он сможет отредактировать любой документ (если тот не находится в группе ресурсов, к которой этот пользователь не имеет доступа), сможет создать любой сниппет и т.д. В общем получит полный root.
Вариант 2: проверить доступ к конкретному ресурсу. Пишем такой код:
print '<pre>'; $resource = $modx->getObject('modResource', $id); $policy = $resource->findPolicy(); print_R($policy);
Если проверяете текущий документ, то можно просто
print '<pre>'; $policy = $modx->resource->findPolicy(); print_R($policy);
Опять-таки, если политики доступа не настроены, то получите пустой массив. Если настроены, то увидите типа этого:
Array ( [modAccessResourceGroup] => Array ( [1] => Array ( [0] => Array ( [principal] => 1 [authority] => 0 [policy] => Array ( [load] => 1 [list] => 1 [view] => 1 ) ) ) ) )
Здесь принцип такой же, как и с контекстами. principal — группа пользователей, authority — ранг, policy — массив разрешенных политик.
Так же видим, что все политики хранятся в объекте modAccessResourceGroup (таблица access_resource_groups).
Вот наверно и все, что нужно знать о политиках.
P.S. Вот еще пара заметок на тему политик доступов:

Добавить комментарий