Inside Paulo Abrantes' head
[ start | index | login or register ]
start > 2007-10-22 > 1

Java Programming: Proxies and Access Control

Created by pabrantes. Last edited by pabrantes, 303 days ago. Viewed 1,400 times. #7
[diff] [history] [edit] [rdf]
labels

Java Programming: Proxies and Access Control

As a result of my last post - >>Java Programming: Proxies and References - I received an email asking how could access control be implemented using the same proxy mechanism.
This post's goal is to answer that question. I'll show a possible solution, provide example source code and discuss the advantages and disadvantages of using such mechanism.

Since I already explained what a proxy is I'll skip that part. Anyone interested in reading about proxies should read my previous post, >>Java Programming: Proxies and References.

First let's define clearly what I mean by access control. When using the term access control I'm referring to a mechanism that is able to enforce policies. A policy is nothing more than a rule or a set of rules, example of policies may be "Only editors can edit object" or "Only authenticated users can read and write object" among others.

Design

flowchartACLThe idea of using access control with proxies isn't new and most of the times is implemented - with minor variations - using the design pattern >>intercepting filter.

In this particular case where proxies are used, the interception is done by the InvocationHandler.
The InvocationHandler is an interface that makes the JVM translate all the methods invocations into a single method named invoke. This method receives the object, the method name and the arguments.
Such behaviour ensures that all methods invoked will be intercepted, as long as they're being invoked in the proxy object instead of the actual object. That is why the application needs to assure that the object itself is only accessible through a proxy.

The interception algorithm in the invocation handler is quite simple and is the following:

  1. Retrieve all existing policies that affect the current invocation
  2. Execute each one of those policies. If at least one fails deny access, otherwise allow the invocation on the object being proxied.
The flowchart can be found at the left.

Implementation

As I usually say there are no perfect solutions, the implementation I'm suggesting isn't an exception. There are various ways of implementing the design presented above and I chose one.
It may have flaws but allows me to show how access control should be separated from the application's domain code and also how to use annotations (readers interested in knowing how to create custom annotations should read >> Java Programming: Doing your own annotations).

The guide lines for this specific implementation are the following:

  • The policies, called rules in this example, implement an interface that is named AccessControlRule. This interface forces the implementation of a single method, verify.
Show AccessControlRule source code
package net.pabrantes.accessControl;

public interface AccessControlRule {

public boolean verify(ApplicationContext context); }

  • There is a custom annotation - named AccessControlCheck - that is used in methods and classes, which allows as argument an array of strings, where each string is a class that implements the AccessControlRule interface. Since this annotation will be searched at runtime it's retention policy needs to be RUNTIME.
Show AccessControlCheck source code
package net.pabrantes.annotations;

import java.lang.annotation.Retention; import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE;

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Target({METHOD,TYPE})

public @interface AccessControlCheck {

public String[] value() default {}; }

  • The custom InvocationHandler, named AccessControlHandler, is responsible for finding the acustom annotation, create instances of the rules, execute them and finally decide if access to the object should be allowed or denied.
Below can be found the implemented model.

acl-model

Looking at the model there are two classes that haven't been presented yet, they are the ApplicationContext - which actually could be previously seen in the AccessControlRule interface - and the AccessControlGenerator.

The ApplicationContext, like the name indicates, represents the application's context in a given invocation, in this particular example it holds things like the method that is being invoked, who invoked it and the arguments. This context is then used by AccessControlRules to decide if the requested invocation is allowed or not.

The AccessControlGenerator can be seen as a factory class. It contains a single static method, named newAccessControlInstance, responsible for creating proxies which will be using the custom InvocationHandler, AccessControlHandler. Although the signature of the method states Object as argument, only objects that implement at least one interface will be accepted, since the proxy needs at least one interface to implement.

Show AccessControlGenerator source code
package net.pabrantes.accessControl;

import java.lang.reflect.Proxy;

public class AccessControlGenerator {

public static Object newAccessControlInstance(Object object) { Class[] interfaces = object.getClass().getInterfaces(); if(interfaces.length == 0) { throw new IllegalArgumentException(object.getClass().getName() + " must implement at least one interface"); } return Proxy.newProxyInstance(object.getClass().getClassLoader(),interfaces, new AccessControlHandler(object)); } }

Finally let's take a look at the core class, the AccessControlHandler. It all starts when a method is invoked over the proxy and method invoke is executed. The method invoke implements the algorithm that was already presented, here's the source code:

public Object invoke(Object arg0, Method method, Object[] arguments) throws Throwable { AccessControlCheck annotation = findAnnotation(method, arguments); if (annotation != null) { checkRules(annotation.value(), method, arguments); } return method.invoke(interceptedObject, arguments); }

The first thing to do is to locate the custom annotation in the given object, findAnnotation method will access the Class of the object being proxied, retrieve the method that is being invoked and see if it contains the custom annotation, if it can't find it then defaults to finding the annotation in the Class object.

Show findAnnotation method source code
private AccessControlCheck findAnnotation(Method method, Object[] arguments) { AccessControlCheck annotation = null; try { Method actualMethod = interceptedObject.getClass().getMethod( method.getName(), getClasses(arguments == null ? Collections.EMPTY_LIST.toArray() : arguments)); annotation = actualMethod.getAnnotation(AccessControlCheck.class); } catch (Exception e) { e.printStackTrace(); } return annotation != null ? annotation : interceptedObject.getClass() .getAnnotation(AccessControlCheck.class); }

If the annotation is found then the policies specified in it must be executed. The method checkRules receives the array of strings specified in the annotation and also the method and arguments that the proxy received which are needed to created the ApplicationContext. Each string will be proccessed into a class, an instance will be created and the verify method will be ran. If any of verifications fail then a SecurityException is thrown.
As I said before the ApplicationContext contains information related with the context, which, among other things holds the user that originated the request. Since this implementation is only a simple example a mock object is being used to provide the requesting user.

Show checkRules method source code
private void checkRules(String[] classes, Method method, Object[] arguments) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ApplicationContext context = new ApplicationContext(method, UserMockObject.readCurrentUser(false), arguments); for (String className : classes) { Class clazz = Class.forName(className); AccessControlRule rule = (AccessControlRule) clazz.newInstance(); if (!rule.verify(context)) { throw new SecurityException( "Denied to perform the operation by: " + rule.getClass().getName()); } } }

In the end if there were no rules to check or all rules allowed the invocation, the requested method is invoked over the object that is being proxied.

Advantages and disadvantage

The main advantages, in my opinion, are:

  1. Access control code is separated from domain specific code.
  2. Simplicity of this particular implementation.
  3. In this particular implementation is possible to define generic rules for example a rule that verifies any method in Class A which starts by set can only be executed by an admin (for more information see GenericSetAdminAccess rule in code).
The main disadvantage of using this mechanism are already known from the previous post they are the explosion of interfaces and possible performance and memory issues. This disadvantages are mostly related with the fact that proxies are used in this implementation.

Conclusions

This example showed a way of implementing access control but there are various other solutions. Many Java Frameworks already offer this kind of features out of the box, although I think it's always important to know how things work inside the black box.

It might not be the best solution for large applications but small ones may use it very well, SnipSnap - this bliki's software - is an example of an application that implements access control using proxies.

Finally, for the ones interested in the full source code I made it >>available for downloaded.

Please login to www.pabrantes.net.
Who am I?
paulo-roca2My name is Paulo Abrantes AKA pabrantes and I'm a software developer. I'm currently employed at >>CIIST working as a Java developer in >>FenixEDU.

This blog is mostly about Java programming, domain driven design and snipsnap bliki developing. Everything written in this blog is my personal opinion and it may not reflect the opinions of my employer and co-workers.


Blog subscription
subscribe by rss subscribe by email

Links
>> Home
>> Paulo's Profile
>> Post History
>> Add to Technorati Favorites
>> Paulo's Photo Gallery
>> WishList
>> Posting without Login

Search Blog
Fellow Bloggers

Recent Posts

Java Programming: Bytecode Injection
Intermission: Sorry For Downtime
Software Developing: Studying The Bliki Domain Model
SnipSnap Developing: Trying to settle a roadmap
System Administration: Load Balancing with Apache
Blogging: Two years have passed
Software Developing: The SnipSnap Saga
Java Programming: Getting your code spicy with Groovy
Software Developing: Fluent Interfaces
Software Developing: Implementing a ShoutBox on SnipsSnip
Software Developing: SnipSnap, SnipIt and SnipSnip
Java Programming: Proxies and Access Control
Java Programming: Proxies and References
Java Programming: References' Package
YALM: Yet Another Layout Modification

For older posts, please refer to post-history for a complete Post History

Logged in Users: (0)
… and 5 Guests.
This is a modified version of snipsnap.org created by >>Paulo Abrantes