Notes from Daily Encounters with Technology RSS 2.0
 
# Sunday, January 08, 2012

Web.config transformations are a great but often overlooked feature introduced with ASP.NET 4.0. They provide a simple way to define a different configuration for Debug and Release builds of your project by only specifying the differences (typically only connection strings and similar settings) in a separate transformation file while keeping the core of the configuration file common and consequentially making it easier to manage.

Unfortunately files aren’t transformed until the web deployment package is created, i.e. they aren’t part of the core build process. This makes the transformations more difficult to use in some scenarios such as having a different configuration file for automated testing as a part of the build process.

Since the transformation is done by the TransformXml MSBuild task in Microsoft.Web.Publishing.Tasks.dll assembly there is a way to make this work by modifying the project file manually, though. Here is a snippet that I added to my project file (line break in the AssemblyFile added for readability only):

<UsingTask TaskName="TransformXml" 
           AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\
                         v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild">
    <MakeDir Directories="obj\$(Configuration)" 
             Condition="!Exists('obj\$(Configuration)')" />
    <TransformXml Source="Web.Config" 
                  Transform="Web.$(Configuration).config" 
                  Destination="obj\$(Configuration)\Web.config" 
                  StackTrace="true" 
                  Condition="Exists('Web.$(Configuration).config')" />
</Target>

As you can see the transformed configuration file is put inside the obj folder from where I copy it to the desired final location before running the tests. I find this a suitable spot for it unless you are precompiling views in your ASP.NET MVC project. A web.config file anywhere in obj folder brakes the build so you’ll have to put the file elsewhere or make sure you delete it before precompiling the views.

If you want to make this work on your build server without having Visual Studio installed on it, there is one more step remaining. The above mentioned assembly is not installed with .NET SDK meaning that you have to copy it there from your development machine. The file is located in the MSBuild extension folder, i.e. “C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\” by default.

Transformation files displayed under the original fileThis takes care of transforming web.config files during build. How about transforming app.config files and even other XML files in your projects? I guess it would be possible to use the same MSBuild task on them but no Visual Studio support for transforming those files would mean even more tempering with project files to make transformation files appear under the file they are used to transform.

This is where SlowCheetah – XML Transforms Visual Studio extension can help. After installing it you can define transformations for app.config the same way you are already doing it with web.config files. The transformation is part of the build process in this case, i.e. the transformed file is put in its usual spot in the build output folder (usually bin) instead of the original one. It couldn’t get any simpler.

As an added bonus the extension features support for previewing the results of the transformation: after right clicking the transformation file and selecting the Preview Transform command a diff window will open graphically showing the differences between the original and the transformed file. Of course this works with web.config transformations as well making the debugging of transformations much simpler. A good reason in itself to install this extension.

To avoid failed builds on your build server as well as with the other members of your team not having this extension installed it’s best that you copy the SlowCheetah MSBuild targets and the corresponding assembly containing the tasks to your solution directory and put it in source control. In this case you’ll again have to modify the project file to set the new path. Just search for the SlowCheetahTargets property definition and modify the path accordingly (line breaks added for readability):

<PropertyGroup>
    <SlowCheetahTargets Condition=" '$(SlowCheetahTargets)'=='' ">
        ..\_References\SlowCheetah\SlowCheetah.Transforms.targets
    </SlowCheetahTargets>
</PropertyGroup>

Are there any ways to make your life simpler by using configuration transformations in your projects?

Sunday, January 08, 2012 12:34:56 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | ASP.NET | Software | VisualStudio
# Monday, December 19, 2011

In a previous post I addressed the issue of using HTTP module based authentication in WCF. The presented solution worked in most cases but failed completely with Windows authentication. In this post I’ll describe the necessary changes to make this work as well.

Let’s first see what goes wrong with the existing solution and why. To configure WCF for Windows authentication, the following changes are required in web.config:

<system.serviceModel>
    <!-- ... -->
    <bindings>
        <basicHttpBinding>
            <binding name="HttpWindowsBinding" 
                     maxReceivedMessageSize="2147483647">
                <security mode="TransportCredentialOnly">
                    <transport clientCredentialType="Windows" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <services>
        <service name="WcfAuthentication.Service">
            <endpoint address="windows"
                      binding="basicHttpBinding"
                      bindingConfiguration="HttpWindowsBinding"
                      contract="WcfAuthentication.IService" />
        </service>
    </services>
</system.serviceModel>

Of course the settings have to be matched in IIS: Windows authentication should be enabled for the application while anonymous authentication should be disabled, as well as all the other types of authentication.

After setting all this up any calls to our service will throw a MessageSecurityException: "The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'." If you try searching the web for solutions, you’ll notice the same error pops up in many different situations not related to our case. So what’s going on here?

The problem is being caused by the following method in HttpAuthenticationModule:

void context_AuthenticateRequest(object sender, EventArgs e)
{
    HttpContext.Current.User = ProcessAuthentication();
}

Setting the user in the current HttpContext to a custom IPrincipal implementation confuses WCF which expects a WindowsPrincipal as configured. The only way to make it work is to pass through the original user information in this case:

void context_AuthenticateRequest(object sender, EventArgs e)
{
    if (!(HttpContext.Current.User is WindowsPrincipal) && 
        HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.EndsWith(".svc"))
        HttpContext.Current.User = ProcessAuthentication();
}

The extension based filtering is there so that the authentication will still work for the rest of our web application. This change alone is not enough, of course. We still to need the authentication somewhere for the WCF case. HttpContextAuthorizationPolicy is the right spot for it. Evaluate method should be modified as follows:

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
    HttpContext context = HttpContext.Current;

    if (context != null)
    {
        if (context.User is WindowsPrincipal)
        {
            IPrincipal principal = HttpAuthenticationModule.ProcessAuthentication();
            evaluationContext.Properties["Principal"] = principal;
            evaluationContext.Properties["Identities"] = new List<IIdentity> { principal.Identity };
        }
        else
        {
            evaluationContext.Properties["Principal"] = context.User;
            evaluationContext.Properties["Identities"] = new List<IIdentity> { context.User.Identity };
        }
    }

    return true;
}

Keep in mind that calling a static method in HttpAuthenticationModule to authenticate the user is just a shortcut to make this sample work and is not suggested practice. In production code you’ll want to have your authentication logic implemented somewhere in the business layer and call it from both HttpAuthenticationModule and HttpContextAuthorizationPolicy.

Monday, December 19, 2011 9:12:16 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | ASP.NET | WCF
# Monday, December 12, 2011

WCF has great built-in support for most types of authentication so there aren’t many good reasons to use HTTP module based authentication with it. Having an existing ASP.NET application already using such authentication certainly is one of them. Finding resources on how to do it might be a challenge though. I managed to stumble upon an article by Microsoft patterns & practices team which helped a lot. In a way this post is its abridged and more practical version.

From here on I assume you already have an IHttpModule in your application (ProcessAuthentication() being the method implementing the actual authentication of the user):

public class HttpAuthenticationModule : IHttpModule
{
    public void Dispose()
    { }

    public void Init(HttpApplication context)
    {
        context.AuthenticateRequest += context_AuthenticateRequest;
    }

    void context_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpContext.Current.User = ProcessAuthentication();
    }

    private static IPrincipal ProcessAuthentication()
    {
        // implement your authentication here
        IIdentity identity = new GenericIdentity("Authenticated User");
        return new GenericPrincipal(identity), null);
    }
}

The module should also already be registered in web.config:

<system.web>
    <!-- ... -->
    <httpModules>
        <add name="HttpAuthenticationModule" 
             type="WcfAuthentication.HttpAuthenticationModule"/>
    </httpModules>
</system.web>

The goal is of course getting access to the authenticated user (i.e. IPrincipal instance) in WCF service through ServiceSecurityContext. The following test method is a great way for testing that:
public string GetUser()
{
    if (ServiceSecurityContext.Current != null)
        return ServiceSecurityContext.Current.PrimaryIdentity.Name;
    else
        return null;
}

IAuthorizationPolicy is the interface to implement custom authorization in WCF with. In our case the authenticated user can be accessed through current HttpContext:

public class HttpContextAuthorizationPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        HttpContext context = HttpContext.Current;

        if (context != null)
        {
            evaluationContext.Properties["Principal"] = context.User;
            evaluationContext.Properties["Identities"] = new List<IIdentity>() { context.User.Identity };
        }

        return true;
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return ClaimSet.System; }
    }

    public string Id
    {
        get { return "HttpContextAuthorizationPolicy"; }
    }
}

Of course the class should be registered in web.config so that our service will use it:

<system.serviceModel>
    <!-- ... -->
    <behaviors>
        <serviceBehaviors>
            <behavior>
                <!-- ... -->
                <serviceAuthorization>
                    <authorizationPolicies>
                        <add policyType="
                             WcfAuthentication.HttpContextAuthorizationPolicy, 
                             WcfAuthentication, Version=1.0.0.0, 
                             Culture=neutral, PublicKeyToken=null"/>
                    </authorizationPolicies>
                </serviceAuthorization>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

There is still one thing missing. If you try out the above code, you will realize that HttpContext.Current is always null even if authorization in our HTTP module was successful. To get access to it you need to enable ASP.NET compatibility:

<system.serviceModel>
    <!-- ... -->
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" 
                               aspNetCompatibilityEnabled="true"/>
</system.serviceModel>

To make your WCF service work in this mode you need decorate it with AspNetCompatibilityRequirementsAttribute:

[AspNetCompatibilityRequirements(RequirementsMode = 
    AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
    // ...
}

Finally, we’re done. If you’ve implemented all of the above correctly, our test method GetUser() should return the user who was authenticated in the HTTP module. Unless you’re trying to use Windows authentication which still doesn’t work in this setup. That’s already a subject for another post, though.

Monday, December 12, 2011 9:29:48 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | ASP.NET | WCF
# Sunday, September 17, 2006

ASP.NET offers several ways of mapping nice public URLs to cryptic internal URLs matching the actual implementation.

The simplest way is to match each public URL to its internal value. You can do this by either creating dummy pages which do the redirecting or by adding them to the urlMappings section of the web.config file (brought by framework 2.0). The latter solution is preferable because of simpler maintenance but both of them have the downside of being completely static (an entry is necessary for each mapped page).

A better solution is to implement an IHttpHandler or IHttpModule and do the mapping by calling HttpContext.RewritePath(). You could also change the requested page by using Server.Transfer() or maybe even Response.Redirect() but they have their disadvantages when used for URL mapping.

To avoid having to recompile your IHttpHandler implementation every time the mapping logic changes or extends you could configure it through a custom section in the web.config file. Regular expressions are a nice tool for defining such mappings as demonstrated by here (also check the comments) or implemented in dasBlog sources (check newtelligence.DasBlog.Web.Core.UrlMapperModule). You could even use UrlRewritingNet.UrlRewrite if you don’t like reinventing the wheel.

Sunday, September 17, 2006 1:33:22 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | ASP.NET
# Saturday, June 10, 2006

Be careful when hosting your web site based on DasBlog from a Windows XP machine. While IIS 6 in Windows 2003 prevents the download of files with unknown extensions by default, the IIS 5.1 in Windows XP allows downloading such files. In the case of DasBlog all *.blogtemplate files are at risk. There are a few sites out there where these files can be downloaded. Although this probably isn’t a big security risk it might be something you want to prevent. Probably the easiest way to do that is by modifying the web.config file. You should add the following line at the end of the <httpHandlers> section:

<add verb="*" path="*.snippet" type="System.Web.HttpForbiddenHandler" />

Saturday, June 10, 2006 11:56:44 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | ASP.NET | Software | Windows
DasBlog – the weblog engine this site is running on – allows extensibility through macros. You’ll need to use them as soon as you want any additional dynamic content on your site. (The ads you can see at the bottom of the right side bar are an example of a macro which I’ve recently updated to make the ad selection a little more advanced.) The documentation doesn’t mention their development at all therefore the following post by Vasanth Dharmaraj is probably the best source of information available on it. It actually discusses everything you need to know to get going. Make sure you read it before trying to write your first macro.
Saturday, June 10, 2006 11:42:10 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | ASP.NET | Software | DasBlog
# Thursday, January 19, 2006

The problem is pretty straightforward: the web site redesign causes the structure to change, thus the old addresses become invalid. Since you don’t want the users to get the dreaded error 404: Object not found, there are a couple of options available to you (if you’re using IIS – Internet Information Services, that is).

You could just change the error page to match the style of your web site and inform the visitor about the now missing page or just make the redirection to your new starting page. This is a bit unfriendly to the visitors if you kept the old content since they have to find it themselves. It would certainly be better to redirect them directly to the new address of the old content. But still it’s not a bad idea to do this. It’s an easy way to keep the users on your website even when they encounter invalid URLs by whatever reason. Just open up the Custom Errors tab of the virtual directory or web site properties and set the desired URL for the error 404. But don’t forget that you have to enter the complete path starting from the root of the site, for example: "/mydirectory/myurl.html".

If you want to make a different redirection for each page you could just keep the old pages but instead of having any actual content they would just make a redirection to the correct new address. This solution has two problems:

  • It’s difficult to maintain if you have many pages.
  • You’re stuck with the client side redirection, i.e. meta refresh tag.

To make the redirection server side you could use the redirect options on the Home Directory tab of the virtual directory or web site properties. But they have some serious limitations and tend not to work as expected, even more so because the documentation doesn’t explain them very well. But there’s no reason to worry, I have a better solution for you. Setup a special 404 URL on the Custom Errors tab as already suggested. But this time use an asp or aspx page for it. The supplied query string (Request.QueryString) contains the missing URL which you can parse out and use to determine the correct new address corresponding to it. For a few pages a simple select or switch clause will do but nothing prevents you from having the mappings stored externally, in a special file or a database table for example. All that’s left is to make a Response.Redirect to the new address.

There’s one more thing to take care of. If you moved your site to a new subdirectory and chose the last suggested solution, don’t forget to setup a similar simple starting page which just redirects the visitors to the new starting page. Trying to open the site without this page will namely cause an error 403: Forbidden, because a directory listing will be attempted which you have (hopefully) prevented.

Thanks go to Peter Forret for some of the ideas I used to make this work when redesigning my page.

Thursday, January 19, 2006 11:35:50 PM (Central European Standard Time, UTC+01:00)  #    Comments [1] - Trackback
Development | ASP.NET | Software | Windows
Page 1 of 1 in the DevelopmentASPNET category
Sponsored Ads

About Me
Twitter
I really need to give this a try: Notify Property Weaver : http://t.co/WRiDR7Rt 1 day ago
The Web is the new Terminal: Are you using the Web's Keyboard Shortcuts and Hotkeys? http://t.co/4PSPFgIy via @shanselman 1 day ago
Do Hard Things | Sealed Abstract http://t.co/6LDRAcrb (via Instapaper) 1 day ago
Potepanja v naravi: Abram na Nanosu http://t.co/vtlUEWJg 1 day ago
@MladenPrajdic @andrejt use the middle mouse button then 3 days ago
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

All Content © 2012, Damir Arh, M. Sc. Send mail to the author(s) - Privacy Policy - Sign In
Based on DasBlog theme 'Business' created by Christoph De Baene (delarou)
Social Network Icon Pack by Komodo Media, Rogie King is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.