An Impersonation Aspect With Spring.NET AOP

One of the most useful features of Spring.NET is its Aspect Oriented Programming (AOP). Object Orientation (OO) decomposes an application into a hierarchy of entities linked through references. AOP on the other hand, decomposes an application into ‘service layers’ (referred to as aspects or cross cutting concerns in AOP lingo). This allows us to transfer infrastructure code out of our core business logic and entities. AOP has some really confusing jargon, like advice, weaving, crosscutting concern, joinpoint, pointcut, etc. To get a grip of these basic building blocks of AOP I refer you to Spring’s excellent documentation that will get you started in no time.

This post is for those of you that covered the basic concepts and terms of AOP, and Spring.NET AOP in particular, but are still struggling a bit to put it all together. I’ll give an overview of how all the different parts of AOP come together, and then follow it up with a working example.

Transparently Wrapping Infrastructure Around Objects

Even after going over the fundamentals of AOP can one still be confused. So let’s start of with two very simplified diagrams that contrast OO against AOP. These diagrams are not ment to be technically accurate, just demonstrate the very high level concept of AOP.

Figure 1

The frist diagram of figure 1 shows how all of OO’s operating logic is contained within an object’s methods. In OO the object (or class) is the main construct that distills data (properties) and tasks (methods) into a type of entity. Each class has several methods, and in turn each method consists of business and infrastructure logic. Before we move on, I’d just like to mention that it’s not a case of AOP vs OO, and which one is the better than the other. AOP complements OO, and improves the quality and adaptability of OO.

OO works wonderfully when we only consider data, methods, and statements directly related to the bigger concept they collectively represent. But when we take things like logging, caching, security, and transactions into concideration we get this uneasy feeling that we are adding code that do not have any direct relation to the logical entity. Traditionally we would combine this infrastructure code with our business logic. This results in dirty business logic code, that becomes more complex, confusing, and less maintainable as we increase its coupling with infrastructure code. Not only this, but let’s say you quickly wanted to cache the results of several methods, without modifying each object’s method? You would have to go and manually duplicate the same code in each method were you want to perform the caching. That’s a lot of hard coding labor, and even harder maintenance should you want to change the way you do things.

Along comes AOP that allow you to move the infrastructure code out of our object’s methods into aspects that has specific roles with tasks. So you’ll have an aspect that provides security around methods, and another logging method execution. Our object’s business logic does not have to know anything about the infrastructure code in which it’s embedded. The infrastructure code is dynamically injected before or after a method executes. That is what the second diagram of figure 2 demonstrates: We have our object, and dynamically we specify when, and how infrastructure aspects intercept incoming and outgoing method calls. The result is that we are left with pure business logic code in the object’s methods, which are themselves transparently wrapped in the necessary infrastructure code.

Interfaces And Proxies

Spring.NET’s AOP framework uses proxies and interfaces to transparently wrap objects in aspects. An AOP proxy is an object that intercepts calls to, and results from, our actual business entity objects. The proxy has the exact same public members as our business entity object. When we request an object that we want to wrap in aspects, we actually request its proxy. In our configuration we will tell the proxy which object provides the actual implementation:


<objects xmlns="http://www.springframework.net">

    <object id="crmService" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="proxyInterfaces" value="Shinobido.GhostBlade.Customers.ICRMService" />
<property name="target">

<object type="Shinobido.GhostBlade.Customers.CRMService, Shinobido.GhostBlade">
<property name="Url" value="http://localhost:2491/CRMService.asmx" />
<property name="UseDefaultCredentials" value="true" />

</object>
         </property>
<property name="interceptorNames">
	<list>
                 <value>impersonationAspect</value>

</list>
        </property>

</object>

     <!-- Interception AOP Begin -->

<object id="impersonationAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop">
<property name="Pointcut">

<object id="regexPointcut" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
<property name="patterns">
	<list>

<value>.*</value>

</list>
                  </property>
               </object>
       </property>
<property name="Advice" ref="impersonationAdvice" />

</object>

     <object id="impersonationAdvice" type="Shinobido.GhostBlade.Security.ImpersonationAdvice, Shinobido.GhostBlade" />

     <object id="Shinobido.GhostBlade.Security.IIdentityProvider"
                    type="Shinobido.GhostBlade.Security.HttpContextIdentityProvider, Shinobido.GhostBlade" />

</objects>

public interface ICRMService
{
    CustomerInformation GetCustomerById( int id ) ;
}

public class CRMService: System.Web.Services.Protocols.SoapHttpClientProtocol, ICRMService
{
    public CustomerInformation GetCustomerById( int id )
    {
            object[] results = this.Invoke( "GetCustomerById", new object[] { id } );
            return ( CustomerInformation )results[0];
    }
}

Figure 2

From the above example we can infer a number of things:

  1. ProxyFactoryObject is going to intercept calls to our target object, CRMService. In Spring.NET there are several proxying strategies to choose from like inheritance or composition (interface) based, autoproxies, and so forth. Here we’re using a GUID-based composition ProxyFactoryObject. When you instantiate this proxy through Spring.NET’s object factory, you’ll notice that the object’s type is actually a runtime generated object of a type like “CompositionAopProxy_4f054a8f2cd349479fe1673d4427aca3″ that implements ICRMService.
  2. ProxyInterfaces expose the members that will be visible to client code and intercepted by the proxy through interface ICRMService. In other words aspects will only be applied to members specified by this interface. Any members specified by this interface will automatically receive advice based on their pointcuts.
  3. Target specifies the business entity object that we’re going to wrap.
  4. InterceptorNames specify that the aspect impersonationAspect must be applied to method requests and outputs.
  5. impersonationAspect‘s Pointcut uses a regualr expression that selects all members of the given class to receive impersonationAdvice.

In point 1 I mentioned composition vs inheritance based AOP proxies. We are using a composition based proxy, because the proxy never inherits from our target object, CRMService. A composition based proxy has a private field called m_targetSourceWrapper of type Spring.Aop.Framework.StaticTargetSourceWrapper that stores the actual business entity class we’re redirecting to. An inheritance based proxy inherits from the target object.

Writing A Custom Impersonation MethodInterceptor

Spring.NET provides a number of out-of-the-box aspects for caching, logging, transactions, and exceptions. What we’re going to do is write custom ‘around advice’, called a MethodInterceptor. Remember, Advice are objects that perform the actual work. So logging advice will actually write to the log file. Pointcuts tell the proxy when to pass control to the specified advice. You get four types of advice:

  • Before a target method executes.
  • After a target method executes.
  • Before and after (around) a method executes.
  • When a target method throws an unhandled exception.

The AOP advice we’re going to write, will impersonate the current thread with a WindowsIdentity obtained from an identity provider. This is used when an application runs under a Windows service account, but selected methods need to be called with the current user’s identity, instead of the service account’s. The IIdentityProvider is dynamically injected, and retrieves the WindowsIdentity from the applicable context. For instance when you’re impersonating the current thread in a web application, you need to implement a HTTP IIdentityProvider, that gets the current user’s WindowsIdentity from the HttpContext.

In our specific scenario we are applying impersonationAdvice to all methods (.*) of the ICRMService, implemented by CRMService. In a nutshell, we are impersonating all our calls to CRMService.

Control is passed to an IMethodInterceptor before a target method is entered, and after it returns:

 public class ImpersonationAdvice : IMethodInterceptor
 {   
 #region IMethodInterceptor Members

 public object Invoke( IMethodInvocation invocation )
 {
     using ( IdentityImpersonator.Begin( ObjectManager.New<IIdentityProvider>().Provide() ) )
     {         
         return invocation.Proceed();
     }
 }

 #endregion
 }

ImpersonationAdvice implements IMethodInterceptor. You can almost say that Invoke will replace the target method it’s wrapping. To execute the actual target method, you need to call Proceed() on the supplied IMethodInvocation argument.

Here’s the code for our implementation of IIdentityProvider, IdentityImpersonator, and ObjectManager:

internal class HttpContextIdentityProvider : IIdentityProvider
{
    #region IIdentityProvider Members

    /// <summary>
    /// Provides the identity of the current user.
    /// </summary>
    /// <returns></returns>
    public WindowsIdentity Provide()
    {
        return (WindowsIdentity) HttpContext.Current.User.Identity;
     }

     #endregion
}

public sealed class IdentityImpersonator : IDisposable
{
    #region Globals

    private WindowsImpersonationContext ctx;
    private bool disposed;

    #endregion

    #region Construction       

    /// <summary>
    /// Initializes a new instance of the <see cref="IdentityImpersonator"/> class.
    /// </summary>
    private IdentityImpersonator()
    {
        // Static Construction in Begin().
    }

    #endregion

    #region Public Members

    /// <summary>
    /// Begins this instance.
    /// </summary>
    /// <returns></returns>
    public static IdentityImpersonator Begin(WindowsIdentity identity)
    {
        var impersonator = new IdentityImpersonator {ctx = identity.Impersonate()};
        return impersonator;
    }

    /// <summary>
    /// Undoes this instance.
    /// </summary>
    public void Undo()
    {
        if (ctx != null)
        {
            ctx.Undo();
        }
    }

    #endregion

    #region IDisposable Members

    /// <summary>
    /// Releases unmanaged and - optionally - managed resources
    /// </summary>
    public void Dispose()
    {
        if (!disposed)
        {
            Undo();
            GC.SuppressFinalize(this);
            disposed = true;
        }
    }

    #endregion
}

/// <summary>
/// Instantiates objects based on their name, and provides
/// a standard set of configured utility objects.
/// </summary>
public static class ObjectManager
{
    private static IApplicationContext context;

    /// <summary>
    /// Instantiates a default object with an id/name
    /// that is the same as the class's full name.
    /// </summary>
    public static T New<T>()
    {
        return New<T>(typeof(T).FullName);
    }

    /// <summary>
    /// Instantiates the object with the specified name.
    /// </summary>
    /// <typeparam name="T">The type of object to return.</typeparam>
    ///<param name="name">The name of the object.</param>
    /// <returns>A newly created object with the type specified by the name, or a singleton object if so configured.
    /// </returns>
    public static T New<T>(string name)
    {
        return (T)Context.GetObject(name);
    }

    /// <summary>
    /// Instantiates the object with the specified name, and constructor arguments.
    /// </summary>
    public static T New<T>(string name, object[] arguments)
    {
        return (T)Context.GetObject( name, arguments);
    }

    /// <summary>
    /// Gets the context.
    /// </summary>
    /// <value>The context.</value>
    internal static IApplicationContext Context
    {
        get
        {
            if (context == null)
            {
                lock ( ContextRegistry.SyncRoot )
                {
                    context = ContextRegistry.GetContext();
                }
             }

             return context;
         }
    }
}

ObjectManager serves as the object factory that wraps Spring.NET’s ApplicationContext with some additional functionality. Specifically in this scenario, ObjectManager instantiates our proxied objects.

The important tasks are performed by IdentityImpersonator. HttpContextIdentityProvider supplies IdentityImpersonator.Begin(…) with the current user’s WindowsIdentity from HttpContext.Current.User, which in turn calls WindowsIdentity.Impersonate(). To stop impersonating the current thread, you need to call WindowsIdentity.Undo(). The Dispose method stops the impersonation, so that you need to put code that you want to impersonate, in a using {…} clause. GC.SuppressFinalize(…) tells the garbage collector to ignore IdentityImpersonator‘s finalize method, since we already cleaned up the object when it was Disposed.

Usage and Conclusion

So, to impersonate a call to CRMService becomes a completely transparent process. You don’t need to duplicate IdentityImpersonator.Begin() every time you want to impersonate the current thread. The methods that need impersonation is dynamically selected from one place, impersonationAspect’s Pointcut, and the impersonation work is also performed from one place, in ImpersonationAdvice:

var crmService = ObjectManager.New( “crmService” );
crmService.GetCustomerById( 1 );< [/sourcecode] This should clearly demonstrate the power of AOP: Injecting all that functionality into GetCustomerById(…), without adding a single line of code to it, and we can change it all without making any code changes!