JBoss.orgCommunity Documentation

Chapter 10. Identity Management - Permissions API and Permission Management

Table of Contents

10.1. Overview
10.2. Checking permissions for the current user
10.3. ACL Permissions
10.3.1. Managing ACLs
10.3.2. Configuring resources for ACL usage
10.3.3. Restricting resource operations
10.4. PermissionResolver SPI

The Permissions API is a set of extensible authorization features that provide capabilities for determining access privileges for application resources. This chapter describes the ACL (Access Control List) features and the management of persistent resource permissions via the PermissionManager. It also explains how the PermissionResolver SPI can be used to in conjunction with a custom PermissionVoter implementation, allowing you to plugin your own custom authorization logic.

The Permission interface is used in a number of places throughout the Permissions API, and defines the following methods:

public interface Permission {


    Object getResource();
    Class<?> getResourceClass();
    Serializable getResourceIdentifier();
    IdentityType getAssignee();
    String getOperation();
}

Each permission instance represents a specific resource permission, and contains three important pieces of state:

  • The assignee, which is the identity to which the permission is assigned.

  • The operation, which is a string value that represents the exact action that the assigned identity is allowed to perform.

  • Either a direct reference to the resource (if known), or a combination of a resource class and resource identifier. This value represents the resource to which the permission applies.

The primary method for accessing the Permissions API is via the Identity bean, which provides the following two methods for checking permissions for the currently authenticated user:

boolean hasPermission(Object resource, String operation);

boolean hasPermission(Class<?> resourceClass, Serializable identifier, String operation);

The first overloaded method may be used when you have a reference to the actual resource for which you wish to check privileges:

@Inject Identity identity;


public void deleteAccount(Account account) {
    // Check the current user has permission to delete the account
    if (identity.hasPermission(account, "DELETE")) {
        // Logic to delete Account object goes here
    } else {
        throw new SecurityException("Insufficient privileges!");
    }
}

The second overloaded method may be used when you don't have a reference to the resource object, but you have it's identifier value (for example the primary key value of an entity bean):

@Inject Identity identity;


public void deleteCustomer(Long customerId) {
    // Check the current user has permission to delete the customer
    if (identity.hasPermission(Customer.class, customerId, "DELETE")) {
        // Logic to delete Customer object goes here
    } else {
        throw new SecurityException("Insufficient privileges!");
    }
}

This method is generally used for performance reasons, for example when you don't necessarily wish to load a resource object because of a possibly expensive instantiation cost, or you wish to check whether there are suitable permissions assigned to the user before loading the resource.

An ACL (Access Control List) can be used to control which identities may invoke specific operations on application resources. Underneath the covers, ACL security checks are handled by the PersistentPermissionResolver, which reads the ACL entries for each resource via the PermissionStore, which is typically a wrapper around some form of persistent storage such as database table.

ACL permissions are managed via the PermissionManager. An instance of this bean can be obtained by first injecting the PartitionManager, then getting an instance to the PermissionManager via the createPermissionManager() method:

@Inject PartitionManager partitionManager;

        
public void managePermissions() {
    PermissionManager permissionManager = partitionManager.createPermissionManager();
}

Once you have a reference to the PermissionManager, you can use it to grant permissions:

public void allowRead(User user, Customer customer) {

    permissionManager.grantPermission(user, customer, "READ");
}

The grantPermission() method accepts three parameters:

void grantPermission(IdentityType assignee, Object resource, String operation);

The assignee is the identity to which you wish to grant the permission. The resource is the application resource for which the permission applies. The operation is a String value representing the action that the assignee may invoke in relation to the resource.

Resources may conceivably be any type of Object so long as there exists a unique, serializable value that can be determined or in some way calculated from the resource object, which uniquely identifies that resource from other resources of the same type. This unique value is called the identifier, an example of which might be the primary key value of an entity bean. The PermissionHandler SPI (see section below) is responsible for generating identifier values for resource objects.

The revokePermission() method is used to remove permissions. Like grantPermission(), it also accepts three parameters:

void revokePermission(IdentityType assignee, Object resource, String operation);

It is also possible to revoke all assigned permissions for a single resource via the clearPermissions() method. This is useful for example if you wish to delete the resource and don't wish to leave orphaned permissions:

void clearPermissions(Object resource);

There are also a number of overloaded methods available for querying permissions. These methods take an assortment of parameters depending on exactly which permissions you wish to find:

List<Permission> listPermissions(Object resource);


List<Permission> listPermissions(Class<?> resourceClass, Serializable identifier);
List<Permission> listPermissions(Object resource, String operation);
List<Permission> listPermissions(Class<?> resourceClass, Serializable identifier, 
                                 String operation);

Here's some examples:

// List all permissions for a known Product

Product p = lookupProduct("grapes");
List<Permission> permissions = permissionManager.listPermissions(p);
// List all permissions for a Product where we know the resource class 
// and the identifier
List<Permission> permissions = permissionManager.listPermissions(
    Product.class, "bananas");
// List all "DELETE" permissions that have been granted for a Product
Product p = lookupProduct("apples");
List<Permissions> permissions = permissionManager.listPermissions(p, "DELETE");
// List all "UPDATE" permissions for a Product where we know the 
// resource class and the identifier
List<Permissions> permissions = permissionManager.listPermissions(
    Product.class, "oranges", "UPDATE");

Every resource class for which you wish to support ACL permissions is required to have a corresponding PermissionHandler. This interface is primarily responsible for the generation of resource identifier values, plus a couple of other utility methods (please refer to the API documentation for more details):

public interface PermissionHandler {


    boolean canHandle(Class<?> resourceClass);
    Serializable getIdentifier(Object resource);
    Class<?> unwrapResourceClass(Object resource);
    Set<String> listClassOperations(Class<?> resourceClass);
    
    Set<String> listInstanceOperations(Class<?> resourceClass);
}

There are two ways that a resource class can be associated with a PermissionHandler - the first way is by providing a @PermissionsHandledBy annotation on the resource class itself:

import org.picketlink.idm.permission.annotations.PermissionsHandledBy;

        
@PermissionsHandledBy(CustomPermissionHandler.class)
public class MyResourceClass {
    
}

For the circumstances where it is not possible to annotate the resource class directly, the second way is to register a custom PermissionHandler instance for which the canHandle() method returns true for the resource class:

public boolean canHandle(Class<?> resourceClass) {

    return MyResourceClass.class.equals(resourceClass);
}

Registering a custom PermissionHandler is very easy - simply include it in your application deployment as an @ApplicationScoped bean, and it will be registered automatically. Here's a complete example of a PermissionHandler that allows permissions to be assigned to arbitrary string values (this handler is actually provided by PicketLink):

@ApplicationScoped

public class StringPermissionHandler implements PermissionHandler {
    @Override
    public boolean canHandle(Class<?> resourceClass) {
        return String.class.equals(resourceClass);
    }
    @Override
    public Serializable getIdentifier(Object resource) {
        checkResourceValid(resource);
        return (String) resource;
    }
    @Override
    public Class<?> unwrapResourceClass(Object resource) {
        checkResourceValid(resource);
        return String.class;
    }
    private void checkResourceValid(Object resource) {
        if (!(resource instanceof String)) {
            throw new IllegalArgumentException("Resource [" + resource + 
                "] must be instance of String");
        }
    }
    @Override
    public Set<String> listAvailableOperations(Class<?> resourceClass) {
        return Collections.emptySet();
    }
}

For many resource types it makes sense to restrict the set of resource operations for which permissions might be assigned. For example, an application might have an entity bean containing lookup values for countries. This Country bean is likely to only require the bare minimum in terms of data management and so you might like to restrict the available operations for it to the typical CREATE, READ, UPDATE, DELETE operations. To do this we use the @AllowedOperations annotation - this annotation allows us to provide a child array of @AllowedOperation values that specify exactly which operation values that permissions can be assigned for:

import org.picketlink.idm.permission.annotations.AllowedOperation;

import org.picketlink.idm.permission.annotations.AllowedOperations;
@Entity
@AllowedOperations({
    @AllowedOperation(value = "CREATE", mask = 1, classOperation = true),
    @AllowedOperation(value = "READ", mask = 2),
    @AllowedOperation(value = "UPDATE", mask = 4),
    @AllowedOperation(value = "DELETE", mask = 8)
})
public class Country implements Serializable {

The optional mask value can be used to specify a bitmask value to allow for more efficient storage of permission values. If the mask values are set, the operation values for that object's permissions will be stored as a numerical value with the corresponding bit values turned on. For example, if a single user was assigned permission for both the READ and UPDATE operations for one of our Country beans, then this operation value would be stored as 6 (READ (2) + UPDATE (4)).

The other optional value, classOperation can be set to true if the permission applies to the class itself, and not an instance of a class. For example, you might wish to check that the current user has permission to actually create a new Country bean. In this case, the permission check would look something like this:

@Inject Identity identity;

       
public void createCountry() {
    if (!identity.hasPermission(Country.class, "CREATE")) {
        throw new SecurityException(
          "Current user has insufficient privileges for this operation.");    
    }
    // snip
}

This functionality is provided by the ClassPermissionHandler permission handler.