6.2.6.1. Ограничения
Ограничения (Constraints) позволяют реализовать row-level access control, т.е. контроль доступа на уровне экземпляров сущностей. В отличие от разрешений, которые накладываются на классы сущностей, ограничения накладываются на конкретные экземпляры. Ограничения можно накладывать на чтение, создание, модификацию и удаление сущностей, так что фреймворк будет отфильтровывать некоторые экземпляры или запрещать операции с экземплярами, соответствующими ограничениям. Кроме того, можно задать специальные ограничения, не привязанные к действиям CRUD.
Пользователь получает набор ограничений от всех групп начиная со своей и вверх по иерархии. Поэтому чем ниже пользователь в иерархии групп, тем больше у него ограничений. |
Следует отметить, что все ограничения проверяются при операциях с данными, осуществляемых клиентом через стандартный DataManager. В случае несоответствия проверяемой сущности условиям ограничений при создании, модификации или удалении, выбрасывается исключение RowLevelSecurityException
. См. также раздел Проверки доступа к данным для получения информации о том, как ограничения доступа к данным используются различными механизмами фреймворка.
Существует два типа проверки ограничений: проверка в базе данных и проверка в памяти.
-
Для ограничений с проверкой в базе данных условия задаются с помощью фрагментов выражений на языке JPQL. Эти фрагменты подставляются в каждый запрос, выбирающий экземпляры данной сущности. Таким образом, сущности, не соответствующие условиям ограничения, отфильтровываются на уровне базы данных. Ограничение с проверкой в базе данных можно задать только на чтение сущностей, и они затрагивают только корневые сущности в загружаемых графах объектов.
-
Для ограничений с проверкой в памяти условия задаются с помощью кода Java (во время разработки), или выражений на Groovy (во время работы приложения). Эти выражения выполняются для каждой сущности проверяемого графа объектов, и если какая-либо сущность не соответствует условиям - она отфильтровывается из графа объектов.
- Задание ограничений во время разработки
-
Ограничения можно описывать в классе, расширяющем
AnnotatedAccessGroupDefinition
, который используется для определения группы доступа. Класс должен располагаться в модулеcore
. Ниже приведен пример группы доступа, определяющей несколько ограничений для сущностейCustomer
иOrder
:@AccessGroup(name = "Sales", parent = RootGroup.class) public class SalesGroup extends AnnotatedAccessGroupDefinition { @JpqlConstraint(target = Customer.class, where = "{E}.grade = 'B'") (1) @JpqlConstraint(target = Order.class, where = "{E}.customer.grade = 'B'") (2) @Override public ConstraintsContainer accessConstraints() { return super.accessConstraints(); } @Constraint(operations = {EntityOp.CREATE, EntityOp.READ, EntityOp.UPDATE, EntityOp.DELETE}) (3) public boolean customerConstraints(Customer customer) { return Grade.BRONZE.equals(customer.getGrade()); } @Constraint(operations = {EntityOp.CREATE, EntityOp.READ, EntityOp.UPDATE, EntityOp.DELETE}) (4) public boolean orderConstraints(Order order) { return order.getCustomer() != null && Grade.BRONZE.equals(order.getCustomer().getGrade()); } @Constraint(operations = {EntityOp.UPDATE, EntityOp.DELETE}) (5) public boolean orderUpdateConstraints(Order order) { return order.getAmount().compareTo(new BigDecimal(100)) < 1; } }
1 - загружать только покупателей, у которых атрибут grade
равенB
(соответствует значению перечисленияGrade.BRONZE
).2 - загружать только заказы покупателей, у которых атрибут grade
равенB
.3 - ограничение с проверкой в памяти, которое исключает из графа загружаемых объектов все отличные от Grade.BRONZE. 4 - ограничение с проверкой в памяти, которое разрешает работать только с заказами для покупателей с grade == Grade.BRONZE
.5 - ограничение с проверкой в памяти, которое разрешает изменять и удалять только заказы с amount < 100
.Правила формирования ограничения на JPQL:
-
В качестве алиаса извлекаемой сущности необходимо использовать строку
{E}
. При выполнении запросов она будет заменена на реальный алиас, заданный в запросе. -
В параметрах JPQL можно использовать следующие предопределенные константы:
-
session$userLogin
− имя учетной записи текущего пользователя (в случае замещения − имя учетной записи замещаемого пользователя). -
session$userId
− ID текущего пользователя (в случае замещения − ID замещаемого пользователя). -
session$userGroupId
− ID группы текущего пользователя (в случае замещения − ID группы замещаемого пользователя). -
session$XYZ
− произвольный атрибут текущей пользовательской сессии, где XYZ − имя атрибута.
-
-
Содержимое атрибута
where
добавляется в выражениеwhere
запроса по условиюand
(И). Само словоwhere
писать не нужно, оно будет добавлено автоматически, даже если исходный запрос его не содержал. -
Содержимое атрибута
join
добавляется в выражениеfrom
запроса. Оно должно начинаться с запятой или словjoin
илиleft join
.
-
- Задание ограничений во время работы приложения
-
Для создания ограничения, выберите в экране Access Groups группу, на которую нужно наложить ограничение, и перейдите на вкладку Constraints. Экран редактирования ограничения содержит мастер Constraint Wizard, который помогает создавать простые JPQL и Groovy условия для атрибутов сущности. Если выбран тип операции Custom, то появляется обязательное поле Code, где нужно указать строку, по которой будет идентифицироваться данное ограничение.
Редактор JPQL в полях Join Clause и Where Clause поддерживает автодополнение имен сущностей и их атрибутов. Для вызова автодополнения нажмите Ctrl+Space. Если вызов произведен после точки, будет выведен список атрибутов сущности, соответствующей контексту, иначе - список всех сущностей модели данных.
В Groovy скрипт ограничения с проверкой в памяти необходимо использовать
{E}
в качестве переменной, содержащей проверяемый экземпляр сущности. Кроме того, в скрипт передается переменнаяuserSession
типаUserSession
. В примере ниже приведен скрипт ограничения, проверяющий, что сущность была создана текущим пользователем:{E}.createdBy == userSession.user.login
При нарушении ограничения пользователю показывается уведомление. Заголовок и текст уведомления для каждого ограничения можно локализовать: см. кнопку Localization на вкладке Constraints экрана Access Groups.
- Проверка ограничений в коде приложения
-
Разработчик может проверить условия ограничений для конкретной сущности с помощью методов интерфейса
Security
:-
isPermitted(Entity, ConstraintOperationType)
- для проверки ограничений по типу операции. -
isPermitted(Entity, String)
- для проверки ограничений по коду ограничия.
Кроме того, существует возможность связать любое действие, унаследованное от класса
ItemTrackingAction
, c проверкой ограничений. Для этого в XML-элементеaction
следует задать атрибутconstraintOperationType
, либо использовать методsetConstraintOperationType()
в контроллере экрана. Имейте в виду, что код ограничения будет выполняться на клиентском уровне, поэтому он не должен содержать обращения к классам среднего слоя.Пример:
<table> ... <actions> <action id="create"/> <action id="edit" constraintOperationType="update"/> <action id="remove" constraintOperationType="delete"/> </actions> </table>
-