CodeCampOz: Not a WIF of federation

My presentation of Not a WIF of federation is done and dusted for CodeCampOz 2010. I got a little cut short by the early arrival of pizza’s so there are a couple of things I had to skip.

One of the important points I was not able to make is that the demo code displayed is not production ready. It’s not that the code itself is flawed rather that the certificates used were development certificates. It is important to secure your WIF implementation with production quality certificates in order to ensure that system security is maintained.

Identity delegation was the last demo that I presented. I didn’t get the opportunity to show the code that was changed in the STS application to make this work. The code in the STS project had not be changed up until this point. The GetOutputClaimsIdentity method of the CustomSecurityTokenService class simply returned a new ClaimsIdentity created from the provided principal. The delegated identity scenario needs to explicitly cater for the ActAs scenario by attaching the delegating identity to the Actor property on the delegated identity being returned.

protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
{
    if (null == principal)
    {
        throw new ArgumentNullException("principal");
    }
 
    IClaimsIdentity claimsIdentity = new ClaimsIdentity(principal.Identity);

    // If there is an ActAs token in the RST, return a copy of it as the top-most identity
    // and put the caller's identity into the Actor property of this identity.
    if (request.ActAs != null)
    {
        IClaimsIdentity actAsSubject = request.ActAs.GetSubject()[0];
        IClaimsIdentity actAsIdentity = actAsSubject.Copy();

        // Find the last actor in the actAs identity
        IClaimsIdentity lastActor = actAsIdentity;
        while (lastActor.Actor != null)
        {
            lastActor = lastActor.Actor;
        }

        // Set the caller's identity as the last actor in the delegation chain
        lastActor.Actor = claimsIdentity;

        // Return the actAsIdentity instead of the caller's identity in this case
        claimsIdentity = actAsIdentity;
    }

    return claimsIdentity;
}

The RP that receives the delegated identity can now look at the Actor property on the IClaimsIdentity to see if the RP is being invoked by a delegating identity.

You can download the source code for the demos and the PowerPoint deck from the link below. The solution in the zip requires that you have WIF installed and the default development certificates configured by FedUtil. You can do this by creating a new project and running FedUtil and adding an STS/IP project. The other requirement is that you have a localhost SSL certificate configured in IIS.

CCOZ2010-NotAWIFOfFederation.zip (1.06 mb)

Managing content correlation failures

I posted yesterday about how to get content correlation of multiple WCF service operations to work with hosted WF services. The request and reply data of the WCF service operations is the basis of content correlation. It is therefore quite easy for a client application to send data to the service that does not satisfy business validation.

If the invalid data sent to the service operation participates in content correlation then the WorkflowServiceHost will not identify a WF instance. This unfortunately means that business validation of this data will not run in the workflow as no workflow instance is executing. The result is that the client will receive the following very unfriendly FaultException (assuming no exception shielding is in place).

System.ServiceModel.FaultException: The execution of an InstancePersistenceCommand was interrupted because the instance key 'bea948ac-70fa-b125-91e6-f06752ce7671' was not associated to an instance. This can occur because the instance or key has been cleaned up, or because the key is invalid. The key may be invalid if the message it was generated from was sent at the wrong time or contained incorrect correlation data.
   at System.ServiceModel.Activities.Dispatcher.ControlOperationInvoker.ControlOperationAsyncResult.Process()
   at System.ServiceModel.Activities.Dispatcher.ControlOperationInvoker.ControlOperationAsyncResult..ctor(ControlOperationInvoker invoker, Object[] inputs, IInvokeReceivedNotification notification, TimeSpan timeout, AsyncCallback callback, Object state)
   at System.ServiceModel.Activities.Dispatcher.ControlOperationInvoker.InvokeBegin(Object instance, Object[] inputs, IInvokeReceivedNotification notification, AsyncCallback callback, Object state)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

If exception shielding is in place by using an IErrorHandler implementation then the client should get a generic service fault message instead. Neither scenario is appropriate as the actual failure is due to the service receiving invalid business data. The service should be able to provide a meaningful business fault to the client in this case to indicate what the client application has done wrong.

This is the outstanding issue from yesterday’s post. How can the service identify that the FaultException thrown relates to a correlation failure and determine which content correlation was the cause?

My scenario from yesterday’s post was using a TransactionId as the value that participates in content correlation between the RegisterAccount and ConfirmRegistration service operations. The client should expect a known business fault that identifies that the TransactionId is invalid rather than the above generic FaultException or shielded exception.

The solution to this issue is to extend the behaviour of an IErrorHandler implementation. The change in question will be to determine that the thrown exception is a content correlation exception. It will then convert the exception to a specific business fault based on the current service operation.

The ProvideFault method in the IErrorHandler implementation in the service has been updated to manage this detection and conversion process.

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
    Exception errorToProcess = AttemptBusinessFailureExceptionConversion(error);

    BusinessFailureException<BusinessFaultCode> businessFailure = errorToProcess as BusinessFailureException<BusinessFaultCode>;

    if (businessFailure != null)
    {
        IEnumerable<BusinessFaultItem> faultSet = CreateFaultSet(businessFailure.Failures);


        BusinessFault businessFault = new BusinessFault(businessFailure.Message, faultSet);
        FaultException<BusinessFault> faultException = new FaultException<BusinessFault>(businessFault, businessFailure.Message);
        MessageFault messageFault = faultException.CreateMessageFault();

        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
    else
    {
        SystemFault systemFault;
        {
            SystemFailureException systemFailure = errorToProcess as SystemFailureException;
            SecurityException securityFailure = errorToProcess as SecurityException;

            if (systemFailure != null)
            {
                systemFault = new SystemFault(systemFailure.Message);
            }
            else if (securityFailure != null)
            {
                systemFault = new SystemFault(Resources.FaultHandler_AccessDenied);
            }
            else
            {
                systemFault = new SystemFault(Resources.FaultHandler_UnknownFailure);
            }
        }

        FaultException<SystemFault> faultException = new FaultException<SystemFault>(systemFault, systemFault.Message);

        MessageFault messageFault = faultException.CreateMessageFault();

        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
}

The existing code already managed a BusinessFailureException<T> and converted it to a BusinessFault that is a known fault contract for the service. The method now makes a call out to AttemptBusinessFailureExceptionConversion as an initial step.

private static Exception AttemptBusinessFailureExceptionConversion(Exception errorToProcess)
{
    FaultException thrownFaultException = errorToProcess as FaultException;

    if (thrownFaultException == null)
    {
        return errorToProcess;
    }

    FaultCode faultCode = thrownFaultException.Code;

    if (faultCode == null
        || faultCode.IsPredefinedFault == false
        || faultCode.IsSenderFault == false)
    {
        return errorToProcess;
    }

    FaultCode subCode = faultCode.SubCode;

    if (subCode == null
        || subCode.Namespace != "http://schemas.datacontract.org/2008/10/WorkflowServices"
        || subCode.Name != "InstanceNotFound")
    {
        return errorToProcess;
    }

    String contractName = OperationContext.Current.IncomingMessageHeaders.Action;

    if (contractName == "http://tempuri.org/IRegistration/ConfirmRegistration")
    {
        return new BusinessFailureException<BusinessFaultCode>(BusinessFailureStore.InvalidTransactionIdProvided);
    }
    
    return errorToProcess;
}

The AttemptBusinessFailureExceptionConversion method looks for a predefined FaultException thrown by the service that has a particular failure code. Correlation failures have a Code.SubCode name of InstanceNotFound with the namespace of http://schemas.datacontract.org/2008/10/WorkflowServices. The method can return a specific business failure now that a correlation failure has been identified.

The business failure to return will be determined based on the current service operation. In this scenario there is a check that the failed service operation is the IRegistration.ConfirmRegistration method. A TransactionId value is the content correlation value for this service operation. If the WorkflowServiceHost is not able to identify a workflow instance using the provided TransactionId then the TransactionId value is invalid. This is the business failure that is then returned. The ProvideFault method then continues its existing logic by processing the business failure as an appropriate business fault.

Hosted workflow service with content correlation

Content correlation in hosted WF services seems daunting but is surprisingly simple to use with the available designer support. Content correlation uses data from the workflow to link (correlate) multiple WCF service operations to the same hosted workflow instance. Content correlation is different to context correlation where WCF communication down at the binding level handles correlation. There is unfortunately a lack of detailed examples available on how to get content correlation to work. This post aims to fill that gap.

I have an account registration scenario where the first service operation is to request a new account. The request needs to provide a first name, last name, email address and a password. The register account workflow will run some validation logic and then send an email to the email address with a link back to a website for confirmation.

The confirmation of a registration requires a TransactionId and ChallengeCode. The confirmation link in the email contains the TransactionId and registration request operation returns the ChallengeCode to the user. The service never returns the TransactionId and the email never contains the ChallengeCode.

The user must provide both the TransactionId and the ChallengeCode to the confirmation screen of the website to confirm the registration. This design provides registration security because it ensures that the registration belongs to a valid email address and that the person confirming the registration is the person who made the registration request because only they have both the TransactionId and the ChallengeCode combination. This is similar to how public/private keys work in cryptography.

The registration service workflow looks like the following.

image

The registration service receives the message for RegisterAccount and runs the register account logic. This process generates the TransactionId and a ChallengeCode and sends the confirmation email. The workflow then waits for the confirmation request to come back or for a timeout value to occur. Some kind of correlation is required to link the RegisterAccount service operation to the ConfirmRegistration service operation to ensure that the same workflow instance executes the related service operations.

This registration process cannot use context correlation because there is no WCF binding available that would support this scenario. Different applications may execute the registration and confirmation operations with no knowledge between them. A rich client may make the original registration request while the website processes the confirmation. Context correlation still would not work even if the original registration request and subsequent confirmation were processed by the same website as there is no WCF session between the two website requests. This workflow must use content correlation to link the original registration request with the subsequent confirmation request.

The first step of correlation is to define a CorrelationHandle variable on the workflow at a scope level that is available to both service operations. This variable is the reference that links the service operations using the nominated correlation data.

image

There are two ways that content correlation may satisfy this registration workflow. The first method uses content references that are only available in the service contract. The second method provides a content correlation solution that only partially relies on the service contract data.

Method 1 – Content correlation based on service contract data

This first method uses the correlation support in solely available in the SendReceive activity pairs. The value to correlate on in this case is the ChallengeCode value as it exists as part of the service contract for the RegisterAccount call (the response) and the ConfirmRegistration call (a request property).

The CorrelationInitializers property for the RegisterAccount SendReceive activities must define the content data to correlate the two service operations. The ChallengeCode is generated as part of the RegisterAccount workflow so this value cannot be assigned in CorrelationInitializers of the Receive RegisterAccount Request activity as the value has not yet been determined. It can however be assigned in the related Send RegisterAccount Reply activity.

image

image

The CorrelationHandle variable must be entered on the left. When selected, there are several options available for correlation support. The option to select is Query correlation initializer. The next step is to define an XPath query to the content value in the service contract. The dialog helps out here by providing a dropdown (indicated in the screenshot). The dropdown options provide an easy way to enter the XPath Queries value based on the service contract related to the activity. In this case the RegisterAccountResult (the ChallengeCode) is selected and the XPath Query of sm:body()/xgSc:RegisterAccountResponse/xgSc:RegisterAccountResult is entered automatically.

The next step is to configure the Receive activity of the ConfirmRegistration service operation.

image

The CorrelationHandle variable is defined on the activity as indicated in the above screenshot. The CorrelatesOn property now needs to be set in order to link the ChallengeCode in the ConfirmRegistration service operation with the ChallengeCode generated by RegisterAccount. This will allow the WorkflowServiceHost to identify the correct workflow instance to load to process ConfirmRegistration.

image

The CorrelatesOn dialog provides similar assistance in defining the XPath query to match the content correlation data. In this case the service contract is seen in the drop-down list and the ConfirmRegistrationRequest.ChallengeCode value can be chosen. This results in the XPath Query of sm:body()/xgSc:ConfirmRegistration/xgSc:request/xg0:ChallengeCode. It is important to note that the CorrelationInitializers and CorrelatesOn property values between these activities define the same correlation key value.

Content correlation will now be able to load the correct workflow instance if the ConfirmRegistration service operation request contains the ChallengeCode value returned from a prior call to RegisterAccount (unless the timeout activity has fired first).

Method 2 - Content correlation based on partial service contract data

Defining content correlation on the SendReceive activities in this way restricts you to data that is common between the service contracts for both service operations. This has forced the implementation to use ChallengeCode instead of TransactionId. This is not technically a problem as both values will be unique. The TransactionId value is closer to the concepts of WF instance management and persistence than the ChallengeCode and is a little more appropriate from a design point of view. The ChallengeCode is more of a business-orientated value that has nothing to do with workflows or correlation. Some minor changes to the workflow will be able to use TransactionId rather than ChallengeCode.

The first change is to remove the CorrelationInitializers configuration in Send RegisterAccount Reply activity. Next, we need to drop on an InitializeCorrelation activity before Send RegisterAccount Reply activity. The Correlation property is assigned the CorrelationHandle variable that has been used so far.

image

The CorrelationData property is not restricted to values defined in the service contract. This allows us to define content correlation to use another variable defined in the workflow. We can now select View… and enter the TransactionId value as this is stored in a workflow variable.

image

The first thing to note here is that the same correlation key name is used. The second important thing to note is that content correlation must use a string value. The TransactionId value is a Guid so a ToString will cater for this.

The final change is to update the CorrelatesOn definition on the Receive ConfirmRegistration Request activity. This property will now reference the TransactionId of the ConfirmRegistrationRequest object rather than ChallengeCode.

image

The content correlation will still work here even though the correlation data is a string representation of the TransactionId. Presumably this is because the XPath query is run against the WCF message (as an XML structure) which will result in a string value for TransactionId. Correlation will work in the same way as when ChallengeCode was used as long as the same TransactionId value is provided to the ConfirmRegistration service operation that was generated by RegisterAccount.

In this post we have looked at two ways of getting content correlation support to link two WCF service operations in a hosted workflow service. The remaining obstacle is how to return a meaningful service fault back if the user provides an invalid TransactionId to the ConfirmRegistration operation. I hope that a viable solution will pop up in the next post.

Mixed WCF endpoint bindings for hosted workflow services

I am working on a project where I have multiple hosted WF services. All the services were secured using federated security with WIF and a custom STS. This has all worked fine until I added a new service workflow that needed to be accessible by anonymous users. This is where things get a little tricky.

The WCF configuration for the hosted services looked a little like this before the anonymous service was added.

    <system.serviceModel>
        <protocolMapping>
            <add scheme="http"
                 binding="ws2007FederationHttpBinding" />
        </protocolMapping> 
        <bindings>
            <ws2007FederationHttpBinding>
                <binding>
                    <security mode="TransportWithMessageCredential">
                        <message establishSecurityContext="false">
                            <issuerMetadata address="https://localhost/JabiruSts/Service.svc/mex" />
                            <claimTypeRequirements>
                                <!--Following are the claims offered by STS 'https://localhost/JabiruSts/Service.svc'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
                                <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
                                     isOptional="true" />
                                <add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
                                     isOptional="true" />
                            </claimTypeRequirements>
                        </message>
                    </security>
                </binding>
            </ws2007FederationHttpBinding>
        </bindings>
        <behaviors>
            <serviceBehaviors>
                <behavior>
                    <federatedServiceHostConfiguration />
                    <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
                    <serviceMetadata httpGetEnabled="true" />
                    <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceCredentials>
                        <serviceCertificate findValue="DefaultApplicationCertificate"
                                            storeLocation="LocalMachine"
                                            storeName="My"
                                            x509FindType="FindBySubjectName" />
                    </serviceCredentials>
                    <errorHandler type="Neovolve.Jabiru.Server.Services.FaultHandler, Neovolve.Jabiru.Server.Services" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="errorHandler"
                     type="Neovolve.Toolkit.Communication.ErrorHandlerElement, Neovolve.Toolkit" />
                <add name="federatedServiceHostConfiguration"
                     type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            </behaviorExtensions>
        </extensions>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    </system.serviceModel>

The key thing to notice is that WCF4 uses default configuration values. These services use ws2007FederationHttpBinding in order to communicate with the STS but this is not the default binding for the HTTP stack. The configuration makes this work by defining the federation binding for HTTP using the protocolMapping/add element. The bindings/ws2007FederationBinding configuration does not specify a name as it represents the default configuration for that type of binding.

I attempted to modify this configuration with an explicit service configuration for my anonymous service that uses an IRegistration contract. There were a few roadblocks along the way, but here are the tips to get you across the line.

The first step is to define a configuration name against the xamlx file. This is accessed by viewing the properties of the workflow (the workflow, not any child activity).

image

This configuration name needs to match to a WCF service configuration name. This configuration can then define a different binding than the default for the HTTP stack. It can then also define the behaviour to use for that endpoint binding.

The relevant change to the above configuration is to add the following.

<services>
    <service name="Neovolve.Jabiru.Server.Services.Registration">
        <endpoint binding="basicHttpBinding"
            bindingConfiguration="SecureBasicHttpBindingConfig"
            contract="IRegistration" />
    </service>
</services>
<bindings>
    <basicHttpBinding>
        <binding name="SecureBasicHttpBindingConfig">
            <security mode="Transport"></security>
        </binding>
    </basicHttpBinding>
</bindings>

The second important step is to define the “correct” contract name. Ordinarily the contract name in WCF would be the [Namespace].[TypeName] value of the service contract. For some reason this only worked when I specified just the type name.

My hosted workflow services can now respond to a mix of endpoint bindings for the service.

Updated CodeCampOz presentation abstract

The preparations for my CodeCampOz presentation have come a long way as has my understanding of the subject matter. I have tweaked my presentation abstract as a result. The new abstract is the following:

Not a WIF of federation

The Windows Identify Foundation (WIF) provides the latest Microsoft implementation for working in the claims-based identity space. WIF has particular strengths in providing federated security for systems that target users across multiple security domains, multiple credential types and multiple credential stores.

Developers of small systems may find it difficult to understand how WIF fits into their system design. Small systems tend to have their own security store, do not cross security domains and may not even run within an Active Directory managed domain. How do developers leverage claims-based security when they do not require federated security?

This session will provide a brief introduction to claims-based security and then look at how to implement WIF in ASP.Net and WCF applications without federation dependencies. It will then extend this to include a federation capable architecture.

Unit testing a workflow that relies on Thread.CurrentPrincipal.Identity.Name

This is a bit of a curly one. I have a workflow that is mostly abstracted from security concerns in that authentication and authorization logic has already been processed. The workflow does however need to get the name of the current user via Thread.CurrentPrincipal.Identity.Name. Unfortunately this returns an empty string when executing the workflow directly in a unit test.

The reason for the workflow not having access to the current principal is that workflows are executed on a new thread. The principal associated with the workflow thread is determined according to the PrincipalPolicy assigned to the AppDomain. By default the AppDomain will return an unauthenticated GenericPrincipal. See my Thread identity propagation post from a few years ago for the background information.

Fortunately there are two easy workarounds for this problem. Both workarounds make changes to the configuration of the AppDomain and how it manages the current principal. This will work because the workflow is executed within the same AppDomain as the unit test, just on a different thread.

These workarounds leverage the logic that Reflector shows in AppDomain.GetThreadPrincipal. This method gets called by Thread.CurrentPrincipal when the current principal is null. Fortunately this isn't a problem for executing a workflow as the workflow engine does not assign a principal for the new workflow thread.

internal IPrincipal GetThreadPrincipal()
{
    IPrincipal principal = null;
    IPrincipal principal2;
    lock (this)
    {
        if (this._DefaultPrincipal == null)
        {
            switch (this._PrincipalPolicy)
            {
                case PrincipalPolicy.UnauthenticatedPrincipal:
                    principal = new GenericPrincipal(new GenericIdentity("", ""), new string[] { "" });
                    goto Label_0073;

                case PrincipalPolicy.NoPrincipal:
                    principal = null;
                    goto Label_0073;

                case PrincipalPolicy.WindowsPrincipal:
                    principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
                    goto Label_0073;
            }
            principal = null;
        }
        else
        {
            principal = this._DefaultPrincipal;
        }
    Label_0073:
        principal2 = principal;
    }
    return principal2;
}

1. Change the AppDomain PrincipalPolicy

// Configure the app domain to put the current windows credential into the thread when Thread.CurrentPrincipal is invoked
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

Changing the PrincipalPolicy on the AppDomain will result in the current WindowsIdentity being returned if the thread did not already have a principal assigned.

There are a few caveats for this workaround to be aware of:

  • Thread.CurrentPrincipal will end up with the current users WindowsPrincipal.
  • No flexibility for other principal types
  • No flexibility for custom principal values

2. Assign a default principal against the AppDomain

Changing the AppDomain PrincipalPolicy will work however the outcome locks you into testing against the current WindowsIdentity which is not ideal. The AppDomain class also has the ability to define a default principal. This overrides the PrincipalPolicy as seen in the above GetThreadPrincipal method. Assigning the principal in this way provides a lot more control over the principal that is used in the workflow thread.

AppDomain.CurrentDomain.SetThreadPrincipal(newPrincipal);

There is a caveat for this workaround as well. The default principal for the app domain can only be set once. You will need to have all your tests running against the same principal to avoid any issues.

Clean up and test method usage

It is important to clean up any changes made when the test either completes or fails. This is where a handle context/scope style class comes into play.

namespace Neovolve.Jabiru.Server.TestSupport
{
    using System;
    using System.Security.Policy;
    using System.Security.Principal;
    using System.Threading;

    public class TestUserContext : IDisposable
    {
        private readonly IPrincipal _originalPrincipal;

        public TestUserContext(IPrincipal newPrincipal)
        {
            _originalPrincipal = Thread.CurrentPrincipal;

            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal);

            Thread.CurrentPrincipal = null;

            IPrincipal defaultAppDomainPrincipal = Thread.CurrentPrincipal;

            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal);

            if (defaultAppDomainPrincipal != null)
            {
                // Check if the app domain default principal has been set to another principal
                const String AppDomainHasAlreadyBeenAssignedADifferentPrincipal = "App domain has already been assigned a different principal";

                if (newPrincipal.GetType().Equals(defaultAppDomainPrincipal.GetType()) == false)
                {
                    throw new PolicyException(AppDomainHasAlreadyBeenAssignedADifferentPrincipal);
                }

                if (newPrincipal.Identity.GetType().Equals(defaultAppDomainPrincipal.Identity.GetType()) == false)
                {
                    throw new PolicyException(AppDomainHasAlreadyBeenAssignedADifferentPrincipal);
                }

                if (newPrincipal.Identity.Name != defaultAppDomainPrincipal.Identity.Name)
                {
                    throw new PolicyException(AppDomainHasAlreadyBeenAssignedADifferentPrincipal);
                }
            }
            else
            {
                AppDomain.CurrentDomain.SetThreadPrincipal(newPrincipal);
            }

            Thread.CurrentPrincipal = newPrincipal;
        }

        public void Dispose()
        {
            Thread.CurrentPrincipal = _originalPrincipal;
        }
    }
}

This class will configure the AppDomain to use a provided principal. It will also attempt to detect if the app domain is already configured with a different default principal. The Dispose method will then restore the original principal back onto the thread.

using (TestUsers.DefaultUser.CreateContext())
{
    IDictionary<String, Object> outputParameters = ActivityInvoker.Invoke(target, inputParameters);

    itemSetId = (Guid)outputParameters["ItemSetId"];
}

The unit test method can then use this context in a using statement with the help of some nice syntactic sugar provided by extension methods. The using statement here will ensure that the current thread is restored by TestUserContext.Dispose regardless of whether an exception is thrown.

Updated: Found issue with AppDomain default principal only being allowed to be set once. Updated TestUserContext to attempt to cater for this.

Extension methods that add fluent elegance

Last night I was updating some integration tests for one of my projects. I have many test methods that configure a directory path from several sources of information and found that path concatenation often results in ugly unreadable code.

Consider the scenario where you have a base path to which you want to add several other directory names.

namespace ConsoleApplication1
{
    using System;
    using System.IO;

    class Program
    {
        static void Main(String[] args)
        {
            String basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            const String ProductName = "SomeProductName";
            String instanceName = Guid.NewGuid().ToString();
            const String dataName = "DataStore";

            String hardToReadEvaluation = Path.Combine(Path.Combine(Path.Combine(basePath, ProductName), instanceName), dataName);

            Console.WriteLine(hardToReadEvaluation);
            Console.ReadKey();
        }
    }
}

The statement with multiple Path.Combine evaluations is very difficult to read. A simple extension method can turn this into a fluent API design to achieve the same result and allow the intention of the code to be crystal clear.

namespace ConsoleApplication1
{
    using System;
    using System.IO;

    public static class Extensions
    {
        public static String AppendPath(this String basePath, String appendValue)
        {
            return Path.Combine(basePath, appendValue);
        }
    }

    class Program
    {
        static void Main(String[] args)
        {
            String basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            const String ProductName = "SomeProductName";
            String instanceName = Guid.NewGuid().ToString();
            const String dataName = "DataStore";

            String fluentEvaluation = basePath.AppendPath(ProductName).AppendPath(instanceName).AppendPath(dataName);

            Console.WriteLine(fluentEvaluation);
            Console.ReadKey();
        }
    }
}

The code is now much more readable. You no longer need to backtrack along the line of code to figure out which variables related to which operation in the evaluation as the code now reads fluently from left to right.

Configuration support for custom IErrorHandler in WCF

My post about implementing IErrorHandler for WCF a few years ago is my second top post on this site. My Toolkit project on Codeplex has had support for hooking up an IErrorHandler using an attribute on the service implementation class which is an extension of the original post.

My preference has always been to hook up IErrorHandler using an attribute to avoid any potential security holes. This would be a scenario where the configuration for IErrorHandler is removed and exception shielding is no longer available to prevent potentially sensitive information from being displayed to clients. I am now playing with workflow services and am not able to use an attribute for this purpose. I no longer have a choice and must use a configuration based IErrorHandler implementation.

I have added configuration support for IErrorHandler to my Toolkit project based on the original posts above to assist with this process.

namespace Neovolve.Toolkit.Communication
{
    using System;
    using System.Configuration;
    using System.ServiceModel.Configuration;

    public class ErrorHandlerElement : BehaviorExtensionElement
    {
        public const String ErrorHandlerTypeAttributeName = "type";

        private ConfigurationPropertyCollection _properties;

        protected override Object CreateBehavior()
        {
            return new ErrorHandlerAttribute(ErrorHandlerType);
        }

        public override Type BehaviorType
        {
            get
            {
                return typeof(ErrorHandlerAttribute);
            }
        }

        [ConfigurationProperty(ErrorHandlerTypeAttributeName)]
        public String ErrorHandlerType
        {
            get
            {
                return (String)base[ErrorHandlerTypeAttributeName];
            }

            set
            {
                base[ErrorHandlerTypeAttributeName] = value;
            }
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                if (_properties == null)
                {
                    ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection();

                    properties.Add(
                        new ConfigurationProperty(
                            ErrorHandlerTypeAttributeName, typeof(String), String.Empty, null, null, ConfigurationPropertyOptions.IsRequired));

                    _properties = properties;
                }

                return _properties;
            }
        }
    }
}

The ErrorHandlerElement class allows for WCF configuration to configure an IErrorHandler for a service. This class provides the configuration support to define the type of error handler to use. The CreateBehavior method simply forwards the configured IErrorHandler type to the ErrorHandlerAttribute class that is already in the toolkit.

<?xml version="1.0" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="ErrorHandlerBehavior">
                    <errorHandler type="Neovolve.Toolkit.IntegrationTests.Communication.KnownErrorHandler, Neovolve.Toolkit.IntegrationTests"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="errorHandler"
                     type="Neovolve.Toolkit.Communication.ErrorHandlerElement, Neovolve.Toolkit"/>
            </behaviorExtensions>
        </extensions>
        <services>
            <service behaviorConfiguration="ErrorHandlerBehavior"
                     name="Neovolve.Toolkit.IntegrationTests.Communication.TestService">
                <endpoint address=""
                          binding="basicHttpBinding"
                          bindingConfiguration=""
                          contract="Neovolve.Toolkit.IntegrationTests.Communication.ITestService"/>
            </service>
        </services>
    </system.serviceModel>
</configuration> 

The WCF configuration defines the ErrorHandlerElement class as a behavior extension. This service behavior can then use this extension and identify the type of IErrorHandler to hook into the service.

This class has been added into the next beta of my Toolkit. You can get it from here.