openxava / documentation / Restricting data by user/role

×News: OpenXava with AI - Refine the UI (Part 2) - December 1 · Read more

Table of contents

Restricting data by user/role
Filtering data by user in the list
Restricting data in detail mode
Default value for user
Restricting data by role (new in v7.6)

In many business applications, it is necessary for each user to see only the data that corresponds to them, such as their orders, invoices, or tasks. This control is essential to streamline work and also to protect information. This would be what is known as row-level security in the database world.

OpenXava solves this problem in a simple way through filters and base conditions in the lists, which allows restricting the visibility of data according to the connected user or their role.

Filtering data by user in the list

It is enough to define a baseCondition in the @Tab for the property that contains the user name, using UserFilter as filter. For example, if we have a Task entity with a user property that stores the user who has the task assigned, we could define the @Tab like this:

import org.openxava.filters.UserFilter;

@Entity
@Tab(filter=UserFilter.class, baseCondition="${user} = ?")
public class Task {
    
    @Column(length=50, name="USERNAME")
    private String user;
    
    // Rest of properties and methods
}

In this example, the annotation @Tab(filter=UserFilter.class, baseCondition="${user} = ?") establishes a base condition that automatically filters the data by the user currently identified in the application. The UserFilter is a predefined filter in OpenXava that provides the name of the connected user, while the expression ${user} refers to the user property of the entity. The question mark ? will be replaced by the value returned by the filter, that is, the name of the user who has logged in.

With this configuration, each user will only see their own tasks in the list.

Restricting data in detail mode

Since OpenXava version 7.4.2, the framework automatically prevents access in detail mode to data that is not included in the list. This means that if you have configured a UserFilter in your @Tab as explained in the previous section, you don't need to do anything additional to restrict access to data in detail mode.

This behavior works with both permanent links (permalinks) and the standard search action, ensuring that users can only see in detail the records that correspond to them according to the applied filters.

If you are using a version prior to 7.4.2, you should implement your own search action (using XAVA_SEARCH_ACTION) with the necessary logic to prevent the query of data that does not belong to the current user. For more information on how to override the default search, see the section Overwriting default search in the controllers documentation.

Default value for user

To automatically assign the current user to new records, there are two main options depending on whether you want the user field to be visible or not in the user interface.

Option 1: Visible but non-editable user

If you want the user field to be visible in the interface but not editable:

@DefaultValueCalculator(CurrentUserCalculator.class)
@ReadOnly
@Column(length=50, name="USERNAME")
private String user;

In this case, @DefaultValueCalculator(CurrentUserCalculator.class) automatically assigns the name of the current user when a new record is created, while @ReadOnly prevents the user from modifying this value.

Remember to import the CurrentUserCalculator class:

import org.openxava.calculators.CurrentUserCalculator;

Option 2: Hidden user

If you prefer that the user field is not visible in the interface:

@Hidden
@Column(length=50, name="USERNAME")
private String user;

@PrePersist
public void assignCurrentUser() {
    this.user = Users.getCurrent();
}

In this option, the @Hidden annotation hides the field in the user interface, and the method annotated with @PrePersist is automatically executed before persisting the entity, assigning the current user through Users.getCurrent().

This second option is useful when the user does not need to see who is the owner of the record, since it will always be themselves, and it simplifies the interface by hiding fields that do not require interaction.

Remember to import the Users class if you use the second option:

import org.openxava.util.Users;

Restricting data by role (new in v7.6)

This functionality is only available in XavaPro

In XavaPro, a user can have a group of roles associated. The framework provides a filter called RolesFilter that allows restricting access to data according to the roles of the current user. This filter works by comparing the user's roles with a property of the entity that contains a role name.

To implement this restriction, follow these steps:

1. Define a property for the role in your entity

@Column(length=30, columnDefinition = "VARCHAR(30) DEFAULT 'user'")
private String role;

This property will store the name of the role that has permission to access the record. You can set a default value if you wish, as in the previous example where the default value is 'user'.

2. Configure the filter in the @Tab annotation

@Tab(baseCondition = "${role} IN (?)",
    filter=com.openxava.naviox.filters.RolesFilter.class,
    properties="year, number, date, customer.number, customer.name, ...")

The base condition ${role} IN (?) indicates that records will be filtered where the value of the role property is included in the list of roles of the current user. The RolesFilter automatically provides this list of roles.

3. How it works

When a user accesses the list, the RolesFilter gets all the roles assigned to the current user and uses them to filter the data. For example:

This allows implementing a role-based data access system, where different types of users can see different sets of data according to their assigned roles.

Remember to import the necessary classes:

import com.openxava.naviox.filters.RolesFilter; // Note: package com.openxava.naviox, not org.openxava