Browsing Posts published in May, 2006

For our first event, the Calgary Code Camp was a huge success, if I do say so myself. We had over 80 developers attend and both tracks were constantly buzzing with great discussions. Thanks to everyone who presented for generously sharing their time and knowledge! Thanks also to everyone who attended and made the day a success. We’ll be posting slidedecks and demos to the Calgary Code Camp website in the coming week. In the meantime you can find mine here. You can find some extra goodies in the zip file that I didn’t have time to demo, such as the DatabaseImageHandler and the SiteAvailabilityModule, which I’ll be blogging about in the next while.

If you’re headed to TechEd 2006 in Boston in a few weeks and are interested in discussing what’s new and happening in data access, drop Jonathan Bruce an email. He’s organizing an informal get together of folks interested in ADO.NET. Sounds like it should be a fun time. Hope to see you there.

In the previous two parts (Part 1 and Part 2), I introduced the ImpostorHttpModule as a way to test intranet applications that use role-based security without having to modify your group memberships. (I’ll assume that you know what I’m talking about. If not, go back and re-read the first two parts.) In the final part, let’s look at what exactly is going on behind the scenes with ImpostorHttpModule…

The ImpostorHttpModule requires surprisingly little code to work its magic. Let’s think about exactly what we want to do. We want to intercept every HTTP request and substitute the list of roles defined for the incoming user in the ~/App_Data/Impostors.xml file instead of the user’s actual roles. (In an intranet scenario, a user’s roles are often just the local and domain groups to which the user belongs.) To do this, we need to implement a HttpModule. We’ll start with the simplest HttpModule, which we’ll call NopHttpModule for “No operation”.

using System.Web;

namespace JamesKovacs.Web.HttpModules {
    public class NopHttpModule : IHttpModule {
        public void Init(HttpApplication context) {
        }

        public void Dispose() {
        }
    }
}

To be a HttpModule, we simply need to implement IHttpModule and provide implementations for the two methods, Init() and Dispose(). We now have to register ourselves with the ASP.NET pipeline. We do this using the <httpModules> section of Web.config.

<?xml version=”1.0″?>
<configuration>
    <system.web>
        <httpModules>
            <add name=”NopHttpModule” type=”JamesKovacs.Web.HttpModules.NopHttpModule, JamesKovacs.Web.HttpModules”/>
        </httpModules>
    </system.web>
</configuration>

That’s it. Not terribly interesting because it does absolutely nothing. So let’s move on and implement the HelloWorldHttpModule, which simply returns “Hello, world!” no matter what you browse to, whether it exists or not!

using System;
using System.Web;

namespace JamesKovacs.Web.HttpModules {
    public class HelloWorldHttpModule : IHttpModule {
        public void Init(HttpApplication context) {
            context.BeginRequest += new EventHandler(context_BeginRequest);
        }

        void context_BeginRequest(object sender, EventArgs e) {
            HttpContext.Current.Response.Write(“<html><body><h1>Hello, World!</h1></body></html>”);
            HttpContext.Current.Response.End();
        }

        public void Dispose() {
        }
    }
}

Try browsing to /Default.aspx, /Reports/Default.aspx, /ThisDoesNotExist.aspx, or even /ThisDoesNotExistEither.jpg. They all return “Hello, World!” (N.B. ASP.NET 1.X will return a 404 for the JPEG. ASP.NET 2.0 will return “Hello, World!” In 1.X, static
files were served up directly by IIS without ASP.NET getting involved. Although this gives excellent
performance for images, CSS, JavaScript files, etc., it also meant that
those files were not protected by ASP.NET security. With ASP.NET 2.0, all unknown files types are handled by
the System.Web.DefaultHttpHandler, which allows non-ASP.NET resources to be protected by ASP.NET security as well. See here for more information.)

Now back to our regularly scheduled explanation… In our Init() method, we tell the HttpApplication which events we would like to be informed of. In this case, we grab the BeginRequest event, which is the first event of the ASP.NET pipeline. It occurs even before we determine if the URL is valid, hence our ability to serve up “missing content”.

ASP.NET provides many hooks into its processing pipeline. Here is an excerpt from MSDN2 on the sequence of events that HttpApplication fires during processing:

  1. BeginRequest
  2. AuthenticateRequest
  3. PostAuthenticateRequest
  4. AuthorizeRequest
  5. PostAuthorizeRequest
  6. ResolveRequestCache
  7. PostResolveRequestCache

    After the PostResolveRequestCache event and before the PostMapRequestHandler event, an IHttpHandler (a page or other handler corresponding to the request URL) is created.

  8. PostMapRequestHandler
  9. AcquireRequestState
  10. PostAcquireRequestState
  11. PreRequestHandlerExecute

    The IHttpHandler is executed.

  12. PostRequestHandlerExecute
  13. ReleaseRequestState
  14. PostReleaseRequestState

    After the PostReleaseRequestState event, response filters, if any, filter the output.

  15. UpdateRequestCache
  16. PostUpdateRequestCache
  17. EndRequest

The pipeline in ASP.NET 1.X had many, but not all, of these events. ASP.NET 2.0 definitely gives you much more flexibility in plugging into the execution pipeline. I’ll leave it as an exercise to the reader to investigate why you might want to capture each of the events.

Armed with this information, you can probably figure out which event we want to hook in the ImpostorHttpModule. Let’s walk through the thought process anyway… We are trying to substitute the actual user’s roles/groups for one that we’ve defined in the ~/App_Data/Impostors.xml file. To do this we need to know the user. So we need to execute after the user has been authenticated. We need to execute (and substitute the groups/roles) before any authorization decisions are made otherwise you might get inconsistent behaviour. For instance, authorization may take place against your real groups/roles and succeed, but then a PrincipalPermission demand for the same group/roles might fail because the new groups/roles have been substituted. So which event fits the bill? PostAuthenticateRequest is the one we’re after. In this event, we know the user, which was determined in AuthenticateRequest, but authorization has not been performed yet as it occurs in AuthorizeRequest.

public void Init(HttpApplication context) {
    context.PostAuthenticateRequest += new EventHandler(context_PostAuthenticateRequest);
}

We know which event we want to hook. Now what to do once we hook it. In .NET, we have Identities and Principals. An Identity object specifies who has been authenticated, but does not indicate membership in groups/roles. A Principal object encapsulates the groups/roles and the identity. So what we want to do is construct a new Principal based on the authenticated Identity and populate with the groups/roles that we read in from ~/App_Data/Impostors.xml. As it so happens, the built-in GenericPrinicpal fits the bill quite nicely. It takes a IIdentity object and a list of roles (in the form of an array of strings). N.B. It doesn’t matter if the Identity is a WindowsIdentity, a FormsIdentity, a GenericIdentity, or any other. All that matters is that the Identity implements the IIdentity interface. This makes the group/role substitution code work equally well regardless of authentication technology.

IIdentity identity = HttpContext.Current.User.Identity;
string[] roles = lookUpRoleListFromXmlFile(identity);    // pseudo-code
IPrincipal userWithRoles = new GenericPrincipal(identity, roles);

Armed with userWithRoles, we just need to patch it into the appropriate places:

HttpContext.Current.User = userWithRoles;
Thread.CurrentPrincipal = userWithRoles;

We have discarded the original principal (but kept the original identity) and patched in our custom one. That’s about it. Any authorization requests are evaluated against the new GenericPrincipal and hence the group/role list that we substituted.

An additional feature I would like to point out is caching of the users/roles as you probably don’t want to parse a XML file on every request. The users/roles list will auto-refresh if the underlying ~/App_Data/Impostors.xml file changes. Let’s see how this works. We store a Dictionary<string, string[]> in the ASP.NET Cache, which contains users versus roles as parsed from the ~/App_Data/Impostors.xml file. If it doesn’t exist in the Cache, we parse the XML file and insert it into the Cache along with a CacheDependency like this:

HttpContext.Current.Cache.Insert(“ImpostorCache”, impostors, new CacheDependency(pathToImpostorsFile));

When the underlying file changes, the entry is flushed from the cache. The next time the code runs, the cache is re-populated with the contents of the updated ~/App_Data/Impostors.xml.

One last point… The ImpostorHttpModule is meant for development/testing purposes, which means that I haven’t optimized it for performance, but for ease of implementation and comprehension.

So there you have it – the ImpostorHttpModule. Hopefully you have a better appreciation for the power and extensibility built into ASP.NET as well as some cool ideas of what else you can implement using HttpModules. Full source code can be found here.

A light news show… hopefully. You can grab it here. The episode will hit MSDN Canada Community Radio in the near future.

Show Notes

Resources

In our last cliff-hanger episode, I introduced the ImpostorHttpModule. I’m going to show how you can use it to implement and test a sitemap and navigation menu in ASP.NET. We’ll use the new ASP.NET 2.0 Master Pages feature because it’s the easiest way to ensure that the same menu ends up on every page. We’ll start from the previous solution with the ImpostorHttpModule registered in the Web.config. I’ve created three pages, ~/Default.aspx, ~/Reports/Default.aspx, and ~/Admin/Default.aspx. The Web.config files are set up as follows:

Path Allowed Roles
~/Default.aspx User, Manager, Administrator
~/Reports/Defaults.aspx Manager, Administrator
~/Admin/Default.aspx Administrator

We’ll be using the security trimming feature of sitemaps, which removes nodes from the sitemap that are not accessible by the current user. Note that this is simply a UI nicety and not actual security – without the appropriate Web.config files and <authorization> sections, users could still access those areas by navigating directly to them. So let’s start by implementing the menu and then we’ll enable security trimming. We’ll see how ImpostorHttpModule can help us in testing these features.

Our first step is to add a sitemap, which we’ll leave with the default name of Web.sitemap. We’ll add three nodes – Home, Reports and Administration. One of the odd things about the sitemap XML schema is that the child of <siteMap> must be a single <siteMapNode>. So if you want to have multiple nodes, you need to create an empty <siteMapNode> with further child nodes like this:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0” >
    <siteMapNode>
        <siteMapNode url=”~/Default.aspx” title=”Home”  description=”Return to the Home page” />
        <siteMapNode url=”~/Reports/Default.aspx” title=”Reports” description=”Go to reports” />
        <siteMapNode url=”~/Admin/Default.aspx” title=”Administration”  description=”Go to site administration” />
    </siteMapNode>
</siteMap>

One quick note is the use of ~/ which allows us to create links that are relative to the application’s virtual directory. This is a great ASP.NET feature, which works in most places links are found in server-side controls, as it allows you to deploy the site at http://servername/ or http://servername/someVdir/ or anywhere else.

Now that we have our sitemap set up, we can create a Menu that is driven by the sitemap. We’ll create a master page called Main.master and add the Menu to it in design mode. We’ll create a new SiteMapDataSource and point the Menu control at it. Since we have an empty starting node, we’ll set SiteMapDataSource.ShowStartingNode = false. (I also changed the menu to use the Classic style and horizontal orientation.) We should now see three nodes: Home, Reports, and Administration in the Menu. If we try to browse the site, we’ll get a 401 – Access Denied because our user account is not in any of the appropriate roles. At least the Web.config files are doing their jobs. Let’s see how to use the ImpostorHttpModule to give ourselves access to these directories without having to create a local or domain group or add our user account to it. (In a deployment situation, you would likely have a domain group that a domain admin would add users to. We’re trying to avoid all the overhead of calling up your friendly neighbourhood domain admin every time you want to test a different security context when accessing your app. Believe me – your domain admin will thank you.)

So let’s add our user, DOMAIN\Foo, to the User, Manager, and Administrator role in the ~/App_Data/Impostors.xml file. (N.B. If you’re logged on using a local account, you’ll need to specify MACHINE\Bar. If you logged in using the alternate user name syntax, foo@example.com, you’ll need to use this in the name.)

<?xml version=”1.0″ encoding=”utf-8″ ?>
<impostors>
    <impostor name=”DOMAIN\Foo” roles=”User, Manager, Administrator”/>
</impostors>

Clicking on the Menu links should allow you to see the Reports and Administration sections. All is good in the world. Now let’s implement security trimming! Our next stop is Web.config where we’ll have to add a new sitemap provider as the default one declared in Machine.config does not have security trimming enabled. The key attribute here is securityTrimmingEnabled=”true”.

<siteMap defaultProvider=”XmlSiteMapProvider” enabled=”true”>
   <providers>
      <add name=”XmlSiteMapProvider” description=”Default SiteMap provider.” type=”System.Web.XmlSiteMapProvider” siteMapFile=”Web.sitemap” securityTrimmingEnabled=”true”/>
   </providers>
</siteMap>

With this change, the menu disappears entirely! We haven’t defined any roles that can see the nodes. So they are all trimmed off. Let’s update the Web.sitemap to add the roles:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0” >
    <siteMapNode roles=”User, Manager, Administrator”>
        <siteMapNode url=”~/Default.aspx” title=”Home”  description=”Return to the Home page” roles=”User, Manager, Administrator” />
        <siteMapNode url=”~/Reports/Default.aspx” title=”Reports” description=”Go to reports” roles=”Manager, Administrator” />
        <siteMapNode url=”~/Admin/Default.aspx” title=”Administration”  description=”Go to site administration” roles=”Administrator” />
    </siteMapNode>
</siteMap>

A quick note is that you must specify all the roles on the empty <siteMapNode>. If you do not, it gets trimmed and none of the child nodes get displayed either. (Took me awhile to realize this the first time I used sitemaps and security trimming.)

Now that we have everything in order, try removing your user account from the Administrator role in ~/App_Data/Impostors.xml and browse to ~/Default.aspx. The Administration link disappears. You can now happily add and remove roles from your user account to test what different kinds of users would see when they browse the site.

In summary, the ImpostorHttpModule allows us to easily test different security configurations for our site without having to change your local or domain group memberships. Since we’re replacing the list of roles that the incoming user is a member of, this technique not only works for testing sitemaps and website security using <authorization>, but it also works for declarative and imperative security demands in code. Full source code is available here. In our next episode, we’ll look at how the ImpostorHttpModule works under the covers.

If you’ve developed any moderately complex web application, you’ve probably implemented some form of role-based security. For instance, only administrators can access the /Admin directory. Only ContentCreators can update web page content. Only RegisteredUsers can access the site. ASP.NET makes all of this relatively easy through the <authorization> section of the web.config file(s), programmatic security, and security trimming of site maps. Managing roles is even easier with ASP.NET 2.0 as we now have the role manager (represented by System.Web.Security.Roles) and various RoleProviders. If you’re using the SqlRoleProvider, you can test different configurations of your website by adding and removing your user from various roles and not caching role memberships in cookies. (If you cache your role memberships in cookies, you either have to wait for the cookie to expire or clear your browser cookies every time you change your role membership to ensure that you have the correct memberships.) It’s tedious, but it can be done. Even more tedious is testing role-based security for an intranet application if you’re using Windows groups via the WindowsTokenRoleProvider. Basically your Windows group memberships become your roles. Simple enough – add your user to the appropriate local or domain groups using the Local Users and Groups or Active Directory MMC snap-in and you’re good to go. Right? Not quite. Your group memberships in your user token don’t get updated until you log out and back in again. (I’m ignoring some advanced security APIs that allow you to invalidate your user token and re-acquire it.) Now this is really tedious and is likely going to result in you doing less testing under different role memberships than you really should. Is there a way that we can improve this? I’m glad you asked. Enter the ImpostorHttpModule.

The ImpostorHttpModule is an ASP.NET HttpModule that re-writes an authenticated user’s roles based on a XML file, ~/App_Data/Impostors.xml. To use the module, simply drop the assembly into ~/Bin/ and add the following section to your web.config file:

<httpModules>
  <add name=”ImpostorHttpModule” type=”JamesKovacs.Web.HttpModules.ImpostorHttpModule, JamesKovacs.Web.HttpModules”/>
</httpModules>

Then create a XML file called ~/App_Data/Impostors.xml, which looks like this:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<impostors>
  <impostor name=”DOMAIN\Jane” roles=”User, Manager, Administrator”/>
  <impostor name=”MACHINE\Bob” roles=”User”/>
</impostors>

The file format should be fairly self-explanatory. The name attribute is the log-in name of the user. It can be a local, domain, forms auth, or other user and is not case sensitive. The roles attribute is a comma-separated list of groups or roles to place the user in. The users can be authenticated by Windows, Forms, Passport, or any other authorization mechanism. The ImpostorHttpModule inserts itself after the authentication event and replaces the role membership of the incoming user with a GenericPrincipal if, and only if, the user is found in Impostors.xml. If the user is not found, their existing role memberships remain untouched. Impostors.xml file is located in ~/App_Data because by default, ASP.NET 2.0 doesn’t allow browser access to any files in this directory. It saves you from having to filter out and deny requests for this file yourself. ImpostorHttpModule monitors the Impostors.xml file for changes. So as soon as you update it with new user or role information, subsequent requests will acquire the new roles. You can see the new roles by browsing to ShowContexts.aspx, which was created by Dominick Baier. (You can download full source from his site here.) At the bottom, you’ll see your role memberships listed. Play around with Impostor.xml and see how your memberships change.

I should note that ImpostorHttpModule is only meant for development purposes. It will get very upset (e.g. blow chunks) with a bad Impostors.xml file. If you need a robust role management system, consider using Windows groups or the SqlRoleProvider. ImpostorHttpModule is meant as a quick-n-dirty testing tool. That said, you can download the ImpostorHttpModule with source code from here. In my next installment, I’ll show how to create a security-trimmed sitemap and test it with the ImpostorHttpModule. Also stay tuned for a detailed walkthrough of the source code where I’ll explain in painstaking detail how ImpostorHttpModule actually works.

EDIT: Updated ImpostorHttpModule to use a comma-separated list of roles to match <authorization> in Web.config and sitemaps.

Kenny Kerr has updated Window Clippings, which I mentioned here, with some cool new features including:

  • Capturing the PrtScn key to initiate clippings
  • Saving images in the TIFF format
  • Prompt for storage location (rather than always dropping it in the same location)
  • Windows Vista support

You can download x86 or x64 versions, as appropriate, from here. Awesome tool! Thanks, Kenny!!! The x64 build is a great touch.

Jean-Paul reappears on dnrTV to talk about Model-View-Presenter, which is a cool pattern for abstracting away your UI implementation technology. For example you can re-use the same Model and Presenter code with ASP.NET and WinForms. Additionally the view ends up being really dumb, which means that parsing, validation, and other logic traditionally in your ASPX code-behind or WinFom is now encapsulated in the presenter and can be identical between the two. This episode is well worth the watch.

Jean-Paul Boodhoo on Model-View-Presenter

Also worth watching is Jean-Paul’s earlier double-header on Test-Driven Development (TDD). You want to see how TDD is meant to be done, watch and learn!

Jean-Paul Boodhoo on Test-Driven Development Part 1 of 2

Jean-Paul Boodhoo on Test-Driven Development Part 2 of 2

Consolas is a new mono-spaced font being introduced with Vista. John, Scott, and others have been raving about it for months. It’s designed to be a comfortable font for programming and is a huge leap forward from Courier. (Personally I’ve been using Lucida Console for years as it’s easier on the eyes than Courier. Consolas is a step better than that.) Various folks have documented how to “borrow” the font from Vista builds to use on your WinXP computers. Now there is no reason to hack it as Microsoft is providing a download of Consolas for Visual Studio 2005. The installer even sets your default editor font in VS2005 to Consolas so you don’t have to. Sweet!

N.B. Consolas has been optimized for ClearType. So make sure you turn on ClearType! You can use the ClearType Web interface or downloadable PowerToy Tuner. (You can also turn it on via Display Properties, but you can’t adjust it. It’s also buried deep in the options. So it’s easiest to use the web interface or PowerToy.)

[Via Scott Hanselman]

[Image "borrowed" from John Bristowe]

UPDATE: For some strange reason, Consolas wasn’t appearing in my list of available fonts for any program. (This included VS2005 and Notepad!) The registry entries for the Consolas font in VS2005 looked kosher for my user account and if I launched VS2005 as the local Administrator, Consolas appeared correctly in font selection dialogs. I just couldn’t get it to appear for my user account. Logging out and logging back in (without changing any security settings, registry entries, or other configuration) resulted in Consolas spontaneously appearing and working in VS2005 and Notepad.

Time to let the proverbial cat out of the bag. It’s probably the worst kept secret this side of the Rockies, but I’ve gone independent. JamesKovacs.com Consulting Inc. is open and ready for business. If you’re looking for assistance with .NET development, architecture, agile techniques, or object-oriented analysis and design, email me at jkovacs@post.harvard.edu and I would be happy to chat. I’m interested in both short-term and long-term engagements. Whether you need an experienced team lead to mentor your team on .NET a few hours a week or a seasoned architect to design your paradigm-shifting* application, I can help.

* Paradigm-shifts are limited to one per customer, some restrictions may apply, void where prohibited, …