Rolling Your Own Website Administration Tool - Part 2

Introduction
To help administer users, roles, and authorization settings, ASP.NET 2.0 includes the Web Site Administration Tool (WSAT), which is available from Visual Studio 2005 by going to the Website menu and then choosing the ASP.NET Configuration option. Launching the WSAT from Visual Studio, however, allows only local websites to be administered. Such restrictions are limiting when hosting a website remotely with a web hosting company. Consequently, I decided to build my own WSAT-like tool from the ground up. Part 1 of this article series provided an overview of this application and a look at the user management side of things.

This second and final installment looks at the remaining pieces of the custom WSAT application in detail: role management and specifying access rights. The complete application can be downloaded from the end of this article. For instructions on using my custom WSAT in a new or existing web application, refer back to the "Using My Custom Website Administration Tool" in Part 1. Read on to learn more about my custom WSAT's role management and access rights capabilities!




 

Be Sure to Read Part 1 First!
If you've not yet read Part 1 of this article series, be sure to do so before continuing with Part 2. The first part includes a general overview of my custom WSAT, step-by-step instructions on how to plug the custom WSAT into a new or existing application, and a look at managing user accounts. The complete code for the custom WSAT application can be downloaded from the end of this article or at the the end of Part 1.

Managing Roles
My custom WSAT's role management performs the following three tasks:

  • Displays the roles currently in the system,
  • Allows an existing role to be deleted, and
  • Enables new roles to be added.
The user interface for these three tasks fits nicely into a single web page, as the screen shot below shows.

I used a GridView to display all the roles. The Roles class's GetAllRoles method returns a string array of the role names in the system. In addition to listing each role, I also wanted to display the number of users associated with each role. Therefore, rather than directly binding the string array returned by the GetAllRoles method to the GridView, I instead decided to build my own DataTable to use as the data source for the GridView. This is the code as it appears in the page's PreRender method:

// Create a DataTable and define its columns
DataTable RoleList = new DataTable();
RoleList.Columns.Add("Role Name");
RoleList.Columns.Add("User Count");

// Get the list of roles in the system and how many users belong to each role
string[] allRoles = Roles.GetAllRoles();

foreach (string roleName in allRoles)
{
   int numberOfUsersInRole = Roles.GetUsersInRole(roleName).Length;
   string[] roleRow = { roleName, numberOfUsersInRole.ToString() };
   RoleList.Rows.Add(roleRow);
}

// Bind the DataTable to the GridView
UserRoles.DataSource = RoleList;
UserRoles.DataBind();

Adding a new role to the system requires just a single line of code:

Roles.CreateRole(roleName);

Ditto for deleting a role:

Roles.DeleteRole(roleName);

Of interest, the default provider throws an exception if we attempt to delete a role that has one or more users in it. If you want to first automatically remove all members from a role when deleting it, update the delete-related code to include a call to the Role class's RemoveUsersFromRole method. This method expects two input parameters: a string array of the members in the role to remove and the role name. You can get all of the users in a particular role (as a string array) using the GetUsersInRole method.

Specifying Access Rules
The Access Rules Management page uses a TreeView control to display a list of all folders inside the web application. The treeview is populated by a call to the PopulateTree method from the Page_Load event. (I copied the PopulateTree and AddNodeAndDescendents methods almost verbatim from Scott Mitchell's article Using the TreeView Control and a DataList to Create an Online Image Gallery.)

This page is intended for use by an application administrator who understands how ASP.NET applies these access rules. Without going into detail, the text from the equivalent page in the WSAT sums it up pretty well: "Rules are applied in order. The first rule that matches applies, and the permission in each rule overrides the permissions in all following rules."

Access rules are persisted in a Web.config file specific to each subfolder. These Web.config files can be created manually, or they can be created by the web server in the course of using the WebConfigurationManager class to add the first access rule to a folder. Note that the Windows account under which ASP.NET runs, typically NT Authority\Network Service, must have NTFS read/write access to the Web.config file.

Administrators can click the appropriate folder to view and manage its access rules. Rules that are local to the selected subfolder can be moved or deleted. Rules that are inherited from a higher folder are displayed, but cannot be modified at the subfolder level. New rules are created by specifying an action (Deny or Allow) and the user or role to which to the action applies. Although the default roles provider appears to support rules that apply to both users and roles, I've followed the WSAT's lead by forcing the user to select one or the other. The special user groups "All Users (*)" and "Anonymous Users (?)" are additional options.

I fixed a couple quirks from the WSAT: the WSAT allows you to select multiple users through a rather awkward cross-page postback page flow, but then throws an ugly page error when you attempt to create the rule with multiple users. (I didn't delve into this too deeply, but it appears that the developer who wrote the WSAT treated the user name field as a string rather than as a potential array of strings.) Another oddity that I noticed was this: even though ASP.NET's URL authorization accepts authorization rules that are applied to both users and roles (as you can confirm by manually creating a single <allow> or <deny> element with both users and roles attributes), the user interface of the WSAT forces us to choose either a role or a user, but not both. I decided that this simplifies things, so I built my version to follow the WSAT's lead. My version differs in that I permit only single-user selection, implemented through a standard DropDownList.

The code for adding a rule is a two-step process: first create the rule, then add it to the web configuration.

// Create the rule.
AuthorizationRule newRule;
if (ActionAllow.Checked)    newRule = new AuthorizationRule(AuthorizationRuleAction.Allow);
else
   newRule = new AuthorizationRule(AuthorizationRuleAction.Deny);


// Assign the rule to the appropriate user or role, based on what was selected on the page.
if (ApplyRole.Checked && UserRoles.SelectedIndex > 0)
{
   newRule.Roles.Add(UserRoles.Text);
   AddRule(newRule);
}
else if (ApplyUser.Checked && UserList.SelectedIndex > 0)
{
   newRule.Users.Add(UserList.Text);
   AddRule(newRule);
}
else if (ApplyAllUsers.Checked)
{
   newRule.Users.Add("*");
   AddRule(newRule);
}
else if (ApplyAnonUser.Checked)
{
   newRule.Users.Add("?");
   AddRule(newRule);
}

The AddRule method, which is provided in the same page's code section, gains access to the configuration file specific to the subfolder and adds the AuthorizationRule object that was just built:

private void AddRule(AuthorizationRule newRule)
{
   string virtualFolderPath = FolderTree.SelectedValue;

   Configuration config = WebConfigurationManager.OpenWebConfiguration(virtualFolderPath);
   SystemWebSectionGroup systemWeb = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
   AuthorizationSection section = (AuthorizationSection)systemWeb.Sections["authorization"];

   section.Rules.Add(newRule);

   try
   {
      config.Save();
      RuleCreationError.Visible = false;
   }
   catch (Exception ex)
   {
      RuleCreationError.Visible = true;
      RuleCreationError.Text = "<div class=\"alert\">An error occurred and the rule was not added.<i>" + ex.Message + "</i></div>";
   }
}

Moving the rules up and down is more involved because the default provider doesn't supply a built-in method to do this. My technique for shuffling the rules differs substantially from the WSAT, which makes use of a session array to accomplish things. I think my technique is simpler. Without going into detail, I pull all local rules out of the config file into an ArrayList, simultaneously deleting them from the config file. I then shuffle the array based on what button the user clicked, and then add the re-ordered rules back to the config file and save it.

A Background on Self-Disclosing Security Policy
In addition to a page for creating access rules, my custom WSAT implementation also includes an Access Rules Summary page that allows the administrator to select a role or specific user and quickly see what authorization rights this user or role possesses. Before we delve into the Access Rules Summary page, however, it is worthwhile to take a moment to look at what I call Self-Disclosing Security Policy (SDSP), a security policy that I have developed based on previous projects.

The bulk of my Web development work has been intranet application development for a variety of companies. My typical scenario is a "scratch deployment", in which I build a company's first significant intranet application. Oftentimes, a new member from the management team pulls me in - a leader who has enough experience outside the company to realize that he needs a web-based application to help him run his department. Oftentimes, we meet resistance from departments that are concerned about data security. For example, managers will ask for reports containing sensitive data such as sales figures, but when presented with the report on their new intranet, they want an assurance that only appropriate personnel have access to the information.

This is a legitimate concern. Rather than merely implementing role-based security and calling it a day, I have also included means by which managers can view how this security is implemented. I've been developing this concept over the past couple years, and my thoughts really came together as I delved into the built-in Membership and Roles features of ASP.NET, to the point where I coined the term "Self-Disclosing Security Policy". In addition to the Access Rules Summary page that we'll build in just a bit, every page on the intranet will render a security message that will indicate why the current user has access to the page. For example, a non-secured page will render, "This page is non-secured and is available to all users who have access to the corporate intranet." A secured page will render something like, "This page is secured to members of the Executive Management role." In this manner, managers will have faith in the security of their confidential, online reports. If an application administrator were to make an errant entry that opens up the folder to non-privileged users, then the managers who regularly view the page will have an immediate notification the next time they view the page, courtesy of our SDSP. SDSP is a process that gives us self-policing intranet security.

Examining the Access Rule Summary Page
With that as a background, let's build our Access Rule Summary page, which has the distinction of being the only novel page in this WSAT rebuild. This page is intended for both application administrators and appropriate members of management, and it will allow them to view how access rules are applied to users and roles throughout their intranet.

To use this page, users can choose from a DropDownList showing all roles or another DropDownList showing all users. When a role or user is selected, a TreeView control displays a list of all folders in the web application. It's similar to the TreeView in the Access Rules Management page, but differs in that it displays either a green light or a red light for each folder, instead of the standard folder icon. As you might guess, the green light indicates that the selected role or user has access to the folder, while the red light indicates the opposite. In addition to the green lights and red lights, text is displayed indicating why the selected role or user has access to the folder. For example, a subfolder that reads DENY: All Users indicates that the selected role or user does not have access. In the screenshot below, note that the selected user, Franklin Forester, has access to the marketing folder by virtue of his assignment to the Marketing role.

Securing the Custom WSAT Application
Now that we've built our custom WSAT as an integral part of our new or existing website, we'll want to secure it so that only authorized administrators can tamper with the website's users, roles, and access rules. To accomplish this, add two access rules. In my example, I granted the Administrator role access to the admin folder, then denied all other users, as shown below. Note that the ALLOW Administrator rule must appear above the DENY * rule, otherwise no one will have access to the folder. (In fact, you may lock yourself out of the admin folder if you're not careful. If so, then you'll need to manually edit the Web.config file specific to the admin folder to regain access to the custom WSAT application.)

Note that the Access Rules Summary page is exceptional because it is the only page inside the admin folder that is intended for use by roles other than Administrator. This page is central to my Self-Disclosing Security Policy; therefore I intend to make it available to authorized management. This can be accomplished by putting a second copy of the page in another section of the website that is secured to the appropriate role or roles.

Conclusion
While users, roles, and permissions can be managed through ASP.NET 2.0's Website Administration Tool (WSAT), this tool is designed to be used locally and is lacking some common and useful features. The code that we examined in this article and in Part 1, offers a custom implementation of the WSAT. This custom implementation can be added to a new or existing ASP.NET web application on a remote site. Moreover, the custom implementation offers additional features and, in my opinion, improves on the WSAT's user experience.

Happy Programming!

By Dan Clem
posted on 2007-06-11 08:44  心悦  阅读(371)  评论(0编辑  收藏  举报