Intercepting WCF Operation Calls with Impersonated Identity
4 min read
4 min read
Take the following scenario. You have a WCF webservice with several operations. The communication runs within a secure SSL channel and uses Kerberos authentication. The webservice application runs with an assigned application pool user, in your .Net code represented by the WindowsIdentity.
The requirement is to make a call to another webservice - let's call it the authorization service - which takes the currently authenticated Identity and performs some further business logic in order to determine whether the user has the right to access given resources.[OperationBehavior(Impersonation = ImpersonationOption.Required)]This would work. I don't like this approach however. The problem is that most of the webservices would need to first check the user authorization by calling the authorization service. So every single programmer would need to implement the call which is not an acceptable, clean solution and way too much error prone.
public string Hello(string message)
{
//execute the call to the authorization service
//if the current identity is authorized execute the custom
//business logic
return "hello";
}
IAuthorization
interface appropriately.public class MyCustomAuthorizationPolicy : IAuthorizationPolicyIn the service's configuration you need to register your policy
{
public boolean Evaluate(EvaluationContext context)
{
evaluationContext.Properties["Principal"] = //call the authorization service to retrieve an appropriate principal
...
}
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)The method names are quite self-explanatory. However, you already guessed it, impersonation didn't work at this level neither.
{ }
public object BeforeCall(string operationName, object[] inputs)
{ }
IOperationInvoker
interface are the Invoke(...)
,
BeginInvoke(...)
and EndInvoke(...)
class AuthenticationOperationInvoker : IOperationInvokerNote, my custom OperationInvoker is mainly a wrapper, taking the default operation invoker in the constructor and delegating all operations to it in order to not break anything existing. In line 17, within the Invoke method I do inject my custom authorization logic and yes, here impersonation works.
{
private IOperationInvoker defaultInvoker;
public AuthenticationOperationInvoker(IOperationInvoker defaultInvoker)
{
this.defaultInvoker = defaultInvoker;
}
public object[] AllocateInputs()
{
return defaultInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
//execute the custom authorization logic and set the thread principal accordingly
return defaultInvoker.Invoke(instance, inputs, out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return defaultInvoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return defaultInvoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return defaultInvoker.IsSynchronous; }
}
}
IOperationBehavior
interface.public class AuthorizedMethodAttribute : Attribute, IOperationBehaviorIn line 11 and 12 I fetch the default operation invoker and inject it into my custom which then replaces the operation invoker used by my webservice.
{
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{ }
public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
IOperationInvoker defaultInvoker = dispatchOperation.Invoker;
dispatchOperation.Invoker = new AuthenticationOperationInvoker(defaultInvoker);
}
public void Validate(OperationDescription operationDescription)
{}
}
[AuthorizedMethod]
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string Hello(string message)
{
//execute the custom webservice's business logic
return "hello";
}