3.2.14. Entity Attribute Access Control
The security subsystem allows you to set up access to entity attributes according to user permissions. That is the framework can automatically make an attribute read-only or hidden depending on a set of roles assigned to the current user. But sometimes you may want to change the access to attributes dynamically depending also on the current state of the entity or its linked entities.
The attribute access control mechanism allows you to create rules of what attributes should be hidden, read-only or required for a particular entity instance, and apply these rules automatically to Generic UI components and REST API.
The mechanism works as follows:
-
When DataManager loads an entity, it locates all managed beans implementing the
SetupAttributeAccessHandler
interface and invokes theirsetupAccess()
method passing theSetupAttributeAccessEvent
object. This object contains the loaded instance in the managed state, and three collections of attribute names: read-only, hidden and required (they are initially empty). -
The
SetupAttributeAccessHandler
implementations analyze the state of the entity and fill collections of attribute names in the event appropriately. These classes are in fact containers for the rules that define the attribute access for a given instance. -
The mechanism saves the attribute names, defined by your rules, in the entity instance itself (in a linked
SecurityState
object). -
On the client tier, Generic UI and REST API use the
SecurityState
object to control the access to entity attributes.
In order to create a rule for particular entity type, do the following:
-
Create a managed bean in the core module of your project and implement the
SetupAttributeAccessHandler
interface. Parameterize the interface with the type of handled entity. The bean must have the default singleton scope. You have to implement the interface methods:-
supports(Class)
returns true if the handler is designed to work with the given entity class. -
setupAccess(SetupAttributeAccessEvent)
manipulates with the collections of attributes to set up the access. You should fill the collections of read-only, hidden and required attributes using theaddHidden()
,addReadOnly()
andaddRequired()
methods of the event. The entity instance, which is available via thegetEntity()
method, is in the managed state, so you can safely access its attributes and attributes of its linked entities.
-
For example, provided that Order
entity has customer
and amount
attributes, you could create the following rule for restricting access to the amount
attribute depending on the 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");
}
}
}
}
- Attribute Access Control in Generic UI
-
The framework automatically applies attribute access restrictions to a screen at the moment between sending BeforeShowEvent and AfterShowEvent. If you don’t want to apply restrictions to a particular screen, add the
@DisableAttributeAccessControl
annotation to the controller class.You may want to recompute and apply the restrictions while the screen is opened, in response of user actions. You can do it using the
AttributeAccessSupport
bean, passing the current screen and the entity which state has changed. For example:@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()); } } }
The second parameter of the
applyAttributeAccess()
method is a boolean value which specifies whether to reset components access to default before applying new restrictions. If it’s true, programmatic changes to the components state (if any) will be lost. When the method is invoked automatically on screen opening, the value of this parameter is false. But when invoking the method in response of UI events, set it to true, otherwise the restrictions on components will be summed and not replaced.Attribute access restrictions are applied only to the components bound to single entity attributes, like TextField or LookupField. Table and other components implementing the
ListComponent
interface are not affected. So if you write a rule that can hide an attribute for some entity instances, we recommend not showing this attribute in tables at all.