5.2.14. Контроль доступа к атрибутам сущностей
Подсистема безопасности позволяет управлять доступом к атрибутам сущностей в соответствии с правами пользователя. Т.е. фреймворк может сделать атрибут read-only или скрыть его в зависимости от набора ролей, назначенных текущему пользователю. Однако, иногда может потребоваться изменять доступ к атрибуту в зависимости также от текущего состояния экземпляра сущности или связанных сущностей.
Механизм контроля доступа к атрибутам позволяет создавать правила того, какие атрибуты должны быть скрыты, нередактируемы или обязательны к заполнению для некоторого экземпляра сущности, и применять эти правила к компонентам Generic UI и в REST API.
Данный механизм работает следующим образом (см. также изменения в версии 6.8.1 ниже):
-
Когда DataManager загружает сущность, он посылает Spring application event типа
SetupAttributeAccessEvent
. Объект события содержит загруженный экземпляр в состоянии managed и три коллекции имен атрибутов: read-only, hidden и required (изначально они пустые). -
Вам необходимо создать event listener, который будет анализировать состояние сущности и заполнять списки имен атрибутов соответствующим образом. Данный event listener является по сути контейнером правил, задающих доступ к атрибутам экземпляров.
-
Описываемый механизм сохраняет имена атрибутов, заданные правилами, в самом экземпляре (в связанном объекте
SecurityState
). -
На клиентском уровне, Generic UI и REST API используют объект
SecurityState
для управления доступом к атрибутам сущностей.
Для создания правил для некоторого типа сущностей, необходимо выполнить следующее:
-
Создайте управляемый бин в модуле core проекта. Бин должен иметь дефолтный singleton scope.
-
Создайте метод, принимающий единственный параметр типа
SetupAttributeAccessEvent
. Метод должен иметь аннотациюorg.springframework.context.event.EventListener
. ТипSetupAttributeAccessEvent
является generic и должен быть параметризован типом сущности. -
В данном методе необходимо заполнить коллекции скрываемых, только для чтения и обязательных атрибутов используя методы
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.events.SetupAttributeAccessEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component("sample_AttributeAccessRules")
public class AttributeAccessRules {
@EventListener
public void orderAccess(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");
}
}
}
}
- Изменения в версии 6.8.1
-
В связи с проблемами, найденными в случае имплементации обработчиков в виде Spring application event listeners, данный механизм был изменен в версии 6.8.1 платформы. Правила, описанные выше, по прежнему действуют по умолчанию, но в дополнение был введен альтернативный способ объявления обработчиков. Данный способ включается с помощью свойства приложения cuba.useSpringApplicationEventsToSetupAttributeAccess, и будет основным и единственным способом начиная с версии 6.9. Поэтому мы рекомендуем изменить ваши обработчики в соответствии с правилами, описанными далее.
Обработчик должен быть управляемый бином модуля core проекта и реализовывать интерфейс
SetupAttributeAccessHandler
. Бин должен иметь дефолтный singleton scope. Вам необходимо реализовать методы интерфейса:-
supports(Class)
- возвращает true если данный обработчик предназначен для работы с сущностями переданного класса. -
setupAccess(SetupAttributeAccessEvent)
- оперирует коллекциями атрибутов для настройки доступа.
Например:
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
-
Фреймворк автоматически применяет ограничения доступа к экрану перед вызовом метода
ready()
его контроллера. Если вы не хотите этого для некоторого экрана, переопределите методisAttributeAccessControlEnabled()
его контроллера и верните из негоfalse
.Пересчитать и применить ограничения к экрану можно и когда он уже открыт, в ответ на действия пользователя. Для этого необходимо использовать бин
AttributeAccessSupport
, передавая ему текущий экран и сущность, состояние которой изменилось. Например:package com.company.sample.web.order; import com.company.sample.entity.Order; import com.haulmont.cuba.gui.AttributeAccessSupport; import com.haulmont.cuba.gui.components.AbstractEditor; import com.haulmont.cuba.gui.data.Datasource; import javax.inject.Inject; import java.util.Map; public class OrderEdit extends AbstractEditor<Order> { @Inject private Datasource<Order> orderDs; @Inject private AttributeAccessSupport attributeAccessSupport; @Override public void init(Map<String, Object> params) { orderDs.addItemPropertyChangeListener(e -> { if ("customer".equals(e.getProperty())) { attributeAccessSupport.applyAttributeAccess(this, true, getItem()); } }); } }
Второй параметр метода
applyAttributeAccess()
- булевское значение, которое указывает, нужно ли сбрасывать ограничения доступа к компонентам в дефолтные настройки перед тем, как применить новые. Если передано true, возможные программные изменения в этих настройках будут потеряны. Когда данный метод вызывается автоматически перед открытием окна, передается false. Когда же вы вызываете данный метод в ответ на UI-события, передавайте true, иначе ограничения компонентов будут суммироваться, а не заменяться.WarningОграничения доступа к атрибутам применяются только к компонентам, связанным с одним атрибутом сущности, например TextField или LookupField. Table и другие компоненты, реализующие интерфейс
ListComponent
, не затрагиваются. Поэтому если вы пишете правило, скрывающее атрибут для некоторых экземпляров, рекомендуется не показывать этот атрибут в таблицах совсем.