Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Asp Net 2.0 Security Membership And Role Management

.pdf
Скачиваний:
52
Добавлен:
17.08.2013
Размер:
12.33 Mб
Скачать

Chapter 13

When the page runs, it lists the roles that the current user belongs to:

protected void Page_Load(object sender, EventArgs e)

{

foreach (string role in ((RolePrincipal)User).GetRoles()) Response.Write(“Belongs to: <b>” + role + “</b><br/>”);

}

If you login to the sample application initially, the test page lists:

Belongs to: Administrators in store A

If you then click the link that includes the query-string variable with a value of “true,” the custom event handler creates a RolePrincipal that uses the second configured provider. As a result, the test page displays:

Belongs to: Administrators in store B

You can seamlessly flip back and forth between using a default provider (and hence the default RoleManagerModule logic) and the second nondefault provider by clicking on the two links. Aside from the simple logic in the custom event handler for determining which provider to use, the rest of the code shown in this section is exactly what you need to effectively use multiple Role Manager providers in an application.

Because the code manipulates both Thread.CurrentPrincipal and HttpContext.Current.User, the code must be running in Medium trust or higher. The policy files for Medium trust and above include the necessary permission to change the principal object. Alternatively, you can factor out the event handler code into a GAC’d assembly where you can create SecurityPermission(SecurityPermissionFlag

.ControlPrincipal)and then assert it. If you attempt to run the sample code in Low or Minimal trust, it will instead fail with a SecurityException because these trust levels do not allow user code to manipulate the principal on either context or the thread.

RoleProvider

As with the Membership feature, Role Manager depends heavily on providers. In fact, the major pieces of functionality within the Role Manager feature are effectively implemented in RoleManagerModule, RolePrincipal and concrete implementations of the RoleProvider base class. Because Role Manager does not have an object model for a role, the RoleProvider definition is pretty simple. Roles are just strings — and the users associated with those roles are also just strings. As a result, the RoleProvider base class is just an abstract class definition. Unlike MembershipProvider, RoleProvider does not have any helper methods or private methods implementing base portions of the Role Manager feature.

public abstract class RoleProvider : ProviderBase

{

//Properties

public abstract string ApplicationName { get; set; }

//Authorization related methods

public abstract bool IsUserInRole(string username, string roleName);

//Methods that deal with fetching a user’s role information

542

Role Manager

public abstract string[] GetRolesForUser(string username);

//Methods for creating, deleting and managing roles public abstract void CreateRole(string roleName);

public abstract bool DeleteRole(string roleName, bool throwOnPopulatedRole); public abstract bool RoleExists(string roleName);

public abstract void AddUsersToRoles(string[] usernames, string[] roleNames);

public abstract void RemoveUsersFromRoles(string[] usernames, string[] roleNames);

public abstract string[] GetUsersInRole(string roleName); public abstract string[] GetAllRoles();

public abstract string[] FindUsersInRole(string roleName,

string usernameToMatch);

}

Because the RoleProvider treats a role as a string, and some of the providers internally convert array parameters into comma-delimited strings, roles normally are not allowed to have a comma character. For example, if you attempt to create a role called “this,is,a,role”, both the Roles class and most of the default providers will throw an ArgumentException. The reason for this restriction is that not all data stores can accept an array type. Methods like AddUsersToRoles that accept string arrays may have these arrays converted into a comma-delimited string of roles that is then passed down to a database for subsequent parsing and processing. To prevent confusion over whether a comma is a delimiter as opposed to part of a role name, the Roles class and all of the default role providers, except for WindowsTokenRoleProvider, disallow the use of a comma when creating roles.

One thing to keep in mind if you are thinking about implementing a custom provider is the relative simplicity of the Role Manager feature. For custom providers implemented against relational data stores, it is a pretty trivial exercise to write a basic RoleProvider implementation. The core portion of RoleProvider is the GetRolesForUser method; if a custom provider does not implement this method, then the RolePrincipal class will not work properly. And of course without the RolePrincipal class there isn’t much point to using Role Manager. The IsUserInRole method is a logical adjunct to GetRolesForUser. At one point, providers also needed to implement IsUserInRole for the RolePrincipal to work properly, but with some of the later changes to the way the role cache cookie works, it turns out that RolePrinicpal no longer calls IsUserInRole. However, given the nature of authorization checks, it is reasonable to expect a minimal RoleProvider implementation to also implement IsUserInRole (if your data store supports getting all roles for a user, then it implicitly supports role checks like IsUserInRole).

The remainder of the methods on the provider base class are optional from a runtime perspective. If you already create roles and associate users to roles using some other management tool or interface, then you can stub out the rest of the methods on a custom RoleProvider and just throw a

NotSupportedException from them instead.

Note that the RoleProvider definition does not really expose the concept of nesting roles within roles. The administrative portion of RoleProvider does not have methods like AddRoleToRole or RemoveRole FromRole. If you have a custom data store that supports the nesting of roles, you can still expose most of this functionality from methods like IsUserInRole. There is nothing wrong with a custom provider that internally has the logic to recurse through a nested hierarchy of roles to perform authorization checks or to determine membership in a role. If necessary, a custom provider can add a few methods to its implementation to support the necessary administrative methods for nesting roles within roles.

543

Chapter 13

The AuthorizationStoreRoleProvider discussed in Chapter 15 is an example of a RoleProvider that works against a data store that supports role nesting. Because the AuthorizationStore RoleProvider uses the Authorization Manager (aka AzMan) functionality that was first available as part of Windows Server 2003, when you call IsUserInRole on this provider it will properly handle group nesting. However, this provider does not expose any special methods to administer nested roles; instead, the expectation is that developers and administrators will use the MMC or management API available for AzMan policy stores.

Basic Configuration

Just like MembershipProvider, a RoleProvider can partition its data based on an application name from configuration.

ApplicationName — Custom providers should at least implement the getter for this property. The concept of separating data by application name is so common to many of the new provider-based features in ASP.NET 2.0, that the getter should always be implemented. If it turns out that you are mapping role data to a data store that doesn’t really have the concept of an “application,” you can have the setter throw a NotSupportedException. In this case, your custom provider can just ignore the application name that it loaded from configuration.

Authorization Methods

A basic provider implementation should always implement the following two methods:

GetRolesForUser — As mentioned earlier, RolePrincipal always calls this method on a provider at least once prior to making an authorization check inside RolePrincipal.IsInRole. If the username parameter doesn’t exist, the usual convention is to return an empty string array. Similarly, if the user exists in the data store but doesn’t belong to any roles, a provider should return an empty string array as well.

IsUserInRole — Developers may call this method directly on a provider as opposed to calling IsInRole on RolePrincipal. For users who belong to a large number of roles where GetRolesForUser may take an excessive amount of time to run, it will be faster (up to a point) to call IsUserInRole on a provider. There is a bit of a trade-off when developers need to balance the up-front cost of making a single round trip to the data store that returns a large result set when calling GetRolesForUser, versus calling the data store multiple times with IsUserInRole, in which case each individual query in the data store is much faster. For this reason, custom provider implementers should implement IsUserInRole and GetRolesForUser; furthermore, the implementation of IsUserInRole should ideally be faster than the implementation of GetRolesForUser (technically, a custom provider could implement IsUserInRole in terms of GetRolesForUser, but then there is no performance gain for single authorization checks when calling IsUserInRole). If the user specified by the username parameter doesn’t exist in the data store or if the role specified by the roleName parameter doesn’t exist, a custom provider should return false. Developers normally would not expect an authorization check to throw an exception for these cases.

Managing Roles and Role Associations

The remaining methods on RoleProvider are primarily used by administrative tools like the Web Administration Tool (WAT) available inside of Visual Studio. If you already have other management tools for your custom role stores, you can stub out these methods and throw a NotSupportedException. If

544

Role Manager

your intent, though, is for your provider to be useable from administrative tools like the WAT, then you should implement the following methods.

CreateRole — Creates a new role in the data store. Providers should throw a Provider Exception if an attempt is made to create a role, and the role already exists.

DeleteRole — Removes a role from the data store. If the parameter throwOnPopulatedRole is set to true, the provider should throw a ProviderException if an attempt is made to delete a role and the role still has users associated with it. If throwOnPopulatedRole is set to false, this is an indication that the caller is all right with deleting the role, and any remaining user-to- role associations. If an attempt is made to delete a role that doesn’t exist in the data store, a custom provider should just return false from this method rather than throw an exception. If the role is found, and the deletion is successful, then a custom provider should return true.

RoleExists — A provider returns true if the roleName exists in the data store; otherwise, a provider should return false.

AddUsersToRoles — This method allows a developer to add one or more users to each of the roles specified in the roleNames parameter. A provider should check to see that each user specified in the usernames parameter exists and that each role specified in the roleNames parameter exists. If either of these checks fails, the provider should throw ProviderException. Also, if any user in the usernames parameter already belongs to one of the roles specified in the roleNames parameter, the provider should throw ProviderException. It is up to custom provider implementers to determine how the transactional semantics of adding multiple users to roles are handled. For example, the SqlRoleProvider performs all of the adds in a single transaction, or else it fails the entire chunk of work. However, not all authorization data stores will be able to use transactions.

RemoveUsersFromRoles — This companion method to AddUsersToRoles enables a developer to remove each user specified in the usernames parameter from each role specified in the roleNames parameter. The validation checks noted earlier for AddUsersToRoles should also be implemented by custom providers for this method. Although in the case of removal, if an attempt is made to remove a user from a role and the user does not already belong to that role, a ProviderException should be thrown. (This is the reverse case of the validation that providers should implement in AddUsersToRoles.) It is also up to a custom provider implementer as to whether any transactional semantics are enforced. For example, the SqlRoleProvider will either successfully perform all requested removals, or it will roll the entire chunk of work back.

GetUsersInRole — Returns a string array containing the names of all of the users that are currently members of the role specified by the roleName parameter. If the role is empty, the provider should just return an empty string. However, if a request if made to get the users for a nonexistent role then a ProviderException should be thrown.

GetAllRoles — Returns a string array containing a list of all of the roles currently defined in the data store. If no roles currently exist, then a provider should return an empty string instead.

FindUsersInRole — Returns a string array containing all of the users whose names match the search parameter specified by usernameToMatch that are members of the role specified by the roleName parameter. If no user matches are found, a custom provider should return an empty string array. However, if an attempt is made to search for users in a nonexistent role, a provider should throw a ProviderException. If the underlying data store supports wildcard characters for searches, a custom provider should allow these wildcard characters in the usernameToMatch parameter and pass the wildcard characters to the data store for further processing.

545

Chapter 13

WindowsTokenRoleProvider

Although we will cover the SQL and AzMan providers in their Chapters 14 and 15 respectively, WindowsTokenRoleProvider has very limited functionality, so one section should suffice for explaining how it works. As the name suggests, the provider works with a Windows security token. Although the provider can theoretically run in any trust level (IsUserInRole will work in Minimal trust), it is intended for use at Low trust or above. Unlike other providers, WindowsTokenRoleProvider does not internally check the trust level during initialization. The reason for this is that if the runtime environment can get a Windows security token for a user, the provider will work. If the runtime environment cannot get a token, then the provider fails. So, explicitly checking trust levels at initialization time is not necessary for this provider.

The token the provider uses is the value from the Token property on a WindowsIdentity object. In an ASP.NET environment, the provider gets a WindowsIdentity from the User property on the HttpContext when using Windows authentication. In non-ASP.NET environments, the provider will get the token from Thread.CurrentPrincipal. For both runtime environments, these are the only two places the provider will look; there is no facility for passing an arbitrary token to the provider. In other words, WindowsTokenRoleProvider works only with the credentials of the currently executing user.

The provider supports only the following two methods defined on RoleProvider:

IsUserInRole — There are two overloads for this method — the overload that is defined by the RoleProvider base class, as well as a special overload that accepts a System.Security

.Principal.WindowsBuildInRole value. Both overloads carry out an access check against the current WindowsIdentity. There is also no trust level restriction on this method — it will work in any of the ASP.NET trust levels.

GetRolesForUser — Note that inside of this method, the provider makes an explicit check for Low trust. Unlike IsUserInRole, if you new() up the provider and manually initialize it in Minimal trust, GetRolesForUser will still fail. Calling IsUserInRole, however, will succeed in Minimal trust because there is no explicit trust level check in that method.

The additional overload for IsUserInRole was added to the provider as a convenience. Internally, the additional overload just takes the current user’s WindowsIdentity, wraps it in a WindowsPrincipal, and then calls the IsInRole overload on WindowsPrincipal that accepts a WindowsBuiltInRole parameter. These steps are necessary because when you use WindowsTokenRoleProvider with RoleManager in ASP.NET, the principal object on the context is a RolePrincipal wrapping a WindowsIdentity (as opposed to a WindowsPrincipal wrapping a WindowsIdentity which is what happens when you use Windows authentication with an application and you have not enabled Role Manager).

There are two reasons why you might use WindowsTokenRoleProvider during development:

You may need to start developing an application that will use Role Manager with a different provider, but you currently are only running Windows authentication in your development environment. Because Role Manager is provider-based, you can start writing code while using the WindowsTokenRoleProvider and then later point swap in the provider that will be used in production.

Your application depends on fetching the group names for each authenticated user and then performing custom authorization checks and business logic against this set of group names. The

WindowsTokenRoleProvider’s GetRolesForUser method already does this for you, so you can make use of the provider to easily retrieve a string array of a user’s group membership. The Framework’s WindowsPrincipal object doesn’t provide this functionality.

546

Role Manager

If you use WindowsTokenRoleProvider on a site where the current user is considered anonymous (for example a dummy WindowsPrincipal and WindowsIdentity were initially placed on the

HttpContext), then IsUserInRole will always return false and GetRolesForUser will always return and empty string array. This behavior is consistent with the same values returned from RolePrincipal for anonymous users. The extra IsUserInRole overload will also return false because the WindowsIdentity that ASP.NET sets on the context for anonymous users is just a dummy WindowsIdentity that doesn’t belong to any built-in roles.

The internal logic of WindowsTokenRoleProvider compares the username parameter for IsUserInRole and GetRolesForUser to the string username of the current WindowsIdentity. This check is necessary because at the provider level there are no overloads that implicitly work with the current user. So, there is nothing preventing a developer from calling the provider’s methods passing in arbitrary usernames in a domain. However, because the purpose of WindowsTokenRoleProvider is to work with the credentials of only the currently authenticated user, the provider makes a quick sanity check to ensure that the username parameter passed to it actually matches the username associated with the currently authenticated user. If a mismatch exists, IsUserInRole will always return false, and GetRolesForUser always returns an empty string (that is, the same behavior as the anonymous user case).

Assuming that the current user is authenticated, and no mismatch occurs, the provider uses the security token of the user to carry out its work. For IsUserInRole, the provider converts the roleName parameter to a group security identifier (SID) and then checks the user’s security token to see if that group SID exists. Depending on the value of the roleName parameter (that is, a Windows group name), translating from a string to a SID with a call to LookupAccountName may be an expensive operation. The GetRolesForUser method can be even more expensive because, internally, it must perform a SID-to-name translation on each of the group SIDs contained in the user’s security token. This is a very important point to keep in mind because it means in complex domain environments a great deal of network traffic may be generated attempting to convert each user’s group SID into a name. If some of the groups a user belongs to sit in remote domains, GetRolesForUser can be a very long call.

For this reason, you should experiment with using cookie caching in conjunction with WindowsToken RoleProvider because after the role information is retrieved with a call to GetRolesForUser, cookie caching can prevent you from having to resolve groups SIDs to names for the duration of a user’s browser session. If it turns out that your users belong to so many groups that you can’t fit them into a cookie, you could disable cookie caching but still increase the maxCachedResult limit so that you can call ToEncryptedTicket and get back a non-null value. Instead of storing the encrypted string in a cookie you can use an alternative data store like a database. Although the earlier code sample for RoleManagerModule showed you how to use the GetRoles event to handle multiple providers, you could use the same approach to retrieve large encrypted tickets from a database and automatically reconstruct a RolePrincipal on each request during the GetRoles event.

When you call IsUserInRole, the value of the roleName you pass in must include the appropriate “domain” value of the role (that is, group) you are checking against. If the role is a well-known group (that is, a built in group or NT AUTHORITY–based group), then the roleName parameter may need to include NT AUTHORITY\\ or BUILTIN\\ before the group name: Note that the extra backslash is necessary for escaping this character in C#. If you leave out these specifiers, the IsUserInRole check will sometimes fail depending on the group you are checking. Always prepending either NT AUTHORITY\\ or BUILTIN\\ to the group name prevents any random problems. For domain groups, you always use the familiar syntax of “DOMAIN\\GROUPNAME”. If you are calling IsUserInRole for a local machine group though, you can use either the syntax “MACHINENAME\\GROUPNAME” or just “GROUPNAME”. Either syntax is interpreted as referencing a group in the local machine’s SAM database.

547

Chapter 13

As an example of this, the following code dumps the group membership for a user:

WindowsTokenRoleProvider wp =

(WindowsTokenRoleProvider)Roles.Providers[“AspNetWindowsTokenRoleProvider”];

string[] roles = wp.GetRolesForUser(User.Identity.Name); foreach (string r in roles)

Response.Write(“You belong to: “ + r + “<br/>”);

Aside from enabling Role Manager in configuration, and disallowing anonymous access to the test site, this code is all that is needed to start using a WindowsTokenRoleProvider. The reason is that a provider named AspNetWindowsTokenRoleProvider is defined by default in the machine.config file. As a result every application, ASP.NET and non-ASP.NET, has access to this provider instance assuming that the Role Manager feature has been enabled. Running this code results in the following output when I am logged in:

You belong to: CORSAIR\Domain Users

You belong to: Everyone

You belong to: TestLocalMachineGroup

You belong to: BUILTIN\Administrators

You belong to: BUILTIN\Users

You belong to: NT AUTHORITY\INTERACTIVE

You belong to: NT AUTHORITY\Authenticated Users

You belong to: NT AUTHORITY\This Organization

You belong to: LOCAL

You can see that on my test machine I belong to a variety of groups: one located in the CORSAIR domain, one that is clearly a local machine group (the TestLocalMachineGroup), and a number of other default and built-in groups. One thing to note about this output is that when the provider’s GetRolesForUser method returns the string names of groups located on the local machine, it always strips off the machine name. That is why the local machine group is shown as

TestLocalMachineGroup instead of MACHINE\TestLocalMachineGroup.

Remember that the return value from GetRolesForUser can be cached internally by a RolePrincipal — and that the internally cached set of roles in a RolePrincipal is used whenever you call IsInRole against the principal. From a completeness perspective, it would have been nice to store local machine groups that a user belongs to in both MACHINENAME\\GROUPNAME and GROUPNAME format. From a Windows API perspective both of these syntaxes are valid. However, if the provider did so, developers who depended on the count of roles returned from RolePrincipal.GetRoles would end up with twice the number of local machine groups because they would be stored twice.

As a compromise, the WindowsTokenRoleProvider strips the machine name off the local machine groups before returning the groups’ names from GetRolesForUser. The local machine names are not left prepended to group names because if you need to deploy an application across different staging and production environments, and you are using Role Manager (and potentially URL authorization), you probably don’t want to be incessantly changing the machine name string used in all of your authorization checks. So, it made more sense to strip off the machine name, thus making it easier to write applications that use local machine groups without needing to reconfigure group names each time the code is moved to a different machine.

548

Role Manager

You won’t encounter this behavior if you make authorization checks by calling IsUserInRole directly on the provider; when calling the provider’s IsUserInRole method directly you can use either syntax for local machine groups. However, if you depend on RolePrincipal.IsInRole for authorization checks you may run into this behavior and it may cause some unexpected problems. For example, using the TestLocalMachineGroup shown in the earlier results, the following URL authorization check when using Role Manager will fail:

<authorization>

<allow roles=”DEMOTEST\TestLocalMachineGroup”/> <deny users=”*”/>

</authorization>

This exact same check will succeed if you turn off Role Manager and just use Windows authentication instead. The WindowsPrincipal class never has to return roles as a string array, so when WindowsPrincipal.IsInRole is called, internally, it can test local machine groups using alternative syntaxes. The reason that the preceding check fails when using Role Manager is that RolePrincipal internally caches the string array returned by WindowsTokenRoleProvider.GetRolesForUser. And this array has only a string entry of TestLocalMachineGroup, so the string comparison against DEMOTEST\TestLocalMachineGroup fails. The following configuration though will succeed:

<authorization>

<allow roles=”TestLocalMachineGroup”/> <deny users=”*”/>

</authorization>

Now that the machine name is no longer part of the role name, the URL authorization check against RolePrincipal succeeds because there is a string match on just TestLocalMachineGroup. If you happen to be developing an application, and authorization checks against local machine groups suddenly fail when you switch from using only Windows authentication to using Windows authentication and Role Manager with the WindowsTokenRoleProvider, the likely culprits are the group names in your <authorization /> configuration element.

You can write some sample code that tries different ways of making role checks against the group names shown earlier that were returned from GetRolesForUser:

Response.Write(“This Organization: “ +

wp.IsUserInRole(User.Identity.Name, “This Organization”));

Response.Write(“This Organization: “ +

wp.IsUserInRole(User.Identity.Name, “NT AUTHORITY\\This Organization”));

This code performs an authorization check against the “This Organization” default group. The first check does not include “NT AUTHORITY\\” in the roleName parameter, while the second role check does include it. This code results in the following output:

This Organization: False

This Organization: True

Now clearly the user account belongs to this group, but in the first case, without “NT AUTHORITY\\” prepended to the roleName parameter, the group name was interpreted as a local machine group and thus the check failed. If you use a different well-known group that has been around for a while, you get different behavior:

549

Chapter 13

Response.Write(“Local administrators: “ + wp.IsUserInRole(User.Identity.Name, “Administrators”) + “<br/>”);

Response.Write(“Local administrators: “ +

wp.IsUserInRole(User.Identity.Name, “BUILTIN\\Administrators”) + “<br/>”);

This code uses two different variations for checking to see if the current user belongs to the local Administrators group. As you can see in the following output, both coding styles result in the same results:

Local administrators: True

Local administrators: True

Because of the subtle differences in behavior when performing authorization checks with special group names, it is easier to always prepend either “NT AUTHORITY\\” or “BUILTIN\\”. For local machine groups, you can be more lax in your coding style when calling IsUserInRole, as the following code snippet demonstrates:

Response.Write(“A local machine group: “ + wp.IsUserInRole(User.Identity.Name, “TestLocalMachineGroup”));

Response.Write(“A local machine group: “ + wp.IsUserInRole(User.Identity.Name, “DEMOTEST\\TestLocalMachineGroup”));

Both of these authorization checks will succeed:

A local machine group: True

A local machine group: True

With either syntax for the roleName parameter, the provider interprets the roleName as a local machine group. For groups that you create in a domain, though, you must always prepend the group name with the domain name as the next sample demonstrates:

Response.Write(“The domain Users group: “ + wp.IsUserInRole(User.Identity.Name, “CORSAIR\\Domain Users”));

Response.Write(“The domain Users group: “ + wp.IsUserInRole(User.Identity.Name, “Domain Users”));

The first call will succeed because the provider can successfully resolve this to the default “Domain Users” group that is present in every domain. However, the second check fails because the provider is looking for a group called “Domain Users” on the local machine.

The domain Users group: True

The domain Users group: False

To summarize all of this, keep the following rules in mind when calling the provider’s IsUserInRole method:

Always prepend “NT AUTHORITY\\” or “BUILTIN\\” when working with these types of groups.

Always prepend “DOMAINNAME\\” when working with nonlocal groups located somewhere in a domain.

550

Role Manager

Optionally, include “MACHINENAME\\” when working with local groups. See the following note, though.

If you are using RolePrincipal.IsInRole to make authorization checks against local machine groups (either in your code or indirectly by using URL authorization), make sure to always leave off the machine name from any local groups.

Summar y

The Role Manager feature gives you an easy way to create roles, assign users to roles, and then carry out various authorization checks based on these associations. As with the Membership feature, the Role Manager feature can be used to make authorization checks in both ASP.NET and non-ASP.NET environments. The static Roles class is used for performing authorization checks if your application only has a single default provider, though for more complex sites you will probably end up getting references to specific RoleProvider instances directly instead. If your site uses multiple providers, you will probably also need to hook the GetRoles event on RoleManagerModule so that your RolePrincipal instances are associated with the proper provider.

RoleManagerModule is the “magic” that exposes the user-to-role associations stored by providers as a RolePrincipal instance available from HttpContext.Current.User. You have to explicitly enable the Role Manager feature (it is off by default in machine.config) — but after you enable the feature RoleManagerModule automatically handles looking at the current user, and constructing a RolePrincipal that represents the current user. RolePrincipal can be used for declarative authorization checks such as URL authorization as well as code-based authorization checks using IPrincipal.IsInRole. Because Role Manager has no hard-coded dependencies on a specific type of authenticated identity, the RolePrincipal can wrap authenticated identities obtained from Windows authentication, forms authentication, or any custom authentication mechanism you may author.

For performace reasons, RolePrincipal will fetch all of a user’s roles the first time the roles are needed, and it will then cache that information internally for the duration of a page request. You can optionally enable caching this information in a cookie so that on subsequent page requests RolePrincipal will initialize its cached role information from the cookie as opposed to calling the provider. The maxCachedResults configuration setting partially determines how many roles RolePrincipal is willing to stuff into a cookie. RoleManagerModule also enforces a maximum 4096 character limit on the size of a role cache cookie, so you will need to experiment with cookie caching in your applications to see if you can use it effectively.

One of the default providers supplied with the Framework is WindowsTokenRoleProvider. This provider is very basic because it only implements the IsUserInRole and GetRolesForUser methods, and these methods only work with the currently authenticated user. However, the GetRolesForUser method can be very handy for developers who want to get all of the roles that a domain user belongs to.

551