
В этой статье мы обсудим возможности настройки прав доступа к материалам. Людям, которые более-менее знакомы с этой системой известно, что базовые права пользователей регулируются по ссылке /admin/people/permissions.
Можно настроить CRUD действия для типов материалов в целом. Но что делать, если нужна детальная настройка прав? Если нужно ограничить доступ на просмотр или запретить редактировать только конкретные материалы?
Задача: Настроить права доступа к материалам, находящимся в закрытой группе.
Представим, что вы создаете треккер задач для своей команды. Ваши коллеги могут просматривать все проекты и задачи, но редактировать и создавать их могут только сотрудники, задействованные на конкретном проекте. Согласитесь, стандартная таблица настройки прав здесь будет бессильна. На помощь вам придут Гранты (Gratns).
Drupal Grants API — верхнеуровневая система настройки прав доступа к материалам.
Основная архитектура Drupal является масштабируемой и достаточной, поэтому все верхнеуровненвые права транслируются вниз. Все действия ядра Drupal и некоторых Contrib-модули над материалами используют гранты для настройки необходимых прав. Самым ярким примером может послужить модуль Views: все представления проверяют доступ к данным с помощью грантов.
Если взглянуть на грант детальнее, то его можно сравнить с ключом, который дает пользователю право на совершение действия, например, позволяет создавать материал или изменять его. В применении к нашей задачи у нас будет три типа ключей:
- Просмотр определенного сообщения в группе: Грант1;
- Редактирование определенного сообщения в группе: Грант2;
- Удаление определенного сообщения в группе: Грант3.
Логично предположить, что если вы можете удалять сообщения, то можете и просматривать их. В таком случае можно немного модифицировать ключи, применив наследование. Таким образом, ключ на удаление также позволит просматривать и редактировать сообщения.
Теперь давайте подумаем, как мы будем выдавать эти ключи. Действия с сообщениями могут совершать только те пользователи, которые состоят в группе. Поэтому первоначальные типы ключей дополняются следующим образом:
- Пользователь состоит в группе: Грант1;
- Пользователь состоит в группе и имеет право редактировать: Грант2;
- Пользователь состоит в группе и имеет право удалять: Грант3.
Допустим, что клиент изъявил желание участвовать в работе над проектом. Вы добавили его в треккер и теперь в праве скрыть от него некоторые сообщения. Для этого потребуется ввести еще один ключ, который запрещает чтение: Грант4. Выбрав пункт «Не показывать клиенту», мы сможем ограничить доступ на чтение. Следовательно членам группы выдается два ключа Грант1 и Грант4. Таким образом, клиенты не могут просматривать сообщения с дополнительной блокировкой или сообщения «неклиента».
Попробуем это реализовать:
/**
* Implements hook_node_access_records().
*/
function laresan_test_node_access_records($node) {
if ($node) {
// Get 'show clients' field.
$items = field_get_items('node', $node, 'field_shared_show_clients');
// Default to zero for now.
$client_switch = 0;
// Check if set.
if ($items != FALSE) {
foreach ($items as $item) {
$client_switch = $item['value'];
}
}
// Initialise grants array.
$grants = array();
// We start with setting grants for group.
if ($node->type == "work_group") {
$grants[] = array(
'realm' => 'laresan_test_node_access_view',
'gid' => $node->nid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
// Add extra realm for non-client groups.
if ($client_switch == 0) {
$grants[] = array(
'realm' => 'laresan_test_node_access_view_nonclients',
'gid' => $node->nid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 10,
);
}
}
// So now we've done Groups, it's time for other content types.
// We need the Group reference nid from these guys, not nid.
// Companies and teams are not referenced to a group in this context, skip
// them.
elseif ($node->type == "workgroup_message") {
// Get reference fields value.
$items = field_get_items('node', $node, 'field_shared_group_reference');
foreach ($items as $item) {
$nid = $item['nid'];
}
// Set Grants.
else {
$grants[] = array(
'realm' => 'laresan_test_node_access_view',
'gid' => $nid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
$grants[] = array(
'realm' => 'laresan_test_node_access_edit',
'gid' => $nid,
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 0,
'priority' => 0,
);
// Add extra realm for non-client nodes.
if ($client_switch == 0) {
$grants[] = array(
'realm' => 'laresan_test_node_access_view_nonclients',
'gid' => $nid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 10,
);
$grants[] = array(
'realm' => 'laresan_test_node_access_edit_nonclients',
'gid' => $nid,
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 0,
'priority' => 10,
);
}
}
}
return $grants;
}
}
В этом коде мы определили какие бывают «замки» на каждом материале в нашем случае и сделали это с помощью хук-функции hook_node_access_records. Данная функция вызывается каждый раз, когда материалы изменяются или по нажатию на «переопределить права» в административной панели /admin/reports/status/rebuild. Вся информация о замках хранится в таблице node_access.
Осталось только раздать ключи пользователям. Для этого воспользуемся хук-функцией hook_node_grants. Она вызывается на каждой странице и проверяет доступы к текущему материалу и всем с ним связанным.
Замечание: Результат выполнения данной функции не кэшируется! Старайтесь не перегружать ее.
/**
* Implements hook_node_grants().
*/
function laresan_test_node_grants($account, $op) {
// Load user entity.
$user = user_load($account->uid);
// Load groups that user is granted.
$group_ids = field_get_items('user', $user, 'field_groups');
// Provide the user his group grants.
$grants = array();
foreach ($group_ids as $data) {
// Grants access for non-client nodes.
if (user_access("ol show non-client content")) {
$grants['laresan_test_node_access_view_nonclients'][] = $data['nid'];
}
if (user_access("create ol_group content")) {
// Check if Group is Archived, then don't give edit perms.
// @TODO: for efficiency, do custom query.
$groupnode = node_load($data['nid']);
if (isset($groupnode->status) && $groupnode->status == 1) {
$grants['laresan_test_node_access_edit'][] = $data['nid'];
$grants['laresan_test_node_access_edit_nonclients'][] = $data['nid'];
}
}
// Grants access for all other the nodes in users group.
$grants['laresan_test_node_access_view'][] = $data['nid'];
}
// Tell good old Drupal about users grants.
return $grants;
}
Данный код загружает данные пользователя, проверяет его роль и затем назначает гранты на действия с материалами.
