CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Eric Wise

Business & .NET

ASP .NET :: Know Your Roles - Level 200

Setting up role based security for a website is a Very Good Thing (tm) if you require flexibility in security.  Many organizations I've come across in my career struggle with security because they use a system based on user type instead of groups and roles.  The obvious flaw with user type is that over time you end up with a new user that is a "hybrid" and then you either have to add a new type, or adjust all the pages that reference the types which grants other users priviliges they've never had.

The worst of all sins I've seen in this scenario is actually HARD CODING the user's userid into the page logic.  Messy!  So I'm going to show you a fairly simple way to handle role based security in a Forms Authenticated application.

Step 1: Roles, Groups, and Users
First things first, we'll need a structure that allows us to define application roles, define groups, assign roles to groups, and assign users to groups.  For the purposes of this exercise we'll set up the database structure like so:

CREATE TABLE [dbo].[ApplicationRoles] (
    [applicationRoleID] [int] IDENTITY (1, 1) NOT NULL ,
    [roleName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [roleDescr] [varchar] (200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[SecurityGroupRoles] (
    [assocID] [int] IDENTITY (1, 1) NOT NULL ,
    [applicationRoleID] [int] NOT NULL ,
    [securityGroupID] [int] NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[SecurityGroupUsers] (
    [assocID] [int] IDENTITY (1, 1) NOT NULL ,
    [securityGroupID] [int] NOT NULL ,
    [userID] [varchar] (25) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[SecurityGroups] (
    [securityGroupID] [int] IDENTITY (1, 1) NOT NULL ,
    [securityGroupName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [securityGroupDescr] [varchar] (200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
) ON [PRIMARY]
GO


Step 2: Setting up Forms Authentication In the Web.Config
You've seen it before, and here it is again.  Simply edit your web.config as follows:

    <authentication mode="Forms">
        <forms name="BrilliantAuth" loginUrl="LogIn.aspx" protection="Encryption" timeout="30" path="/"/>
    </authentication>

    <authorization>
        <deny users="?"/>
        <allow users="*"/>
    </authorization>

Step 3: Edit the global.asax
Now we have to set up the application to accept a list of roles and assign them to the current user.  We do this in the authenticate_request method like so:

    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
        Dim CookieName As String = FormsAuthentication.FormsCookieName
        Dim authCookie As HttpCookie = Context.Request.Cookies(CookieName)

        'Check for cookie
        If IsNothing(authCookie) Then Return

        Dim authTicket As FormsAuthenticationTicket
        Try
            authTicket = FormsAuthentication.Decrypt(authCookie.Value)
        Catch
            Return
        End Try

        If IsNothing(authTicket) Then
            'Cookie failed to decrypt
            Return
        End If

        Dim roles() As String = authTicket.UserData.Split("|")
        Dim id As New FormsIdentity(authTicket)
        Dim principal As New System.Security.Principal.GenericPrincipal(id, roles)

        Context.User = principal
    End Sub

Do notice that I split authentication ticket's UserData property.  Sadly this is a string property so we can submit a collection of role objects so in your code somewhere you'll have to combine all your roles into a delimited string.

Step 4: Login page ticket creation
The last step is to create the forms authentication ticket for a successful login.  You do this like so:

            Dim authTicket As New FormsAuthenticationTicket(1, loginUser.UserID, DateTime.Now, DateTime.Now.AddMinutes(30), False, loginUser.GetRoles())
            Dim encryptedTicket As String = FormsAuthentication.Encrypt(authTicket)
            Dim authCookie = New HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
            Response.Cookies.Add(authCookie)

This does require Imports System.Web.Security.  Don't fret over loginUser, it's just a class that I had created.  All you have to do is swap that out with your userID and the GetRoles() should be your delimited role string.  The SQL I use to GetRoles() is as simple as this:

SELECT roleName from ApplicationRoles WHERE ApplicationRoleID IN
    (SELECT DISTINCT ApplicationRoleID FROM SecurityGroupUsers
      INNER JOIN SecurityGroups ON SecurityGroupUsers.SecurityGroupID = SecurityGroups.SecurityGroupID
      INNER JOIN SecurityGroupRoles ON SecurityGroups.SecurityGroupID = SecurityGroupRoles.SecurityGroupID
      WHERE SecurityGroupUsers.UserID = @UserID)


Cheers


Check out Devlicio.us!

Our Sponsors