Click here to Skip to main content
Click here to Skip to main content

Permission-by-aspect

, 7 Dec 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Implementation of a permission management using custom attributes and Aspect Oriented Programming (AOP).

Introduction

Introduction to Aspect Oriented Programming (AOP) is mostly done by exercising an example, adding logging as an aspect to an existing application. But, it is really simple to realise more sophisticated functionality using AOP. This article will demonstrate the use of aspects to build an easy to integrate and extensible permission management. This is done by applying custom attributes to methods, and checking these attributes while invoking annotated methods.

Background

Aspect Oriented Programming is targeted to separate code with different goals from each other. For example, a method within a business logic normally contains code to achieve different goals: first, the code making up the logic (e.g., calculating sums, creating a new customer, or everything else I just can't think of now.). Besides this, a method consists of code to write log entries, or to check if the current user has the permission to execute this method. Just think of a business logic method creating a new customer. This method can be called by user A, but not by user B. AOP is an ideal way to separate all these aspects, resulting in code which is better to understand and maintain.

Using the code

The provided code is more a template than a library. You could implement your business logic within the given project using the proposed style. Otherwise, you could examine the template and implement the "Permission-by-aspect" idea on your own. To use the code for your own purpose, you have to do two different things: first, annotating your business logic methods with attributes to define the permissions needed to execute them, and second, implementing a system to check if a user has a specific permission.

Annotating the business logic

Let's assume you need to implement part of a business logic dealing with customers. First of all, as a wise programmer, you declare all methods in an interface. (We will focus here on the CreateCustomer method.)

/// <summary>
/// Declares all methods used to interact with customers.
/// </summary>
public interface ICustomerBusinessLogic {

    /// <summary>
    /// Creates a new customer
    /// </summary>
    /// <returns>Newly created customer</returns>
    DataObjects.Customer CreateNewCustomer();
}

Then, there will be a class implementing the interface. Let's assume it looks like this:

/// <summary>
/// Implementation of the ICustomerBusinessLogic-interface.
/// </summary>
public class CustomerBusinessLogic:Interfaces.ICustomerBusinessLogic {

    /// <summary>
    /// Creates a new customer
    /// </summary>
    /// <returns>Newly created customer</returns>
    [PermissionManagement.Permission("CreateCustomer")]
    public DataObjects.Customer CreateNewCustomer() {
        // create a new customer object and return it
        return new DataObjects.Customer { 
            Id=Guid.NewGuid(), 
            Name = "Customer", 
            FirstName = "New", 
            Address = "Home" };
    }
}

The logic itself is not really exciting. A new Customer object is created and returned. The interesting part is the custom attribute PermissionManagement.Permission("CreateCustomer"), which says nothing more than the permission "CreateCustomer" is needed to execute this method.

To enable the permission check, we have to interweave the permission aspect with the business logic code above. This could be done by implementing a factory to create a CustomerBusinessLogic object. Within the factory, the Spring.NET Framework will be used to get the functionality of "interweaving" aspects. The attached PermissionAdvice is an instance of a class which implements the IMethodInterceptor interface provided by Spring.NET. The following code shows an example:

/// <summary>
/// Factory to create a new BusinessLogic-instance.
/// </summary>
public sealed class BusinessLogicFactory {

    /// <summary>
    /// Creates a new ICustomerBusinessLogic-instance.
    /// </summary>
    /// <returns>New BusinessLogic for customers</returns>
    public Interfaces.ICustomerBusinessLogic CreateCustomerBusinessLogic() {
        // create a proxy
        ProxyFactory facProxy = new ProxyFactory(new CustomerBusinessLogic());
        // add the permission-advice
        facProxy.AddAdvice(new PermissionManagement.PermissionAdvice());
        // create the proxy and return this proxy as needed interface
        return facProxy.GetProxy() as Interfaces.ICustomerBusinessLogic;
    }
}

The advice named PermissionAdvice checks if an invoked method is annotated with the Permission attribute, and will ask the permission management if the current user has the specified permission.

Permission management

As mentioned above, you need some logic to check if a user has a specific permission. Within the example, this is done by a class named PermissionLogic. This class maintains a dictionary containing a list of all users owning a specific permission. To check if a user has a permission, the logic checks if the list within the dictionary contains the user for the specified permission. If a user does not have the needed permission, an exception is thrown.

// check if the permission is known
if (this.m_dictPermissions.ContainsKey(sCurPermission)) {
    // check if user list is set and contains the given user
    if (! ((this.m_dictPermissions[sCurPermission] != null) && 
        (this.m_dictPermissions[sCurPermission].Contains(oUser)))) {
        // user does not have the permission -> throw exception
        throw new MissingPermissionException(oUser, sCurPermission);
    } 
} else {
    // user does not have the permission -> throw exception
    throw new MissingPermissionException(oUser, sCurPermission);
}

That is all that is needed to know to implement a business logic on your own with the functionality of permission by aspect.

Points of interest

There are two points of interest that I want to discuss here. First, let's take a look at the implementation of the custom attribute. The PermissionAttribute inherits the class System.Attribute. In combination with setting the attribute usage to AttributeTargets.Method, the PermissionAttribute can be used to annotate methods. To set the needed permission, the constructor accepts a string containing the permission name. If more than one permission is needed, the names of the permission can be separated by comma.

/// <summary>
/// This is a custom attribute, that sets the permission needed
/// to execute a method annotated with this attribute.
/// </summary>
[System.AttributeUsage(AttributeTargets.Method)]
public class PermissionAttribute:System.Attribute {

/// <summary>
/// Default constructor with passed permission string.
/// </summary>
/// <param name="sPermissionString">Permission string,
/// more than one permission seperated by a ","</param>
public PermissionAttribute(string sPermissionString) {
    // analyse passed permission string
    this.AnalysePermissionString(sPermissionString);
}

These custom attributes will be analysed during the invocation of the specified method by the permission advice. The proxy created by Spring.NET for the ICustomerBusinssLogic will intercept all method invocations and run the following code instead:

/// <summary>
/// This Advice adds the permission check to attributed methods in the Business Logic.
/// </summary>
public object Invoke(IMethodInvocation oInvocation) {

    // First, get the CustomAttribute Permission for the invoked method
    object[] aCustomAttributes = 
      oInvocation.Method.GetCustomAttributes(typeof(PermissionAttribute), true);
    // check array of custom attributes
    if ((aCustomAttributes != null) && (aCustomAttributes.Length > 0)) {
        // iterate through the attributes
        foreach (PermissionAttribute oCurAttribute in aCustomAttributes) {
            // call the check method -> this will throw an exception if the 
            // needed permissions are not given to the current user
            // (this will break the execution of the method)
            PermissionLogic.Instance.CheckPermissionsForUser(
               UserProvider.CurrentUser, oCurAttribute.GetNeededPermissions());
        }
    }

    // proceed with method invocation (all permissions needed are given)
    return oInvocation.Proceed();
}

Using the passed IMethodInvocation object, the invoked method and all of its custom attributes can be retrieved. For all PermissionAttributes found, the PermissionLogic will be asked if the current user has the needed permissions. If not, the execution will break, because the logic will throw an unhandled exception, and the invocation of the annotated method is cancelled.

Perspective

This article shows a basic implementation of the permission-by-advice topic. One future addition could be that the method arguments and results will also be checked. This could be useful if users could read customer records, but not all customer records can be changed and saved by all users. When a method like ICustomerLogic.SaveCustomer(a) is called, the PermissionAdvice could check if the current user has the permission to save the passed customer a. The same applies to result objects. Maybe, not all customer records returned from ICustomerLogic.GetCustomers() are accessible for the current user. The PermissionAdvice could filter the result and return only the records the user is allowed to view.

Another improvement would be that the custom attributes are not attached to methods of the concrete implementation of the business logic interface, but to the interface itself. In my opinion, this is the better way, because the permissions needed are part of the interface of a component.

Please let me know if you like further examples on this topic, like filtering the result objects or checking the passed arguments.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

SeMartens
Software Developer (Junior)
Germany Germany
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 7 Dec 2008
Article Copyright 2008 by SeMartens
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid