Introduction
In Web applications, typically there exist certain parts of the site that only
certain individuals, or groups of individuals, can access. For example, imagine an
intranet Web site used to administer the content on a company's public Internet Web
site, where the public Web site lists products sold by the company. From the
administrative site, all company employees might be able to make minor changes to
the products' descriptions, quantity, and other such non-essential information. However,
perhaps only a subset of trusted employees might be able to change the products'
prices. And even a smaller subset of employees would be able to add new products or
delete existing products from the database.
To handle such a hierarchy of capabilities, a traditional security model to use is
to divide users into roles, and then to assign permissions to various
resources on a role-by-role basis. For example, our fictional company's administrative
Web site might be setup so that the President, CEO, and CIO are made "Administrators" of
the product database, and have access to change the product database via the online
interface in any way they desire. The top-level managers might be added to the
Price Changer role, meaning they can change the prices of existing products, while
all other company employees were inserted into the Minor Updates role. Such a security
model is typically referred to a role-based authorization model, as the authorization
users enjoy is based upon the role they play within the system.
In this article we will examine how to utilize role-based authorization in an ASP.NET
Web application using Forms authentication. Specifically, we'll examine the data model
needed for implementing roles, we'll see how to determine what roles a given user belongs to,
and then how to restrict (or grant) access to resources based on a user's roles.
A Quick Security Overview
When thinking or talking about security, there are two fundamental underpinnings which
are central to understanding role-based authorization and security models in general.
These two concepts are:
Authentication - authentication is the act of identifying who a user is.
In Web applications, this is typically done by having the user provide some credentials,
such as a username and password.
Authorization - authorization is the act of granting or denying access
to a resource based upon the user attempting to access the resource.
Before we can discuss role-based authorization, we must first think about how we will
authenticate our users. ASP.NET provides three methods for authentication:
Windows Authentication - Useful when working on an intranet, where every user
who needs to be authenticated has a Windows account on the Web server's domain,
Passport Authentication - Uses Microsoft's Passport service, and
Forms Authentication - Prompts the user for a set of credentials (typically
username and password).
This article will focus on using role-based authorization with Forms authentication.
(If you are unfamiliar with Forms authentication, be sure to check out Darren Neimke's
excellent article, Using Forms
Authentication in ASP.NET.)
Getting Role-Based Authorization Working - the First Steps
In order to provide role-based authorization, we need some way to model the roles and
the users that participate in these roles. Typically, when using Forms
authentication a user is modeled as a row in a database table. Each user usually
contains information pertinent to the Web application, including the
user's credentials (i.e., their username and password).
So, before we can get started at looking at the code necessary for implementing role-based
authorization, we need to provide a data model. Our data model will consist of three
related tables:
Users - each record in this table models an individual user. The
pertinent fields in this table include UserID (a unique identifier for each
user), Username, and Password.
Groups - each record in this table models a group. The Groups
table's pertinent fields are GroupID and Name. Groups are the
classifications to which users can be assigned. For example, a Web application might
have groups like: Administrators, CanEdit, CanDelete, CanInsert, and so on.
Roles - the Roles table models the many-to-many relationship
between Users and Groups. Specifically, a role maps a user to
a group. The Roles table needs only two fields: UserID and
GroupID.
An example database might be as follows:
Users
UserID
Username
Password
1
Bob
password1
2
Scott
password2
3
Jisun
password3
4
Sam
password4
5
John
password5
Groups
GroupID
Name
1
Administrators
2
CanEdit
3
CanDelete
4
CanInsert
Roles
UserID
GroupID
1
1
2
2
2
3
3
4
4
3
4
4
In this sample database there are five users and four groups. Bob is in the
Administrator role, Scott is in the CanEdit and CanDelete roles, Jisun is in the
CanInsert role, Sam is in the CanDelete and CanInsert roles, and John is not in
any roles.
Determining What Roles a User Is In
The .NET Framework contains an HttpApplication class that represents
an ASP.NET Web application. This class has a number of events that can fire
during various times of the Web application's lifecycle. The invent we're interested
in is the AuthenticateRequest event, which fires each time when the
ASP.NET security module has established the identity of a user. We can write an event
handler for this event in the Global.asax file. In this event handler
we need to determine what roles this user belongs to.
The following shows the code for the AuthenticateRequest event handler:
Sub Application_AuthenticateRequest(sender As Object, e As EventArgs)
If Request.IsAuthenticated Then
'Determine this user's roles
Dim reader As SqlDataReader = _
SqlHelper.ExecuteReader(connection string, _
CommandType.StoredProcedure, "rolesForUser", _
New SqlParameter("@Username", User.Identity.Name))
' Create an array of role names
Dim roleList As New ArrayList
Do While reader.Read()
roleList.Add(reader("Name"))
Loop
'Convert the roleList ArrayList to a String array
Dim roleListArray As String() = roleList.ToArray(GetType(String))
'Add the roles to the User Principal
HttpContext.Current.User = _
New GenericPrincipal(User.Identity, roleListArray)
End If
End Sub
Let's examine this code sample line-by-line. The first line checks to see if
the user is an authenticated user. That is, if we are dealing with an anonymous
Web user, there's no point in hitting the database to determine what roles the
anonymous user belongs to. By the data model I presented earlier, only users that
exist in the database can be assigned roles.
If the user has been authenticated, then the Request.IsAuthenticated property
will return True, and the code inside the If statement will execute.
Next, the code uses the Microsoft Data Access Application Block to execute the stored
procedure titled rolesForUser, passing in the authenticated user's
name as the value for the @Username parameter. (If you are unfamiliar
with the MS Data Access Application Block, it is a free assembly from Microsoft that
can be used to encapsulate a lot of the extra complexity involved in making
database calls. For more information, be sure to read:
Examining the Data Access Application Block.)
The results of the stored procedure - whose syntax we'll examine shortly - are then
inserted into an ArrayList. The ArrayList is then converted into a String array using
the ToArray() method. Finally, the current user's User context
is reassigned to a new GenericPrincipal instance with the user's existing
Identity and the set of roles. (Don't worry if you do not yet understand
the syntax here or the purpose of the code - we'll discuss this last line of code in
more detail in the next section.)
The rolesForUser stored procedure is fairly straightforward - it simply
takes in a single parameter (the username to search on) and then returns the list of
roles the user belongs to.
CREATE PROCEDURE rolesForUser
(
@Username varchar(50)
)
AS
SELECT G.Name
FROM Roles R
INNER JOIN Groups G ON
R.GroupID = G.GroupID
INNER JOIN Users U ON
R.UserID = U.UserID AND U.Username = @Username
In Part 2 we'll examine the code for this
event handler in more detail, specifically examining the HttpContext
class and Principals, Roles, Groups, and Users!