Resource Roles
Resource roles give users permissions to specific objects and operations in the system: CRUD operations on entities, entity attributes, UI screens, and so on.
A user without resource roles has no permissions and cannot access the application’s data and UI. |
Creating Resource Roles
You can create resource roles at design time using annotated Java interfaces or at runtime using UI screens available at Administration → Resource roles.
A role has a user-friendly name and a code. The code is used when assigning the role to users, so don’t change if some users already have this role assigned.
Example of defining a design-time role:
@ResourceRole( (1)
name = "Full Access", (2)
code = "system-full-access") (3)
public interface FullAccessRole {
// ...
void fullAccess(); (4)
}
1 | The @ResourceRole annotation indicates that the interface defines a resource role. |
2 | A user-friendly name of the role. |
3 | The role’s code. |
4 | The interface can have one or more methods to define policy annotations (see below). Different methods are used just to group related policies. Method names can be arbitrary, they are displayed as Policy group when the role is shown in UI. |
Resource Policies
Resource roles define permissions by specifying resource policies. A resource policy combines a resource with a permitted type of access to it.
Entity Policy
Entity policy specifies what CRUD operations are permitted for an entity.
In a design-time role, the entity policy is defined using the @EntityPolicy
annotation, for example:
@EntityPolicy(
entityClass = Customer.class,
actions = {EntityPolicyAction.READ,
EntityPolicyAction.CREATE,
EntityPolicyAction.UPDATE})
void customer();
The entityClass
parameter specifies the entity. If you want to define a policy for all entities, use the entityName
parameter with *
value.
The actions
parameter specifies permitted CRUD operations. If you want to enable all operations, use EntityPolicyAction.ALL
value.
Entity Attribute Policy
Entity attribute policy specifies what entity attributes are permitted.
In a design-time role, the entity attribute policy is defined using the @EntityAttributePolicy
annotation, for example:
@EntityAttributePolicy(
entityClass = Customer.class,
attributes = {"name", "region", "details"},
action = EntityAttributePolicyAction.MODIFY)
void customer();
The entityClass
parameter specifies the entity. If you want to define a policy for all entities, use the entityName
parameter with *
value.
The attributes
parameter specifies entity attribute names. If you want to define a policy for all attributes of the entity, use *
value.
The action
parameter specifies the permitted access level: "view" or "modify". The "modify" level automatically grants also the "view" access.
Screen Policy
Screen policy specifies permitted Backoffice UI screens.
In a design-time role, the screen policy is defined using the @ScreenPolicy
annotation, for example:
@ScreenPolicy(
screenIds = {"sample_Customer.browse", "sample_Customer.edit"})
void customer();
The screenIds
parameter specifies identifiers of permitted UI screens. If you want to enable all screens, use *
value.
Menu Policy
Screen policy specifies permitted Backoffice UI main menu items.
In a design-time role, the screen policy is defined using the @MenuPolicy
annotation, for example:
@MenuPolicy(
menuIds = {"sample_Customer.browse"})
void customer();
The menuIds
parameter specifies identifiers of permitted menu items. If you want to enable all menu items, use *
value.
Specific Policy
Specific policy defines permissions on arbitrary named functionality.
The framework uses specific permissions to restrict access to various mechanisms. For example, Generic REST API defines the rest.enabled
policy, so a user must have this permission to be able to interact with the application through the Generic REST endpoints.
You can use specific policies to restrict access to your application functionality too. Below is an example of such access control.
In a design-time role, the specific policy is defined using the @SpecificPolicy
annotation:
@SpecificPolicy(
resources = {"customer.notify"})
void customer();
The policy is checked in the application code using the AccessManager
and SpecificOperationAccessContext
classes provided by the framework:
@Autowired
private AccessManager accessManager;
public void notifyCustomer(Customer customer) {
SpecificOperationAccessContext accessContext =
new SpecificOperationAccessContext("customer.notify");
accessManager.applyRegisteredConstraints(accessContext);
if (accessContext.isPermitted()) {
// do notify
}
}
You can learn more about checking permissions in the access constraints section.
Role Granularity
A resource role can have any number of policies, and a user can have any number of roles. So you can design roles with different levels of granularity:
-
Fine-grained roles define permissions to closely related resources like an entity, its UI screens and menu items. For example, "Full access to Customers", "Can create and update Orders". You would normally assign several such roles to the user.
-
Coarse-grained roles define all permissions required for a specific job, like "Salesperson". Such a role can define all permissions itself or inherit them from child roles, so a course-grained role can be made as an aggregation of fine-grained ones.
We recommend creating fine-grained roles at design time and using runtime capabilities only for combining them into different coarse-grained roles for simple assignment to users, and for rare ad-hoc changes in your security model.
Example
Below is a complete example of a design-time resource role that enables limited access to the following entities and their UI screens:
@ResourceRole(
name = "Customers: non-confidential info only, cannot delete",
code = "customer-nonconfidential-access")
public interface CustomerNonConfidentialAccessRole {
@EntityPolicy(
entityClass = Customer.class,
actions = {EntityPolicyAction.READ,
EntityPolicyAction.CREATE,
EntityPolicyAction.UPDATE})
@EntityAttributePolicy(
entityClass = Customer.class,
attributes = {"name", "region", "details"},
action = EntityAttributePolicyAction.MODIFY)
@ScreenPolicy(
screenIds = {"sample_Customer.browse", "sample_Customer.edit"})
@MenuPolicy(
menuIds = {"sample_Customer.browse"})
void customer();
@EntityPolicy(
entityClass = CustomerDetail.class,
actions = EntityPolicyAction.ALL)
@EntityAttributePolicy(
entityClass = CustomerDetail.class,
attributes = {"content"},
action = EntityAttributePolicyAction.MODIFY)
@ScreenPolicy(
screenIds = {"sample_CustomerDetail.edit"})
void customerDetail();
@MenuPolicy(
menuIds = {"application"})
void commonMenus();
}