Open michaelcsikos opened 7 years ago
The roles:
are only checked on the static DataPortal.XYZ or the Save method by CSLA.
are executed on a per-instance AuthzRules (CanRead../CanWrite../CanExecute...) and by default the result is is cached.
Assuming that you transform the authorization config into "permissions" and load these into the PrincipalObject to be checked by IsInRole - then yes, you can get it to work.
When a Delete is done by DataPortal.Delete only the static DeleteObject rule is checked. When a Save is done, depending on the status of the object the CreateObject/EditObject or DeleteObject is checked.
You should not load roles dynamically in AddObjectAuthorizationRules(). The roles to check should be a constant in your code. You should however transform the permissions into roles in the user principal on login.
Remember: AddObjectAuthorizationRules is only called once-per-type in the application.
IE: If your rules are to be dynamic in that they must also check the database for another configuration on a "per data object instance" - then this must be done in the Execute method of the AuthorizationRule. And you must also remember that there can only be one-1 AuthorizationRule per Type/(instance)/Action.
Thanks for your detailed reply, Jonny.
What is the recommended way to apply GetObject
authorisation for particular instances? e.g. Project 123 requires role Top-secret clearance, but regular projects don't.
Tricky business. Depending on your requirements - you could implement this in a GetObject rule where you Get the ID as parameter or you could implement this in your DataPortal_Fetch.
There could even be requirements for a search that the user should only see the records that he/she has permission to get and that should also be handled in the Fetch logic.
The search returns a list that should show only objects you are allowed to read. This a pretty common scenario and can be handled by the database query itself.
I'm going to piggyback off of this question here since I am trying to do something similar. I'm curious to know if there are any pitfalls in what I am doing and how I am doing it. This might also be helpful to @michaelcsikos if he has not come up with a solution yet.
Here's a couple things up front:
IsAccessGranted
field in my user organization table.Developer
, Admin
, etc).I am currently writing a custom "IsAuthorized" rule which subclasses AuthorizationRule
. This is added to my business object in the AddBusinessRules
override as a per-method rule. This rule is also tightly coupled to my business object since it takes advantage of context.Target
. The goal of authorization rule is as follows:
Developer
can see all organizationsAdmin
can see all organizations except what they are explicitly deniedBelow is a generic version of my code that works but might not be implemented correctly:
[Serializable()]
public class EditableObject : BusinessBase<EditableObject>
{
//code omitted...
protected override void AddBusinessRules()
{
base.AddBusinessRules();
//AUTHORIZATION RULE ADDED HERE
BusinessRules.AddRule(new Project.Library.Rules.Authorization.IsAuthorized(Csla.Rules.AuthorizationActions.ExecuteMethod, EditableMethod));
}
protected static void AddObjectAuthorizationRules()
{
//Per-Type authorization like CreateObject, EditObject, etc. set up here with roles.
}
public static readonly MethodInfo EditableMethod = RegisterMethod(typeof(EditableObject), "GetEditableObject");
/// <summary>
/// Retrieves a business object by its Id
/// </summary>
/// <param name="id">The business object's Id</param>
/// <returns></returns>
public static EditableObject GetEditableObject(int id)
{
return DataPortal.Fetch<EditableObject>(id);
}
private EditableObject() { /* require use of factory methods */ }
private void DataPortal_Fetch(int id)
{
using (var ctx = ContextManager<ProjectDataContext>.GetManager(Database.Project))
{
var data = (from c in ctx.DataContext.Editable_SelectById(id)
select c).Single();
using (BypassPropertyChecks)
{
//load properties
}
//AUTHORIZATION RULE CHECKED HERE AFTER PROPERTIES ARE LOADED
//SINCE WE NEED THE DATA FIRST
CanExecuteMethod(EditableMethod);
}
}
//code omitted...
}
public class IsAuthorized : AuthorizationRule
{
public IsAuthorized(AuthorizationActions action, Csla.Core.IMemberInfo element) :
base(action, element)
{
CacheResult = false;
}
protected override void Execute(AuthorizationContext context)
{
var target = context.Target as EditableObject;
var EditableString = string.Empty;
if (target != null)
{
EditableString = target.OrgString;
}
var _user = Csla.ApplicationContext.User.Identity as CustomIdentity;
var _userOk = Csla.ApplicationContext.User.IsInRole("Developer");
if (!_userOk)
{
//If user is Admin AND the EditableString does not exist in
//their DeniedOrgs list, then they're ok; otherwise block access
if (Csla.ApplicationContext.User.IsInRole("Admin"))
{
_userOk = !_user.DeniedOrgs.Contains(EditableString) ? true : false;
}
else
{
//If the EditableObject OrgString is in the user's AccessGranted list, then
//they're ok; otherwise block access
//This logic is for users that are not Developer or Admin
_userOk = _user.Orgs.Contains(EditableString);
}
}
//"At the very least, every authorization rule must set the HasPermission
//property of the AuthorizationContext parameter to the result of the rule."
context.HasPermission = _userOk;
}
}
Hello Rocky, Jonny, et al
In a WinForms project, I've implemented a user login with the
ApplicationContextManager
,CslaIdentityBase<T>
andCslaPrincipal
. It's working well.We want to add roles and permissions now, and I'd appreciate some advice before I code myself into a corner.
I have read the relevant section in book 2 of the Using CSLA 4 e-book series, and Rocky's Permission-based authorization vs role-based authorization blog.
The
Roles
will be fairly typical, but they will be defined at runtime, except for a hardcoded Administrator role. Each user will have a collection of roles.Permissions
will be defined per type, e.g. the Legal role may be allowed create, get, edit, and delete all projects, by default. In the staticAddObjectAuthorizationRules()
method, is there any problem with retrieving thestring[] roles
from another static command which loads them from the database?We are hoping to be able to override (grant or deny) the per-type
Permissions
per instance. For example, Project 123 may grant roles as:Legal
According to the per-type permissions, the Legal role is allowed to do anything, but for this project they have been denied permission to edit and delete.
Accounting
By default, the Accounting role may have no permissions granted for projects, but for this project they have get and edit permissions.
Is there any problem effectively overriding the default permissions granted per type?
If a delete is done with a factory method using
DataPortal.Delete()
is it only the static per-type authorization rules that are checked?Thanks for your wisdom and this wonderful framework. —Michael