Identity Stack Propagation

The second example of using the generic interceptor is about security identity propagation. As explained in Chapter 10, impersonation as a mechanism for identity propagation has many liabilities. Still, sometimes your service is required to pass the identity of the original caller (or all callers) down to the resources or other services with which it interacts. Instead of impersonating the callers or passing their identities as explicit parameters, you can pass the identities out-of-band, in the message headers, and use the generic interceptor to automate processing of those identities.

The first step is to define the stack of callers. To that end, I defined the SecurityCallFrame, which represents a single caller identity as well as some additional information about the caller, such as its address and the operation it invoked:

[DataContract]
public class SecurityCallFrame
{
   [DataMember(IsRequired = true)]
   public string Authentication
   {get;}

   [DataMember(IsRequired = true)]
   public string IdentityName
   {get;}

   [DataMember(IsRequired = true)]
   public string Address
   {get;}

   [DataMember(IsRequired = true)]
   public string Operation
   {get;}

   //More members
}

Next, I defined the security call stack:

[DataContract]
public class SecurityCallStack
{
   internal void AppendCall(  );

   public SecurityCallFrame OriginalCall
   {get;}

   public int Count
   {get;}

   public SecurityCallFrame[] Calls
   {get;}

   //More members
}

The implementation details of these types are irrelevant for this appendix.

Using the GenericContext<T> from Appendix B, I defined the security call stack context:

[DataContract]
public class SecurityCallStackContext
{
   public static SecurityCallStack Current
   {
      get
      {
         if(GenericContext<SecurityCallStack>.Current == null)
         {
            return null;
         }
         return GenericContext<SecurityCallStack>.Current.Value;
      }
      set
      {
         GenericContext<SecurityCallStack>.Current =
                                     new GenericContext<SecurityCallStack>(value);
      }
   }
}

To automate passing the call stack, I then defined the class SecurityCallStackClientBase<T> shown in Example E-10.

Example E-10. The SecurityCallStackClientBase<T> class

public abstract partial class HeaderClientBase<T,H> : InterceptorClientBase<T>
{
   protected H Header
   {get;set;}

   protected override void PreInvoke(ref Message reply);

   //Rest of the implementation
}
public abstract class SecurityCallStackClientBase<T> :
                                             HeaderClientBase<T,SecurityCallStack>
{
   protected SecurityCallStackClientBase(  )
   {
      InitializeCallStack(  );
   }

   //More constructors

   void InitializeCallStack(  )
   {
      if(OperationContext.Current == null || Header == null)
      {
         Header = new SecurityCallStack(  );
      }
      else
      {
         Header = SecurityCallStackContext.Current;
      }
   }
   protected override void PreInvoke(ref Message request)
   {
      Header.AppendCall(  );
      base.PreInvoke(ref request);
   }
}

SecurityCallStackClientBase<T> derives from HeaderClientBase<T,H> (also defined in Appendix B), and HeaderClientBase<T,H> in turn derives from InterceptorClientBase<T>.

Every time the client invokes calls on the SecurityCallStackClientBase<T> proxy, the proxy will automatically append the current identity to the call stack and pass it in the headers. If all services down the call chain use SecurityCallStackClientBase<T> (or manually use SecurityCallStackContext), on every call the call stack will contain the new frame.

Security Call Stack Interceptor

To process and manage the identities stack, define a subclass of the generic interceptor. The processing could be as simple as logging the identities, or as complex as digitally signing the call stack to prevent spoofing by malicious intermediaries along the call chain and validating the current call stack, as shown in Example E-11.

Example E-11. Validating and logging the call stack with an interceptor

class SecurityCallStackInterceptor : GenericInvoker
{
   public SecurityCallStackInterceptor(IOperationInvoker oldInvoker) :
                                                                  base(oldInvoker)
   {}

   protected override void PreInvoke(object instance,object[] inputs)
   {
      SecurityCallStack callStack = SecurityCallStackContext.Current;

      if(callStack != null)
      {
         LogCallChain(callStack);
         ValidateCallChain(callStack);
         SignCallChain(callStack);
      }
   }

   void LogCallChain(SecurityCallStack callStack)
   {...}

   void ValidateCallChain(SecurityCallStack callStack)
   {
      //Perform custom validation steps here
   }

   void SignCallChain(SecurityCallStack callStack)
   {
      //Digitally sign call stack here
   }
}

You can apply the SecurityCallStackInterceptor at the operation or service level using dedicated one-liner attributes:

public class OperationSecurityCallStackAttribute :
                                             OperationInterceptorBehaviorAttribute
{
   protected override GenericInvoker CreateInvoker(IOperationInvoker oldInvoker)
   {
      return new SecurityCallStackInterceptor(oldInvoker);
   }
}
public class SecurityCallStackBehaviorAttribute :
                                                ServiceInterceptorBehaviorAttribute
{
   protected override OperationInterceptorBehaviorAttribute
                                                       CreateOperationInterceptor(  )
   {
      return new OperationSecurityCallStackAttribute(  );
   }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.55.18