Помогите разобраться с hook_node_grants и hook_node_access_records в D7

Главные вкладки

Аватар пользователя long.skinny.boy long.skinny.boy 15 февраля 2022 в 23:13

Добрый день, вроде тема простая, но запутался окончательно с логикой всех этих realm-ов и gid-ов :/

Задача выглядит просто:
* есть роль пользователя REVIEWER
* есть типа материала NEWS для хранения материалов типа новости,
внутри которого есть числовое поле STATE, представляющее собой текущий статус жизненного цикла новости
данное поле может хранить значения 2,3,4 (2 - это черновик, 3 - премодерация, 4 - публикация)

Суть такова, что нужно сделать так, чтобы когда значения STATE - 2, то новость как бы в режиме черновика у автора, и данную новость автор может просматривать, редактировать и удалять, все остальные при этом (кроме админа соответственно) не могут её видеть в принципе.
Когда же статус 3, т.е. на премодерации, то автор её уже может только просматривать (редактировать и соответственно удалять не может), а также просматривать её может любой пользователь с ролью REVIEWER
И собственно когда статус 4 - любой пользователь сайта может её просматривать, и никто не может редактировать и удалять

Другими словами получается тут скорее в статусе 2 нужно запретить просмотр, редактирование и удаление всем пользователям кроме автора, в статусе 3 нужно запретить просмотр всем пользователям кроме автора и пользователей с ролью REVIEWER, при этом также запретить всем редактирование и просмотр, а в статусе 4 - просто всем запретить редактирование и удаление.

Читая справки так понимаю, что по уму здесь нужно делать через 2 хука - hook_node_access_records и hook_node_grants

В первом хуке (hook_node_access_records) будет происходит запись в базу - в пределах какого реалма (realm)? какой уровень доступа (gid) необходим пользователю для доступа к ноде (nid) и какие при этом будут доступны действия над ней (grant_view, grant_update, grant_delete)
А во втором (hook_node_grants) в зависимости от какого действия над нодой на текущий момент ($op) и для какого текущего пользователя ($account) будет предоставлен определенный грант

И в общем - совсем запутался

Начал с простого:

<?php

/**
 * Implements hook_node_access_records().
 */
function mymodule_node_access_records($node) {
  
$grants = array();
  if (
$node->type == 'news') {
    
$workflow_state field_get_items('node'$node'field_news_workflow');
    if (
$workflow_state[0]['value'] == 2) {
      
$grants[] = array(
        
'realm' => 'mymodule_access_news',
        
'gid' => $node->uid,
        
'grant_view' => 1,
        
'grant_update' => 1,
        
'grant_delete' => 1,
        
'priority' => 0,
      );
    }
  }
  return 
$grants;
}

/**
 * Implements hook_node_grants().
 */
function mymodule_node_grants($account$op) {
  
$grants = array();
  
$grants['mymodule_access_news'] = array($account->uid);
  return 
$grants;
}
?>

Получается сделал проверку, если нода со статусом 2 (т.е. черновик), то создай для неё грант с id-шником автора ноды, при этом предоставь этому гранту: просмотр, правку и удаление. А дальше относительно текущего пользователя, без разницы кто он вообще, осуществляй проверку что ему доступно на основе его гранта в виде его uid-шника.
Ну и как результат ему будет доступно все его ноды в полных правах, которые имеют статус 2 (черновик)

А дальше я туплю, как нужно описать например для статуса 3, т.е. когда нужно автору предоставить только просмотр, а reviewer-у тоже только просмотр.

Получается первый хук будет представлять собой, что то следующее:

<?php

/**
 * Implements hook_node_access_records().
 */
function mymodule_node_access_records($node) {
  
$grants = array();
  if (
$node->type == 'news') {
    
$workflow_state field_get_items('node'$node'field_news_workflow');
    if (
$workflow_state[0]['value'] == 2) {
      
$grants[] = array(
        
'realm' => 'mymodule_access_news',
        
'gid' => $node->uid,
        
'grant_view' => 1,
        
'grant_update' => 1,
        
'grant_delete' => 1,
        
'priority' => 0,
      );
    } elseif (
$workflow_state[0]['value'] == 3) {
      
$grants[] = array(
        
'realm' => 'mymodule_access_news',
        
'gid' => ??????????,
        
'grant_view' => 1,
        
'grant_update' => 0,
        
'grant_delete' => 0,
        
'priority' => 0,
      );
    }
  }
  return 
$grants;
}

?>

Но что нужно указывать в gid-е? И как это нужно описать ниже в hook_node_grants?
Вроде всё наверняка просто, но не могу логику понять

Комментарии

Аватар пользователя ivnish ivnish 16 февраля 2022 в 12:37
1

Суть такова, что нужно сделать так, чтобы когда значения STATE - 2, то новость как бы в режиме черновика у автора, и данную новость автор может просматривать, редактировать и удалять, все остальные при этом (кроме админа соответственно) не могут её видеть в принципе.
Когда же статус 3, т.е. на премодерации, то автор её уже может только просматривать (редактировать и соответственно удалять не может), а также просматривать её может любой пользователь с ролью REVIEWER
И собственно когда статус 4 - любой пользователь сайта может её просматривать, и никто не может редактировать и удалять

Я бы сделал проще. В hook_form_alter проверял бы значение поля статуса и давал разрешение на редактирование по вашей логике. Аналогично и на удаление. Для управлением просмотром есть hook_node_view

В общем, Вы слишком глубоко закопались, я думаю Smile

Если, конечно, это не тестовое задание и сделать его нужно именно этими хуками Pardon

Аватар пользователя long.skinny.boy long.skinny.boy 16 февраля 2022 в 12:42

Это делаю для себя, просто как читал - через гранты это более правильное решения, а также считая, что отображать контент я ещё буду через Views, то он на них тоже внимание обращает при рендеринге

Аватар пользователя voviko voviko 16 февраля 2022 в 16:51

вы уверены что gid - Это ид пользователя? что если это просто число от 0 до xxx определяющее логику в hook_node_grants

Аватар пользователя long.skinny.boy long.skinny.boy 16 февраля 2022 в 17:32

По идее это не id пользователя, просто в первом случае это как бы наверное идеально подошло бы, когда нужно разграничить доступ только для автора ноды, т.е. чтобы могло быть как бы одно совпадение между hook_node_grants и hook_node_access_records касательно этой ноды.
Т.е. предоставить полный доступ только одному уникальному gid-у, и таким гид как раз касательно ноды может быть uid её автора

А вот дальше, когда уже на втором и третьем статусе нужно дать доступ уже не только автору, но и reviewer-у (а также всем), то там уже какое то идеологическое значение должно быть

Т.е. первый статус у меня сейчас вроде как работает, так как я и ожидаю, а со следующими вариантами не могу додуматься

Аватар пользователя long.skinny.boy long.skinny.boy 16 февраля 2022 в 22:08

Вот я как раз у Никиты уже вдоль и поперёк прочитал этот материал Biggrin , но не могу додуматься как в моём случае сделать

В его статье он получается создаёт 2 варианта определенного типа ноды, первый в котором есть галочка members_only и во втором когда её нету, соответственно в первом случае это типа приватный материал, во втором публичный. Таким образом в пространстве реалма mymodule_access_article создал 2 вида гранта (приватный и публичный) - их ещё называют, что то вроде замков
А далее когда приходит пользователь к этому замку (реалму), он на основе его членства в определенной роли, выдаёт ему один ключ (публичный) либо сразу 2 (публичный и приватный)

Иногда практикую одно и тоже пересказывать как будто самому себе на паровозиках, и иногда понимает разобраться, но в данном случае не помогло Smile

Так вот в моём случае похоже есть 3 замка
1) первый всегда имеет один ключ, и он может быть только у одного пользователя в разрезе одной ноды (именно поэтому наверняка в таблице node_access первичный ключ состоит из nid, gid и realm). Потому я в реалме mymodule_access_news создаю ему gid с id-шником автора.
2) второй получается должен уже предоставлять немного иные права, только на view, но при этом gid уже похоже будет более статичный, и похоже скорее всего здесь уже другое имя realm-а должно быть
3) ...

Таким образом я сейчас уже пришёл к следующему коду:


<?php

/**
 * Implements hook_node_access_records().
 */
function mymodule_node_access_records($node) {
  
$grants = array();
  if (
$node->type == 'news') {
    
$workflow_state field_get_items('node'$node'field_news_workflow');
    if (
$workflow_state[0]['value'] == 2) {
      
$grants[] = array(
        
'realm' => 'mymodule_draft_news',
        
'gid' => $node->uid,
        
'grant_view' => 1,
        
'grant_update' => 1,
        
'grant_delete' => 1,
        
'priority' => 0,
      );
    } elseif (
$workflow_state[0]['value'] == 3) {
      
$grants[] = array(
        
'realm' => 'mymodule_review_news',
        
'gid' => 0,
        
'grant_view' => 1,
        
'grant_update' => 0,
        
'grant_delete' => 0,
        
'priority' => 0,
      );
    } elseif (
$workflow_state[0]['value'] == 4) {
      
$grants[] = array(
        
'realm' => 'mymodule_publish_news',
        
'gid' => 0,
        
'grant_view' => 1,
        
'grant_update' => 0,
        
'grant_delete' => 0,
        
'priority' => 0,
      );
    }
  }
  return 
$grants;
}

/**
 * Implements hook_node_grants().
 */
function mymodule_node_grants($account$op) {
  
$grants = array();
  if (
$op == 'view') {
    
$grants['mymodule_draft_news'] = array($account->uid);
    if (
in_array('reviewer'$account->roles)) {
      
$grants['mymodule_review_news'] = array(0);
    }
  }
  return 
$grants;
}

?>

Т.е. я создал 3 реалма под каждый статус, первый отрабатывает успешно,
второй успешно уже предоставляет доступ только на просмотр для пользователей с ролью reviewer (только для нод со статусом 3 = review)
но тут проблема, получается нужно также дать доступ только на просмотр этой ноды ещё и автору, и как вот это описать в hook_node_grants

Аватар пользователя voviko voviko 17 февраля 2022 в 13:25

<?php
/**
 * @file
 * Здесь мы будем писать весь код.
 */

define('ADVANSED_ACCESS_REALM''advansed_access_node_access_records');
define('ADVANSED_ACCESS_VIEW'1);
define('ADVANSED_ACCESS_EDIT'2);
define('ADVANSED_ACCESS_DENIED'3);

/**
 * Используем hook_node_grants().
 *
 * Данный хук срабатывает при просмотре содержимого и выдаёт пользователю
 * соответствующий уровень доступа к содержимому.
 *
 * $account - информация о пользователе, который обратился к ноде.
 * $op - операция которая выполняется (view, edit, delete).
 */
function advansed_access_node_grants($account$op) {
  
// Получим автора ноды
  // Нас интересует лишь просмотр  и редактирование содержимого.
  // Удаление будет ограничено системными правами (что в админке друпала).

  

if ($op == 'view') {
    
$grants[ADVANSED_ACCESS_REALM] = array(
      
ADVANSED_ACCESS_VIEW,
      
ADVANSED_ACCESS_EDIT,
    );
  }
  if (
$op == 'update') {
    
$grants[ADVANSED_ACCESS_REALM] = array(
      
ADVANSED_ACCESS_VIEW,
      
ADVANSED_ACCESS_EDIT,
    );
  }
  if (
$op == 'delete') {
    
$grants[ADVANSED_ACCESS_REALM] = array(
      
ADVANSED_ACCESS_VIEW,
      
ADVANSED_ACCESS_EDIT,
    );
  }
  return 
$grants;
}

/**
 * Используем hook_node_access_records().
 *
 * В данном хуке определяется, какой уровень доступа необходим для ноды.
 * Данная запись делется при редактировании\добавлении нового материала.
 *
 * Если у вас уже есть содержимое, которому нужно "пересобрать" права, то
 * воспользуйтесь фукнцией node_access_rebuild() или в админке:
 * admin/reports/status/rebuild
 */
function advansed_access_node_access_records($node) {

  

// Мы задаем права доступа только для нашего типа содержимого 'Article'.

  

if ($node->type == 'article') {
    global 
$user;
    
watchdog('advansed_access'$user->uid);
    
$members_only field_get_items('node'$node'field_members_only');
    
$members_only $members_only[0]['value'];
    
// Если отмечено "Для своих".
    
if ($members_only == 2) {
      
// Указываем ноде, что смотреть её могут пользователи только с gid
      // который отвечает за просмотр приватного содержимого.
      
if ($user->uid == 0) {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_VIEW,
          
'grant_view' => 1,
          
'grant_update' => 1,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
        return 
$grants;
      }
      elseif (
$user->uid == $node->uid && $user->uid != 0) {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_EDIT,
          
'grant_view' => 1,
          
'grant_update' => 1,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
      }
      elseif (
in_array('authenticated user'$user->roles)) {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_EDIT,
          
'grant_view' => 1,
          
'grant_update' => 1,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
      }
      else {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_DENIED,
          
'grant_view' => 0,
          
'grant_update' => 0,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
      }
    }
    if (
$members_only == 3) {
      if (
$user->uid == $node->uid) {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_EDIT,
          
'grant_view' => 1,
          
'grant_update' => 0,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
      } else {
        
$grants[] = array(
          
'realm' => ADVANSED_ACCESS_REALM,
          
'gid' => ADVANSED_ACCESS_DENIED,
          
'grant_view' => 1,
          
'grant_update' => 0,
          
'grant_delete' => 0,
          
'priority' => 0,
        );
      }
    }
  }

  return 

$grants;
}

?>

Написано на коленке. Надо разобраться с очерёдностью и условиями.