Applying Role Authentication for Groups of Users

With user authentication, after a visitor is authenticated, they have full access to all the pages on a site. In many cases, this is not adequate. Most sites have administrative interfaces, and others have different areas for people with special privileges, such as moderators for discussion forums.

The solution to this problem is to implement role authentication in the Forms-Based Authentication process for the site. Role authentication is a capability that should be implemented in addition to the existing user authentication method on a site. There are only a few extra modifications to get this working. This section extends the user authentication methods from the last section to support role authentication. As shown in Listing 13.5 and Listing 13.6, the code is very much the same.

Listing 13.5 Login Page for Role Authentication (LOGIN.ASPX)
 <%@ Page language="c#" Debug="true" Codebehind="Login.aspx.cs" AutoEventWireup="false" Inherits="Roles.Login" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>   <head>     <title></title>     <meta name="GENERATOR" content="Borland ASP.NET Designer for c# Package Library 7.1">   </head>   <body ms_positioning="GridLayout">     <form runat="server">       <p>         <asp:label  runat="server" font-bold="True">Please Enter         User Name and Password:</asp:label>       </p><asp:label  runat="server">User Name:</asp:label>       <asp:textbox  runat="server"></asp:textbox><br>       <asp:label  runat="server" width=       "74px">Password:</asp:label>       <asp:textbox  runat="server" width="156px"                    textmode="Password"></asp:textbox><br>       <br>       <asp:checkbox  runat="server" text="Remember Me">       </asp:checkbox><br>       <br>       <p>         <asp:label  runat="server"></asp:label>       </p>       <asp:button  runat="server" text="Log In"></asp:button>     </form>   </body> </html> 
Listing 13.6 Code-Behind File for Login Page (Login.aspx.cs)
 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace Roles {    /// <summary>    /// Summary description for WebForm1.    /// </summary>    public class Login : System.Web.UI.Page    {       protected System.Web.UI.WebControls.Label lblInfo;       protected System.Web.UI.WebControls.Label lblUserName;       protected System.Web.UI.WebControls.TextBox txtUserName;       protected System.Web.UI.WebControls.Label lblPassword;       protected System.Web.UI.WebControls.TextBox txtPassword;       protected System.Web.UI.WebControls.CheckBox cbxRememberMe;       protected System.Web.UI.WebControls.Label lblMessage;       protected System.Web.UI.WebControls.Button btnLogin;       private void Page_Load(object sender, System.EventArgs e)       {          // Put user code to initialize the page here       }       #region Web Form Designer generated code       override protected void OnInit(EventArgs e)       {          //          // CODEGEN: This call is required by the ASP.NET Web Form Designer.          //          InitializeComponent();          base.OnInit(e);       }       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {          this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);          this.Load += new System.EventHandler(this.Page_Load);       }       #endregion       private void btnLogin_Click(object sender, System.EventArgs e)       {          if (FormsAuthentication.Authenticate(              txtUserName.Text, txtPassword.Text))          {             FormsAuthentication.RedirectFromLoginPage(                txtUserName.Text, cbxRememberMe.Checked);          }          else          {             lblMessage.Text = "Invalid Login!";          }       }    } } 

As shown in Listing 13.5 and Listing 13.6, role authentication doesn't change the implementation of individual Web pages. Rather, it is controlled via application events and configuration files. Configuration files are set with roles attributes in the child elements of the authorization element. Listing 13.7 shows a web.config file configured for role authentication.

Listing 13.7 Authentication and Authorization for Roles (web.config)
 <?xml version="1.0" encoding="utf-8" ?> <configuration>   <system.web>     <!--  Portions omitted for clarity.  -->     <authentication mode="Forms">       <forms loginUrl="Login.aspx">         <credentials passwordFormat="Clear">           <user name="Joe" password="abc123" />           <user name="May" password="def456" />         </credentials>       </forms>     </authentication>     <authorization>       <allow roles="Moderator" />       <deny users="*"/>     </authorization>     <!--  Portions omitted for clarity.  -->   </system.web> </configuration> 

The authorization element in Listing 13.7 contains both an allow and a deny child element. The allow element permits anyone with the Moderator role to log in. The deny element prevents all users from logging in. In this list, order is important. The first child element of the authorization element takes precedence over lower elements. In the case of Listing 13.7, anyone in the Moderator role is allowed because that permission was set first. Conversely, if the deny element were first in the list, no one would be able to log in, regardless of their role or presence in the credentials element.

Another thing to notice about the deny element is its attribute setting. Listing 13.3 used the question mark to prevent access by anonymous users, but Listing 13.7 prevents access by everyone. This is an important distinction to make because the goal of role-based authentication in this application is to only allow access to users in the Moderator role. Had this attribute been changed to only prevent anonymous users, the May user ID would have been able to log in by virtue of being authorized through the entry in the credentials element.

The only piece missing in this example is a method of knowing which user ID in the credentials element belongs to the Moderator role. This is the task of the Application_AuthenticateRequest event handler in Global.asax.cs. Chapter 12, "ASP.NET Application Events and State Management," discussed application events handled by the Global.asax.cs file and promised a discussion in this chapter. Listing 13.8 shows the Global.asax.cs for this application, describing how to match a user ID with a role for authentication. Remember, the Global.asax file is automatically created along with all the other Project Manager files when the C#Builder wizard creates the initial ASP.NET project.

Listing 13.8 Performing Role Authentication with the Application_AuthenticateRequest Event Handler (Global.asax.cs)
 using System; using System.Collections; using System.ComponentModel; using System.Security.Principal; using System.Web; using System.Web.Security; using System.Web.SessionState; namespace Roles {    /// <summary>    /// Summary description for Global.    /// </summary>    public class Global : System.Web.HttpApplication    {       public Global()       {          InitializeComponent();       }       protected void Application_Start(Object sender, EventArgs e)       {       }       protected void Session_Start(Object sender, EventArgs e)       {       }       protected void Application_BeginRequest(Object sender, EventArgs e)       {       }       protected void Application_EndRequest(Object sender, EventArgs e)       {       }       protected void Application_AuthenticateRequest(Object sender, EventArgs e)       {          if (Request.IsAuthenticated)          {             FormsIdentity ident = (FormsIdentity)User.Identity;             string[] roles = GetRoles(ident.Name);             Context.User = new GenericPrincipal(ident, roles);          }       }       protected string[] GetRoles(string userId)       {          if (userId == "Joe")          {             return new string[] { "Moderator" };          }          else          {             return new string[] { "Visitor" };          }       }       protected void Application_Error(Object sender, EventArgs e)       {       }       protected void Session_End(Object sender, EventArgs e)       {       }       protected void Application_End(Object sender, EventArgs e)       {       }       #region Web Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {       }       #endregion    } } 

The Application_AuthenticateRequest method in Listing 13.8 will be called on every request to this application. During that time, the user ID will be obtained, a match of the roles the user belongs to will be done, and the routine will configure the user with a new identity that associates their roles with their user ID.

Before performing any actions, the routine checks the IsAuthenticated property to see if Forms-Based Authentication has already authenticated the user. If not, nothing should be done.

The User object contains information about the user logging in. One of these items is an Identity object, which is used in Forms-Based Authentication and used in Listing 13.8 to obtain the user ID through the Name property. After the user ID has been obtained, the code calls the GetRoles method to get a list of roles the user belongs to. This routine simply returns the Moderator role if the user is Joe. However, a proper implementation would use a database to store and retrieve these values. Forms-Based Authentication uses a GenericPrinciple object, a member of the System.Security.Principle namespace, to associate a user ID with a list of roles. The new GenericPrinciple is then assigned to the User property of the Context object. Forms-Based Authentication will use this value to make the association between the user ID and the role the user belongs to, allowing users who pass authentication to progress to the page they intended to visit.

SHOP TALK
STORING AUTHENTICATION CREDENTIALS

In many examples, including the ones in this book, ASP.NET Forms-Based Authentication is performed using credentials from configuration files. This is convenient for demonstrating basic Forms Authentication or setting up authentication for a site that only needs to authenticate a small finite amount of users. However, when the number of users is unknown and expected to grow, trying to manage credentials via a configuration file is inefficient.

The best way to handle authentication for sites with multiple accounts is via database. You could set up users and roles tables and perhaps a userrole join table for users that could have multiple roles. Then use ADO.NET, discussed in Chapter 15, to communicate with the database to extract information when needed. The places to migrate from configuration files to database are on the event generated by clicking the Log In button on the login page and where roles are obtained during the Application_AuthenticateRequest event in the Global.asax.cs file. Getting user IDs and roles from a database will make your application easier to maintain and more efficient.

It is possible to provide security for individual pages or portions of a Web site through location elements and subdirectories with unique web.config files. Listing 13.9 describes how to use the location element to secure an individual page.

Listing 13.9 The location Element
 <?xml version="1.0" encoding="utf-8" ?> <configuration>     <!   Portions omitted for clarity.   >    <location path="PageToProtect.aspx">       <system.web>         <authorization>           <allow roles="Moderator" />           <deny users="*"/>         </authorization>       </system.web>    </location> </configuration> 

In Listing 13.9, the location element contains a path attribute identifying the Web page to apply Forms-Based Authentication to. Another option, well suited for securing groups of pages, is to create a subdirectory with a unique web.config file containing an authorization element that applies only to the Web pages in that subdirectory.



C# Builder KickStart
C# Builder KickStart
ISBN: 672325896
EAN: N/A
Year: 2003
Pages: 165

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