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.

Use Studio role designer to create 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 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 specifies permitted UI main menu items.

In a design-time role, the menu 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.

If you want to enable all existing specific policies, use * value in the @SpecificPolicy.resources annotation attribute.

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:

Diagram
@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();
}