Role Manager
The ASP.NET Role Manager feature is designed to simplify managing roles and the users that belong to those roles. After authentication, when Role Manager is enabled, ASP.NET will automatically add the users to the role(s) he or she belongs to. When ASP.NET authorization occurs, the user is either allowed or denied access to the requested resource based on his or her role(s). [15]
URL-based role authorization is a feature of ASP.NET 1.0. We can control what users are allowed to access by specifying access permissions within configuration (see Listing 6.15).
Example 6.15. Configuring Roles
<configuration> <system.web> <authorization> <deny users="?" /> </authorization> </system.web> <location path="PremiumContent.aspx"> <system.web> <authorization> <allow roles="Premium" /> <deny users="*" /> </authorization> </system.web> </location> </configuration>
The above web.config file could be added to any application. It states that anonymous users are denied access to all resources. Furthermore, only users in the role Premium are allowed access to PremiumContent.aspx. All other users are denied access.
Before we can control access to resources through roles, we need to create some roles and then add users to them. Let's look at how we can do this with the new Role Manager feature.
Setting Up Role Manager
Similar to Membership, Role Manager relies on a provider to store data and thus allow us to create roles and associations between users and their roles. [16] Unlike Membership, Role Manager is not enabled by default. Therefore, before we can use the Role Manager API, we need to enable the feature in its configuration settings.
Similar to Membership configuration settings, Role Manager configuration settings are defined in machine.config and can be overridden or changed within an application's web.config file. Listing 6.16 shows a sample web.config enabled for Role Manager.
Example 6.16. Configuring Role Manager
<configuration> <system.web> <roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPXROLES" cookieTimeout="30" cookiePath="/" cookieRequireSSL="false" cookieSlidingExpiration="true" cookieProtection="All" defaultProvider="AspNetAccessProvider" > <providers> <add name="AspNetAccessProvider2" type="System.Web.Security.AccessRoleProvider, System.Web" connectionStringName="AccessFileName" applicationName="/" description="Stores and retrieves roles data from the local Microsoft Access database file" /> </providers> </roleManager> </system.web> </configuration>
Table 6.3 shows an explanation of the various configuration settings.
Table 6.3. Role Manager Configuration Settings
Attribute |
Default Value |
Description |
---|---|---|
enabled |
false |
Controls whether or not the Role Manager feature is enabled. By default it is disabled because enabling breaks backward compatibility with ASP.NET 1.0. |
cacheRolesInCookie |
true |
Allows for the roles to be cached within an HTTP cookie. When the roles are cached within a cookie, a lookup for the roles associated with the user does not have to be done through the provider. |
cookieName |
.ASPXROLES |
Sets the name of the cookie used to store the roles when cookies are enabled. |
cookieTimeout |
30 |
Sets the period of time for which the cookie is valid. If cookieSlidingExpiration is true, the cookie timeout is reset on each request within the cookieTimeout window. |
cookiePath |
/ |
Sets the path within the application within which the cookie is valid. |
cookieRequireSSL |
false |
Specifies whether or not the cookie must be sent over an SSL channel. |
cookieSlidingExpiration |
true |
Sets the cookie timeout. When true, the cookie timeout is automatically reset each time a request is made within the cookieTimeout window, effectively allowing the cookie to stay valid until the user's session is complete. |
cookieProtection |
All |
Controls how the data stored within the cookie is secured. |
defaultProvider |
string |
Sets the friendly name of the provider to use for roleManager. By default this is AspNetAccessProvider. |
Now that we've seen how to configure the settings of Role Manager, let's create some roles.
Creating Roles
The Roles API supports a single method for creating roles:
CreateRole(rolename As String)
This API is used to create the friendly role name, such as Administrators, used to control access to resources. Listing 6.17 provides sample code for creating roles in an ASP.NET page.
Example 6.17. Creating and Viewing Roles
<script runat="server"> Public Sub Page_Load (sender As Object, e As EventArgs) If Not Page.IsPostBack Then DataBind() End If End Sub Public Sub CreateRole_Click(sender As Object, e As EventArgs) Try ' Attempt to create the role Roles.CreateRole (Rolename.Text) Catch ex As Exception ' Failed to create the role Status.Text = ex.ToString() End Try DataBind() End Sub Public Overrides Sub DataBind() RoleList.DataSource = Roles.GetAllRoles() RoleList.DataBind() End Sub </script> <html> <body style="FONT-FAMILY: Verdana"> <H1>Create Role</H1> Below is a list of the current roles: <asp:datagrid id="RoleList" runat="server" /> <hr /> <form runat="server"> Rolename to create: <asp:TextBox id="Rolename" runat="server" /> <asp:button Text="Create Role" OnClick="CreateRole_Click" runat="server"/> </form> <font color="red" size="6"> <asp:Label id="Status" runat="server"/> </font> </body> </html>
This code sample allows us to enter a role name, which is then created using the Roles.CreateRole() API. If the role already exists, an exception is thrown. Finally, all of the available roles are enumerated through a DataGrid using the Roles.GetAllRoles() API, discussed shortly.
Now that we can create roles, let's add some users to the roles.
Adding Users to Roles
Membership and Role Manager are not rigidly coupled. They are designed to work together, but you do not have to use one to use the other. Both use the authenticated username as the only shared piece of data. For example, it is possible to add a user to a role even if the user is not created through the Membership system. [17]
Adding users to roles is accomplished by using the following methods supported by the Roles API:
AddUserToRole(username As String, rolename As String) AddUserToRoles(username As String, rolenames() As String) AddUsersToRole(usernames() As String, rolename As String) AddUsersToRoles(usernames() As String, rolenames() As String)
These various methods allow for adding users to roles in bulk or individually. Listing 6.18 demonstrates Roles.AddUserToRole().
Example 6.18. Adding Users to Roles
<%@ Page Language="VB" %> <script runat="server"> Public Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) DataBind() End Sub Public Sub AddUserToRole_Click(sender As Object, e As EventArgs) Roles.AddUserToRole(Username.Text, _ RoleList.SelectedItem.Value) DataBind() End Sub Public Overrides Sub DataBind() RoleList.DataSource = Roles.GetAllRoles() RoleList.DataBind() End Sub </script> <html> <body style="FONT-FAMILY: Verdana"> <H1>Add User to Role</H1> <form runat="server"> User: <asp:TextBox id="Username" runat="server" /> Role to add user to: <asp:DropDownList id="RoleList" runat="server" /> <asp:button Text="Add User To Role" OnClick="AddUserToRole_Click" runat="server"/> </form> <font size="6" color="Red"> <asp:Label id="StatusCheck" runat="server"/> </font> </body> </html>
This code sample data binds the results of Roles.GetAllRoles() to a DropDownList control and then allows us to enter a user to add to a role. [18] The Roles.AddUserToRole() API is then used to add the user to the role.
When adding multiple users to roles or a user to multiple roles, the addition occurs within the context of a transaction. Either all updates succeed or all fail.
We can now use another Roles API to determine to what roles a particular user belongs.
Returning a User's Roles
To return a list of the roles to which a user belongs, we can simply use one of the following APIs:
GetRolesForUser() As String() GetRolesForUser(username As String) As String() GetUsersInRole(rolename As String) As String()
The Roles.GetRolesForUser() method will return a string array of all the roles that the current user is in. The overloaded version of this method that accepts a username parameter allows us to specify for which user we want a listing of roles. The last method, Roles.GetUsersInRole(), allows us to get a string array listing of usernames that belong to the specified role.
Listing 6.19 demonstrates the overloaded version of Roles.GetRolesForUser().
Example 6.19. Finding the Roles for a User
<script runat="server"> Public Sub GetRolesForUser_Click(sender As Object, e As EventArgs) RolesForUser.DataSource = Roles.GetRolesForUser(Username.Text) RolesForUser.DataBind() End Sub </script> <html> <body style="FONT-FAMILY: Verdana"> <H1>Roles user is in</H1> <hr /> <form runat="server"> Username: <asp:TextBox id="Username" runat="server" /> <asp:button Text="Roles User Is In" OnClick="GetRolesForUser_Click" runat="server"/> </form> User is in roles: <asp:DataGrid runat="server" id="RolesForUser" /> </body> </html>
This code sample simply asks for the name of a user. When the page is posted back, the Roles.GetRolesForUser() API is called, passing in the name of the specified user. The results are then data-bound to a DataGrid.
Checking Whether a User Is in a Role
Access to resources can be controlled by which roles the user belongs to. As shown in the beginning of this section, it is possible to control access to URLs based on settings made in the configuration file. In addition to this declarative security access control, we can also perform programmatic checks for the role the user belongs to.
ASP.NET 1.1 allowed for programmatic checks for determining whether the user was in a role through User.IsInRole(username as String); the result of this method returned True or False. The Roles API supports a similar Roles.IsUserInRole(rolename As String) API:
IsUserInRole(rolename As String) As Boolean IsUserInRole(username As String, rolename As String) As Boolean
Now that we've seen how to add users to roles and check whether users are in a particular role, let's look at how we can remove a user from a role.
Removing Users from Roles
Similar to the methods used for adding a user to roles, we have four different methods for removing users from roles:
RemoveUserFromRole(username As String, rolename As String) RemoveUserFromRoles(username As String, rolenames() As String) RemoveUsersFromRole(usernames() As String, rolename As String) RemoveUsersFromRoles(usernames() As String, rolenames() As String)
Again, similar to adding users to roles, when the process of removing users from roles is transacted, either all succeed or all fail.
Deleting a Role
Roles can be deleted easily by using the Roles.DeleteRole(rolename As String) method. Listing 6.20 shows a sample ASP.NET page that demonstrates how to use this API.
Example 6.20. Deleting a Role
<%@ Page Language="VB" %> <script runat="server"> Public Sub Page_Load() If Not Page.IsPostBack Then DataBind() End If End Sub Public Sub DeleteRole_Click(sender As Object, e As EventArgs) Try Roles.DeleteRole(Rolename.Text) Catch ex As Exception StatusCheck.Text = "There was an error removing the role(s)" End Try DataBind() End Sub Public Overrides Sub DataBind() RoleList.DataSource = Roles.GetAllRoles() RoleList.DataBind() End Sub </script> <html> <body style="FONT-FAMILY: Verdana"> <H1>Delete Role</H1> Below is a list of the current roles: <asp:datagrid id="RoleList" runat="server" /> <hr /> <form runat="server"> Rolename to delete: <asp:TextBox id="Rolename" runat="server" /> <asp:button Text="Delete Role" OnClick="DeleteRole_Click" runat="server"/> </form> <font color="red" size="6"> <asp:Label id="StatusCheck" runat="server"/> </font> </body> </html>
This code lists all the available roles by binding the result of Roles.GetAllRoles() to a DataGrid. It then allows for a specific role to be named and deleted using the Roles.DeleteRole() method, as shown in Figure 6.14. There is a second form of DeleteRoles that takes two parameters: the first is the role name and the second a Boolean to indicate whether an exception should be thrown if the role being deleted has users.
Figure 6.14 Deleting roles
Role Manager uses a provider to write back to and read from a data store in which the roles and user-to-role mapping is done. Rather than reading/writing to this database on each requestsince a list of the roles the user belongs to must be obtaineda cookie can optionally be used to cache roles, as described in the next subsection.
Role Caching
Role caching is a feature of Role Manager that enables user-to-role mappings to be performed without requiring a lookup to the data store on each request. [19] Instead of looking up the user-to-role mapping in the data store, the roles the user belongs to are stored, encrypted, within an HTTP cookie. If the user does not have the cookie, a request is made against the provider to retrieve the roles the user belongs to. The roles are then encrypted and stored within a cookie. On subsequent requests the cookie is decrypted and the roles obtained from the cookie.
Internally, in cases where there are more roles than can fit in the cookie, the cookie is marked as an incremental role cookie. That is, the cookie stores as many roles as possible but likely not all the roles. When role checking is performed, and the user is not in one of the roles being checked for, ASP.NET will call the Roles API and check whether the user belongs to that role. If not, access is denied. If the user is in the role and the role is not currently stored in the cookie, the last role stored within the cookie is removed and the requested role is added. Thereby, in cases where the user has more roles than can fit in the cookie, the cookie over time will contain a list of the most frequently accessed roles.