Koen about .Net

May 5, 2011

Converting User Controls into Custom Controls

Filed under: Development — Tags: , — koenwillemse @ 16:30

In the project I’ve been working on, we had to deal with a design agency, which creates a lot of html markup, which must stay exactly the way it was created by them be. Next to that, the product should eventually work in SharePoint, so we have to use WebForms :-(. I’ve been developing a framework which will be used which looks very similar to the FrontController pattern, however there are a few differences, for instance, the Commands (or ViewActions as we call them) will not give a ActionResult, but our controller will do this, since there is a lot of knowledge there around navigation between forms etcetera. But that’s not the subject of this post.

One of the things we’ve also been working on is to make the transition of the designers stuff to the asp.net stuff as small as possible, so that changes made by them can easily be merged in our controls. So the designers have been working on control-like pieces of html which will be reused to compose pages. Nothing strange here from a developers point of view, so we made UserControls out of them which will be the equivalent of their control-like html markup. These UserControls had to be used in several portal web project. So there were a few options on the reuse of the controls:

  1. Create CustomControls for each control-like element of the designersThis was not an option because the html delivered by the designers was sometimes very much in one control (over 300 lines) which would make it a nightmare (and laborious task) to keep CustomControl in sync with their markup files.
  2. Use the ‘Add as Link’ option to add the user controls to the portal projectsThis option was killed by the specific software factory what we had to use, since that didn’t support this construction which made all several nightly builds fail.
  3. Copy each UserControl to each portal projectYeah right… not an option at all of course.
  4. Convert UserControls into CustomControls.OK, that option was the only option left worth investigating.

So I had some work to do. This post by David Ebbo was a very good starting point on how to do it. So I started out with a small demo project where I did all the steps outlined by David and I got that part working. However, there were some things that were not the way we wanted it.

  1. The fixednames was not handy for us since we want to have one resulting dll, otherwise there were way to many dlls to reference in the portal projects.
  2. The name of the assembly of the controls was something like App_Web_BLABLA_ascx.dll which just looks ugly and might change over time, which made it useless for us in a automated scenario.
  3. The functionality should be integrated in the build of the project, to be able to make it work how the other projects work using the software factory

So I started out to fix these points. The first two points I wanted to fix was to merge the assemblies that were created and name them the way I wanted it. So I started with using ILMerge to do this. In my demo I had created two very small user controls to do this and after the ILMerge, it seemed to work. I then added a larger user control and tried that also, but that one just did not work correctly. Just a small part of the markup was visible and that was it. I did a lot of googling on it but eventually found out that when you go over a specific number of characters in your user control (I can’t remember the actual amount anymore :-(), resources are used in the generated code. And then ILMerge breaks it. After being stuck there for a while, one of my colleagues mentioned that I could try aspnet_merge instead. I didn’t know that it existed, but I tried it out and it worked like a charm.

So I only had to fix the 3rd point mentioned above. The software factory has a mechanism built in to copy assemblies created by the project to a specific location every time you build the project. So that lead me to the idea to add the aspnet_compile and aspnet_merge as a post build command, since the magic of the software factory was done using a AfterBuild MSBuild task, so I could not use that. Eventually my post-build command looked like this:

rmdir /S /Q "%TEMP%\Publish_$(TargetName)"
del  "%CD%\$(TargetName).dll"
del  "%CD%\$(TargetName).pdb"

"C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe" -v "$(ProjectName)" -p "%CD%\.." -f "%TEMP%\Publish_$(TargetName)"

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\aspnet_merge.exe" "%TEMP%\Publish_$(TargetName)" -keyfile "%CD%\..\..\..\..\src\_Key\Product.snk" -a -copyattrs -o $(TargetName) -debug

copy "%TEMP%\Publish_$(TargetName)\bin\$(TargetName).dll" "%CD%\$(TargetName).dll" /Y
copy "%TEMP%\Publish_$(TargetName)\bin\$(TargetName).pdb" "%CD%\$(TargetName).pdb" /Y

 

First I remove the directory where I’m going to publish the web app (using the aspnet_compile).
Next step is also related to the software factory workings. The resulting assembly of the project has to be exactly the same name, otherwise it won’t be copied. So I delete the current dll created by the project, otherwise my aspnet_merge step will create an assembly which is not exactly the name I wanted. However, the result is that there can’t be worked with code behind files, but that was not a problem for this.
Then I use aspnet_compile to precompile the site and write the resulting stuff to a temporary folder.
Then the aspnet_merge is  executed to merge the created App_Web assemblies to one assembly with the same name as the assembly that would have been created by my original web application project. Note that I’m also resigning the assembly to make sure it is strong named.
The last step is to copy the merged dll and pdb files back to the original location.

With this small piece of post-build commands I got the solution eventually working and it is being used now in multiple solutions without problems. I hope some of you guys can also benefit from this. Please let me know if it helps.

Advertisements

August 2, 2010

Making a web application use an active STS

Filed under: Development, dotnetmag — Tags: , , , — koenwillemse @ 19:46

At my current assignment we’re working on a solution which among others consists of a custom Security Token Service which is used for the authentication of users in a web portal. In our case we’ve created an active STS by using Windows Identity Foundation and WCF. We’re now working a demo web application which uses the STS (and some other functionality we’re building) to show the team which creates the portal how they should consume the STS.

Since there is almost NO decent documentation about WIF, I started googling. The problem which I ran into was that the information I found was almost all about scenario’s with a passive STS. I found a bit information about consuming an active STS but all not complete, so I’ll put all the steps I had to take right here, so it can help some others.

First of all, I eventually found this blog post, which made it almost work for me. There was just some information missing which caused me to search for another 1 – 2 hours.

These are the steps to take when you want to consume an active STS from a web application:

  1. Add a reference to the Microsoft.IdentityModel assembly (WIF).
  2. Add the definition of the microsoft.IdentityModel config section to your config like this (check the correct version of the dll of course):
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    
  3. Add the following two HttpModules to your config (when using IIS7, add them to you system.webserver section, otherwise to you system.web section):
    <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  4. The authentication mode should be set to None:
    <authentication mode="None" />
  5. Add the configuration for the microsoft.IdentityModel section, for instance:
    <microsoft.identityModel>
    <service>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
    <add thumbprint="{Add the thumbprint of the certificate used by your STS, for instance: 80481e4041bd6758400c62e2c811831b98eed561}" name="{Add the name of the certificate, for instance: CN=devsts}" />
    </trustedIssuers>
    </issuerNameRegistry>
    <audienceUris>
    <add value="{Add the applies to url of your web application}"/>
    </audienceUris>
    <federatedAuthentication>
    <wsFederation passiveRedirectEnabled="false" issuer="{The address of the STS, for instance: https://devsts/mySts.svc}" realm="{The applies to address of your web application, for instance: http://myrelyingparty.nl}" persistentCookiesOnPassiveRedirects="true" />
    <cookieHandler requireSsl="false" />
    </federatedAuthentication>
    <serviceCertificate>
    <certificateReference x509FindType="FindByThumbprint" findValue="{The certificate used by your STS, for instance: 80481e4041bd6758400c62e2c811831b98eed561}" storeLocation="LocalMachine" storeName="My"/>
    </serviceCertificate>
    </service>
    </microsoft.identityModel> 

    As you can see, you register your information about the certificate being used by your sts and the information about your application, the relying party.
    The line about the cookieHandler was the one that caused me some problems because I didn’t have that. The problem is that my local site was working on http and not https, but the created cookies required https. I didn’t have this line at first, which had the effect that the session cookie was not maintained over postbacks.

  6. After you’ve configured everything, you can use the following code to consume your STS and get an IClaimsIdentity:
    // authenticate with WS-Trust endpoint
    var factory = new WSTrustChannelFactory(
    new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
    new EndpointAddress("<a href="https://devsts/MySts.svc">https://devsts/MySts.svc</a>"));
    factory.Credentials.UserName.UserName = usernameField.Text;
    factory.Credentials.UserName.Password = passwordField.Text;
    var channel = factory.CreateChannel();
    var rst = new RequestSecurityToken
    {
    RequestType = RequestTypes.Issue,
    AppliesTo = new EndpointAddress("http://myrelyingparty.nl/"),
    KeyType = KeyTypes.Bearer
    };
    var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
    // Now you parse and validate the token which results in a claimsidentity
    var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
    var token = handlers.ReadToken(new XmlTextReader(new StringReader(genericToken.TokenXml.OuterXml)));
    var identity = handlers.ValidateToken(token).First();
    
    // Create the session token using WIF and write the session token to a cookie
    var sessionToken = new SessionSecurityToken(ClaimsPrincipal.CreateFromIdentity(identity));
    FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
    
    //Perform some redirect
    Response.Redirect("~/secure/default.aspx");
    

In our situation, this was not complete, since we need the SAML token received from the STS furtheron to authenticate to WCF services which we consume. After using reflector and trying something out, it can be easily done by changing just two lines of code. I can be done by the following:

var identity = handlers.ValidateToken(token).First();
Thread.CurrentPrincipal = new ClaimsPrincipal(new IClaimsIdentity [] { new ClaimsIdentity(identity.Claims, token) });

The code itself looks probably a bit strange, since we get a ClaimsIdentity from the ValidateToken and then we create another ClaimsIdentity. I hoped that WIF would have used this way to construct the identity, of at least provide an overload or something to do this. I created the identity like this, since the securitytoken is now availalbe in the BootstrapToken property of the ClaimsIdentity. At first we were thinking that we had to keep the security token in session but that is not necessary when you do it like this. Now we can access it simply with the following lines of code:

var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
var theOriginalSecurityToken = identity.BootstrapToken;

I hope this helps somebody else. It would have saved me a lot of time if I could have found this information somewhere.

Update (17-8-2010)

We’ve been using the code as listed above, but we ran into some problems, because the retrieved token is a GenericXmlSecurityToken which caused problems when supplying it to our backend services.

After some searching I found that it is possible to get the BootstrapToken property filled by WIF, but you need to set a configuration switch (some decent documentation would really be helpfull). All you have to do is change the following in your web application configuration (add the saveBootstrapTokens attribute):

<microsoft.identityModel>
<service saveBootstrapTokens="true>
<issuerNameRegistry ……

The code to creating your principal is then the following:

var identity = handlers.ValidateToken(token).First();
Thread.CurrentPrincipal = new ClaimsPrincipal(new [] { identity });

Now the BootstrapperToken is a SamlSecurityToken which is exactly what we want to be able to authenticate to the backend services. I’ll show in a new post how we have all this tied together.

Blog at WordPress.com.

%d bloggers like this: