Active Directory Role Provider

Having an asp.net application with forms authentication enabled authenticate users against an Active Directory is an easy thing. All you have to do is use an ActiveDirectoryMembershipProvider and configure it’s connectionString property in the web.config file.

Retrieving user’s role information is a different thing. One would expect an ActiveDirectoryRoleProvider to connect to the Active Directory and retrieve the current user’s group information, however such object doesn’t exist.

What follows is a custom implementation of this role provider that queries an Active Directory and retrieves user’s group information.

First you have to define a class that inherits from System.Web.Security.RoleProvider in order to use it as your application role provider

public class CustomActiveDirectoryRoleProvider : System.Web.Security.RoleProvider

Next, you should retrieve the configuration information from the web.config file in the Initialize method

private string _loginProperty = "sAMAccountName";
private string _connectionString = string.Empty;
private string _applicationName = string.Empty;

public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
    _connectionString = config["connectionStringName"];
    _applicationName = config["applicationName"];
    if (!string.IsNullOrEmpty(config["attributeMapUsername"]))
        _loginProperty = config["attributeMapUsername"];
    base.Initialize(name, config);
}

At last, whe have to implement GetRolesForUser method where we will get all the groups where the current user belongs to (other methods such as IsUserInRole can be implemented later by querying the resultset from GetRolesForUser)

public override string[] GetRolesForUser(string userName)
{
    List allRoles = new List();

    DirectoryEntry root = new DirectoryEntry(WebConfigurationManager.ConnectionStrings[_connectionString].ConnectionString);
    foreach (DirectoryEntry entry in root.Children)
    {
        if (entry.SchemaClassName.ToLower() == "group")
        {
            object members = entry.Invoke("Members", null);
            foreach (object member in (IEnumerable)members)
            {
                DirectoryEntry child = new DirectoryEntry(member);

                if (_getProperty(child, _loginProperty) == userName)
                {
                    string name = _getProperty(entry, "name");
                    allRoles.Add(name != "" ? name : entry.Name);
                }
            }

        }
    }

    return allRoles.ToArray();
}

Finally I provide the _getProperty function source code

private string _getProperty(DirectoryEntry entry, string propertyName)
{
    if ((entry.Properties[propertyName] != null) &&
        (entry.Properties[propertyName].Value != null))
    {
        return entry.Properties[propertyName].Value.ToString();
    }

    return "";
}

And that’s it ! hope it’s useful and don’t forget to leave a comment if you think so ;-)

About these ads

13 Responses to Active Directory Role Provider

  1. David says:

    WOW!!!… you published this in just the nick of time for me! :) So helpful. I successfully implemented this on Friday. A couple comments:
    1. “List” didn’t work for me. I had to do List. Other users will find it helpful to know they can get that class in System.Collections.Generic. Took a bit of time to find that.
    2. DirectoryEntry is in System.DirectoryServices, which they’ll need to reference. (I didn’t have it to begin with.)
    3. Sometimes the foreach statement fails because root.Children is null. Does new DirectoryEntry() contain an asynchronous process that takes time to populate? I was able to reduce the impact of this by setting cacheRolesInCookie to true in the web.config.
    4. This only works for roles that the user is DIRECTLY in. If the user is in a role, and the role is itself in a second role, and we check to see if the user is in that second role, it will return false. Not a big deal, and I’m sure we could do some kind of recursion to deal with this. I don’t need it myself, so I haven’t looked into it.

    5 (and most important). I’m doing this because I’m mixing site-only users with network Active Directory acct holders. AD acct holders have usernames that include the domain. (At least mine do.) All site-only users get permission to everything on the site, but not all network users have access to the site. So I use that domain name to distinguish between the two, and I add a “fake” role to the site-only users’ roles array.

    Hope that helps you and anyone else trying to do this. THANKS AGAIN!

  2. Cyril says:

    Hi,

    Thank you for all, but I’ve got some problems (problem n°0 : excuse my poor english)

    In fact, I’m working with ADAM (AD Application Mode). It’s quite similar. I implemented the same custom roleProvider class and I would like to retrieve the path of ADAM from the Web.config file but it doesn’t work. I never enter into the Initialize function.

    Normally, do I have to instanciate a new CustomRoleProvider to use it, for example to know the role of someone?

    Could you precise me which paramaters did you add in the web.config file. I suppose it begins with .

    Perhaps, I should take an other way : to declare the path of ADAM in applicationSettings and work with that.

    What do you think?

  3. oricode says:

    The role provider you pretend to use must be configured in the web.config file under the section:

    <roleManager enabled=”true” cacheRolesInCookie=”true” defaultProvider=”MyADRoleProvider” cookieName=”.ASPXROLES” cookiePath=”/” cookieTimeout=”30″ cookieRequireSSL=”false” cookieSlidingExpiration=”true” createPersistentCookie=”false” cookieProtection=”All”>
    <providers>
    <add name=”MyADRoleProvider” type=”System.Web.Security.AuthorizationStoreRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, publicKeyToken=b03f5f7f11d50a3a” connectionStringName=”ADConnectionString” applicationName=”/”/>
    </providers>
    </roleManager>

    Changing the type of the role provider by your custom implemented type.

    Hope it works !

  4. Cyril says:

    I followed your advise, but I’m very annoyed : if I put a break point in the Initialize function of my custom class roleProvider, it never goes into. Correct me if I make a mistake, but if I call for example this function to retrieve a user : “Membership.ValidateUser(“jean”, “azer”)” , .NET may call the Initialize function of the roleProvider.
    I’m sure the web.config file is ok because if I specify an uncorrect name of my class, it throws. So, for that, it’s ok.
    A second question, how can you retrieve the role(s) of an user using my implemented class without make an instance of it, because, finally it’s my goal…

    Thank you for your first message

  5. oricode says:

    The initialize function of your roleProvider class must be called when the membership provider is initialized and should get as a parameter the web.config file role provider attributes. However, it is not called for each call to ValidateUser you make, only on the initialization of the provider.

    You could also put this method in another class and call it just like any other ordinary method if this is what you need, just pass the current user login as a parameter (from HttpContext.Current.User) and that should work.

  6. mohd says:

    Hi,

    I tried to follow thru your example but I am getting the following error: Can you help?

    The authorization store component is not installed
    Description: The AuthorizationStoreRoleProvider requires the authorization store components to be installed on the machine. The authorization store components are only installed and available by default on Windows Server 2003. Currently it appears that either the components have not been installed, or that the primary interop assembly has not been registered in the global assembly cache (GAC). Both of these steps can be accomplished by downloading the Authorization Manager installation package from the web for your operating system, and installing the package on the machine. Installations for other operating systems can be found by navigating to http://download.microsoft.com and searching with either the keyword “AzMan” or the keywords “authorization manager”.

  7. oricode says:

    Which operating system are you using ?

    At first, I’d suggest you to do exactly what the error asks you to do. Check your OS version, download the Authorization Manager runtime from microsoft and install it. That should register in the GAC the required dlls.

  8. Kourosh saleh says:

    Hi, and thank you for your helpful blog.

    my problem is the web.config file. when i use ActiveDirectoryRoleProvider I have to write a username and password that has administration permsissions on AD. Is there a way to work around it?
    thanks agian.

  9. oricode says:

    Unfortunately, not.

    You must provide credentials for connecting to the AD server just as you would provide them for connecting to a database or use windows authentication instead.

    However, if this is a security issue, you can easily cypher web.config sections where this information is stored (and it’s really straightforward to do it with vs 2008, I’ll write a post on that soon !)

  10. Andrew says:

    Could someone post a complete code sample including the web.config file? I have most of the above implemented, but not sure from the samples above how the CustomActiveDirectoryRoleProvider gets used/assiciated to the role manager in the web.config file.

  11. oricode says:

    The association is made at the roleprovider section of the webconfig file. Unfortunately I haven’t the one of the post available at the moment but it should be something like:

    <roleManager enabled=”true”>
    <providers>
    <clear />
    <add applicationName=”/”
    connectionStringName=”<connection_string>”
    name=”MyRoleProvider”
    type=”<ProjectNamespace>.CustomActiveDirectoryRoleProvider” />
    </providers>
    </roleManager>

  12. That was osam.
    I have done in nick of time.

  13. Daniel says:

    Hi,

    I just stumbled upon this page, and thought I’d post my compliments on your work. Also, you might be interested in a similar project I’ve been working on at http://www.codeproject.com/KB/aspnet/active_directory_roles.aspx. If you have any thoughts, ideas for improvements, or just want to gripe about how awful it is to query Active Directory, I’m always happy for feedback.

    Daniel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: