6.2.4. Roles

A role combines a set of permissions that can be granted to users.

The user may have multiple roles, in which case a logical sum (OR) is computed from all of the assigned roles. For example, if a user has roles A and B where role A does not grant a permission to X and role B allows X, then X will be allowed.

A role can grant permissions to individual targets, as well as to the whole categories of targets: screens, entity operations, entity attributes, specific permissions. For example, you can easily give the Read permission to all entities and the View permission to all their attributes.

The screen component permissions are a notable exception to the above rules: they can be defined only for a concrete component, and if no role defines a permission to a component, it is fully available to the user.

A role can be "default" with regards to users, which means that it is assigned to new users automatically. It allows you to grant a certain set of permissions to each new user by default.

Defining roles at design time

The recommended way to define a role is to create a class extending AnnotatedRoleDefinition, override the methods returning permissions for different target types, and add annotations that specify what permissions this role contains. The class must be located in the core module. For example, a role granting permissions to work with the Customer entity and its browse and edit screens may look as follows:

@Role(name = "Customers Full Access")
public class CustomersFullAccessRole extends AnnotatedRoleDefinition {

    @EntityAccess(entityClass = Customer.class,
            operations = {EntityOp.CREATE, EntityOp.READ, EntityOp.UPDATE, EntityOp.DELETE})
    @Override
    public EntityPermissionsContainer entityPermissions() {
        return super.entityPermissions();
    }

    @EntityAttributeAccess(entityClass = Customer.class, modify = "*")
    @Override
    public EntityAttributePermissionsContainer entityAttributePermissions() {
        return super.entityAttributePermissions();
    }

    @ScreenAccess(screenIds = {"application-demo", "demo_Customer.browse", "demo_Customer.edit"})
    @Override
    public ScreenPermissionsContainer screenPermissions() {
        return super.screenPermissions();
    }
}

The annotations can be specified multiple times. For example, the following role gives read-only access to all entities and their attributes, allows to modify customer’s grade and comments attributes, allows to create/update order entity and all its attributes:

@Role(name = "Order Management")
public class OrderManagementRole extends AnnotatedRoleDefinition {

    @EntityAccess(entityName = "*", operations = {EntityOp.READ})
    @EntityAccess(entityClass = Order.class, operations = {EntityOp.CREATE, EntityOp.UPDATE})
    @Override
    public EntityPermissionsContainer entityPermissions() {
        return super.entityPermissions();
    }

    @EntityAttributeAccess(entityName = "*", view = "*")
    @EntityAttributeAccess(entityClass = Customer.class, modify = {"grade", "comments"})
    @EntityAttributeAccess(entityClass = Order.class, modify = "*")
    @Override
    public EntityAttributePermissionsContainer entityAttributePermissions() {
        return super.entityAttributePermissions();
    }
}

You can create roles at design time only if the cuba.security.rolesPolicyVersion application property is set to 2, which is the default for new projects created with CUBA 7.2 or newer. If you are migrating from an earlier version, see Legacy Roles and Permissions.

Defining roles at run time

The framework contains UI that allows you to define roles in the running application, see Administration > Roles. Roles created at run time can be modified or removed. Roles defined at design time are read-only.

On the upper part of the role editor screen you can define common role parameters. The bottom part of the screen contains tabs for defining permissions.

  • The Screens tab configures screen permissions. The tree reflects the structure of the application’s main menu. In order to set permissions for a screen not accessible through the main menu (for example, an entity edit screen), find it in the Other screens which is the last tree element.

    Allow all screens checkbox enables all screens at once. It is an equivalent of @ScreenAccess(screenIds = "*").

  • The Entities tab configures entity operation permissions. The Assigned only checkbox is selected by default, so that the table contains only the entities that have explicit permissions in this role. Therefore, the table for a new role will be empty. In order to add permissions, uncheck Assigned only and click Apply. The entity list can be filtered by entering a part of an entity name in the Entity field and clicking Apply. The System level checkbox enables viewing and selecting system entities marked with the @SystemLevel annotation, which are not shown by default.

    Use Allow all entities panel to enable operations for all entities. It is an equivalent of @EntityAccess(entityName = "*", …​).

  • The Attributes tab configures entity attribute permissions. The Permissions column in the entity table shows the list of the attributes that have explicit permissions. Entity list can be managed similarly to the list in the Entities tab.

    Use Allow all attributes panel to enable viewing or modification of all attributes for all entities. If you want to enable all attributes for a specific entity, select the "*" checkbox at the bottom of the Permissions panel for this entity. The same can be achieved using the "*" wildcard in the entityName and view/modify attributes of the @EntityAttributeAccess annotation.

  • The Specific tab configures specific permissions. The permissions.xml configuration file defines the names of specific permissions used in the project.

    Allow all specific permissions checkbox is an equivalent of @SpecificAccess(permissions = "*").

  • The UI tab configures UI screen component permissions. In order to create a permission, select the desired screen in the Screen drop-down list, specify the component path in the Component field, and click Add. Follow the rules described in the Permissions sections when specifying the target component. You can use the Components tree button to see the screen components structure: select a component in the tree and click Copy id to path in the context menu.

Security scopes

Security scopes allow users to have different sets of roles (and hence different permissions) depending on what client technology they are using to access the application. The security scope is specified using the securityScope attribute of the @Role annotation or using the Security scope field of the role editor screen if the role is defined at run time.

The core framework has a single client - Generic UI, so all roles have the GENERIC_UI scope by default. All users logging in to the generic UI of your application will get the set of roles marked with this scope.

The REST API add-on defines its own REST scope, so if you add it to the project, you should configure a separate set of roles for users logging in to the system through the REST API. If you don’t do it, the users will not be able to login via REST because they won’t have any permissions including the cuba.restApi.enabled specific permission.

System roles

The framework provides two predefined roles for the GENERIC_UI scope:

  • system-minimal role contains the minimal set of permissions to allow users to work with Generic UI. It is defined by the MinimalRoleDefinition class. The role grants the cuba.gui.loginToClient specific permission, as well as permissions to some system-level entities and screens. The system-minimal role has the default attribute set, so it is automatically assigned to all new users.

  • system-full-access role gives all permissions and can be used for creating administrators having all rights to the application. The built-in admin user has this role by default.

Defining access to main menu items

To configure permissions for viewing some screens, you should also configure access to menu items up the hierarchy.

  • For roles defined at design time, the @ScreenAccess annotation is used, for example:

    @ScreenAccess(screenIds = {"application-demo", "demo_Customer.browse", "demo_Customer.edit"})

    In addition to screen identifiers, add the ids of top-level menu items to the list of identifiers (for example, screenIds = {"application-demo"}).

  • For roles defined at run time

    On the Screens tab of the role editor screen, you should allow all menu items up the hierarchy in addition to the allowed screens. Otherwise, the screens will not be shown to the user.