Zulfiqar's weblog

Architecture, security & random .Net

Archive for June, 2009

Pass-through SAML tokens & Secure Conversation (Part2)

Posted by zamd on June 7, 2009

Few people have contacted me regarding follow-up on my last post so this post continues where the last one left off.

To keep the API simple, I have created few extension methods. Let’s first review these.

public static class Extensions

{

    public static void EnableSamlTokenCaching(this ServiceHostBase source)

    {

        var orignal = source.Description.Behaviors.Remove<ServiceCredentials>();

        source.Description.Behaviors.Add(new ServiceCredentialsEx(orignal));

    }

 

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken;

        return null;

    }

 

    public static void EnableSamlTokenFlow<TChannel>(this ClientBase<TChannel> source, SamlSecurityToken tokenToFlow) where TChannel : class

    {

        var orignal = source.Endpoint.Behaviors.Remove<ClientCredentials>();

        source.Endpoint.Behaviors.Add(new ClientCredentialsWrapper(orignal));

    }

}

EnableSamlTokenCaching is what you have to call to enable SAML token caching functionality for a particular ServiceHost. Here is an example of a simple host (created using a custom factory (IIS/WAS hosted scenario)) being configured with this API

public class EchoServiceHostFactory : ServiceHostFactoryBase

{

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)

    {

        var sh = new ServiceHost(typeof(EchoService), baseAddresses);

        sh.EnableSamlTokenCaching();

        return sh;

    }

}

The highlighted call simply injects couple of customized components (interceptors) into WCF’s security framework. Specifically it is injecting a new customized SamlSecurityTokenAuthenticator, which wraps the original authenticator and simply delegates all calls to it. 

SamlSecurityTokenAuthenticator is called during secure conversation handshake. Here our custom authenticator (SamlSecurityTokenAuthenticatorEx) enables us to get hold of the incoming SAML token etc.

public class ServiceCredentialsEx : ServiceCredentials

{

    public ServiceCredentialsEx(ServiceCredentials other)

        : base(other){}

 

    public override SecurityTokenManager CreateSecurityTokenManager()

    {

        return new ServiceCredentialsSecurityTokenManagerEx(this);

    }

    protected override ServiceCredentials CloneCore()

    {

        return new ServiceCredentialsEx(this);

    }

}

 

public class ServiceCredentialsSecurityTokenManagerEx : ServiceCredentialsSecurityTokenManager

{

    public ServiceCredentialsSecurityTokenManagerEx(ServiceCredentials parent)

        : base(parent){}

 

    public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)

    {

        var authenticator = base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);

        if (authenticator is SamlSecurityTokenAuthenticator)

            return new SamlSecurityTokenAuthenticatorEx((SamlSecurityTokenAuthenticator)authenticator);

        return authenticator;

    }

}

Inside SamlSecurityTokenAuthenticatorEx, I’m simply creating an additional token caching policy and   adding it to the collection of policies created by default SAML token authenticator.

public class SamlSecurityTokenAuthenticatorEx : SamlSecurityTokenAuthenticator

{

    SamlSecurityTokenAuthenticator wrapped;

 

    public SamlSecurityTokenAuthenticatorEx(SamlSecurityTokenAuthenticator wrapped)

        : base(new List<SecurityTokenAuthenticator>())

    {

        this.wrapped = wrapped;

    }

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)

    {

        var orignalPolicies = wrapped.ValidateToken(token);

        List<IAuthorizationPolicy> finalPolicies = new List<IAuthorizationPolicy>(orignalPolicies);

 

        finalPolicies.Add(new SamlTokenCachingPolicy((SamlSecurityToken)token));

        return finalPolicies.AsReadOnly();

    }

}

My SamlTokenCachingPolicy is just a wrapped around the cached token.

public class SamlTokenCachingPolicy : IAuthorizationPolicy

{

    public const string PolicyID = “{32835E28-3ED4-42d4-A2EA-FA71E13AF51F}”;

    public SamlTokenCachingPolicy(SamlSecurityToken token)

    {

        this.IncommingSamlToken = token;

    }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)

    {

        return true;

    }

 

    public ClaimSet Issuer

    {

        get { throw new System.NotImplementedException(); }

    }

 

    public string Id

    {

        get { return PolicyID; }

    }

 

    public SamlSecurityToken IncommingSamlToken { get; private set; }

}

IAuthorizationPolicy model nicely aligns with WCF security framework (so I have used it here). If you are not comfortable with this approach, you could get the same results by choosing a different caching strategy.

Inside your service method you can get hold of the incoming SAML token using following piece of code.

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Prefix)]

public class EchoService : IEchoService

{

    public string Echo(string input)

    {

        // Get token from incoming security context..

        var samlToken = OperationContext.Current.ServiceSecurityContext.GetIncommingSamlToken();

    }

}

GetIncommingSamlToken is again an extension which extracts the actual token from my custom AuthorizationPolicy(SamlTokenCachingPolicy).

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken; 

        return null;

    }

Oh, this post is already way too long. Probably next time I will show you, how to pass this token to backend services (sharing the same certificate).

Posted in Federation/STS | 3 Comments »

Duplex communication with .Net 3.5 Workflow Services

Posted by zamd on June 5, 2009

Workflow Services was introduced as part of .Net 3.5 and enables us to use WF workflows as implementation of our WCF services. Workflows are long running by nature so the usual WCF duplex messaging constructs (where correlation between client & server is done based on actual physical connection/socket) are not suitable for most cases. Primarily because it is simply not possible or practical to keep a socket open until workflow finishes its processing (which could potentially take days to produce any meaningful response).

So channel based WCF duplex messaging is not good for long running scenarios. So how can we achieve duplex messaging in long running world?

The general guidance is to use an explicit callback channel (sometime known as “back channel”) for notifications. You can argue that addressability of the client (who may be behind NAT/Firewall) is a real blocker for explicit back channel & I totally agree. However with Relay technologies like .Net Service Bus, reaching to the client endpoints is not an issue anymore hence explicit back channel is much more practical/feasible option now.

Anyways in this post I will show you, how we can achieve durable duplex messaging using Workflow Services. Here is a basic scenario:

Frond end service (orange box) is a long running workflow service – actual service implementation is a WF workflow so it can go idle, persist, sit in the database & brought back into memory if and when needed (all the usual stuff).

Now with above setup if we do usual request-reply messaging – Send activity will simply wait for the reply, workflow will stay in memory & ultimately underlying WCF call will timeout and bad things will happen (workflow might terminate etc).

So first change we have to do is to move the “wait for reply logic” out of the workflow into the host. With this change workflow can go idle, persist and ultimately gets unloaded while host is still listening for the response. On the response, host can bring workflow back into memory and resume the execution.  Important takeaway is that we don’t have to do all this ourselves. Workflow Services & associated “context-exchange protocol” can do most of this for us.

Following is the implementation of front end service…

Dowork is a one-way call. As part of Dowork I’m also passing instanceId of the current workflow along with a callback address (using the standard WS-Addressing ReplyTo header – for protocol purists: this might not be the correct use of ReplyTo header but I’m using it here to keep things simple). So essentially I’m saying “please call me back on this number, when you are done.” J

 Dowork call returns immediately by triggering long running work in the Backend service. Workflow will become idle & can potentially persist.

To override the value of default replyTo header, I have created a simple message inspector.

class ReplyToInspector : IClientMessageInspector

{

    EndpointAddress replyTo;

    public ReplyToInspector(EndpointAddress replyTo)

    {

        this.replyTo = replyTo;

 

    }

    #region IClientMessageInspector Members

 

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)

    {

       

    }

 

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)

    {

        request.Headers.ReplyTo = replyTo;

        return null;

    }

 

    #endregion

}

And this can be configured in config file as:

        <endpointBehaviors>

          <behavior name=reply>

            <replyTo address=http://localhost:43211/Callback//>

          </behavior>

        </endpointBehaviors>

Backend service is standard WCF service – doing callbacks using the usual ChannelFactory stuff. The important bit (highlighted) is the explicit context management.

public void Dowork(Guid instanceId)

{

    Console.WriteLine(“Doing long running work…”);

    for (int i = 0; i < 10; i++)

    {

        Console.Write(“…”);

        Thread.Sleep(1000);

    }

    Console.WriteLine();

  

 

    try

    {

        Console.WriteLine(“Sending response…”);

        SendResults(instanceId, OperationContext.Current.IncomingMessageHeaders.ReplyTo);

    }

    catch (Exception exp)

    {

        Console.WriteLine(“Failed: “ + exp.ToString());

    }

}

 

private void SendResults(Guid instanceId, EndpointAddress remoteAddress)

{

    var binding = new BasicHttpContextBinding();

    var cf = new ChannelFactory<IBackendCallback>(binding, remoteAddress);

    var proxy = cf.CreateChannel();

    var cc = proxy as IContextChannel;

 

    using (new OperationContextScope(cc))

    {

        var cmp = new ContextMessageProperty();

        var cm = cc.GetProperty<IContextManager>();

 

        cm.Enabled = false;

        cmp.Context[“instanceId”] = instanceId.ToString();

 

       

        OperationContext.Current.OutgoingMessageProperties.Add(ContextMessageProperty.Name, cmp); 

 

        proxy.LongRunningOperationCompleted(new OperationOutput { Id = 22, Status = “Done..” });

    }

}

In Dowork, I’m passing instanceId as method’s signature however you can easily pass this data as part of context header (remmeber context header is property bag — IDictionary<string,string>)

In the SendResults – I’m creating approparite context header – so that WorflowServiceHost can connect this callback to the correct instance & correct activity inside that instance. WorkflowServiceHost will also load the workflow if it got persisted.

I’m attaching complete solution with this. In next post, I will talk about durable messaging enhancements added in .Net 4.0 Beta 1.

Stay tuned…

 

Download: DurableMessaging.zip

Posted in WF | 1 Comment »

WCF Behavior Ordering

Posted by zamd on June 2, 2009

WCF behaviours are applied based on a particular evaluation order. Exact details are available here 

The ServiceHost applies behaviors in the following order:

1.     Contract

2.     Operation

3.     Endpoint

  1. Service

Please note within any collection of behaviors, ordering is NOT guaranteed. Because of that sometimes when you configure some aspects of runtime in your custom behavior (for example, TransactionRequired property of the operation) – they get overwritten by default WCF behavior. For example, following will not work and TransactionRequired will always be set to false (default value).

class TestBv : Attribute, IOperationBehavior

{

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation){}

 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)

    {

        dispatchOperation.TransactionRequired = true;

    }

 

    public void Validate(OperationDescription operationDescription){}

}

Why? Because here WCF is executing default OperationBehavior after the execution of your custom behavior. Please note, missing OperationBehaviorAttribute doesn’t mean that behavior won’t be applied at all rather it mean a default OperationBehavior will be applied.

So how to fix this?

Use explicit ordering: Remove the bahavior from the collection and apply it at a particular time yourself. Here is the modified behavior which will work correctly.

class TestBv : Attribute, IOperationBehavior

{

    IOperationBehavior orignal;

 

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation){}

 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)

    {

        orignal.ApplyDispatchBehavior(operationDescription, dispatchOperation);

        dispatchOperation.TransactionRequired = true;

    }

 

    public void Validate(OperationDescription operationDescription)

    {

        this.orignal = operationDescription.Behaviors.Find<OperationBehaviorAttribute>();

        if (orignal != null)

             operationDescription.Behaviors.Remove(orignal);

    }

}

HTH…

Posted in WCF | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.