3.2.14. Контроль доступа к атрибутам сущностей
Подсистема безопасности позволяет управлять доступом к атрибутам сущностей в соответствии с правами пользователя. Т.е. фреймворк может сделать атрибут read-only или скрыть его в зависимости от набора ролей, назначенных текущему пользователю. Однако, иногда может потребоваться изменять доступ к атрибуту в зависимости также от текущего состояния экземпляра сущности или связанных сущностей.
Механизм контроля доступа к атрибутам позволяет создавать правила того, какие атрибуты должны быть скрыты, нередактируемы или обязательны к заполнению для некоторого экземпляра сущности, и применять эти правила к компонентам Generic UI и в REST API.
Данный механизм работает следующим образом:
-
Когда DataManager загружает сущность, он находит бины, реализующие интерфейс
SetupAttributeAccessHandler
и вызывает их методsetupAccess()
, передавая в них объект типаSetupAttributeAccessEvent
. Данный объект содержит загруженный экземпляр в состоянии managed и три коллекции имен атрибутов: read-only, hidden и required (изначально они пустые). -
Имплементации
SetupAttributeAccessHandler
анализируют состояние сущности и заполняют списки имен атрибутов соответствующим образом. Данные классы являются по сути контейнерами правил, задающих доступ к атрибутам экземпляров. -
Описываемый механизм сохраняет имена атрибутов, заданные правилами, в самом экземпляре (в связанном объекте
SecurityState
). -
На клиентском уровне, Generic UI и REST API используют объект
SecurityState
для управления доступом к атрибутам сущностей.
Для создания правил для некоторого типа сущностей, необходимо выполнить следующее:
-
Создайте Spring-бин в модуле
core
проекта и реализуйте в нем интерфейсSetupAttributeAccessHandler
. Параметризуйте интерфейс типом обрабатываемой сущности. Бин должен иметь дефолтный singleton scope. Вам необходимо реализовать методы интерфейса:-
supports(Class)
- возвращает true если данный обработчик предназначен для работы с сущностями переданного класса. -
setupAccess(SetupAttributeAccessEvent)
- оперирует коллекциями атрибутов для настройки доступа. В данном методе необходимо заполнить коллекции скрываемых, только для чтения и обязательных атрибутов используя методыaddHidden()
,addReadOnly()
иaddRequired()
объекта события. Экземпляр сущности, доступный через методgetEntity()
, находится в состоянии managed, поэтому можно безопасно обращаться ко всем его атрибутам и атрибутам связанных сущностей.
-
Рассмотрим пример сущности Order
, имеющей атрибуты customer
и amount
. Для ограничения доступа к атрибуту amount
в зависимости от customer
можно создать следующее правило:
package com.company.sample.core;
import com.company.sample.entity.Order;
import com.haulmont.cuba.core.app.SetupAttributeAccessHandler;
import com.haulmont.cuba.core.app.events.SetupAttributeAccessEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component("sample_OrderAttributeAccessHandler")
public class OrderAttributeAccessHandler implements SetupAttributeAccessHandler<Order> {
@Override
public boolean supports(Class clazz) {
return Order.class.isAssignableFrom(clazz);
}
@Override
public void setupAccess(SetupAttributeAccessEvent<Order> event) {
Order order = event.getEntity();
if (order.getCustomer() != null) {
if ("PLATINUM".equals(order.getCustomer().getGrade().getCode())) {
event.addHidden("amount");
} else if ("GOLD".equals(order.getCustomer().getGrade().getCode())) {
event.addReadOnly("amount");
}
}
}
}
- Контроль доступа к атрибутам в Generic UI
-
Фреймворк автоматически применяет ограничения доступа к экрану в момент между посылкой BeforeShowEvent и AfterShowEvent. Если вы не хотите применять ограничения для некоторого экрана, добавьте контроллеру экрана аннотацию
@DisableAttributeAccessControl
.Пересчитать и применить ограничения к экрану можно и когда он уже открыт, в ответ на действия пользователя. Для этого необходимо использовать бин
AttributeAccessSupport
, передавая ему текущий экран и сущность, состояние которой изменилось. Например:@UiController("sales_Order.edit") @UiDescriptor("order-edit.xml") @EditedEntityContainer("orderDc") @LoadDataBeforeShow public class OrderEdit extends StandardEditor<Order> { @Inject private AttributeAccessSupport attributeAccessSupport; @Subscribe(id = "orderDc", target = Target.DATA_CONTAINER) protected void onOrderDcItemPropertyChange(InstanceContainer.ItemPropertyChangeEvent<Order> event) { if ("customer".equals(event.getProperty())) { attributeAccessSupport.applyAttributeAccess(this, true, getEditedEntity()); } } }
Второй параметр метода
applyAttributeAccess()
- булевское значение, которое указывает, нужно ли сбрасывать ограничения доступа к компонентам в дефолтные настройки перед тем, как применить новые. Если передано true, возможные программные изменения в этих настройках будут потеряны. Когда данный метод вызывается автоматически перед открытием окна, передается false. Когда же вы вызываете данный метод в ответ на UI-события, передавайте true, иначе ограничения компонентов будут суммироваться, а не заменяться.Ограничения доступа к атрибутам применяются только к компонентам, связанным с одним атрибутом сущности, например TextField или LookupField. Table и другие компоненты, реализующие интерфейс
ListComponent
, не затрагиваются. Поэтому если вы пишете правило, скрывающее атрибут для некоторых экземпляров, рекомендуется не показывать этот атрибут в таблицах совсем.