For our project we are required to handle authorizations based on a Role/Data Scope basis. This means a user can be assigned to more than 1 role and reach a different Data Scope for each User/Role combination.
For example:
Suppose we have 3 different classes: Orders, Customers and Products
Role 1 has these permissions
Class |
View |
Add |
Update |
Delete |
Products |
Allow |
None |
None |
None |
Customers |
Allow |
None |
None |
None |
Orders |
None |
None |
None |
None |
Role 2 has these permissions
Class |
View |
Add |
Update |
Delete |
Products |
Allow |
None |
Allow |
None |
Customers |
Allow |
None |
None |
None |
Orders |
Allow |
Allow |
Allow |
Allow |
User 1 is assigned to Role 1 with a scope of: Company 1, Company 2 and Company 3
He is also assigned to Role 2 with a scope of: Company 1
This means she can view products and customers for the 3 companies but can only update products and create orders for company 1.
To handle this, we built a class library that includes a custom principal allowing us to do something like this:
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
CanAdd = _principal.BusinessClasses(“Products”).PermissionsForScopes( _
mCompanyKey).HasPermission(enmPermissionIndex.epmx_Add)
Where mCompanyKey is the key of the company a product belongs to.
Or even handle more than one scope, let’s say company and department:
CanAdd = _principal.BusinessClasses(“Products”).PermissionsForScopes( _
mCompanyKey, mDepartmentKey).HasPermission(enmPermissionIndex.epmx_Add)
The problem is I can’t figure it out how to add this to the Product class authorization rules. Maybe have something like the Shared functions CanAddObject, CanGetObject, etc. But because they’re shared I must refer to instance members.
Any help would be greatly appreciated.
I think you can use some decoupling here.
Perhaps an interface like IDataScope that business objects can implement to return their scope (companykey, departmentkey) in a standard manner.
Then you can create a centralized helper method to check the role/company/dept triplets.
Then you can override CanReadProperty() and CanWriteProperty() in a custom base class (which would also implement IDataScope to force subclasses to provide that data!) so the triplet is used to call this helper method.
Finally, your Shared CanXyzObject() methods can accept an IDataScope if appropriate. Though I'm not sure that makes a lot of sense. Remember that the 4 Shared methods exist primarily to allow the UI to enable/disable various menu items or links. If the answer is "maybe" then the UI really must leave the options enabled and so the questions have no value. If they have no value, then don't implement them.
Rocky,
Following your suggestion, we created the IDataScope interface and implemented it in our business classes.
We also modified our Shared CanXyzObject methods to accept an IDataScope object or an array of Guids as follows:
Public Shared Function CanAddObject(ByVal ParamArray scopeKeys() As System.Guid) As Boolean
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(“Product”).PermissionsForScopes(scopeKeys).HasPermission(EnmPermissionIndex.epmx_Add)
End Function
Public Shared Function CanGetObject(ByVal ParamArray scopeKeys() As System.Guid) As Boolean
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(“Product”).PermissionsForScopes(scopeKeys).HasPermission(EnmPermissionIndex.epmx_View)
End Function
Public Overloads Shared Function CanEditObject(ByVal product as IDataScope) As Boolean
If IsNothing(employee) Then
Return False
Else
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(“Product”).PermissionsForScopes(product.ScopeKeys).HasPermission(EnmPermissionIndex.epmx_Update)
End If
End Function
Public Overloads Shared Function CanEditObject(ByVal ParamArray scopeKeys() As System.Guid) As Boolean
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(“Product”).PermissionsForScopes(scopeKeys).HasPermission(EnmPermissionIndex.epmx_Update)
End Function
Public Shared Function CanDeleteObject(ByVal product as IDataScope) As Boolean
If IsNothing(employee) Then
Return False
Else
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(anObject).PermissionsForScopes(product.ScopeKeys).HasPermission(EnmPermissionIndex.epmx_Delete)
End If
End Function
Maybe we should use reflection and instead of specifying the name of the object we could do something like
Public Shared Function CanDeleteObject(ByVal anObject as object) As Boolean
If IsNothing(employee) Then
Return False
Else
Dim _principal As MyCustomPrincipal = Csla.ApplicationContext.User
Return _principal.Identity.IsAuthenticated AndAlso _principal.BusinessClasses(anObject.GetType.Name).PermissionsForScopes(product.ScopeKeys).HasPermission(EnmPermissionIndex.epmx_Delete)
End If
End Function
I don’t know if it’s possible to get the type for the functions that don’t receive an instance as parameter.
We’ll need to do some research before trying overriding CanReadProperty and CanWriteProperty in a new base class, but we can live with the above approach for now.
Thanks a lot for your help!
Copyright (c) Marimer LLC