Wednesday, May 4, 2011

Custom properties for IPrincipal

Sometimes you need to provide application specific values that are associated with a particular user. Let's say your application must handle role based security by using AD groups. Furthermore let's say that you wanted those group mappings to be configurable. A way to solve this would be to create your own implementation of IPrincipal. It might look something like this:

public class ApplicationPrincipal : IPrincipal
{
    private readonly IIdentity _identity;
    private readonly IPrincipal _principal;
    private readonly ISecurityConfiguration _config;

    public class ApplicationPrincipal(IIdentity identity, IPrincipal principal, ISecurityConfiguration config)
    {
        _identity = identity;
        _principal = principal;
        _config = config;
    }

    public IIdentity Identity { get { return _identity; } }

    public bool IsInRole(string role)
    {
        var windowsGroup = MapInternalRoleToWindowsGroup(role);
        return _principla.IsInRole(windowsGroup);
    }

    public string MapInternalRoleToWindowsGroup(string role)
    {
        switch (role)
        {
            case "Admin": return _config.AdminGroup;
            case "User": return _config.UserGroup;
            default: throw new SecurityException("Invalid group name: " + role);
        }
    }
}

Now that we have our own principal implementation we need a way to let the application access it. A good place to do that would be in an HttpModule that handles the PostAuthenticateRequest event.

public class ApplicationPrincipalModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += PostAuthenticateRequest;
    }

    public void Dispose()
    {
        context.PostAuthenticateRequest -= PostAuthenticateRequest;
    }

    private void PostAuthenticateRequest(object sender, EventArgs e)
    {
        var httpContext = HttpContext.Current;
        var principal = new ApplicationPrincipal(
                             httpContext.User.Identity, httpContext.User, 
                             new SecurityConfiguration());
        httpContext.User = principal;
    }
}

Once a user has been authenticated, by whichever method your application is set up to authenticate, this module will set the HttpContext.Current.User to your implementation of IPrincipal. This can then be referenced where needed in your application.

2 comments:

  1. How would you return a friendly username with @Context.User.Identity.Name if the membership "username" is occupied by an illegible user Guid?

    ReplyDelete
  2. @FreshCode you'll need to use another dependency to provide this friendly name. If you're storing your users in an application database you can get it from there. If you're using Windows Authentication, you can get it from active directory.

    ReplyDelete