Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Poor Performance with WindowsTokenRoleProvider

I'm using WindowsTokenRoleProvider to determine Active Directory group membership in an ASP.NET web application.

My problem is that performance is not good, especially when a user is in many groups. As an example, I am in 253(!) groups, and WindowsTokenRoleProvider is taking around 150 seconds to determine what groups I am in.

I know I can use caching so that this isn't done on subsequent requests for a user, but obviously it isn't acceptable to take that long on the first hit.

What are my options? Can I force WindowsTokenRoleProvider to only consider certain groups? (I'm only interested in 5).

like image 614
Cocowalla Avatar asked Dec 18 '09 16:12

Cocowalla


1 Answers

Some testing has revealed that my problem is that calling:

Roles.IsUserInRole(groupName)

is accessing the method GetRolesForUser in the RoleProvider - which is retrieving details of every role the user is a member of.

But calling:

Roles.Provider.IsUserInRole(groupName)

determines whether or not the user is in the group - without retrieving the details of every role the user is in.

Weird, but it looks like using Roles.Provider.IsUserInRole will solve my problem.

* UPDATE *

It turns out that this is just a partial workaround; if I use imperative permission checks, or 'allow' and 'deny' in web.comfig, then WindowsTokenRoleProvider still goes and slowly gets details of every group the user is a member of :o(

So my question still stands...

* UPDATE *

I solved this by creating a class that extends from WindowsTokenRoleProvider and overriding GetRolesForUser so it only checks for membership of roles specified in the configuration. It includes caching too:

/// <summary>
/// Retrieve the list of roles (Windows Groups) that a user is a member of
/// </summary>
/// <remarks>
/// Note that we are checking only against each system role because calling:
/// base.GetRolesForUser(username);
/// Is _very_ slow if the user is in a lot of AD groups
/// </remarks>
/// <param name="username">The user to check membership for</param>
/// <returns>String array containing the names of the roles the user is a member of</returns>
public override string[] GetRolesForUser(string username)
{
    // Will contain the list of roles that the user is a member of
    List<string> roles = null;

    // Create unique cache key for the user
    string key = String.Concat(username, ":", base.ApplicationName);

    // Get cache for current session
    Cache cache = HttpContext.Current.Cache;

    // Obtain cached roles for the user
    if (cache[key] != null)
    {
        roles = new List<string>(cache[key] as string[]);
    }

    // Was the list of roles for the user in the cache?
    if (roles == null)
    {
        roles = new List<string>();

        // For each system role, determine if the user is a member of that role
        foreach (SystemRoleElement role in WebConfigSection.Settings.SystemRoles)
        {
            if (base.IsUserInRole(username, role.Name))
            {
                roles.Add(role.Name);
            }
        }

        // Cache the roles for 1 hour
        cache.Insert(key, roles.ToArray(), null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
    }

    // Return list of roles for the user
    return roles.ToArray();
}
like image 56
Cocowalla Avatar answered Oct 07 '22 17:10

Cocowalla