No-code инструменты в Drupal - это одна из причин, по которой стоит выбрать Drupal для своего будущего проекта. При построении грамотной архитектуры на этапе создания сайта, вы сможете в дальнейшем развивать его с существенно меньшими затратами, поскольку даже для сложных задач можно найти простые решения, не требующие программирования.

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

Задача состоит в том, чтобы настроить права видимости комментариев следующим образом:

  1. Если у пользователя есть права на просмотр всех комментариев, то у него нет ограничений.
  2. Пользователь может видеть комментарии, только если может просматривать задачу, к которой прикреплен комментарий.
  3. Чтобы просмотреть комментарий, пользователь должен быть участником проекта, к которому прикреплена задача, к которой относится комментарий, и в рамках этого проекта у него должны быть права на просмотр комментариев этого типа.

Задача решается с помощью модуля ECA. В результате решения получается модель, которую подробно разберем в этой статье:

 

1

 

Все модели ECA состоят из события, условий и действий.

1. Первым пунктом в модели устанавливается событие определения доступа (Determining Entity Access). Оно срабатывает при любом взаимодействии с сущность - при просмотре, редактировании, создании, удалении. Настройки её выглядят следующим образом:

 

2

 

  1. В поле Name указывается название элемента, которое будет отображаться на экране с моделью. Тут удобно давать краткие пояснения, чтобы глядя на модель было понятно, что происходит в этом месте.
  2. Во втором поле нужно ограничить событие каким-либо типом сущности. Если этого не сделать, то оно будет срабатывать всегда, например, при просмотре проекта. Так как в этой модели работа ведется только с комментариями, то тут указывается машинное имя сущности комментария.
  3. В третьем поле указывается при каком именно событии нужно срабатывать. В данном случае настраивается только реакция на просмотр, поэтому в поле указывается View, чтобы модель не реагировала в остальных случаях.

2. У события определения прав доступа есть два возможных исхода (на самом деле больше, но они тут не понадобятся): разрешить и запретить. И чтобы легче контролировать результат, первым действием запретим просмотр комментария всем, а при выполнении условий будем разрешать. Именно для этого и нужно действие под номером 2. У него очень простые настройки:

 

3

 

В поле 1 указывается название, в поле 2 результат проверки доступа. В данном случае - запретить. Если в модели оставить только эти два элемента, то доступ на просмотр комментариев будет запрещен всем пользователям без исключения.

3. Теперь перейдем к реализации условий, при которых доступ будет разрешен. Сначала поставим такое же действие, как в прошлом пункте, только настроим его на разрешение доступа:

 

4

 

4. Свяжем 2 и 3 между собой и настроим первое самое простое условие - разрешать доступ, если у пользователя есть права на просмотр всех комментариев. Этим правом обладают все сотрудники компании:

 

5

 

Условие называется “Current user has permission”, оно проверяет наличие выбранного права доступа у текущего пользователя. В 1 вписываем название.

Во 2 выбираем право, наличие которого будем проверять. В данном случае это право на просмотр комментариев у всех проектов. Поскольку сама сущность комментария является кастомной, это право также создано программного. Это чуть ли не единственная часть, которая была создана с помощью кода, однако можно обойтись и стандартными средствами.

Теперь модель работает так: При просмотре комментария запрещаем доступ к сущности, а если у пользователя есть права, то разрешаем.

5. Теперь добавим разветвление для пользователей, у которых нет права на просмотр всех комментариев. В новой ветке проверим есть ли у пользователя права на просмотр задачи, к которой относится комментарий:

 

6

 

Условие называется “Entity: is accessible” и проверяет есть ли у текущего пользователя права на выбранное действие, по отношению к указанной сущности.

  1. Вписываем название;
  2. Выбираем интересующее действие. В данном случае проверяем может ли пользователь просматривать задачу;
  3. Нужно указать сущность, для которой будем осуществлять проверку. Тут записано hd_log:ticket:entity - разберем что это значит. В контексте данной модели hd_log - это машинное имя комментария, к которому применяется данная модель. У комментария есть поле entity_reference, через которое он связан с задачей. Поле имеет машинное имя ticket. А Entity - указание на то, что нам нужно не само поле, а сущность, на которую это поле ссылается. Таким образом в этом поле указывается, что в качестве сущности для проверки доступа нужно взять задачу (entity), на которую мы ссылаемся через поле (ticket) в комментарии (hd_log).

Если это условие протянуть к действию 3, то эта ветка модели будет работать следующим образом: При просмотре комментария запрещаем просмотр. Если у пользователя есть доступ к просмотру задачи, то разрешаем ему просматривать комментарий.

6. Теперь нам нужно еще одно разветвление. Одна из веток будет нужна для описания поведения простого комментария, вторая ветка будет для комментария с затраченным временем. Это нужно потому, что не все сотрудники со стороны клиента могут просматривать затраченное на задачу время. Соответственно дальше мы проверяем тип комментария и нам нужно обеспечить одновременное выполнение двух условий: условия 5 и условия 7 в одном случае и 12 во втором случае. Для этого используется действие “Chain action for AND condition”, его можно воспринимать как простое И. То есть условия 5 И 7 должны быть выполнены для движения дальше по модели. А во второй ветке - условия 5 И 12. У этого действия совсем нет настроек, оно только связывает два условия между собой:

 

7

 

7. Тут начинается ветка, относящаяся к комментариям обычного типа. Тип комментария задается в поле с машинным именем “Type”, в котором допустимы два значения: “comment” и “time”. К обычному типу относятся комментарии, где в этом поле указано значение “comment”. То есть в этой ветке необходимо проверить значение этого поля. Для этого поставим условие “Entity: compare field value”, которое нужно для сравнения значения в выбранном поле с заданным значением:

 

8

 

  1. Впишем название для отображения на общей схеме;
  2. Здесь указывается машинное имя поля, в котором хранится сравниваемое значение;
  3. Дальше выбираем оператор сравнения. Тут может быть “больше”, “меньше”, “равно” и т.д. Нам нужно выбрать “равно”;
  4. И тут указывается значение, которое будем ожидать. В данном случае это “comment”, поскольку все комментарии обычного типа имеют это значение в поле “type”.

Теперь это условие будет срабатывать для всех комментариев с обычным типом.

8. Дальше в этой же ветке нужно проверить является ли комментарий скрытым и показать его только в том случае, если комментарий таковым не является. В наших процессах нет необходимости давать права на просмотр скрытых комментарием кому-либо, кроме сотрудников, поэтому здесь не будет дополнительного ветвления и мы просто добавим еще одно условие с помощью действия AND, такого же, как рассмотрено выше, в пункте 6:

 

9

 

9. Тут нужно проверить является ли комментарий скрытым. Скрыт ли комментарий, определяется с помощью поля с машинным именем “hidden”. Это булевое поле, которое может принимать два значения: 0 и 1. Если 0, то комментарий считается видимым, если 1, то скрытым. Соответственно необходимо в этом условии выполнить такую же проверку поля, как было выше:

 

10

 

  1. Впишем название;
  2. Зададим машинное имя проверяемого поля;
  3. Выберем оператор “Равно”;
  4. Укажем, что ожидаемое значение должно равняться 1;
  5. Инвертируем условие. Если этого не сделать, то условие будет выполняться не так как нужно, оно будет срабатывать только на скрытые комментарии, соответственно даст просматривать только скрытые, а так его можно прочесть как “все не скрытые комментарии”. Можно было бы в ожидаемом значении поставить 0 и не делать инвертирование, тогда условие проверяло бы не “не скрытые”, а только видимые, но оно не сработало бы в случае, если поле по какой-то причине осталось пустым, поэтому надежнее будет сделать как описано выше.

10. В этом действии мы переключаем пользователя, от имени которого выполняется вся модель. По умолчанию она выполняется от текущего пользователя, со всеми его возможностями и ограничениями, но тут мы переключаем этот контекст на выполнение от имени администратора, у которого нет ограничений по правам. Это необходимо сделать для того, чтобы мы имели возможность выполнить следующее условие - проверить является ли текущий пользователь участником проекта, к которому относится задача комментария. Подробнее это будет разобрано чуть ниже. Само действие очень простое. Оно называется “User: switch current account”:

 

11

 

1. Тут нужно указать идентификатор пользователя, на которого мы переключаем контекст выполнения модели. в Drupal главным пользователем является пользователь с идентификатором 1, его и используем.

11. Теперь проверим является ли пользователь участником проекта с необходимыми правами. В нашем сервис-деск принадлежность пользователя к проект определяется сущностью (с машинным именем Project_member), которая содержит поля entity_reference со ссылками на пользователя (поле с машинным именем field_user) и на проект (поле с машинным именем field_project), обеспечивающие связь пользователя с проектом, а также несколько булевых полей, описывающих права пользователя в этом проекте, например, возможность заводить задачи, видеть комментарии (поле с машинным именем field_log_comment_view), оставлять комментарии и т.д. Поскольку нам нужно проверить является ли пользователь участником проекта и есть ли у него права на просмотр комментариев в нем, нам просто нужно проверить существует ли сущность project_member, которая ссылается на текущий проект, пользователя и в которой выставлено право на просмотр комментариев. Для этого воспользуемся условием Entity: exists, которое проверяет существование сущности по набору свойств:

 

12

 

13

 

  1. Условие позволяет выбрать по каким свойствам нужно проверять наличие сущности, тут можно выбрать комбинацию типа сущности и её идентификатора или типа сущности и значений в полях. Нам подходит второе.
  2. Дальше указываем тип сущности, среди которых будем искать. В нашем сервис-деске эта сущность реализована с помощью модуля ECK.
  3. Тут разберем подробно каждую строчку. В этом поле указываются свойства, по которым нужно проверять наличие сущности. Мы будем проверять по полям Проект (field_project), Пользователь (field_user), Права на просмотр комментариев обычного типа (field_log_comment_view, статус участника проекта (status). Все свойства нужно указывать с новой строки. Первое свойство выглядит так: field_project: "[hd_log:ticket:entity:project:target_id]" - мы ищем сущность, у которой в поле field_project указано значение [hd_log:ticket:entity:project:target_id]. Это значение является токеном, который можно прочитать так: у текущего комментария (hd_log) взять поле, через которое он ссылается на задачу (ticket), загрузить сущность задачи (entity), в задаче взять поле, через которое она ссылается на проект (project) и использовать идентификатор проекта, указанный в этом поле (target_id). Таким образом мы проходим цепочку комментарий→задача→проект, получаем id проекта и ищем сущность project member, которая ссылается на этот же проект.

Следующая строка: field_user: "[account:uid]". field_user - это имя поля, через которое осуществляется связь между сущностью project_member и пользователем. Нам нужно найти сущность project_member, где в field_user указан текущий пользователь. В начале выполнения модели, текущий пользователь сохраняется в токен [account], а в токене [account:uid] мы получим id этого пользователя.

Далее проверяем права field_log_comment_view: 1 - тут все просто: field_log_comment_view - это машинное имя поля, а 1 - значение соответствующее тому, что права предоставлены. И последним свойством стоит status. Это свойство, определяющее является ли участник проекта действующим, или его участие приостановлено. Поле также может содержать два значения 0 и 1.

Таким образом это условие проверяет, есть ли сущность, которая соответствует условиям:

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

Если все это выполнено, то модель переходит на следующий шаг.

Тут нужно отметить, что согласно нашим процессам, клиенты компании не имеют доступа к сущности project member, поэтому простое выполнение этого условия выдаст ошибку “Доступ запрещен”. Именно для того, чтобы избежать возникновения этой ошибки, и используется действие переключения контекста пользователя на предыдущем шаге.

Это последний элемент в этой ветке. Условие 11 приводит к действию 3, где пользователю дается разрешение на просмотр комментария.

12. Ветка, которая начинается с этого условия, почти такая же, как ветка выше, поэтому рассмотрим её кратко. Условие 12 такое же как условие 7, только проверяется, что комментарий имеет тип “time”, а не “comment”

13. Это действие совершенно аналогично действию 10 и установлено с той же целью.

14. Тут все, как в 11, только проверка стоит не на право просмотра комментария с типом comment, а на просмотр комментария с типом time, то есть условие выглядит так: field_log_time_view: 1

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

Описанная модель корректно работает на версии модуля ECA < 2.1. После обновления требуется её изменение. В новой версии действует принцип “Что запрещено однажды, то запрещено навсегда”, поэтому запрещение первым действием больше не работает и нужно добавлять ответвления с дополнительными проверками, которые будут запрещать доступ при невыполнении условия.

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