Instance-based Access control: Architectural concept and Implementation with SAF, JAAS and Spring

Martin Krasser

Access to an application’s domain objects are often controlled at the class instance level. We will begin by presenting the general architectural principles for this theme and follow with a guide on to implement it. To separate security logic from the business logic in Spring applications we use the Security Annotation Framework (SAF) [1]. To implement the security logic we extend the Java Authentication and Authorization Service (JAAS) instance-based mechanisms for access control.

Instance-based access control regulates access to instances of domain objects. Instance–based means that per access decision both the state of the domain object as well as its static characteristics are taken into account. This could be a unique ID or another security relevant property like for example the User ID of the owner. Instance-based access control is of particular interest in systems where the user can administer access rights to their files. As an example take an online photo album where each user can decide for each of their photos who can access it. The function „Show me the photos from user X“ can be called by all users, however, the user making the call can only see the photos from user X that he has been given access rights to. The access rights can be given to individual users or user groups.

Due to the security features of Java Enterprise Edition this use case can no longer be implemented. Java EE security is well suited to limit function calls to certain roles but access control to individual domain object instances is not addressed [2]. In these cases an alternative solution must be found. For a deeper understanding of these alternatives we need to discuss general access control architecture topics and then the possibilities of implementing them. This implementation is based on an example using the Security Annotation Framework (SAF) and Java SE Security Standards [3,4]. The solution presented here will be briefly compared with other Security Frameworks at the end of the article.

In the body of the article we deal with SAF. It verifies the access to the domain objects in an application at annotated places in the code and it allows the separation of an application’s security logic from its business logic. SAF embeds security logic through a Service Provider interface in Spring applications. Through its declarative approach to instance-based access control SAF can include additional access control features into existing Spring applications without modifying existing business logic. Then we deal with the implementation of a security service based on Java SE security standards. We use the Java Authentication and Authorization service (JAAS) and extend this to include instance-based access control. This extension only affects the authorization part of JAAS. The Authentication part is outside the scope of this article. As standards have been observed the extensions can be used in other environments that are also built on JAAS authorization without having to make architectural changes. Applications always talk to these extensions using the JDK’s standard security APIs.  

The JAAS extensions [5] and the example application [6] are now also part of the open source SAF project. This article only relates to the terms “Security Annotation Framework” or “SAF” from the SAF core module [7] of the project.

Architecture

As a reference point for the other sections see the reference architecture in Figure 1. It defines the components of a general access control architecture, which can be found in many applications. The aim is to monitor client access to protected application resources and to grant or deny access requests. In our example these are an application’s domain objects.

Fig.1 Access Control Reference Architecture

The monitoring is carried out by the Policy Enforcement component. It makes sure that when a resource is accessed that the application’s security policy is observed by carrying out a permissions check. Depending on the resource and the type of access the policy enforcement component queries the AccessManager interface that is provided by the Policy Decision component. The policy decision component now takes a decision as to whether access should be granted or not. To do this it checks that the logged in user has sufficient rights entered in the Policy Store. If the user has the right to access the resources, the policy decision component generates a positive response and the Policy Enforcement component allows the request to go through. If the access decision is negative the request is blocked. In the Policy Store, the access permissions for the user of an application are stored. The administration of permissions is carried out by a policy management component. The change of privileges must be possible at runtime so that, for example, a user may grant another user access to his data. The described access control architecture and the interaction patterns of its components can be found both in distributed applications as well as in applications whose components act within a runtime environment. For this article, we consider only the latter case.

Now we will compare the reference architecture with the solution architecture of our example application (Fig. 2). We use the Security Annotation Framework (SAF) to implement the Policy Enforcement component. We use and extend the JAAS authorization framework to implement the Policy Decision component. Policy management functionality is also provided by this component.

Fig. 2 Access Control Architecture of the Sample Application

The Policy Decision component is just a simple reference implementation [5] and should only serve as a guide to implement these components in production. At this point any other implementations can be used that support instance-based access control. The SAF core module [7], however, is ready to for production use in Spring-based enterprise applications. With SAF we can monitor access to a Spring application’s domain objects and delegate access decisions to the Policy Decision component. SAF can be configured for the Policy Decision component it needs to interact with.

Policy Enforcement

To understand the specifics of instance-based access control we need to take a closer look at the components from Figure 2. We start with the Policy Enforcement component and show how it can be implemented with SAF in Spring applications. First, we give an overview of our sample application (Fig. 3): it is a Spring application for the management of notebooks. Users can manage their own notebooks (create, search and delete) and make entries in notebooks (Change). The notebook management is done through a NotebookService interface. Creating and deleting entries in notebooks is done with the methods addEntry () and removeEntry () on the Notebook domain object itself (Listing 1).The NotebookService is managed as a bean in a Spring application context.

 

Fig. 3 Example Application Overview

Listing 1
public interface NotebookService {
 
    void createNotebook(@Secure(SecureAction.CREATE) Notebook notebook);
    void deleteNotebook(@Secure(SecureAction.DELETE) Notebook notebook);
 
    @Filter Notebook findNotebook(String id);
    @Filter List<Notebook> findNotebooksByUserId(String userId);
    …
}
 
@SecureObject
public class Notebook {
 
    private String id;
    private User owner;
    private List<Entry> entries;
 
    @Secure(SecureAction.UPDATE)
    public void addEntry(Entry entry) {}
    @Secure(SecureAction.UPDATE)
    public void removeEntry(Entry entry) {}}
 
public class User {}
public class Entry {}

Without security mechanisms each user has full access to the notebooks of other users. Initially to restrict access to the notebooks, permissions are enforced at certain places in the code. As an example we consider the method

void deleteNotebook(Notebook notebook)

 

of the NotebookService interface. This method removes notebooks from a database and should only be allowed to be invoked by users of the application if the notebook given in the argument also belongs to the currently logged in user. In other words a user can delete his own notebook but not another user’s notebook. The content of the Notebook object must be checked to see if it can be matched to the logged in user. Depending on the argument there are cases where sometimes a method can be invoked and sometimes not be invoked by the same user. It can’t be determined beforehand who is allowed to invoke the method - just that a check should be made at this point. To achieve this, we provide the method parameters notebook with a SAF @Secure annotation. The type of access is determined with a constant of the enum type SecureAction.

void deleteNotebook(@Secure(SecureAction.DELETE)Notebook notebook)

 

In this case the notebook argument is checked to see if it can be deleted by the current user. SAF delegates access decisions to a bean that the SAF AccessManager interface implements (Listing 2). When the deleteNotebook method is invoked, first the checkDelete method in the AccessManager is invoked and the Notebook instance is passed in the argument which is also given to the deleteNotebook method (Fig 4). If the current user has permissions to delete the Notebook instance, SAF allows the deleteNotebook method to be invoked by the NotebookService, otherwise the security exception thrown by the AccessManager implementation is passed to the client. The check()method exceptions thrown by the AccessManager must be of the type java.security.AccessControlException.



 

Fig. 4 The SAF Reviews Access to an Instance, Passed as an Argument.

Listing 2
public interface AccessManager {
 
    void checkCreate(Object obj);
    void checkRead(Object obj);
    void checkUpdate(Object obj);
    void checkDelete(Object obj);
    …
}

The AccessManager interface will be defined by SAF, but it is not included in the implementation. This must be made available by a Policy Decision component. SAF at this point makes no provisions, as to how an implementation should look or how an access decision should be taken. That is left fully up to AccessManager to implement. Later we will see how we can extend JAAS with instance-based control mechanisms and can integrate these extensions using the AccessManager interface in a Spring application. One could at this point also use any other security services.
   Listing 2 shows only a subset of the methods of AccessManager interface. Other methods also allow for the direct modification of method arguments and return values and therefore the implementation of any access rules. For a description of the complete interface you are referred to the SAF documentation [7].
   SAF can also be used to apply @Secure annotations to objects, that are not managed by the Spring application context. Typically they are an application’s domain objects, in our case- Notebook.  In the domain objects the @Secure annotation is mostly directly applied to a method and not to a method’s parameter. This means that the object, on which the method is invoked, is itself checked and not just any argument (Fig. 5). Take as an example the addEntry() method of the Notebook domain object. This method creates an additional entry in the Notebook instance on which it is invoked and therefore changes it. It is not important to us what the entry (meaning, the argument) looks like. We’re only interested in whether the current user is allowed to alter the notebook instance. We achieve this through an @Secure(SecureAction.UPDATE) annotation on the method level (Listing 1). In this case, SAF calls the checkUpdate () method and passes as an argument the target object, that is the notebook instance itself. One could also call this approach to reviewing an object-oriented approach while reviewing on the parameter level would tend to be described as a procedural or service approach.

Fig. 5 The SAF Checks Access to an Instance, on which a Method has been Called


What’s more SAF supports the checking and filtering of the values returned from a method invocation. This is done with the SAF @Filter annotation. Implicit in the @Filter annotation is read-only access. If a Collection or an array is returned its elements are checked otherwise the object that is returned is checked. In the case of a collection you can still specify whether elements that did not have read permissions should be deleted from the collection, or whether the readable elements should be copied into a new collection. The class of a possibly newly produced Collection can also be specified. Filtering of large Collections can be problematic from a performance point of view. There are possibilities here for improvements but which, due to space limitations can not be detailed here.
   In order for SAF to process annotations and carry out authorization checks it is activated in the Spring application context using the element <sec:annotation-driven> (Listing 3). The schema definition for the sec namespace is supplied by SAF and can be used in Spring 2.x environments. The AccessManager bean, to which SAF delegates, is set via the access-manager attribute of the <sec:annotation-driven> element. SAF allows us with minimal configuration effort access to an application’s domain object instances. Readers, who deal with declarative transaction management and @Transactional annotations in Spring 2.x applications will certainly recognize parallels here. 

Listing 3
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:sec="http://safr.sourceforge.net/schema/core"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://safr.sourceforge.net/schema/core 
http://safr.sourceforge.net/schema/core/spring-safr-core-1.0.xsd">
 
    <sec:annotation-driven access-manager="accessManager"/>
 
    <bean id="accessManager" class="...AccessManagerImpl" />
    <bean id="notebookService" class="...NotebookServiceImpl" /></beans>

Figure 6 illustrates the underlying mechanisms. When loading the Spring Application Context SAF processes the <sec:annotation-driven> element and automatically produces Beans for Spring with SAF annotations AOP proxies. A MethodInterceptor is used before and after method calls to force the appropriate authorization checks. The MethodInterceptor calls the various check() methods via the AccessManager. If a check() method throws an AccessControlException it’s passed to the client via the @Secure annotation. In the case of a @Filter annotation- it is intercepted by the Interceptor and the object is removed from the results.

 

Fig. 6 SAF uses AspectJ and Spring AOP.

For Spring Beans AOP proxies are generated during system runtime. It is done differently for the domain objects.You can not use Spring AOP mechanisms [8] here, because the domain objects are not known to the Spring application context. In this case SAF uses AspectJ [9]. At compile time the bytecode is modified by annotated domain objects so that when a method is called an AspectJ advice is triggered which invokes the relevant check() method via the AccessManager. To ensure that only domain objects and that no Spring beans are enhanced by the AspectJ compiler the domain objects must be additionally annotated with an @SecureObject on the class level. Later versions of the SAF will also support "load-time weaving". With this the bytecode will be modified at the time a domain object class is loaded.
   Without the use of the SAF you would have to configure the interceptors or advices from Figure 6 yourself, which in large applications is confusing and error-prone. The configuration will also contain (parts of) classes and method names, which must be additionally modified when refactoring. This can easily be overlooked and lead to security holes in the application. Through the use of SAF annotations, renamed classes and methods are automatically recognized and protected at implementation time and runtime respectively.

Policy Decision

Up to now we have only annotated places in the source code which should be checked when they are accessed by domain objects. An access decision has not yet been made. SAF delegates them via the AccessManager to a Policy Decision component. This component ultimately decides whether access to a domain object is allowed or not. To make an access decision we want to fall back on the mechanisms provided by the Java SE platform and JAAS and to extend them to meet our requirements. This extension can only be briefly touched on here due to space limitations. For implementation details you are referred to the sample application. [6, 5].
    Figure 7 shows a general overview of the structure of our sample application’s Policy Decision component. The classes of the Java security architecture, which we use and want to extend, are in the java.security package. The SAF AccessManager interface is part of the net.sourceforge.safr.core.provider package.

 

 

Fig. 7 Structure of the policy decision component

The Java security architecture defines the abstract class Permission to describe access to the resources of an application. Since we want to represent access to domain object instances, we are diverted to our own InstancePermission class. The target attribute of this class is a type of reference and describes which instance of a domain object should be accessed. The action attribute describes the type of access (e.g. read access). In order to make an access decision we create an InstancePermission object, and thus invoke the AccessController’s checkPermission() method. This invocation is executed by the adapter class show in Figure 7. This binds, therefore, the AccessController to SAF or, in other words, SAF delegates access decisions using the adapter to the AccessController. The logic for access decisions is encapsulated in a class, which inherits from the JDK’s abstract Policy class. The derived class is known as a Policy Provider. The standard implementation delivered with the JDK can not be used for our purposes because we need specific logic to process InstancePermissions and we also want to be able to alter permissions at runtime. Therefore we make our own implementation available (InstancePolicy). This affects access decisions for InstancePermission objects.
    Figure 8 shows the access decision process. SAF invokes, for example, the adapter’s checkRead() method and passes as an argument the Notebook instance to be checked. The adapter creates an InstancePermission object that specifies read access to the Notebook instance and passes it to the AccessController when it invokes the checkPermission() method. Then the AccessController, (this is a greatly simplified version of what happens!) invokes the implies() method of the InstancePolicy and passes as an argument the InstancePermission produced by the adapter and a ProtectionDomain object. The InstancePermission argument provides information on the domain object instance to be accessed; the ProtectionDomain argument provides, among other things, information about which user accessed it. The InstancePolicy will now examine whether the current user has sufficient rights in the Policy Store (not shown here). If yes, the implies() method returns true and otherwise false.  The return value false causes the AccessController to throw an AccessControlException which is passed to SAF and further via the adapter.

 

 

Fig. 8 Making an Access Decision

The PermissionInstance class was designed so that it can represent access to any domain objects. Together with the Policy instance, it can also be reused as a generic Policy Provider in other applications. This Policy Provider is sufficient for test operation but for production use it would still need to be extended to provide quality persistence and caching mechanisms.
This is why, for example, InterComponentWare AG’s LifeSensor personal health record uses a high-performance provider to restrict access to its customers’ health information [10]. The integration was carried out, as described above, by SAF.

Comparison

This section provides a brief comparison of SAFs functionality with that of the Acegi Security Framework [11] and JBoss Seam Security [12]. Acegi security is a powerful authentication and authorization framework and is used in many production Spring applications. In comparison to the solution we presented, Acegi’s Policy Decision functionality is not based on Java security standards but on a proprietary solution. Some AccessDecisionManager check here the access to the domain objects in an application with the use of Access Control Lists (ACLs). An ACL defines per domain object who has access permissions.

Both Acegi as well as SAF use AspectJ and Spring AOP for Policy Enforcement. However, there is more configuration effort with Acegi. Annotations on domain objects are not (yet) supported. There is still additional work for the developer to do. Whoever wants to use the simple configuration and the versatile annotation possibilities of the SAF together with the Policy Decision functionality of Acegi can do so. This is done using an adapter, which implements the SAF Access Manager interface and forwards requests to the Acegi ACL subsystem or to an AccessDecisionManager.
JBoss Seam follows a different process when implementing Policy Decision functionality and sets access rules with the help of JBoss Rules [13] the JBoss rules engine. The access decision to the domain objects in an application can be based on any number of complex rules. Policy Enforcement in Seam relies on special @Restrict annotations. If the access to a domain object should be checked, the value of the instance’s annotation must be explicitly referenced by a variable name. Here, too, a combination of SAF and JBoss Rules would be conceivable.

Summary

We have learned about the architectural foundation of instance-based access control and ways to implement it. A clear separation was made between Policy Enforcement and Policy Decision. In Policy Enforcement, we have marked the places in the source code where we want to control access to instances with SAF annotations. This causes the corresponding authorization checks to be carried out during runtime. The access decisions were delegated by SAF to a Policy Decision component, accessed through a service provider interface. The Policy Decision component of our sample application was based on Java SE Security standards. Opportunities for the integration of Policy Decision functionality of other security frameworks were also discussed.

Links & Literature

[1] Security Annotation Framework http://safr.sourceforge.net/ und http://sourceforge.net/projects/safr
[2] Java EE 5 Specification (Section EE.3.7.2) http://jcp.org/aboutJava/communityprocess/final/jsr244  
[3] Java Security Architecture http://java.sun.com/javase/6/docs/technotes/guides/security/spec/security-spec.doc.html
[4] JAAS Reference Guide http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html
[5] SAF JAAS Module http://safr.sourceforge.net/safr-jaas/
[6] SAF Notebook Example http://safr.sourceforge.net/safr-sample-notebook
[7] SAF Core Module http://safr.sourceforge.net/safr-core/
[8] Spring AOP APIs http://static.springframework.org/spring/docs/2.0.x/reference/aop-api.html
[9] AspectJ http://www.eclipse.org/aspectj/
[10] LifeSensor Personal Health Record http://www.lifesensor.de
[11] Acegi Security System for Spring http://acegisecurity.org/
[12] JBoss Seam http://www.jboss.com/products/seam
[13] JBoss Rules http://www.jboss.com/products/rules
[14] This article originally appeared in the German language publication Java Magazin issue 07.2007 published by Software & Support Verlag, Frankfurt am Main, Germany. Republished here with their permission.