When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips

Sections:
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
XML Info
Information:
Feedback
Author an Article
ASP ASP.NET ASP FAQs Message Board Feedback
Print this page.
Published: Wednesday, December 19, 2001

Implementing Role-Based Security with ASP.NET, Part 2

By Darren Neimke


  • Read Part 1

  • In Part 1 we examined what role-based security is from a high-level. Furthermore, we looked at the three important sections involved in such a security scheme: Identities, Roles, and Principals. In this part we'll look at how to create our own roles.

    - continued -

    '

    Creating your own roles
    ' Global.asax event handler that fires upon attempting to authenticate the user
    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
      If Request.IsAuthenticated() Then
        ' create an array of roles for the current user
        ' these would most likely be dynamically read
        ' from the data store for each user.
        Dim arrRoles() As String = {"Manager", "Cleaner"}
        
        ' Add our Principal to the current context
        Thread.CurrentPrincipal = New GenericPrincipal(Context.User.Identity, arrRoles)
      End If
    End Sub
    

    This only needs to be done once because the runtime automatically copies a reference to the principal object from the calling thread to the CallContext of the new thread.

    If you cast your mind back to how FormsAuthentication works, you will remember that a user is authenticated and then an authentication cookie attached to the validating Response when you call one of the appropriate static methods of the FormsAuthentication provider. It is at that moment that the Application_AuthenticateRequest handler is called. When authenticating users you do not have to do anything in this handler, nor do you need to explicitly create a new instance of the GenericPrincipal class. However, to dynamically assign custom roles to a user (which is what we are interested in today) you do need to, and the Application_AuthenticateRequest handler is the ideal place to do this.

    Bringing it All Together
    I'll stop here briefly, before we move onto the really cool stuff, to summarize the five steps needed to allow your application to implement role based checking:

    1. Validate the User - check the users credentials against a data store of credentials.
    2. Create an Identity

      Dim objIdentity As GenericIdentity = New GenericIdentity("txtUsername")

    3. Get the roles for the current user

        Dim strRoles() As String
        Dim arrRoles As New ArrayList()
        ' do some database call that returns a reader
        While reader.Read()
          arrRoles.Add(reader("role"))
        End While
        strRoles = arrRoles.ToArray(GetType(String), String())
      

    4. Add the Identity and the Roles to a Principal

      Dim objPrincipal As GenericPrincipal = New GenericPrincipal(objIdentity, strRoles)

    5. Add the Principal to the current context of the current thread

      Thread.CurrentPrincipal = objPrincipal

    Using Roles
    Once you have authenticated a user and defined their identity and roles there are four ways that you can interact with the Principal to enforce permissions based on memberships within the application:

    1. Configuratively
    2. Programmatically
    3. Imperatively
    4. Declaratively

  • Configuratively
    You can configure the authorization and/or the location elements within the Web.config configuration files to grant or deny access to entire areas within the application outright, as opposed to the other 3 methods which suit a more piecemeal approach.

    For example, in the application that we are developing it may be necessary to ensure that only users acting as System Administrators or Moderators are allowed access to certain administrative screens. To demonstrate this, let's presume that all of the forms that allow users to moderate forum posts reside under a directory called "ForumAdmin". Rather than programatically checking the role of a user inside every file that sits in that directory, you can simply configure the Web.config file for that folder to do the checking for you.

    The following snippet excludes all users except for System Administrators or Moderators from accessing files in the directory that the Web.config file resides in (as well as its subdirectories):

    ' Snippet from Web.config
    <authorization>
      <deny users="*" />
      <allow roles="SysAdmin, Moderator" />
    </authorization>
    

    By placing the abovementioned Web.config at the root of the ForumAdmin directory, the application itself will inspect the roles of the user making the Request to determine whether access is granted. That's right, no additional code is neccessary, regardless of how many .aspx files are placed under that folder. (Compare that to classic ASP and the Session-based approach, where every "sensitive" page needs to have a check at the top of the page to determine if the user has rights to view the page...) Even from this simple example, you should get an idea of how this new approach simplifies the task of authorization, and, how it supersedes the methods available under the current practices of classic ASP.

  • Programmatically
    Alternatively, you may have directories that contain files that will be accessed by users acting in a wide variety of roles. An example of this would be the directory that displays the actual forum itself. You need to allow viewing access to all areas of the forum, even to unauthenticated members of the public, but to create a new post, it is imperative that a user not be in the role of Public or Other.

    In your code, whenever you need to check to see if the user is in a role you simply query the IsInRole method of the current user. Let's look at the code snippet responsible for displaying the link to create a new forum post:

    If Not (User.IsInRole("Public")) And Not (User.IsInRole("Other")) Then
      ' Display the link
    Else
      ' Don't display it!
    End If
    

    Or said in another manner:

    If Not (Thread.CurrentPrincipal.IsInRole("Public")) _
    	And Not (Thread.CurrentPrincipal.IsInRole("Other")) Then
      ' Display the link
    Else
      ' Don't display it!
    End If
    

  • Imperatively
    You can also imperatively demand that a user be in a role to access code by creating an instance of the PrincipalPermission class, configuring it with the user and role that you are checking for, then call the Demand method of that object to do the check. If the check fails, a SecurityException is raised. This is the previous example re-written to use imperative checking:

    Dim objPermission As New PrincipalPermission(User.Identity.Name, "manager")
    
    Try
      objPermission.Demand()  
    Catch ex As SecurityException
      ' Don't display it!
    End Try
    

    The downside to the two previous methods is that if you are calling a method several times from different parts of the application, you need to repeat this logic all over the place.

  • Declaratively
    As many of you would know, one of the great benefits of using Stored Procedures in the database is that you can enforce permissions specific to an individual stored procedure. With declarative checks you can bind permissions to an actual method (or event, or whatever) using meta-data attached to each specific object that requires it. For our purposes, let us assume that we have a method called dismissModerator(). Now, for obvious reasons, we do not want the moderator, or anyone else for that matter, to be able to call this method! Therefore, only users who are in the role of System Administrator can access this method. Let's set it up:

    ' Create a method that disables all moderator permissions
    ' and attach a PrincipalPermissionAttribute to it that issues
    ' a Demand.
    
    <PrincipalPermissionAttribute(SecurityAction.Demand, Name:="smith", Role:="SysAdmin")> _
    Public Sub DismissModerator()
      ' logic here
    End Sub
    

    And now, to call it:

    Try
      DismissModerator()
    Catch ex As SecurityException
      ' do something else
    End Try
    

    What has happened here is that the Permission check that is bound to the DismissModerator() Sub is carried out before execution of the Sub takes place. As you can see, this method of authorization is good, because you are enforcing the policy on an actual object, meaning that a developer cannot accidentaly call the method and inadvertently dismiss the poor forum moderator, because the object itself does the role checking. You can enforce declarative checking at class, class member, property or even event level. If you define a permission attribute on a class as well as one of its members the declaration at member level overrides the declaration at class level.

    Summary
    In this article we've had a brief look at what role based security is and how the .NET runtime can assist in implementing it in our solutions. If you found this article interesting I'd encourage you to delve further into the System.Security.Permissions namespace where you'll find even more classes to assist you in producing a customized solution. Pay particular attention to the Union method of the PrincipalPermission class which allows you to bind multiple principal permissions to create a new permission.

    Happy Programming!

  • By Darren Neimke


  • Download the role-based security sample application



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article