Section 11.3. Add Roles to ASP.NET Accounts


11.3. Add Roles to ASP.NET Accounts

You can assign a set of permissions to a group of people. You do so in two steps: first, you assign permissions to a role , and then you assign users to the roles you've created. Any given user may be in more than one role (for example, administrator and manager). The permissions you assign to each role may determine access to a page, or it may determine the content of a given page displayed to members of that role.

Creating a New Application with RolesTo demonstrates how to create roles and assign users to those roles; you'll need to create a new application, setting the appropriate IIS configuration. In the previous exercise, you created the directory for your application before you created the application itself. To see that there is more than one way to accomplish creating the relationship between physical and virtual directories, let's reverse the order.

Start by creating a new web site from within VS2005 and naming it ASPSecurityRoles .

Find the directory in which the Default.aspx page is held by clicking on the page in Solution Explorer and looking at the Properties window. Use the IIS administrator to create a virtual directory and call it ASPSecurityRoles that points to that physical directory. Right-click on the virtual directory and select properties.

Click the ASP.NET tab and then click the Edit Configuration button (as you did in the previous exercise). Again, click the Authentication tab and set Forms authentication, but this time check the Role management enabled checkbox, as shown in Figure 11-22.

When you click OK and close the configuration dialogs, you'll find that a web.config file has been added to the directory, with a system.web element that defines the authentication mode and the membership provider and enables roleManager .

 <system.web>             <authentication mode="Forms" />             <membership defaultProvider="AspNetSqlMembershipProvider" />             <roleManager enabled="True" defaultProvider="AspNetSqlRoleProvider" />         </system.web> 

Figure 11-22. Role management enabled

This time, the roleManager element has been added and its enabled attribute has been set to true .

You need to add the pages you had in the previous exercise to this one. The preferred way is to choose the menu choice WebSite CopyWebsite command.

When you click the Connect to button, as shown in Figure 11-23 (see the cursor), the Open Web Site dialog opens allowing you to navigate to the folder for the original exercise, which we called Security. After you pick the folder, the Remote web site is filled in, as shown in Figure 11-24.

Figure 11-23. Connect to previous web site

Figure 11-24. Copy Web Site

As you can see, the wizard has detected that you have a Default.aspx that is not in the original (i.e., remote) site, and it has found a conflict in that both sites have a web.config . You'll want to copy over all the non-conflicted files. To do that, highlight the files you want to copy, using standard Windows selection techniques and click the left- facing arrow, as shown in Figure 11-25.

Figure 11-25. Copy selected files to new site

After copying the files, delete the Default.aspx and Default.aspx.cs files since you won't need them. You can do this right from the Copy Web page by highlighting the files in the Source Web Site list, right-clicking, and selecting Delete.

Check your web.config file and make sure that you have the line:

 <membership defaultProvider="AspNetSqlMembershipPRovider" /> 

from the previous example.


Add a third hyperlink to the Logged In Template on the Welcome page, with the text "Manage Roles" that will link to a new ManageRoles.aspx page . (Click on End Template Editing, as shown in Figure 11-26.)

Figure 11-26. End Editing Logged In Template

The Manage Roles page, ManageRoles.aspx , has a somewhat complex layout since it must display the list of roles and the list of users supported by your site, as well as which users have been assigned which roles. The page is shown in Figure 11-27. The controls are listed in Table 11-2 and the complete .aspx listing is shown in Example 11-2 though it may be easier to download this code from the O'Reilly or Liberty Associates web sites.

Figure 11-27. ManageRoles.aspx

Table 11-2. Controls in ManageRoles.aspx

Control name

Control type

Control text

linkHome

Hyperlink

Home Page

Msg

Label

 

RolesListBox

ListBox

 

btnAddUsersToRole

Button

Add User(s) to Role

btnCreateRole

Button

Create new Role

pnlCreateRole

Panel

 

Label2

Label

New Role:

txtNewRole

TextBox

 

btnAddRole

Button

Add

UsersInRoleGrid

GridView

 

Example 11-2. Manage Roles page
 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="ManageRoles.aspx.cs" Inherits="ManageRoles_aspx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/ DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server">    <title>Manage Roles</title> </head> <body>    <form id="form1" runat="server">   <h3>Role Membership     <asp:HyperLink ID="linkHome" Runat="server" NavigateUrl="Welcome.aspx">Home page</asp: HyperLink>   </h3>   <asp:Label id="Msg" ForeColor="maroon" runat="server" /><BR>   <table CellPadding="3" border="0">    <tr>     <td valign="top">Roles:</td>     <td valign="top" style="width: 186px"><asp:ListBox id="RolesListBox"         runat="server" Rows="8" AutoPostBack="True">     </asp:ListBox></td>     <td valign="top">Users:</td>     <td valign="top"><asp:ListBox id="UsersListBox" DataTextField="Username"       Rows="8" SelectionMode="Multiple" runat="server" /></td>     <td valign="top" visible="false">         <table>         <tr>            <td>               <asp:Button Text="Add User(s) to Role" id="btnAddUsersToRole"               runat="server" OnClick="AddUsers_OnClick" />            </td>         </tr>         <tr>            <td>               <asp:Button Text="Create new Role" id="btnCreateRole"               runat="server" OnClick="CreateRole_OnClick"               Width="170px" Height="24px" />            </td>         </tr>         <tr>         <td>        <asp:Panel ID="pnlCreateRole" Runat="server" Width="259px"         Height="79px" Visible="False" BackColor="#E0E0E0">           <br />           &nbsp;&nbsp;           <asp:Label ID="Label2" Runat="server" Text="New Role:"            Width="72px" Height="19px"/>           <asp:TextBox ID="txtNewRole" Runat="server"/>&nbsp;<br />           &nbsp;&nbsp;<br />           &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;           &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;           <asp:Button ID="btnAddRole" Runat="server"             Text="Add" OnClick="btnAddRole_Click"             Width="64px" Height="24px" /><br />        </asp:Panel>         </td>         </tr>         </table>     </td>    </tr>    <tr>     <td valign="top">Users In Role:</td>     <td valign="top" style="width: 186px">         <asp:GridView runat="server" CellPadding="4" id="UsersInRoleGrid"                         AutoGenerateColumns="false" Gridlines="None"                         CellSpacing="0"         OnRowCommand="UsersInRoleGrid_RemoveFromRole">                <HeaderStyle BackColor="navy" ForeColor="white" />                <Columns>                 <asp:TemplateField HeaderText="User Name">                   <ItemTemplate>                    <%# Container.DataItem.ToString(  ) %>                   </ItemTemplate>                 </asp:TemplateField>                 <asp:ButtonField Text="Remove From Role" ButtonType="Link" />                </Columns>         </asp:GridView>      </td>    </tr>   </table>   </form> </body> </html> 

This page is not designed to be pretty, just useful. It is based on a demonstration .aspx page provided by Microsoft with Beta software.


The code-behind page must implement five event handlers:

  • Page_Load

  • AddUsers_OnClick (adding users to roles)

  • UsersInRoleGrid_RemoveFromRole (removing users from roles)

  • CreateRole_OnClick (opening panel to create a new role)

  • btnAddRole_Click (adding new role)

To accomplish this, your class will declare three member variables :

  • A string array named rolesArray

  • A string array named usersInRole

  • An instance of MembershipUserCollection named users

The MembershipUserCollection is defined by the framework to hold MembershipUser objects (surprise!). A MembershipUser object, in turn , is defined by the framework to represent a single user in the membership data store (in this case, the tables created in SqlServerExpress ). This class exposes information about the user such as the user's email address, and methods such as those needed to change or reset the user's password.

The complete code behind is shown in Example 11-3 and analyzed in detail immediately afterward.

Example 11-3. ManageRoles.aspx Code Behind
 using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class ManageRoles_aspx : System.Web.UI.Page {     // Page events are wired up automatically to methods     // with the following names:     // Page_Load, Page_AbortTransaction, Page_CommitTransaction,     // Page_DataBinding, Page_Disposed, Page_Error, Page_Init,     // Page_Init Complete, Page_Load, Page_LoadComplete, Page_PreInit     // Page_PreLoad, Page_PreRender, Page_PreRenderComplete,     // Page_SaveStateComplete, Page_Unload     private string[] rolesArray;     private string[] usersInRole;     MembershipUserCollection users;     protected void Page_Load(object sender, EventArgs e)     {         //if ( User.IsInRole("Manager") == false )         //{         //    Response.Redirect("NoPrivs.aspx");         //}         Msg.Text = string.Empty;         if ( ! IsPostBack )         {             rolesArray = Roles.GetAllRoles(  );             RolesListBox.DataSource = rolesArray;             RolesListBox.DataBind(  );             users = Membership.GetAllUsers(  );             this.UsersListBox.DataSource = users;             this.UsersListBox.DataBind(  );         }         if ( RolesListBox.SelectedItem != null )         {             usersInRole = Roles.GetUsersInRole                   (this.RolesListBox.SelectedItem.Value);             UsersInRoleGrid.DataSource = usersInRole;             UsersInRoleGrid.DataBind(  );         }     }     protected void AddUsers_OnClick(object sender, EventArgs e)     {         if (RolesListBox.SelectedItem == null)         {             this.Msg.Text = "Please select a role.";             return;         }         if (UsersListBox.SelectedItem == null)         {             Msg.Text = "Please select one or more users";             return;         }         int sizeOfArray = UsersListBox.GetSelectedIndices(  ).Length;         string[] newUsers = new string[sizeOfArray];         //for (int i = 0; i < newUsers.Length; i++)         //{         //    newUsers[i] =         //        UsersListBox.Items[UsersListBox.GetSelectedIndices(  )[i]].Value;         //}        // get the array of selected indices from the (multiselect) list box        int[] selectedIndices = UsersListBox.GetSelectedIndices(  );        for ( int i = 0; i < newUsers.Length; i++)         {             // get the selectedIndex that corresponds to the counter[i]             int selectedIndex = selectedIndices[i];             //get the ListItem in the UserListBox Items collection at that offset             ListItem myListItem = UsersListBox.Items[selectedIndex];             //get the string that is that ListItem's value property             string newUser = myListItem.Value;             //add that string to the newUsers collection of string             newUsers[i] = newUser;         }         // Add users to the selected role         Roles.AddUsersToRole(newUsers, RolesListBox.SelectedItem.Value);         usersInRole = Roles.GetUsersInRole(RolesListBox.SelectedItem.Value);         UsersInRoleGrid.DataSource = usersInRole;         UsersInRoleGrid.DataBind(  );     }     protected void CreateRole_OnClick(object sender, EventArgs e)     {         this.pnlCreateRole.Visible = true;     }     protected void btnAddRole_Click(object sender, EventArgs e)     {         if (txtNewRole.Text.Length > 0 )         {             string newRole = txtNewRole.Text;             if ( Roles.RoleExists(newRole) == false )             {                 Roles.CreateRole(newRole);                 rolesArray = Roles.GetAllRoles(  );                 this.RolesListBox.DataSource = rolesArray;                 this.RolesListBox.DataBind(  );             }         }         txtNewRole.Text = string.Empty;         pnlCreateRole.Visible = false;     }     protected void UsersInRoleGrid_RemoveFromRole(         object sender, GridViewCommandEventArgs e)     {         int index = Convert.ToInt32(e.CommandArgument);         DataBoundLiteralControl theControl = (DataBoundLiteralControl)                (UsersInRoleGrid.Rows[index].Cells[0].Controls[0]);         string userName = theControl.Text;         Roles.RemoveUserFromRole(userName, RolesListBox.SelectedItem.Value);          usersInRole = Roles.GetUsersInRole(RolesListBox.SelectedItem.Value);          UsersInRoleGrid.DataSource = usersInRole;         UsersInRoleGrid.DataBind(  );     } } 

Here's how the code works. The logic begins with the CreateRole button's onClick event handler, which makes the CreateRole panel visible:

 protected void CreateRole_OnClick(object sender, EventArgs e)     {        this.pnlCreateRole.Visible = true;     } 

The purpose of this is to make the panel visible, which contains a text box for the user to enter a new role, and an Add button, as shown in Figure 11-28.

Figure 11-28. Create new Role

When you click the Add button in the panel, the btnAddRole_Click event handler is called:

 protected void btnAddRole_Click(object sender, EventArgs e)     {        if (txtNewRole.Text.Length > 0 )        {           string newRole = txtNewRole.Text;           if ( Roles.RoleExists(newRole) == false )           {  Roles.CreateRole(newRole);  rolesArray = Roles.GetAllRoles(  );              this.RolesListBox.DataSource = rolesArray;              this.RolesListBox.DataBind(  );           }        }        txtNewRole.Text = string.Empty;        pnlCreateRole.Visible = false;     } 

You check to ensure text is in the New Role text box, and then you check to ensure the role does not exist. If it does not, you will create the new role using the static CreateRole method of the Roles class, provided by the framework.

You do not need an instance of Roles to call CreateRole because CreateRole is static.


You get all the roles by calling the static method GetAllRoles and store the roles in the member array rolesArray , to which you bind the list box. When the role is added, the text box is cleared and the panel is made invisible.

Run the application. If you are starting with a new database, add some users. Next, click Add Roles and add a couple of roles. Click on a role (to highlight it) and one or more users (to highlight the users), and then click Add User(s) to Role. This invokes the AddUsers_OnClick event handler.

You first check to ensure that a role has been selected:

 protected void AddUsers_OnClick(object sender, EventArgs e)     {  if (RolesListBox.SelectedItem == null)        {           this.Msg.Text = "Please select a role.";           return;        }  

and that at least one user has been selected:

 if (UsersListBox.SelectedItem == null)     {        Msg.Text = "Please select one or more users";        return;     } 

Create an array to hold the users to be added:

 int sizeOfArray = UsersListBox.GetSelectedIndices(  ).Length;     string[] newUsers = new string[sizeOfArray]; 

and iterate through the users, retrieving each selected user's name:

 for (int i = 0; i < newUsers.Length; i++)     {        newUsers[i] =           UsersListBox.Items[UsersListBox.GetSelectedIndices(  )[i]].Value;     } 

The statement in the for loop is somewhat complicated. The best way to understand it is to rewrite the for loop using interim variables, like this:

 for ( int i = 0; i < newUsers.Length; i++)     {        // get the array of selected indices from the (multiselect) list box        int[] selectedIndices = UsersListBox.GetSelectedIndices(  );        // get the particular selected selectedIndex that corresponds        // to the counter[i]        int selectedIndex = selectedIndices[i];        //get the ListItem in the UserListBox Items collection at that offset        ListItem myListItem = UsersListBox.Items[selectedIndex];        //get the string that is that ListItem's value property        string newUser = myListItem.Value;        //add that string to the newUsers collection of string        newUsers[i] = newUser;     } 

The advantage of the interim variables is that you can set break points on them and see what their value is, and you can more easily document the code. The disadvantage is minimal, but many programmers ( especially those from the "C" culture) prefer the terser version aesthetically.

Call the static AddUsersToRole on the Roles class, passing in the array of user names and the role you want these users added to. You then rebind the users who are in that role to the UsersInRoleGrid .

 Roles.AddUsersToRole(newUsers, RolesListBox.SelectedItem.Value);     usersInRole = Roles.GetUsersInRole(RolesListBox.SelectedItem.Value);     UsersInRoleGrid.DataSource = usersInRole;     UsersInRoleGrid.DataBind(  ); 

The results are shown in Figure 11-29.

Add each user to one or more roles, and when you are done, you'll be ready to test if these roles have any effect. To do so, stop the application and edit the Welcome page. Click the smart tag for the LoginView and click Edit RoleGroups, as shown in Figure 11-30.

Add a couple of the roles you created earlier, as shown in Figure 11-31.

Switch to Source view on your Welcome.aspx page. You'll see that a new section has been added to the LoginView :

 <RoleGroups>         <asp:RoleGroup Roles="Supervisor">         </asp:RoleGroup>         <asp:RoleGroup Roles="Manager">         </asp:RoleGroup>     </RoleGroups> 

Figure 11-29. Adding users to roles

Figure 11-30. Edit RoleGroups

Figure 11-31. RoleGroup Collection Editor

You can now control what content is seen by members of each role by using contentTemplate elements. You add these between the opening and closing tag of each role, like this:

 <RoleGroups>         <asp:RoleGroup Roles="Manager">            <ContentTemplate>              Welcome              <asp:LoginName ID="LoginNameManager" Runat="server" />              You are a manager            </ContentTemplate>         </asp:RoleGroup>         <asp:RoleGroup Roles="Supervisor">             <ContentTemplate>                 Supervisor tools go here             </ContentTemplate>         </asp:RoleGroup>     </RoleGroups> 

Run the application. In the example shown above, I added jliberty to the Supervisor role, but not milo , who is in the Owner role. When I log in as jliberty , I see the words "Supervisor tools go here", but if I log in as milo , I do not see those words. I see the words dictated by the content template associated with owners (that is, the default logged in template since I don't have a section for Owners ).

11.3.1. Using the Web Administrator Tool to Set Up Roles

Rather than using the IIS virtual directory properties for your application, you can open the Web Site Administration Tool and click on the security tab.

Stop the application. On the Visual Studio menu bar, click Website Asp.NET Configuration, and then choose the Security tab. Click Enable roles, as shown in Figure 11-32.

After you click on Enable roles, the only change will be that the string that currently says "Roles are not enabled" will change to "Existing Roles: 0" and the link will change to "Disable roles".

11.3.2. Restricting Access to Pages Based on Roles

There are two ways to restrict access to a page based on membership in a role. The first is to test if the logged-in user is in a particular role, using the User.IsInRole method:

 Bool isManager = User.IsInRole("Manager"); 

You might redirect the user to an error page if the user is not in the role. As an example, let's add code that blocks non-managers from linking to the Manage Roles page. To do this, add a test in the Page_Load method of ManageRoles.aspx.cs:

  if (User.IsInRole("Manager") == false)     {         Response.Redirect("NoPrivs.aspx");     }  

If the user is not in the role of "Manager", the user is redirected to the page NoPrivs.aspx . That page can display an error message and then allow the user to take other actions. A very simple example is shown in Figure 11-33.

Figure 11-32. Enabling roles through Web Site Administration wizard

Figure 11-33. NoPrivs.aspx

The code for the button on the NoPrivs.aspx.cs page is simple:

 protected void btnHome_Click(object sender, EventArgs e)     {         Response.Redirect("Welcome.aspx");     } 

The second way to restrict access to a set of pages is by adding an authorization section to a web.config file. You can place this section in a subdirectory to control access to all files in that subdirectory and all of its subdirectories, and you can use the location element to control access to specific files.

 <authorization>       <deny users='?' />       <allow roles='Manager' />       <deny users='*' />     </authorization> 

The first line ( deny users = '?' ) prohibits access to anyone who is not logged in. The second line ( allow roles='Manager' ) allows access to anyone in the Manager role, and the final line ( deny users='*' ) disallows anyone but is overridden by the allow roles statement.



Programming ASP. NET
Programming ASP.NET 3.5
ISBN: 0596529562
EAN: 2147483647
Year: 2003
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net