Permissions Primer

Permissions in this context refer to the ability to restrict access to entire screens or portions of screens to only specified users. We didn’t care for the implementation options that shipped with .Net so we rolled our own.

Like many user authentication and authorization mechanisms, ours is built on three core objects:

  • Users represent people who use the system
  • Groups represent groups of people
  • Permissions represent tasks a user can perform

When users are created, they are assigned to groups. Permissions are assigned to groups so it’s easy to learn the permissions of an individual user by looking at the permissions of the groups to which they belong.

Let’s look at the consumption side before we look at how to configure individual users and permissions. If you’ve looked through the pages found in the Chamber project, you might have noticed that all of our pages inherit the base class UnifiedASP.Base.Page or UnifiedASP.Base.PageAuth instead of System.Web.Ui.Page.

We do this so we can expose user authentication and a couple of other functions to every page.

Namespace UnifiedASP.Base

	Public MustInherit Class Page

		Inherits System.Web.UI.Page

		Protected myAppLookup As New UnifiedASP.Domain.AppLookup
		Protected mySession As New UnifiedASP.Domain.AppSession
		Protected myUser As New UnifiedASP.Domain.AppUser
		Protected myPermission As New UnifiedASP.Domain.AppPermission

		Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

			'
			'   Load working user
			myUser.AppUserId = mySession.AppUserId
			myUser.LoadMe()

			'
			'   If working user, load security
			If myUser.hasRecords = True Then
				myPermission.AppUserId = myUser.AppUserId
				myPermission.LoadForUser()
			End If

		End Sub

Our page base instantiates our Session, User, and Permission class and loads it for whatever user is associated with the working session. If a user is found, it then loads the permission object with distinct rows from the permission table for the groups the user has been assigned to.

UnifiedASP.Base.PageAuth differs from UnifiedASP.Base.Page by redirecting the user to the login screen if no user is found.

Namespace UnifiedASP.Base

	Public MustInherit Class PageAuth

		'
		' Pages that require the user to be logged in should inherit this base class

		Inherits UnifiedASP.Base.Page

		Private Sub Page_Init1(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

			If MyUser.hasRecords = False Then

				Dim logonPage As String = ""
				Dim currentPage As String = ""
				Dim appPath As String = ""

				logonPage = "login.aspx"
				currentPage = Replace(HttpContext.Current.Request.Path, HttpContext.Current.Request.ApplicationPath, "", 1, 1)

				If SafeCompare(currentPage, logonPage) = False Then
					SmartRedirect(logonPage & "?entryUrl=" & Server.UrlEncode(currentPage))
				End If

			End If

		End Sub

	End Class

End Namespace

UnifiedASP.Base.PageAuth inherits from UnifiedASP.Base.Page as is shown above. It’s only function is to route the user to the login screen if no valid user was found in the session.

So let’s look the Chamber/Default.aspx.vb to learn how the page consumes the classes that are exposed by UnifiedASP.Base.Page.

The only thing you’ll see in Chamber/Default.aspx.vb is line 46:

divTitleLink.Visible = myPermission.HasPermission("Chamber.Create")

This line shows or hides a link that takes the user to the Chamber/Edit.aspx screen if they are logged in and are in a group that contains this permission. It uses the method HasPermission of the myPermission object declared and instantiated in UnifiedASP.Base.Page. In situations where a user has not logged in, there will be no known permissions so the link will be hidden. If the user is logged in but is not in a group that has been assigned the Chamber.Create permission, the link will be hidden.

If you look at Chamber/Edit.aspx.vb, you will see we are doing something a bit more complicated.

First, we inherit from UnifiedASP.Base.PageAuth because we know if the user is not signed in, they will not have permissions to perform any of the tasks on this page.

Partial Class Chamber_Edit

	Inherits UnifiedASP.Base.PageAuth

In most cases, our Edit screens will support both adding and editing items. By default, we create a separate permission for each task for each module Below is a portion of the Page_Load method of Chamber/Edit.aspx.vb. Here you can see we are first attempting to load a chamber record. If none is found based on the input to the screen, it will be assumed we are in add mode and the permission Chamber.Create with be checked. If a chamber was found, the permission Chamber.Update will be checked. If the user does not have the correct permission, they will be redirected to the login screen.

myChamberId = GetInput("ChamberId", True, False, CLng(Nothing))

myChamber.ChamberId = myChamberId
myChamber.LoadMe()

If (myPermission.HasPermission("Chamber.Create") = False And myChamber.HasRecords = False) _
  Or (myPermission.HasPermission("Chamber.Update") = False And myChamber.HasRecords = True) Then
	SmartRedirect("Login.aspx")
End If

In most scenarios, that is how we consume permissions.

Occasionally, we will restrict access to certain fields within a form. To accomplish this, we add additional permissions to the AppPermission table and assign those permissions to the appropriate groups. For example, if we only wanted certain users to see the phone number of a chamber, we could add the permission Chamber.View.PhoneNumber, and have logic in the page to hide the phone number control if the user was not assigned that permission.

Now that you’ve seen how we consume this information, let’s take a look at the tools to help you create it. Since virtually all applications that come through our shop are data-driven, our user authentication repository is as well. If you have downloaded our sample Chamber project, you can review the tables in your existing database. If you have not, doing so will help you follow along.

There are six core tables that come into play when we support user authentication and authorization:

  • AppUser – Stores the individual user accounts. Username is the primary key but we shift it to EmailAddress in some application and use it instead of a Username
  • AppGroup – Stores the individual groups. Administrator group is protected in the application screens and cannot be edited or deleted so a user cannot accidentally remove all functions from the administrators
  • AppPermission – Stores the individual permissions
  • AppUserAppGroupMap – Stores the relationship between users and groups
  • AppGroupAppPermissionMap – Stores the relationship between groups and permissions. Any time a new permission is added, a row is automatically created in this table for the Administrators group so members of that group can continue to do everything in the application
  • AppSession – Stores individual sessions for the user. See the Session Primer.

CRUD Screens for managing Users, Groups, and Permissions can be found in the Chamber project.

Share It:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • E-mail this story to a friend!
  • Netvibes
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati