Unity and AOP: Cross-Cutting Security Functionality

by Aliaksei YenzhyieuskiJuly 14, 2010
Learn how to use Unity to inject custom security functionality to a target object with minimal efforts.

With the release of Unity 2.0, a dependency injection container, the team at Altoros explored how to implement the capabilities of aspect-oriented programming with the help of the framework. In this post, we demonstrate how the paradigm works on the example of cross-cutting security functionality injection.

 

Challenge

In our example, there is a set of repositories that we can access from domain objects—Employees, Orders, and Devices. These objects are placed in different types of storage—Active Directory, external ERP, or a locally stored XML file accordingly. Each repository has different underlying storage, so it is difficult to control user permissions. In our case, we also enable only users with admin roles to perform the create/edit/delete operations for domain objects, while all the other users are limited to the read operations

Performing key operations for domain objects in a repository

With the help of NUnit, we have formulated these requirements in the following unit tests.

1. Users with the admin role have the Save functionality access. We also ran the same test for Delete.

  
   [Test]
       public void CheckThatAdminHaveSaveAccess()
       {
           // init
           Thread.CurrentPrincipal = new Principal("Admin");
           // action
           var result = employeeRepository.Save(new List&lt;Employee&gt;() {<span>new <span>Employee()});
           // assert
           Assert.AreEqual(result, 1);
       }

2. Users with the user role do not have the Save functionality access (the same for Delete).

       
 [Test]
        [ExpectedException(typeof(MethodAccessSecurityException))]
        public void CheckThatUserHaveNotSaveAccess()
        {
            // init
            Thread.CurrentPrincipal = new Principal("User");
            // action
            var result = employeeRepository.Save(new List&lt;Employee&gt;() {new Employee()});
            // assert
            Assert.AreEqual(result, 1);
        }

3. Users with the user role have the Get functionality access (the same for the admin role).

      
[Test]
       public void CheckThatUserHaveGetAccess()
       {
            // init
            Thread.CurrentPrincipal = new Principal("User");
            // action
            var result = employeeRepository.Get();
            // assert
            Assert.AreNotEqual(result.Count, 0);
       }

The performed unit tests work for each type of the repositories mentioned above.

 

How Unity can help

With cross-cut functionality injection, Unity provides possibility to intercept method calls by using call handlers (e.g., the ICallHandler interface). This interface includes the invokemethod, which contains required functionality that should be executed before actual method execution. In our example, it will be permission to check functionality. In addition, this interface defines the Orders property, which should return the call handler order number. This is useful in case of using several call handlers (we can specify the order of their execution).

The Unity interception process (Image credit)

After having our custom call handlers implemented, we should specify when and where they should be executed. Unity allows for several ways to do it. With the attribute approach, we should mark methods with custom attributes inherited from HandlerAttribute. These attributes are responsible for appropriate call handler creation by overriding the CreateHandler factory method.

 
Step 1

First of all, we will create a call handler that should contain permission check logic.

   
public class MethodAccessCallHandler : ICallHandler
    {
        private readonly string[] roles;

        public MethodAccessCallHandler(params string[] roles)
        {
            this.roles = roles;
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate
                                                                                 getNext)
        {
            IPrincipal principal = Thread.CurrentPrincipal;
            bool allowed = roles.Any(principal.IsInRole);
            if (!allowed)
                return input.CreateExceptionMethodReturn(new
                                             Exceptions.MethodAccessSecurityException());
            return getNext()(input, getNext);
        }

        public int Order { get; set; }
    }

Our call handler throws MethodAccessSecurityException, a custom exception inherited from ApplicationException, in case of a permission check failure.

 
Step 2

After that, we will create a custom attribute inherited from HandlerAttribute that should override the CreateHandler factory method for the appropriate call handler creation (see Step 1).

    
public class MethodAccessAttribute : HandlerAttribute
    {
        private readonly string[] roles;

        public MethodAccessAttribute(params string[] roles)
        {
            this.roles = roles;
        }

        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return newMethodAccessCallHandler(roles);
        }
    }

 
Step 3

Then, we will mark the IRepository interface methods (permission to which should be controlled) with the attribute created during Step 2.

    
public interface IRepository
    {
        [MethodAccess(Roles.AdminRole, Roles.UserRole)]
        IList&lt;T&gt; Get();

        [MethodAccess(Roles.AdminRole)]
        int Save(IList&lt;T&gt; entities);

        [MethodAccess(Roles.AdminRole)]
        int Delete(IList&lt;Guid&gt; ids);
    }

By marking these methods, we actually add permission check functionality to all classes that will implement this interface. Now, you can run unit tests.

Unity allows us to inject cross-cutting functionality with minimal efforts across such areas as domain object changes tracking, automatic PropertyChanged event rising for all domain object properties, etc.

 

About the author

Aliaksei Yenzhyieuski is Senior Software Engineer at Altoros with 16+ years of experience in software development. He is responsible for project management and team leading. Aliaksei can boast of solid expertise in computer science. Along with broad experience in implementation and maintenance of large-scale web, desktop, and mobile applications, he has strong object-oriented design and programming skills. In addition, Aliaksei is experienced in Agile and Scrum methodologies.

 

Further reading