Click here to Skip to main content
Click here to Skip to main content

RoleProvider for web applications that use XML files for roles

, 16 Oct 2006 Ms-PL
Rate this:
Please Sign up or sign in to vote.
Use a personal provider to manage users and roles in a web application.

Sample Image

Introduction

The idea that this article wants to illustrate is the implementation of a personal RoleManager provider to manage the roles of users in a website. This functionality is already built in to the .NET Framework: you simply can use the Roles class to create, delete, or add users, etc. to the rules of the website that you want implement. But... there is a but! Perhaps, not everyone knows that when the Roles class is used, automatically (where not specified) the RolesProvider is used to manage the role in a SQL Server database file, located on the website. Make a step behind. To activate role management, we must add this element in the web.config file of the website:

<roleManager enabled="true" />

If we don't specify the defaultProvider property, the default provider for the management roles is AspNetSqlRoleProvider. When this happens, a MDF file (SQL Server database file) is located in the App_Data directory of the website. This file contains some tables to manage the roles for the site and some other functions. Now, if I don't have the SQL Server Express component installed, I will have problems managing the site's rules. What can we do? The solution is to use another provider for the role management that isn't the SQL provider. In MSDN, there's an example with an Access database. Well, it's a nice sample, but why use a database file (owner file) when we can use an XML file (standard notation) to store the needed info? All right, we can start!

Using the code

All right, the first step to accomplish this work is to prepare the site to test our code. Create a new website project in VS 2005 as an ASP.NET Web Site. Well, usually I prefer to use a real Web Server as IIS, but if you don't have it and you want to use the ASP.NET Development Server, I think there isn't any problem. For this project, I use "LoginRoleManagement" as the name for the project, therefore my website responds to the related address "http://localhost/LoginRoleManagement". For those using the IIS web server, I don't specify the configuration of the website, the default one created by VS2005 is OK, but if you have any problems, try to modify the properties of the site in the IIS management panel. Create two sub folders on the root named "SubFunction1" and "SubFunction2", and create a Web Form file for each folder named "Default.aspx". Those folders represent some functions that the users can or cannot use, depending on their roles. Add also three web.config files, one for each folder (root, SubFunction1, SubFunction2). The root folder already contains the default.aspx file, create another Web Form file named "Login.aspx". Well, all files for the new solution are created, it lacks one other file that we will create later. What you must have in front of you is a a scene similar to this one:

Now, write some code in the file that we have just created. Start with the web.config file in the root folder. We must insert some elements for the security and the authentication method of the website.

<!--<span class="code-comment">
set the authentication to the form mode 
and the login page to the login.aspx page
add also 2(two) user by the name user1 with 
password user1 and user2 with password user2
to simplify the work I chose to use a password 
in clear method to maintain the password, 
but you can change ti, and use a encription method
to finish this section I allow all user 
to view the root node of the site
--></span>
<authentication mode="Forms">
  <forms loginUrl="login.aspx" 
         defaultUrl="default.aspx" protection="All">
    <credentials passwordFormat="Clear">
      <user name="user1" password="user1"/>
      <user name="user2" password="user2"/>
    </credentials>
  </forms>
</authentication>
<authorization>
  <allow users="*"/>
</authorization>

We haven't put the element to enable the role manager, don't worry, we can do this later. In the web.config file of subFunction1, write this code:

<authorization>
  <allow roles="roleFunction1"/>
  <deny users="*"/>
</authorization>

In the web.config file of subFunction2, write this code:

<authorization>
  <allow roles="roleFunction2"/>
  <deny users="*"/>
</authorization>

More code and we are ready to try the first part of our project. In the Default.aspx file of the root website, put two HyperLink components that link to the two sections that we have created. It's important that you modify the NavigateUrl with the section where you want to go ("./SubFunction1" and "./SubFunction2"). In the default.aspx file of each SubFunction section, write some text that identifies it when you access the page. I suggest to insert some label with large font and different color to understand the section immediately. The last part of code is in the login.aspx file. The .NET Framework 2.0 gives us a nice component called LogIn that offers a simple interface for the logon function. Put the control in the page, and don't add any other code. Add also a HyperLink to the main page. Well, we are ready to try our website. Perfect, if you haven't committed any errors, your website will respond with the login page when you try to reach the sub functions 1 and 2.

Now, we must implement the role provider, and finally enable the role manager component. To create and implement the role provider class, create a new item of type class in the project. When you do this, VS2005 asks if you want to put the class file in the app_code folder. You answer yes to this question, but I think it is indifferent. Well, I call my class MyXmlProvider. In the source file to download, you can find all the class code. In this tutorial, I will list only some functions, you can write the others by yourself. All right, we need a namespace for the new provider class, therefore add the namespace token to create it. I call it "Personal.Providers". In addition, we can delete some default packages that VS2005 includes automatically. In particular, all the using System.Web.UI.*, the root included. And add some others. Remember to extend your class from the RoleProvider as a sealed class. The code that you would see must appear like this:

  using System;
  using System.Data;
  using System.Configuration;
  using System.Web;
  using System.Web.Security;
  using System.Collections.Specialized;
  using System.Configuration.Provider;

  namespace Personal.Providers
  {
    public sealed class MyXmlProvider : RoleProvider
    {

Here is the list of the sign methods and attributes that you must override:

public override string ApplicationName;
public override void Initialize(string name, NameValueCollection config);
public override void AddUsersToRoles(string[] usernames, string[] rolenames);
public override void CreateRole(string rolename);
public override bool DeleteRole(string rolename, bool throwOnPopulatedRole);
public override string[] GetAllRoles();
public override string[] GetRolesForUser(string username);
public override string[] GetUsersInRole(string rolename);
public override bool IsUserInRole(string username, string rolename);
public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames);
public override bool RoleExists(string rolename);
public override string[] FindUsersInRole(string rolename, string usernameToMatch);

Well, only one property that you can resolve by writing this:

private string pApplicationName;
public override string ApplicationName
{
  get { return pApplicationName; }
  set { pApplicationName = value; }
}

Now, prepare to read and write from and to our XML files. Create the variable and procedure that we will use to accomplish this work.

/*
 * Roles Table
 */
private string rolesTable = "Roles";
private string []columnRoles = { "RoleName" };
private string rolesTableFile = "Roles.xml";

/*
 * UsersInRoles Table
 */
private string usersInRolesTable = "UsersInRoles";
private string[] columnUsersInRoles = { "Users", "Roles" };
private string usersInRolesTableFile = "UsersInRoles.xml";

private DataSet dtsRoles, dtsUsersInRoles;

private void createTable(string tableName, 
        string[] columnsTable, string XMLFileName)
{
  DataSet dts;
  DataTable dtb;
  System.IO.FileInfo TheFile = new System.IO.FileInfo(
     HttpContext.Current.Server.MapPath("./") + XMLFileName);
  if (!TheFile.Exists)
  {
    dts = new DataSet();
    dtb = dts.Tables.Add(tableName);
    foreach (string column in columnsTable)
    dtb.Columns.Add(column, Type.GetType("System.String"));
    dts.WriteXml(HttpContext.Current.Server.MapPath("./") + 
                 XMLFileName, System.Data.XmlWriteMode.WriteSchema);
  }
}

Analyzing the code, you can note that the two tables that will contain our info about the authorization are structured as:

Simple and useful. Attention, I use MapPath to create the XML files, you must ensure that your aspnet user has write access in this folder, otherwise the code generates an exception error during the writing process. Now, go to describe some of the methods that we must override. In particular, I expose to you the Initialize, IsUserInRole, and GetRolesForUser methods. Here is the code:

public override void Initialize(string name, NameValueCollection config)
{
  name = "MyXmlProvider";
  base.Initialize(name, config);
  createTable(rolesTable, columnRoles, rolesTableFile);
  createTable(usersInRolesTable, columnUsersInRoles, 
              usersInRolesTableFile);
  dtsRoles = new DataSet();
  dtsUsersInRoles = new DataSet();
  dtsRoles.ReadXml(HttpContext.Current.Server.MapPath("./") + 
                   rolesTableFile);
  dtsUsersInRoles.ReadXml(HttpContext.Current.Server.MapPath("./") + 
                          usersInRolesTableFile);
}
public override string[] GetUsersInRole(string rolename)
{
  string tmpUserNames = "";
  DataRow[] dtr = dtsUsersInRoles.Tables[0].Select(
                  columnUsersInRoles[1] + " = '" + rolename + "'");
  foreach (DataRow row in dtr)
  {
    tmpUserNames += row[columnUsersInRoles[1]] + ",";
  }
  if (tmpUserNames.Length > 0)
  {
     tmpUserNames = tmpUserNames.Substring(0, tmpUserNames.Length - 1);
     return tmpUserNames.Split(',');
  }
  return new string[0];
}
public override bool IsUserInRole(string username, string rolename)
{
  bool userIsInRole = false;
  string query = columnUsersInRoles[1] + " = '" + rolename + "' AND " + 
                 columnUsersInRoles[0] + " = '" + username + "'";
  int numRecs = dtsUsersInRoles.Tables[0].Select(query).Length;
  if (numRecs > 0)
  {
    userIsInRole = true;
  }
  return userIsInRole;
}

Well, as you can see, we use the Select method of the DataSet class to find the element, we can simply create a standard SQL query to interrogate the XML files. Using those guidelines, you can implement the other method that I have listed previously. Remember, when you write the RemoveUsersFromRoles, DeleteRole, CreateRole, and AddUsersToRoles methods, to rewrite the XML file using the WriteXml method of the DataSet class. The last step to end our implementation, and then we are ready to test and use our provider class for the management of the authentication in our website. To enable roles management using our provider, we must write this in the web.config file of the root site, and put it in the system.web section:

<roleManager enabled="true" defaultProvider="MyXmlProvider">
  <providers>
    <add type="Personal.Providers.MyXmlProvider" name="MyXmlProvider"/>
    </providers>
</roleManager>

I think there is no need to explain here, it's really simple. Well, I forgot a thing. If you don't insert any entries in the XML files, you cannot access the subFunction areas of the website, therefore, to do this, we can use the method that we have implemented directly from the website. You can put it in the global.asax file when the application starts, but you must also put the code for the authentication in the login.aspx page; therefore, I chose to put it all here. The code:

protected void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    if (!Roles.RoleExists("roleFunction1"))
    {
      Roles.CreateRole("roleFunction1");
      Roles.AddUserToRole("user1", "roleFunction1");
    }
    if (!Roles.RoleExists("roleFunction2"))
    {
      Roles.CreateRole("roleFunction2");
      Roles.AddUserToRole("user2", "roleFunction2");
    }
  }
}
protected void onLogging(object sender, LoginCancelEventArgs e)
{
  if (FormsAuthentication.Authenticate(Login1.UserName, 
                                       Login1.Password))
  {
    FormsAuthentication.RedirectFromLoginPage(Login1.UserName, 
                                              Login1.RememberMeSet);
  }
}

The onLogging function is associated with the LoggingIn event notification of the Login control in the Login.aspx page (too many login words! Smile | :) ). Well, launch your application and enjoy! Access with one user and then with another one. Don't forget to disconnect your first user or to re-launch the web site! See you soon!

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

--==## Andy74 ##==--
Team Leader
Italy Italy
No Biography provided

Comments and Discussions

 
Generalproblem reading updated xml PinmemberSachinKumarK9-Nov-08 23:59 
QuestionSQL Server still connected ? Pinmemberhellspawnfr3-Apr-07 20:11 
AnswerRe: SQL Server still connected ? Pinmember--==## Andy74 ##==--3-Apr-07 20:37 
GeneralRe: SQL Server still connected ? Pinmemberhellspawnfr3-Apr-07 20:53 
Hi again, (and thanks for this ultra FAST reply Wink | ;) )
 
I did not know that IIS could allow me to update the ASP.NET config. It helps to understand what's going on ...
 
Well I think that the problem is with the "Authentication" tab in IIS. There are two drop down for the providers :
- membership provider
- role provider
 
The first one only contains "AspNetSqlMembershipProvider".
The second one contained 3 providers, and among them "MyXmlProvider". I cleaned the two others.
 
So my guess is that the membership provider should also contain "MyXmlProvider" instead of "AspNetSqlMembershipProvider". What do you think ?
 
By the way I run my tests with your project to make it simpler.
 
Bye !
GeneralRe: SQL Server still connected ? Pinmember--==## Andy74 ##==--4-Apr-07 12:03 
GeneralRe: SQL Server still connected ? Pinmemberhellspawnfr4-Apr-07 19:48 
GeneralRe: SQL Server still connected ? New test Pinmemberhellspawnfr3-Apr-07 21:28 
GeneralRe: SQL Server still connected ? New test Pinmemberselcukyazar23-Feb-10 3:48 
GeneralXML Role Rpovider Write Issues PinmemberMark Henke21-Nov-06 4:38 
GeneralRe: XML Role Rpovider Write Issues Pinmember--==## Andy74 ##==--21-Nov-06 21:31 
GeneralRe: XML Role Rpovider Write Issues PinmemberMark Henke22-Nov-06 3:09 
GeneralRe: XML Role Rpovider Write Issues Pinmember--==## Andy74 ##==--22-Nov-06 6:36 
GeneralRe: XML Role Rpovider Write Issues PinmemberMark Henke22-Nov-06 3:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141029.1 | Last Updated 16 Oct 2006
Article Copyright 2006 by --==## Andy74 ##==--
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid