Ben Hickman's Blog

.NET Ramblings

Custom roles for WindowsPrincipals in ASP.NET

I continue to be pleased with the many plug-points within ASP.NET. Recently, I had a student with an interesting ASP.NET security challenge. He wanted to use integrated windows authentication, but wanted to assign custom roles for the windows principals. He was building an intranet site. He needed a set of roles that didn't map to any existing Windows groups and he couldn't get the network admins to add them (and keep them updated). 

It turns out this is quite easy with ASP.NET. First, create a new class to hold the roles (thankfully, WindowsPrincipal isn't sealed!):

using System.Collections;
using System.Security.Principal;

public class CustomPrincipal : WindowsPrincipal
{
    private ArrayList m_Roles;

    public CustomPrincipal(WindowsIdentity identity): base(identity)
    {
        m_Roles = new ArrayList?();
    }

    public override bool IsInRole(string role)
    {
        // May or may not make sense to check
        // WindowsPrincipal role if membership fails
        // for the local list.
        if (m_Roles.Contains(role))
            return true;
        else
            return base.IsInRole(role);
    }

    public void AddRole(string role)
    {
        m_Roles.Add(role);
    }
}

Now, add the following code to Global.asax.cs to hook into the Windows authentication process in ASP.NET and setup the new CustomPrincipal and its roles:

protected void WindowsAuthentication_OnAuthenticate(object sender,
    WindowsAuthenticationEventArgs e)
{
    if (e.Identity != null && e.Identity.IsAuthenticated)
    {
        CustomPrincipal p = new CustomPrincipal((WindowsIdentity)e.Identity);

        //
        // Add the appropriate roles, e.g. read
        // them out of a database.
        p.AddRole("CustomRole");
        HttpContext.Current.User = p;
    }
}

Then, just use the normal ASP.NET authorization services. You can write code that uses Page.User.IsInRole() or using Web.config, e.g.:

<authentication mode="Windows" />
<identity impersonate="true" >
<authorization>
    <allow roles="BUILTIN\Administrators" />
    <allow roles="CustomRole" />
    <deny users="*" />
</authorization>

Comments

Shawn Wheatley said:

Wow. I've been looking for this code for weeks to do the exact same thing. Now I need to tie Active Directory lookups of the groups the user is in, but this is a good start for me. Thanks so much!
# March 18, 2003 2:36 PM

Ren Hammington said:

Ditto! I've been looking for this solution all day.. It's so simple when you can see it. I was just stumped as to how to achieve this. Very worrying. The only problem I've found with this code is the need to cast Page.User.Identity to the new CustomPrincipal before referencing the .IsInRole method. When I didnt, the roles were never picked up. As soon as I cast to the custom type, picked them up just fine. If there is a work-around, I'd love to know... it's been a long day!
Cheers!
# December 9, 2003 10:44 PM

NetNoob said:

Can someone convert this to VB.Net?

This is exactly what I want to do, but I can't read C# yet

In particular the constructor confused me...

public CustomPrincipal(WindowsIdentity identity): base(identity)
{
m_Roles = new ArrayList?();
}

What does arraylist?() do?
# January 31, 2004 2:33 PM

MeasuredSpace said:

Thanks Ben... I went round in cricles with the web.config yesterday. I used the
<allow roles="BUITIN\Administrators" /> with success today... In two books and MSDN help NOWHERE did I see see an example of using "BUILTIN"... always "DomainName\Roles".
Then I went a step futher and implemented your roles adder code. Wala... Custom Roles Work!!! Very, very grateful.

FWIW I removed the "?" from the line \\ m_Roles = new ArrayList?(); becuase I got intellisense and compiler errors. Was this just a typo or was its use intended?

I would buy you lunch right now if I could. Thanks! - Brad
# February 12, 2004 2:23 PM

Stephen Johnston said:

Good article. Definitely straightforward once you see it, but nice to have it spelled out.
# February 23, 2004 1:17 PM

Olivier Monney said:

Thanks. Looks great. But why WindowsAuthentication_OnAuthenticate isn't automatically fired?
# April 18, 2004 7:22 AM

Doug said:

How do I cast Page.User.Identity to the new CustomPrincipal as Ren Hammington says??
# May 20, 2004 3:04 PM

Mark Kola said:

What a wonderful find. I couldn't find anything like this for days.
# June 18, 2004 11:55 AM

Kevin said:

awesome simple code! thx
# June 18, 2004 5:04 PM

Ramesh Shanmugam said:

Thanks for the article I was loking for.

ShanSun
# July 1, 2004 2:48 AM

Jason said:

This is great code! I had to remove the ? in m_Roles = new ArrayList?(); Other then that things work great until I get the windows login dialog box with an unauthorized role. Does anyone know how to force the system to fail over to my 401 error page with out being prompted for user credentials? I know if you use <deny users=”?”> instead of * and perform a if( ! User.IsInRole(“xyz”)) Response.Redirect(“401.htm”); on each page this will be avoided. I’d like to keep everything in the web.config file and not have to write code all over the place for the IsInRole. I’ve tried to use the <customErrors> tag and definded a 401 statusCode, yet the system doesn’t seem to look in these statusCodes while it’s trying to authenticate the user/role. Thoughts? TIA!!!
# July 8, 2004 5:06 PM

Michael said:

Great post!!  Saved me tons of work with such a simple solution,  Bravo!

# June 14, 2007 5:47 PM