RolesPermitted Support
The Payara Public API provides a @RolesPermitted
annotation that creates an authorisation interceptor for usage with CDI beans. This largely works in the same way as the @RolesAllowed
common annotation, which in Java EE/Jakarta EE is primarily used by the EJB specification.
Usage
The authorisation interceptor is defined through the @RolesPermitted
annotation. Specifying this on an interceptable method of a non-passivation capable CDI bean will make that method eligible for interception by the corresponding interceptor. This interceptor will check that the caller of a method is any or all of the specified roles. If this holds, the call to the method proceeds. If not, an exception of type CallerAccessException
is thrown.
The annotation can alternatively be placed at the class level of a CDI bean, in which case it will apply to all interceptable methods of that bean. |
Examples of a non-passivation capable CDI bean are beans defined in the Request and Application scope. |
Example
Here’s an example of defining the roles authorized to call a single method:
@RequestScoped
public class TestRolesPermitted {
@Inject
Principal principal;
@RolesPermitted("payaraAdmin")
public String getUserName() {
return principal.getName();
}
}
See this sample project for a more detailed example.
Configuration
The @RolesPermitted
annotation has several configuration options.
They are detailed below.
Option | Type | Required | Description |
---|---|---|---|
|
|
true |
A static list of comma-separated roles which are allowed to access this method, or an EL expression used to resolve this list at runtime (see below). |
|
|
no |
Whether accessing caller must be in any one of the given roles ( |
Dynamic Roles
The @RolesPermitted
annotation also allows to dynamically specify authorization roles that are allowed to call bean methods at runtime. With this feature, is possible to instruct the container to allow specific roles based on specific contextual information.
Dynamic roles are defined using an Expression Language (EL) 3.0 String
value which should resolve to a valid String
array or Collection
that corresponds to the roles permitted to call the corresponding method. This expression value has contextual access to any CDI-scoped bean at the time of the method call.
As a reminder, you can use the self bean name in order to refer to the method’s bean and access any of its methods and properties at runtime.
|
In order to access the values of any relevant parameters via CDI beans in the method’s signature, annotate them with the javax.inject.Named CDI annotation, and assign them the name which will be used in the expression value. If no names are provided for these parameters and the method only has one parameter, then it will be available as a bean with the param name.
|
Examples
Basic Example
The following example illustrates how to dynamically resolve a specific role based on the data received in the parameter. Assume that the application has declared multiple roles for each of one of the registered customers. Let’s say that the application has 3 customers named A
, B
and C
, in which case the following roles are defined:
-
Customer_A
-
Customer_B
-
Customer_C
With each role granting authorization to each customer respectively. If we have a method for updating a customer’s data based on a parameter received you can setup the corresponding authorization constraint like this:
@RolesPermitted("#{'Customer_'.concat(customer.name)}")
public void updateCustomer(@Named("customer") Customer customer){
...
}
As you can observe, the role name is evaluated at runtime by concatenating the Customer_
prefix with the name of the customer, which is retrieved using the customer
bean (which is injected in the context using the @Named
annotation).
Advanced Expression Example
Based on the previous example, you can also let the corresponding expression be evaluated by calling a bean’s method instead. Imagine that you could use a second variant, namely a product’s name; in conjunction with the customer’s name to evaluate authorization at runtime like this:
@RolesPermitted("#{self.findRole(customer, product)}")
public void updateCustomer(@Named("customer") Customer customer, @Named("product") Product product){
...
}
public String findRole(Customer customer, Product product) {
return customer.getType() + "_" + product.getModel();
}
By using the self
bean name, we can call the bean’s findRole
method and pass it the two parameters defined in its signature.
Default Parameter Example
If no parameters are annotated with the Named
annotation and the method has a single parameter then the param
bean name can be used as a default reference like this:
@RolesPermitted("#{'CustomerCare_'.concat(param.name)}")
public void updateCustomer(Customer customer){
...
}