On the Timestamp vs DateTime issue I think your suggestion sounds fine. We use NHibernate and that provides automatic support for optimistic concurrency checking using a "Version" column on each table in the database. So our convention is to use an integer column called "Version" - it's the version number of the Business Object. We have separate DateTime columns for audit trail style functionality.
On the Authorization issue it sounds again like you're thinking the right way. Make sure you think of the "Roles" that users will perform in your application. Don't think of it as the users called "Luke" and "Darth Vader". Think of it as users who get mapped to the Role "GoodGuys" and users who get mapped to the Role "BadGuys". That may help when you think about what Role Based Security is all about.
Pawz,
With respect to your question regarding Authorization Rules... At some point, in order to make decisions within your business object as to who can do what - you are going to have to "hard code" something.
I had a situation where the business wanted to create their own roles, "define" those roles, and then assign users to those roles. Kind of tricky to use role based permissions when you don't even know what the names of the roles might be. Instead we ended up creating "permissions". Those permissions were used to define the roles. This way the users could create whatever roles they wanted, assigned whatever permissions they wanted to the roles, and then assign the users to the roles. All we ever cared about was the permissions. Obviously the users could NOT create permissions. When the user logged in, the stored procedure did all the hard work gathering all the permissions the user had. Then we just threw the permissions into the custom security context instead of the roles. In the objects, when we used IsInRole() what we were actually checking was if they were assigned a particular permission or not. As the requirements changed, we would change / update the permissions and change / update our business objects accordingly.
Ken
I did something very similar to what Ken describes with Permissions. We have both Roles and Permissions though. Turns out we don't do much with the Roles - so we might have been better off not including them. Then IsInRole could be used to check permissions as Ken describes. This is useful in many places in CSLA and .Net. I have a corresponding method named HasPermission but it is not part of the interface nor does Rocky call it or allow it ot be called (yet.) He is considering adding Delegate support to CSLA so IsInRole is not the only allowed method to be called.
All in all, I wish I did not have any roles and had gone with the idea of using permissions for the IsInRole method. But we did not know all this 4 years ago when we first started.
Joe
Pawz,
You would create the permissions as you need them. In some cases I had a permission called "ProjectName Application Owner" (PAO) and another permission called "ProjectName Permission Administrator" (PPA). For the maintenance of all the lookup tables, only the users assigned either of these permissions (roles) could save the object; however, only a PPA could deactivate an object while both could update the descriptions. Everything works the same as described in Rocky's books and the ProjectTracker example.
The big difference is in the stored procedure you call within the DataPortal_Fetch of your security Identity object. Here is a watered down version of my Login stored procedure:
SELECT DISTINCT
RTrim([Permissions].PermissionDisplayName) As Role
FROM Roles
Left Join Sessions ON Roles.RoleID = Sessions.RoleID
Left Join Users ON Sessions.UserID = Users.UserID
Left Join Access ON Roles.RoleID = Access.RoleID
Left Join [Permissions] ON Access.PermissionID = [Permissions].PermissionID
WHERE Users.UserName = @UserName
AND Roles.ActiveFlag = 1
AND Users.ActiveFlag = 1
AND [Permissions].ActiveFlag = 1
Notice how I am returning my permission as a role (which then gets added to the List _roles member within your Identity object). That is because I didn't want to change the way anything else within the objects worked - all I wanted to do is feed it the information I wanted it to have. The Sessions table is a join table between Roles and Users. This is where users get "assigned" to roles. The Access table is a join table between Roles and Permissions. This is where permissions get "assigned" to roles. So basically I want every distinct permission a user has been "assigned" to where I match his name and everything associated with it is still "Active".
Here is how I setup the authorizations within the object:
protected override void AddAuthorizationRules()
{
AuthorizationRules.AllowWrite("ActiveFlag", "ProjectName Permission Administrator");
AuthorizationRules.AllowWrite("Description", "ProjectName Permission Administrator", "ProjectName Application Owner");
}
public static bool CanEditObject()
{
bool result = false;
if (Csla.ApplicationContext.User.IsInRole("ProjectName Permission Administrator"))
{
result = true;
}
else if (Csla.ApplicationContext.User.IsInRole("ProjectName Application Owner"))
{
result = true;
}
return result;
}
If I need finer control (like on a field by field basis), I could add individual permissions for each field; however, I try to stay away from that unless I have no other option. It becomes a maintenance nightmare for the person maintaining the roles (and their associated "assigned" permissions). As far as your CSLA objects are concerned, your permissions are roles and will treat them as such. Use them just the way you were going to use a role. (It gets a little confusing, but it is really just a matter of semantics)
With respect to your question / concern regarding the sequencing of authorization rules, I really recommend purchasing and reading Rocky's CSLA .NET Version 2.1 Handbook. I have the C# version and it covers your question in detail. Take a look at "Changes to ValidationRules" on page 12, "Adding Per-Instance Validation Rules" on page 15 and "Adding Per-Type Validation Rules" on page 16.
Am I close to answering your question?
Ken
AddAuthorizationRules() is called from inside the constructor in BusinessBase and ReadOnlyBase.
So the answer to your question is "Yes" it would break the DataPortal concept if you tried to access the DB directly from inside this method override.
Create a new BO to get the data from the DB and then use this inside the AddAuthorizationRules()override. That way you can be sure that the code you need to hit the DB will be executed on the App server.
Pawz,
I want to issue an unreserved apology for my post yesterday. I was wrong .
I was correct that the AddAuthorizationRules() method is called from inside the constructor in BusinessBase and ReadOnlyBase.
However, I was incorrect when I said that it would break the DataPortal concept to access the DB from within this method override.
All CSLA objects are instantiated by the server-side DataPortal. Therefore, anything done as part of their construction will have access to the DB.
Needless to say I'm suitably embarrassed
Copyright (c) Marimer LLC