I love starting new projects with a new technology and recently the requirement for an ASP.NET 2.0 custom reporting module landed on my desk. I have some ASP.NET 1.1 experience having created a couple of sites about two years ago but that was it.
Keen to implement some of ASP.NET 2.0's cool features I came across the Membership, Roles and Profiles features @ http://www.4guysfromrolla.com and was impressed by how easy it was to generate a robust authentication system from scratch utilising Roles and various other niceities. However, out of all these articles there didn't seem to be anything that covered the scenario where the developer had already got a database and structure for storage and a library that could handle the authentication, role assignment and various other tasks.
Digging a little deeper it seemed that I could still utilise the login control then capture the 'authenticate' event and then employ my custom logic to authenticate the user. However, I wanted an easy way to identify whether the user was part of a specific role or not.
Now I must admit I checked out creating my own custom 'Role Provider' and whilst this might have been a better approach (especially if I wanted to expose routines to Add, Remove, Amend roles from a user) in the scenario where I'm handling this elsewhere - it seemed to be overkill.
Anyhoo, here's how I did it...
In my 'Authenticate' event of the login control I instantiate my custom authentication class. In my case, this class exposes a method to authenticate the user (returning a boolean value) and a property exposing the role of the user (which is a numerical value).
Note: it is possible of utilise multiple roles, my scenario precluded that option...
Here's the psuedo-code....
' Custom authentication classDim myAuthentication As New UserAuthentication
' Call custom authentication routine (where: conLogin is the Login control)
' and pass the username and password over
If myAuthentication.AuthenticateUser(conLogin.UserName, conLogin.Password) Then
' If sucessful, generate the authentication ticket
Dim ticket
As New FormsAuthenticationTicket( _
1, _
"userName", _
DateTime.Now, _
DateTime.Now.AddMinutes(20), _
False, _
myAuthentication.RoleId, _ ' Retreive the RoleId from the authentication class
FormsAuthentication.FormsCookiePath)
' Encrypt
Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Dim authCookie As New HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
' Add to the cookies
Response.Cookies.Add(authCookie)
' Perform any appropriate navigation from the login page
Response.Redirect("NextPage.aspx")
End If
As you can see when I instantiate my AuthenticationTicket I pass the RoleId into the 'userData' argument. Once in there, I can now utilise that value elsewhere, namely the HttpApplication.AuthenticateRequest event where I can parse the current FormsIdentity, obtain the appropriate RoleId from the 'userData' property and add that to the 'roles' for the current user.
So, if you haven't created a Global.asax file, do so. Within here you need to capture the 'AuthenticateRequest' event of the Application object and then utilise the following code...
Protected Sub Application_AuthenticateRequest(
ByVal sender
As Object,
ByVal e
As System.EventArgs)
' Does the object exist and is the user instantiate?
If (((
Not HttpContext.Current.User
Is Nothing)
AndAlso HttpContext.Current.User.Identity.IsAuthenticated)
AndAlso TypeOf HttpContext.Current.User.Identity
Is FormsIdentity)
Then' Retreive the current identity
Dim id As FormsIdentity = CType(HttpContext.Current.User.Identity, FormsIdentity)
' Reset the User object and incorporate the new roles
HttpContext.Current.User = New System.Security.Principal.GenericPrincipal(id, New String() {id.Ticket.UserData})
End IfEnd Sub
So the User object is reset with the appropriate role. Note: that the 'roles()' argument of instantiation of 'GenericPrincipal' object does accept an array of string to support multiple roles.
Now, it is possible to utilise the base 'User.IsInRole' method and pass the appropriate RoleId over. However, I tend to make my job slightly easier by employing an Enum and utilising that, e.g.
If User.IsInRole(Constants.UserRoles.Administrator)
ThenResponse.Write("You're an Administrator!")
Else
Response.Write("You're a nobody.")
End
If
And that's it. One block of code in the Global.asasx and one Enum to make coding slightly easily (admittedly without Option Strict On) and everything is up and running!
HTH - M