Display XML Menu Items Based On User
 
Published: 27 Jun 2007
Abstract
In this article you will learn how to display menu items based on user name from an XML file without using ASP.NET membership. The author also demonstrates how to secure sub-directories by user name.
by Andrew Mooney
Feedback
Average Rating: 
Views (Total / Last 10 Days): 34043/ 43

Introduction

Displaying a menu that shows items based on the current user can be done easily by using roles in membership. In ASP.NET 2.0 membership and login controls make it easy to setup and maintain users for a web site. However, membership comes with some overhead. For example, a database (usually SQL Server) is required to store user information. Another issue with membership is deployment since all ASP.NET applications can use the same database.

The purpose of this article is to demonstrate how to display menu items based on the user from an XML file without using ASP.NET membership. This has limitations, but very little overhead.

Figure 1 - This is a screenshot of the sample menu application

Security

You can use Windows or Forms authentication with this method. However, the code listings included in this article will be using Forms authentication. The first step is to setup security for the application.

Listing 1 demonstrates how to set up Forms Authentication for the web site's root directory. In this application everyone can access the pages in the root directory and the security for sub directories is accomplished by using a separate Web.config file inside each directory. How to secure sub directories will be discussed later in this article.

Set the authentication mode equal to Forms. Set the forms name equal to "MenuAuth." This is the name of the cookie used for forms authentication. Then under credential, set the password format to SHA1. Later I will demonstrate how to hash the passwords to store them in the web.config file. Then for each user you set the user name and hashed password. And then under authorization, allow all users by using the asterisk (*). This example stores user names and passwords in the web.config file, but you could also use a database to store this information. You may want use a database if you have a lot of user names to store.

To use the code from this article you will need to create a new web application. Start by copying the code from Listing 1 into your favorite text editor and save it in the application's root directory. There are two users in the web.config, user1 and user2. The passwords are the same as the user names.

Listing 1 - This is the Web.config file in the web site's root directory

<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <system.web>
    <authentication mode="Forms">
      <forms name="MenuAuth">
        <credentials passwordFormat="SHA1">
          <user name="User1" 
           password="B3DAA77B4C04A9551B8781D03191FE098F325E67"/>
          <user name="User2" 
           password="A1881C06EEC96DB9901C7BBFE41C42A3F08E9CB4"/>
        </credentials>
      </forms>
    </authentication>
    <authorization>
      <allow users="*"/>
    </authorization>
    <compilation debug="false"/>
  </system.web>
</configuration>

Listing 2 shows the login page for the web site. The web form has an input for user name and input for password, two validators to make these required fields, a label for displaying messages to the user, and a login button. When the user clicks the login button, if both the inputs are filled in, an attempt is made to authenticate the user name and password. If authentication is successful, the user is redirected to the page they were requesting. If authentication fails, the user receives a message that the user name and password they entered are invalid and they are asked to try again. Create the Login.aspx in the application's root directory.

Listing 2 - This is the Login.aspx page

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" 
Title="Menu" Theme="Default" %>
<script runat="server">
void Login_Click(Object sender, EventArgs E)
{
  if (FormsAuthentication.Authenticate(UserName.Value.Trim(), 
      UserPass.Value.Trim()))
  {
    FormsAuthentication.RedirectFromLoginPage(UserName.Value.Trim(), false);
  }
  else
  {
    Msg.Text = "Invalid Credentials: Please try again";
  }
}
</script>
 
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" 
  Runat="Server">
  <h3>Login Page</h3>
  <table>
  <tr>
    <td>User Name:</td>
    <td><input id="UserName" type="text" runat=server/></td>
    <td><asp:RequiredieldValidator ID="RequiredFieldValidator1" 
        ControlToValidate="UserName" Display="Static" ErrorMessage="*" 
        runat=server/></td>
  </tr>
  <tr>
    <td>Password:</td>
    <td><input id="UserPass" type=password runat=server/></td>
    <td><asp:RequiredFieldValidator ID="RequiredFieldValidator2" 
        ControlToValidate="UserPass" Display="Static" ErrorMessage="F*" 
        runat=server/></td>
  </tr>
</table>
<asp:button ID="Button1" text="Login" OnClick="Login_Click" runat=server/>
<asp:Label id="Msg" ForeColor="red" Font-Size="10" runat=server />
</asp:Content>

Hash Password

The passwords must be hashed before you can store them in the web.config file or a database. Listing 3 is a web page that will let you enter a password; click the hash password button and the hashed version of your password is displayed in a label. It is then ready to be copied and stored in the configuration file. The hasing is accomplished by using the FormsAuthentication HashPasswordForStoringInConfigFile event. One note of caution you must use the same format in this hashing web page that you have in the web.config file or the passwords will not authenticate. You can create this file in the application's root directory, but there would be no need to publish this to a live web site.

Listing 3 - This is the web page that will allow you to hash passwords (Hash.aspx)

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Hash" 
Theme="Default" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
  Label1.Text = 
  FormsAuthentication.HashPasswordForStoringInConfigFile(TextBox1.Text, "sha1");
}
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" 
  Runat="Server">
  <asp:TextBox ID="TextBox1" runat="server" Width="200px"></asp:TextBox>
  <asp:Button ID="Button1" runat="server" Text="Hash Password" 
  OnClick="Button1_Click" Width="100px" />
  <asp:Label ID="Label1" runat="server"></asp:Label>
</asp:Content>
Securing Sub Directories

Listing 4 shows the web.config for the Restricted directory in the example. In this web.config file we only use the authorization section. Here we specify which users have access to this sub directory. The example only has one sub directory, but you can have as many as you want. You just need to create one for each sub directory. Use the allow node to set users equal to a comma delimited string of names that will have access to this directory. Then below that, make sure to use deny with users set equal to the asterisk (*) to deny access to everyone else.

You could also use groups with allow if you are using windows authentication. However, one of the limitations of this method is that the menu items are loaded by filtering on the user name. So, if you use a windows group for security, you will probably have to display the menu items for that particular sub directory for everyone.

Create a sub directory name Restricted and place the following web.config file in it.

Listing 4 - This is the Web.config file for the Restricted directory

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <system.web>
    <authorization>
      <allow users="user2" />
      <deny users="*" />
    </authorization>
  </system.web>
</configuration>
Site Map

While the site map XML file can be stored anywhere, I recommend placing it in the App_Data folder. This is the XML file that we will use to store the links for the menu control. This example uses a two level menu. You could add more levels, but you will also need to edit the menu control data bindings in master page. In Listing 5 the XML site map has two levels group and page. Typically, you would use one group for each restricted sub directory and one page for each page in that directory. The group node is the top level of the drop down menu and has a link that you could use for the default page in each directory. However, you can use this link for any URI. You could also have a group with pages that contain all external links. One important thing to note here is that the group user property should be set the same as the users that are allowed in the web.config for the group directory.

The user property of the group node controls whether or not the entire group is displayed for the current user. You can also use the word "none" to allow anonymous users to view a certain menu group and the web.config file in the sub directory allows access to the pages in it. Normally, these users would be the same. You can also use external links which do not require directory access, but can still be displayed only to certain users by setting the group user property in the site map XML file.

The title property of both the group and page nodes is the text that will be displayed in the menu. Of course, the URL property contains the URL for the menu item. Create a sub directory named App_Data and place the SiteMap.xml file in it.

Listing 5 - This is the SiteMap.xml

<?xml version="1.0" encoding="utf-8" ?>
<site>
  <group title="Home" url="~/Default.aspx" users="none,user1,user2">
    <page text="Log In" url="~/Login.aspx"/>
  </group>
  <group title="Weather" url="" users="none,user1,user2">
    <page text="Weather Channel" url="http://weather.com"/>
    <page text="MSN Weather" url="http://weather.msn.com"/>
  </group>
  <group title="MSN TV" url="http://tv.msn.com" users="user1">
    <page text="Yahoo TV" url="http://tv.yahoo.com"/>
  </group>
  <group title="Restricted Area" url="~/Restricted/Default.aspx" users="user2">
    <page text="MSDN" url="http://msdn.microsoft.com"/>
    <page text="ASP.NET" url="http://microsoft.com"/>
    <page text="ASPAlliance" url="http://aspalliance.com"/>
  </group>
</site>
Applying Skins

This application uses a skin to control the appearance of the menu control. This can be seen in Listing 6. The skin file is kept in the App_Theme directory in a folder name Default, which is also the name of the Theme and in this case the skin file.

Create a sub directory named App_Themes and in that directory create another sub directory named Default. Place the Default.skin file in this directory.

Listing 6 - This is the Default.skin file

<asp:Menu SkinId="BlueMenu" runat="server" 
  DynamicEnableDefaultPopOutImage="false" 
  StaticEnableDefaultPopOutImage="false" 
  ForeColor="Black" Font-Size="Small" Font-Names="Arial">
  <StaticMenuItemStyle BackColor="Navy" ForeColor="White" BorderWidth="1" 
  BorderColor="White" HorizontalPadding="5px" VerticalPadding="2px"/>
  <StaticHoverStyle BackColor="#C3DAF9" ForeColor="Black" BorderColor="Navy"/>
  <StaticSelectedStyle BackColor="#C3DAF9" ForeColor="Black"/>
  <DynamicHoverStyle BackColor="#C3DAF9" ForeColor="Black" BorderColor="Navy"/>
  <DynamicSelectedStyle BackColor="#C3DAF9" ForeColor="Black"/>
  <DynamicMenuItemStyle BackColor="Navy" ForeColor="White" BorderWidth="1" 
  BorderColor="White" HorizontalPadding="5px" VerticalPadding="2px"/>
</asp:Menu>
Master Page

Listing 7 shows the master page where both the menu control and the code that loads the site map XML file are located. When the master page loads a check is made to see if a user is logged in. If they are, then the user name is displayed at the bottom of the page and the site map XML file gets loaded into the XML data source for the menu control using the user's name as a filter for the group node's user attribute. If there is no user logged in then the user name label is emptied and the site map is loaded using the word "none" as a filter. It is important to make sure that you include a link to the login page in one of the groups in the site map that allows anonymous access. This way, the login link will always be displayed in the menu. After the user is authenticated they will see the menus that they have access to.

Place the MasterPage.master file (Listing 7) and the home page (Listing 8) in the applications root directory. Place the Default.aspx from Listing 9 in the Restricted sub directory.

Listing 7 - This the master page for the application (MasterPage.master)

<%@ Master Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
  string u = HttpContext.Current.User.Identity.Name.ToLower();
  if (u == String.Empty)
  {
    u = "none";
    LabelUser.Text = "";
  }
  else
  {
    LabelUser.Text = "Current User: " + HttpContext.Current.User.Identity.Name;
  }
  XmlDataSource1.XPath = "site/group[contains(@users,'" + u + "')]";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
  <title>Untitled Page</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:XmlDataSource ID="XmlDataSource1" 
    DataFile="~/App_Data/SiteMap.xml" runat="server"/>
    <asp:Menu ID="Menu1" DataSourceId="XmlDataSource1" runat="server" 
    SkinID="BlueMenu" Orientation="Horizontal" StaticDisplayLevels="1">
      <Databindings>
        <asp:MenuItemBinding DataMember="group" TextField="title" 
         NavigateUrlField="url" />
        <asp:MenuItemBinding DataMember="page" TextField="text" 
        NavigateUrlField="url" />
      </Databindings>
    </asp:Menu>
    <br/>
  </div>
  <div>
    <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
</asp:contentplaceholder>
  </div>
  <div>
    <br />
    <asp:Label ID="LabelUser" runat="server" ></asp:Label>
  </div>
  </form>
</body>
</html>

Listing 8 - Shows the default web form for the sample web site using the master page

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Menu" 
  Theme="Default" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" 
  Runat="Server">
  <p>This is the XML menu example default.aspx page.</p>
</asp:Content>

Listing 9 - Shows the default web form for the Restricted sub directory

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Menu"
  Theme="Default" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" 
  Runat="Server">
  <p>This is the restricted directory default.aspx page.</p>
</asp:Content>
Conclusion

You can now create web applications that display menu items based on the current user without the overhead of membership. It requires no database and deployment is very easy. If you do not need all of the other parts of ASP.NET membership then this can save you much time by eliminating it. Hopefully, you will find some useful ideas in this article that you can use and expand on when creating your own web sites that allow user based preferences.



User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 6:32:13 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search