Alright, I already know what you're thinking after reading the title, but that's why I prefaced it with "Another..." Long story short, I wrote this nice long e-mail to my friend/colleague here at work and I thought maybe it would be of use to one of the three people out there that haven't read 84 different explanations of the abstract factory pattern. Again, it was formally an e-mail so I ran through it and added some clarifications in brackets...hope I made _some_ sense :) A link to an explanation of the abstract factory pattern is here: http://69.10.233.10/KB/cs/Abstract_Factory_Pattern.aspx but IMO this stuff makes a lot more stuff in context. I tend to use this pattern a lot, but [to me] it seems to make a lot of sense when dealing with security/roles - it's an easy way to accommodate adding roles without having to go everywhere in your application and modifying code..but it can definitely be used elsewhere (I use it in JavaScript a lot too) Basically just create an interface with the method signatures you want to implement.
Public Interface ISecurityCheck
Function canEditProfile(ByRef UserInfo As Object, ByVal ProfileID As String) As Boolean
Function isAuthorizedPage(ByVal PageId As Integer, ByVal UserInfo As Object) As Boolean
End Interface
Then, all you have to do is create a class for each role in your application. The class will implement the interface iSecurityCheck...This is what the implementation looks like for anonymous users in [my latest application]. Basically, anonymous users can read everything in [the application] but not write anything.
Public Class AnonymousSecurityCheck
Inherits SecurityBase
Implements ISecurityCheck
Public Function canEditProfile(ByRef UserInfo As Object, ByVal ProfileID As String) As Boolean Implements ISecurityCheck.canEditProfile
Return False
End Function
Public Function isAuthorizedPage(ByVal PageId As Integer, ByVal UserInfo As Object) As Boolean Implements ISecurityCheck.isAuthorizedPage
Return isPageAllowed(PageId, UserInfo)
End Function
End Class
CanEditProfile takes a UserInfo object as a parameter which is basically my user object. There are a few propreties in UserInfo, but the most important ones are ProfileID and Role. CanEditProfile always takes a ProfileID parameter - this should probably be named better, but it is supposed to be the ProfileID of the page you're currently looking at. This allows me to cross-reference the UserInfo.ProfileID property with the ProfileID of the current page. In the case of the anonymous user, it returns false no matter what. isAuthorizedPage basically runs a stored proc to see if the user that is currently logged in can even load/view the page... The implementation of the rest of the roles looks like below, but there really isn't spectacular happening. Basically if you're a normal user, I return true for CanEditProfile assuming your UserInfo.ProfileID and the ProfileID of the page you're looking at is the same. isAuthorizedPage always runs the same stored proc (But the implementation could easily change) Admins can basically do whatever they want...so this always returns true for both functions:
Public Class AdministratorSecurityCheck
Inherits SecurityBase
Implements ISecurityCheck
Public Function canEditProfile(ByRef UserInfo As Object, ByVal ProfileID As String) As Boolean Implements ISecurityCheck.canEditProfile
Return True
End Function
Public Function isAuthorizedPage(ByVal PageId As Integer, ByVal UserInfo As Object) As Boolean Implements ISecurityCheck.isAuthorizedPage
Return True
End Function
End Class
'CanEditProfile returns true only if the ProfileID = UserInfo.EmployeeID
blic Class UserSecurityCheck
Inherits SecurityBase
Implements ISecurityCheck
Public Function canEditProfile(ByRef UserInfo As Object, ByVal ProfileID As String) As Boolean Implements ISecurityCheck.canEditProfile
If TypeOf (UserInfo) Is AppMembershipUser Then
If UserInfo.ProfileID.ToString = ProfileID Then Return True
End If
Return False
End Function
Public Function isAuthorizedPage(ByVal PageId As Integer, ByVal UserInfo As Object) As Boolean Implements ISecurityCheck.isAuthorizedPage
Return isPageAllowed(PageId, UserInfo)
End Function
End Class
So that's all relatively straight forward. The best part of this is that you can add more roles, or change the business rules without a lot of work. So within a page, you use this like this: Dim isec As ISecurityCheck = AuthorizationFactory.GetAuthorization(UserInfo) CanEditProfile = isec.canEditProfile(Me.UserInfo, currentPageProfileId) So I get the true/false value telling me whether a user can edit a profile regardless of how many roles exist or whether I add or remove roles. Since you're running the CanEditProfile method yourself above which returns a value of type ISecurityCheck, this all works.
Public Class AuthorizationFactory
Public Shared Function GetAuthorization(ByVal UserInfo As Object) As ISecurityCheck
If TypeOf (UserInfo) Is AppMembershipUser Then
UserInfo = CType(UserInfo, AppMembershipUser)
With UserInfo
If Common.stringIsInArrayList(Constants.ROLE_ADMINISTRATOR, .rolename) Then Return New AdministratorSecurityCheck
If Common.stringIsInArrayList(Constants.ROLE_USER, .rolename) Then Return New UserSecurityCheck
End With
End If
Return New AnonymousSecurityCheck
End Function
End Class
Hmm. This might be the longest e-mail I've written in a long time...Hope I did a decent job of explaining, though