Supporting optional mobile theming in [BE]

I posted a feature request on the [BE] CodePlex site a few weeks ago because I wanted the ability for mobile users to opt in and out of using the mobile theme. The primary reason for this was that I wanted to be able to access the admin features of my own blog from my phone from time to time. This was not easily possible using the standard mobile theme.

I figured it wouldn’t be too hard to implement so I bashed it out yesterday and pushed up a fork. The change has been accepted by rutr who added it to the [BE] codebase.

I modified the mobile theme to include a “Regular Site” link at the top of the page as highlighted below.

Each theme provided in the [BE] installation has also been updated to display a “Mobile Site” link in the most appropriate place for the theme. I’m using the fantastic Stardust theme which I have also modified in a similar manner as highlighted below.

image

I changed the header links in the stardust site.master page with the following:

<ul >
    <li <%=MenuClass("default.aspx")%>><a href="<%=Utils.AbsoluteWebRoot %>" rel="home"><%=Resources.labels.home %></a></li>
    <li <%=MenuClass("archive.aspx")%>><a href="<%=Utils.AbsoluteWebRoot %>archive.aspx"><%=Resources.labels.archive %></a></li>
    <li <%=MenuClass("contact.aspx")%>><a href="<%=Utils.AbsoluteWebRoot %>contact.aspx"><%=Resources.labels.contact %></a></li>
      <% if (Utils.IsMobile)
 { %>
      <li><blog:MobileThemeSwitch ID="MobileThemeSwitch1" runat="server" /></li>
      <%
 }
      %>
    <li <%=MenuClass("account/login.aspx")%>><a runat="server" id="aLogin" /></li>
</ul>

This change is similar to the changes made to the standard [BE] themes. The theme adds a new custom control to the page if the device is a mobile device. This custom control displays a link button that actions the theme switch.

You can check out this functionality by hitting this site with a mobile device.

WorkflowApplication throws ObjectDisposedException on ActivityContext with LINQ queries

I have written a simple WF4 activity that validates a WIF IClaimsIdentity has certain claims attached to it for a specific claim value. I wrote several unit tests for this activity to validate the implementation. The basic logic is to select a subset of claims that have a specified claim value using Where(). I then fall into a flow chart that validates whether there are any claims (Any() == false) and then whether there is only one claim (First() with a Count()) of a specified claim type that will not be accepted.

image[2]

Almost all of the tests failed with the following error:

Test method Neovolve.Jabiru.Server.Business.UnitTests.Activities.DemandAnyDataChangeClaimTests.DemandAnyDataChangeClaimThrowsExceptionWithManageOnlyClaimTest threw exception System.ObjectDisposedException, but exception System.Security.SecurityException was expected. Exception message: System.ObjectDisposedException: An ActivityContext can only be accessed within the scope of the function it was passed into.
Object name: 'System.Activities.ActivityContext'.

This seemed a little strange as the activity being tested is very simple. The only complexity is that the activity does use a few LINQ queries. Strangely one of the tests did pass and it was one that tested an identity without any claims. I then made a change to one of the other failed tests to remove it’s claim from the test identity and the test then passed. The most likely cause of the exception is the LINQ queries against the claims of the identity.

So this clearly seemed like a bug somewhere in WF. The first net search result turned up with this post on MSDN forums which does indicate a bug with WF and LINQ.

Andrew notes in the forum response that this is a bug that will not get fixed by RTM so it is probably the same issue I am having here. The forum makes some suggestions about how to get around this issue but I was hoping for a simple workaround.

I guessed that perhaps the issue might be due to LINQ queries against a particular IEnumerable<T> type. I then tried putting a ToList() onto the original Where query. Now all the tests pass as expected. This simple workaround is just changing the data type to IList for the other LINQ operations (Any(), First() and Count()) to operate on rather than the IEnumerable returned from Where().

Stardust theme update instructions for BlogEngine.Net 2.0

I had a couple of issues with the excellent Stardust theme I use on this site after upgrading to [BE] 2.0. The following are the changes I made to make the theme compatible.

  1. Post.IsCommentEnabled has been changed to Post.HasCommentEnabled. This appears in themes/stardust/PostView.ascx.
  2. Login.aspx has been moved into the Account folder. Modify the reference to this in themes/stardust/site.master in the code MenuClass("account/login.aspx")
  3. Similarly to #2, modify the reference to login.aspx in themes/stardust/site.master.cs in the two references to aLogin.HRef

All these three references need to be changed from login.aspx to account/login.aspx.

Neovolve.BlogEngine.Web 2.0 released

Along with the updates to my [BE] extensions, Neovolve.BlogEngine.Web 2.0 is now released.

Neovolve.BlogEngine.Web 2.0 contains an HTTP module that supports CS style urls (as of several years ago at least). There is no functional change in this release as it is simply a refresh against the latest [BE] 2.0 binaries.

You can download this release from here.

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.