В этой статье мы обсудим возможности настройки прав доступа к материалам. Людям, которые более-менее знакомы с этой системой известно, что базовые права пользователей регулируются по ссылке /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; }
Данный код загружает данные пользователя, проверяет его роль и затем назначает гранты на действия с материалами.