Per-Instance Authorization Re-visited

Per-Instance Authorization Re-visited

Old forum URL: forums.lhotka.net/forums/t/2664.aspx


SonOfPirate posted on Thursday, April 05, 2007

I know there have been a few other discussions about per-instance authorization solutions, but I am still working out a few issues and hope to re-visit the topic in a more complete way...

 

Let's use the "CanEditObject()" method as the basis for our discussion.  In my Quote class, as with all other classes, this method is static and is implemented simply as:

return ApplicationContext.User.IsInRole("Account Manager");

which works great for most applications.  However, in my case, there are times when the object's state affects the response we want to provide from this method.  For example, here would be the full use-case statement:

So, I need the CanEditObject method to be more like:

public static System.Boolean CanEditObject()
{
    if (ApplicationContext.User.IsInRole("Account Manager"))
        return (State == QuoteStatus.Draft)
   
    return false;
}

The problem is that the State property is not static - and can't be as this is a per-instance value.  So, how to accomplish this then?

In other posts there was a suggestion to implement two classes, Quote and SubmittedQuote, that would return different responses to the authorization methods.  I.e. Quote would simply check IsInRole and SubmittedQuote would always return false.  However, when putting this into action there is a problem implementing this as we are displaying a list of QuoteInfo objects from which the user has selected to open one of the listed records (by double-clicking the row, for instance).  We pass the ID value from the selected row to the new form which then calls Quote.GetQuote(ID) to instantiate our business object.  It seems like we are adding extra code and complications in order to differentiate which type to instantiate.

And what if there are multiple properties that determine whether the object can be edited by the current user?  Perhaps not only the object's state matters but also the dollar value.  Let's say that a Quote that has been submitted for approval worth more than $1 Million can only be updated by the VP of Sales whereas any Quote with a lower dollar value would be accessible to the Sales Manager for approval.

To further complicate the discussion, we are in the design phase on another application that will require us to implement more traditional access control (per object) security whereby each object will maintain a list of users that are allowed to perform actions upon it.  I don't see how we are to have separate classes for each of these cases.  Nor can the ACL's be static as they will vary from instance to instance.

How best to accomplish what I've described?  Any suggestions?  Anyone had any success in these areas???

Thanks in advance.

 

 

ajj3085 replied on Friday, April 06, 2007

Pirate,

I've done this by overriding CanWriteProperty, like so:

public override bool CanWriteProperty( string propName ) {
    bool result;

    if ( Locked ) {
      result = false;
    }
    else {
       result = base.CanWriteProperty( propName );
    }

    return result;
}

Now, is that what Rocky intended?  I'm not sure, but it seems to work out fine.

HTH
Andy

SonOfPirate replied on Friday, April 06, 2007

Actually the CanReadProperty/CanWriteProperty/CanExecuteMethod part isn't a problem because those are not static methods so I can make conditional checks with other properties of the object.  My issue has to do with the static CanAddObject, CanEditObject, CanRemoveObject and CanReadObject methods used by the UI to enable/disable menu items, display forms as read-only, etc.

For example, continuing the Quote scenerio described above, when we display our Quote Details (web) form, we use the CanEditObject method to determine if the FormView is in ReadOnly or Edit mode.  Our business rules, as shown before, dictate that we:

Which brings up a second question that has plauged me for a while.  I do not make much use of the CanReadProperty, etc. methods because access to the object is blocked by the static methods.  However, I understand that having the AuthorizationRules defined provides a safety net in case access is granted despite denial by the static methods - as would be the case if the UI developer failed to include the check!  What do you do then?  Do you throw an exception?  Display an error message?

About 90% of the applications I work on are web apps, so handling an exception is a bit more cumbersome than with Windows apps.  And, since I make use of the various xView controls, we are not binding the enabled/disabled state of the individual edit controls to their corresponding properties but rather using the static methods to determine the appropriate template to display.

Make sense?

 

JoeFallon1 replied on Friday, April 06, 2007

I do not use the Auth rules very much. But when I was looking in to them I decided that for my web app it would make more sense to just blank out the value if CanReadProperty was False. This made more sense to me than throwing an exception and then trying to handle it in the UI. Just let the user see all the values they are supposed to see and blank out the rest.

'CanReadProperty("Desc", True) ' this causes an Exception to be thrown if user is not allowed to Read the Property.
'Can be caught in screen - if screen is coded to Catch ex As DataPortalException when fetching or creating a BO.

If CanReadProperty("Desc") Then
 
Return mDesc
Else
 
Return ""
End If

==========================================================================

Public Class Check

Private Shared Function HasPermission(ByVal permission As String) As Boolean
 
Dim user As MyUser = DirectCast(Csla2.ApplicationContext.User, MyUser)
 
Return user.HasPermission(permission)
End Function

Public Shared Function CanAddObject(ByVal permission As String) As Boolean
 
Return HasPermission(permission)
End Function

Public Shared Function CanGetObject(ByVal permission As String) As Boolean
 
Return HasPermission(permission)
End Function

Public Shared Function CanDeleteObject(ByVal permission As String) As Boolean
 
Return HasPermission(permission)
End Function

Public Shared Function CanEditObject(ByVal permission As String) As Boolean
 
Return HasPermission(permission)
End Function

End Class

Rather than write this kind of code in each BO I decided to centralize it so any BO can call it.My user.HasPermission is similar to IsInRole but much finer grained.

I realize this doesn't answer the question about how you also deal with the state of the object. Not sure how you can do that without an instance of the object. Sort of a Catch-22.

Finally - Rocky may enhance IsReadAllowed so that the *default* implementation is the same as today - call IsInRole. But he will do it using a Delegate. This wa y we can write our own IsInRole boolean function and use a dfiferent delegate (which is hooked up on App start). This delegate could return my HasPermission value instead of IsInRole which would allow me to again have finer control over this mechanism.

Joe

 

ajj3085 replied on Friday, April 06, 2007

Pirate,

I'm doing WinForms, but I think the same solution applies to WebForms.  You would have something similar to ReadWriteAuthorization component, that would disable bound controls on the web form based on the return value of CanWriteProperty.

Your UI would hide the save button completely though based on your Locked property. In addition, you should probably override save to throw an exection of Locked is true.

As far as CanReadProperty, I would think its the same as CanWriteProperty; you throw an exception in the property getting if CanReadProperty returns false (well, you tell CanRead to throw for you).  The equivolent of RWAuth would handle the control so that exceptions don't get thrown.  If the UI developer 'forgets,' well throwing the exception should make it clear what is wrong, and hopefully they will fix their UI during testing.

I haven't used AllowRead or DenyRead yet though, because my use cases don't stipulate hiding certain data from certain roles, either you have full rights to the data or none... although this may be changing soon.


SonOfPirate replied on Friday, April 06, 2007

I still don't see the value in the CanReadProperty and CanWriteProperty methods except as a backup in case the UI developer forgets to check CanReadObject/CanEditObject.  I have never run into a case where I need to allow a user access to only a portion of an object's properties while denying others - the user can either read or not and can either edit or not.  And with regards to the latter, we already have a safe-guard check in the Save method where we make sure that the user has edit rights before allowing the save.  So, again, I'm not looking for answers how to implement these methods as they don't serve my purposes.

Furthermore, we don't really have an equivalent control for web apps as the RWAuth Windows control.  In web apps, we make use of the built-in features by utilizing the various xView controls which allow us to define templates for editing and read-only views of the data.  This is done "globally", so to speak, in that we are either in edit mode or read-only mode.  Again going back to what I referred to before about not working on a per-property basis - as the RWAuth allows us to do in Windows apps (but I've never used cuz it was never applicable).

If anyone has examples of use-cases where you would want to be able to grant edit access to some fields and deny for others within the same instance of a BO, please pass them along because this is one big gray area for me.

 

Let me try to make a more concrete example to further the discussion.

In our application, object instances are "owned" by their creator.  As such, the creator will always have full rights to the object.  He can view it, edit it and delete it.  During the object's lifetime, the creator may need to allow others to work with the object, so it can be temporarily "assigned" to another user.  This user is granted view and edit rights so long as they are assigned to it.  Furthermore, the object has a list of users and roles in which it has been "shared."  Any user in this list (or user belonging to a role that is included) is granted read rights.  Anyone else can do nothing with the object.

So, our authorization logic is as follows:

The only authorization method that is not per-instance is the CanCreateObject method which does the simple IsInRole check.

Again, it doesn't make sense for me to implement these rules on each and every individual property accessor.  Besides the performance impact of having to evaluate this everytime a property is read or written to, the amount of excess code...!!!

But, you can clearly see that three out of the four authorization methods rely on instance property values.  So, how to implement?

Do I make each of the authorization methods non-static?  If so, I am now bound to only checking per-instance and no longer have per-type authorization as I still would use for the CanCreateObject method.

Does this example help to illustrate the problem/question better?

 

ajj3085 replied on Friday, April 06, 2007

SonOfPirate:
I still don't see the value in the CanReadProperty and CanWriteProperty methods except as a backup in case the UI developer forgets to check CanReadObject/CanEditObject.  I have never run into a case where I need to allow a user access to only a portion of an object's properties while denying others - the user can either read or not and can either edit or not.  And with regards to the latter, we already have a safe-guard check in the Save method where we make sure that the user has edit rights before allowing the save.  So, again, I'm not looking for answers how to implement these methods as they don't serve my purposes.


I haven't come across a case myself where CanReadProperty would return something different than static member, but I can see situations where it would.  But for CanWriteProperty I do.  Some users are allowed to view the detail objects using the same screens that the users which edit use.  CanWriteProperty allows me to do just that.  Also, after an document in my system is locked, no one may edit it... but they still need to get to the document and look at it with the same screen.

SonOfPirate:
Furthermore, we don't really have an equivalent control for web apps as the RWAuth Windows control.  In web apps, we make use of the built-in features by utilizing the various xView controls which allow us to define templates for editing and read-only views of the data.  This is done "globally", so to speak, in that we are either in edit mode or read-only mode.  Again going back to what I referred to before about not working on a per-property basis - as the RWAuth allows us to do in Windows apps (but I've never used cuz it was never applicable).

So the only question here is a matter of scope; currently I imagine you're using the static CanEditObject to determine which template to use, but I can easily see using CanWriteProperty if you wanted to only lock portions of the objects but still allow edit access.

SonOfPirate:
If anyone has examples of use-cases where you would want to be able to grant edit access to some fields and deny for others within the same instance of a BO, please pass them along because this is one big gray area for me.

I could see a use case where a sales person could modify some customer information, such as address data, but not other aspects, such as the customer's credit limit, payment terms, etc.

SonOfPirate:
The only authorization method that is not per-instance is the CanCreateObject method which does the simple IsInRole check.

Again, it doesn't make sense for me to implement these rules on each and every individual property accessor.  Besides the performance impact of having to evaluate this everytime a property is read or written to, the amount of excess code...!!!

But, you can clearly see that three out of the four authorization methods rely on instance property values.  So, how to implement?

Do I make each of the authorization methods non-static?  If so, I am now bound to only checking per-instance and no longer have per-type authorization as I still would use for the CanCreateObject method.

The only solution I can think of is use Csla and the CanReadProperty / CanWriteProperty, or your static methods will have to take parameters that give enough information to get a result.  The parameterless static methods would return in general what someone may do.

For example, CanLoadObject would mean the user can load at least some instances.  If CanLoadObject is false, then I would think the static factory get methods would throw exceptions always, and your UI would not even show links to pages which load the object details.

If CanLoadObject is true, then you execute your dataportal fetch as usual, but the first thing you do is some queries to decide if the user may load the instance.  If not, throw a SecurityException at that point.

CanEditObject and CanRemoveObject behave the same; the user may edit or remove instances in general, but each instance still decides for itself if the user may edit or remove it by diong the check in a Save override and throwing an exception if the user does not have permission.

Does that help at all?

SonOfPirate replied on Friday, April 06, 2007

I just stumbled across a great blog post related to Microsoft CRM security that has given me a few new thoughts on how to implement this within Csla.

In CRM, authorization is broken into two parts: PrivilegeCheck and AccessCheck.

A PrivilegeCheck would do exactly what the existing static methods are doing: check IsInRole and return true/false based on whether the current user MAY be able to perform the requested action.  For instance, if I have a user that is in the Account Managers role, they may be able to edit a Quote object so we would enable the Properties toolbar button and allow them to open the Quote details form/page.

An AccessCheck is where the instance part comes in that I am talking about.  An AccessCheck is what determines whether or not the user can actually perform that action on the specific instance of a BO.  This would be where we would have to apply the authorization rules that I described and is what would tell me to display the form as read-only and/or disable the Save button, etc.

 

What dawned on me in reading this is that my problems may be solved as simply as creating overloaded versions of the static methods that accept the instance being checked as a parameter.  For example, to implement the CanReadObject methods given the rules I described previously, I'd have:

public static System.Boolean CanReadObject()
{
    return ApplicationContext.User.IsInRole("Account Manager");
}
 
public static System.Boolean CanReadObject(Quote q)
{
    if (CanReadObject())
    {
        if (ApplicationContext.User.Equals(q.Owner))
            return true;
 
        if (ApplicationContext.User.Equals(q.AssignedTo))
            return true;
 
        if (q.ShareWith.Contains(ApplicationContext.User))
            return true;
 
        // Some check to see if any of the user's roles are in the
        // share list (have to figure out later)
 
        return false;
    }
}

In my UI code, to determine if my FormView, for example, should be displayed in edit or read-only mode, I'd use:

if ((Quote.CanCreateObject()) && (DataItem.IsNew))
    FormView1.ChangeMode(FormViewMode.Insert);
else if (Quote.CanEditObject(DataItem))
    FormView1.ChangeMode(FormViewMode.Edit);
else
    FormView1.ChangeMode(FormViewMode.ReadOnly);

This seems pretty consistant with the description of how it is done within CRM and appears to accomplish what I am after.  I will have to go back through my use cases to see that it fits across the board, but I think it will do the trick.

Thoughts?

 

ajj3085 replied on Monday, April 09, 2007

SonOfPirate:
A PrivilegeCheck would do exactly what the existing static methods are doing: check IsInRole and return true/false based on whether the current user MAY be able to perform the requested action.  For instance, if I have a user that is in the Account Managers role, they may be able to edit a Quote object so we would enable the Properties toolbar button and allow them to open the Quote details form/page.

An AccessCheck is where the instance part comes in that I am talking about.  An AccessCheck is what determines whether or not the user can actually perform that action on the specific instance of a BO.  This would be where we would have to apply the authorization rules that I described and is what would tell me to display the form as read-only and/or disable the Save button, etc.

Hmm, that sound very similar to what I was getting at. Wink [;)]

SonOfPirate:
What dawned on me in reading this is that my problems may be solved as simply as creating overloaded versions of the static methods that accept the instance being checked as a parameter.

Pirate, why the static method though (I know, I threw something similar out there as well..)? 

It seems it would be easier if you had instance properties, then you could even bind those properties to the Enabled state of buttons. 

Anyway, it sounds like you're making progress, so that's good

Andy

JoeFallon1 replied on Monday, April 09, 2007

Looks like you combined my advice with Andy's. <g>

I said you needed an instance of the BO and Andy said: "your static methods will have to take parameters that give enough information to get a result.  The parameterless static methods would return in general what someone may do."

Your detailed explanation was very valuable - it made the "advice" much clearer on how to do it and why.

I am not sure that the sample code at the end matched you original use cases though.

Also - you didn't show *when* you instanitate the instance of the DataItem which is a Quote object.

In your sample code you already had it. Why would you bother creating it if the user wasn't allowed to do something with it? (The paremeterless check.) Once you determine they might be able to do something with it, it makes snese to create an instance and find out exactly what they are allowed to do.

Joe

SonOfPirate replied on Tuesday, April 10, 2007

First answer: the static methods follow the Csla approach and are declared as such so that the current user can be authorized without instantiating an object of the specific type.  For instance, if I have a New Project menu item, I can check Project.CanCreateObject() and set the menu item's enabled or visible property accordingly.  Certainly not someplace you'd want to instantiate an object.

As for having both of the methods I described static, it is mostly for consistency.

Second question, the sample code I shown at the end of my last post is from a real-world application that is (now) running this exact code.  I did not copy the rest of the code from the class because it is not relevant and is quite large.  Nonetheless, the DataItem property returns the BO that the form is bound to - in this case a Quote object.  Since the form is used to display the BO for inserting (creating), editing or in read-only mode, you have to instantiate an object.  The only right that isn't included in this is the right to view the object (via CanReadObject).  This check is performed in another event handler and throws an exception if the user isn't allowed to view the object.  Otherwise, with that right granted, the form is going to be displayed in one of its three modes so we will always need to instantiate the object.

And, ultimately, it was intended as a possible use of the code explained above it and not meant to be comprehensive.

 

Jimbo replied on Saturday, April 14, 2007


We have many cases of the same basic problem where the state of the instance will override the result of the static canEditObject and canDeleteObject methods and this is needed to control the state of the UI. However this cant be transmitted to the UI (developer) without instantiating the object, which makes the concept of the static method overloads tempting.
However I am having difficulty seeing how you can enforce the use of the overload of the static canEdit, canDelete etc methods over the default method.

jimbo

RockyRocks replied on Tuesday, April 17, 2007

use-cases of a user getting read access to only certain properties:

What about an application that holds personal details like salary - say an HR application? Most users should only be able to view 'public' information like e-mail address, emergency contact details etc. but not be able to see whether they get paid more than the person they are enquiring about! Only HR and high up manager types should be able to see salary details.

Another one is credit card details (held no doubt in an encrypted format and using SecureString!) which should not be visible to most people but may need to be visible to one or two high up managers. As mentioned above, in this instance you might not raise an error but return something different. It might be that some people are able to see all the card numbers but most can see only the last 4 digits.

Here in the UK we are more strict on other personal information like home address etc. than I believe is the default case in the States so this may be another area where this kind of per-property read enablement is beneficial. Again, a return of blank details or some suitable message may be more appropriate than raising an error.

 

JabbaTheNut replied on Thursday, May 03, 2007

SonofPirate,

What did you end up doing? 

I have a very similar situation in which I have a User object that can be edited by:

1) Administrator

2) User Admin

3) The user represented by the object (i.e., a user can edit his or her own record)

In the first two cases, I can simply use the static CanEditObject() method.  But in the third case, I would need an instance of the object to make the determination that the user was editing his or her own object.

Plowking replied on Thursday, May 24, 2007

Hi JabbaTheNut

In the particular case you mentioned, where a user is editing their own record, you dont need an object instance! As long as the object you are editing has a unique key corresponding to the application.context.username you can do a check on this, as I indicated! (i.e. there is a one to one relationship between your user and the object)

In your canGetInstance static method you supply the username which uniquely identifies the object you wish to fetch as a parameter. If username parameter matches your logged in username canGetInstance will return true. Use canGetInstance to deny your user access to the edit instance screen. Once yor user has reached the edit instance screen, you have an object instance anyway, so you can in principle use a non shared canEditInstance method to check if they have edit rights, or a shared method where you pass your instances username as a parameter (this is what I was getting at in my last post).

I'm pretty sure this theory holds, also for an address object etc, just so long as there is a one to one relationship between user and address, simply ensure the address object includes a property which is the users login name.

jamie.clayton replied on Thursday, January 22, 2009

I know this post is from a while back, but I'm looking at the problem as a row level database security situation.

For my two cents it would seem that the having an overloaded CanEditObject(typeof(CSLA BusinessObject)) means that the cat is out of the bag and another CSLA business object (Collection/list etc) has already read and retrive the data incorrectly for a user role.

Being a defensive programmer I can see other ways to stop users from reading/editing and use the CSLA AuthorizationRules.

During the DataPortalFetch methods, reading the data and then calling throwing Security.SecurityExceptions or explicity setting the AuthorisationRules after the data retrieval attempt.  This way the BO isn't going to pass information back to the client application and expose data.

Alternatively the SQL text/storedprocedure used by the dataportal fetch could pass security information and then also "block" returning data if the user wasn't allow to get at it.

If the CSLA solution was exposed to the world via a Web service, there is no way I would allow the BObject to pass information out.

Example code that applies AuthorizationRules in another part of the business object ( I don't see why that's not a logical and easiest thing to do?).

Dim DenyPaidRecordUsers As String() = {My.Resources.Role_User}

Dim EditPaidRecordsUsers As String() = {My.Resources.Role_Admin}

If HasBeenPaided Then

With AuthorizationRules

' More importantly if a record has been paid general users can not edit or delete it.

.DenyExecute("DeleteRecord", DenyPaidRecordUsers)

.AllowExecute("DeleteRecord", EditPaidRecordsUsers)

' In defensive mode, lets block the user from saving the record (just case they have loaded a record [or created one from scratch])

.DenyExecute("Save", DenyPaidRecordUsers)

.AllowExecute("Save", EditPaidRecordsUsers)

' If there is a payment, then don't allow all the fields to be edited.

' Admin user role should be allowed to change some information.

.AllowWrite("SaleDate", EditPaidRecordsUsers)

.DenyWrite("SaleAccountBal", DenyPaidRecordUsers)

End With

End If

 

jamie.clayton replied on Thursday, January 22, 2009

Let me add to my previous post. The CSLA 3.x AuthorizationRules add entries only, so you can't change them after you create the object. I do however need to change authorization based on OB instance data. To get the Deny properties to work I've had to use AuthorizationRules.InstanceDenyWrite("PropertyName") and Shadow the  CanWriteProperty("propertyName",Boolean) as follows

''' <summary>

''' Need to change this call to ensure that Deny property entries took preference over Write authorization.

''' </summary>

''' <param name="propertyName"></param>

''' <param name="throwOnFalse"></param>

''' <returns></returns>

''' <remarks></remarks>

Shadows Function CanWriteProperty(ByVal propertyName As String, ByVal throwOnFalse As Boolean) As Boolean

Dim result As Boolean = CanWriteProperty(propertyName)

If throwOnFalse AndAlso result = False Then

Dim ex As New System.Security.SecurityException( _

String.Format("{0} ({1})", "Property set not allowed", propertyName))

ex.Action = System.Security.Permissions.SecurityAction.Deny

Throw ex

End If

Return result

End Function

''' <summary>

''' CSLA tests for write/read authorisation and ignores deny

''' As we want to apply instance authorisation (rules that look as BO data) we add deny(Property) calls

''' and then check to see if a property was denied and make sure it has priority.

''' </summary>

''' <param name="propertyName"></param>

''' <returns></returns>

''' <remarks></remarks>

Shadows Function CanWriteProperty( _

ByVal propertyName As String) As Boolean

Dim result As Boolean = MyBase.CanWriteProperty(propertyName)

' Deny authorization should be check for the property

' In most security mechanisms deny overpowers and read/write privalages.

If AuthorizationRules.IsWriteDenied("SaleProgress") Then

result = False

End If

Return result

End Function

Plowking replied on Tuesday, April 17, 2007

    This is much like the suggestion to pass an instance of the object into the shared function . I kind if implemented this in a reverse sense, instead of passing an instance into a shared function , I created a class level function which can be called on an instance

   In this example, a user can edit an instance if their Username matches the instances mUserName property, OR they have CanEditObject rights.

    mUserName as String

    Public Function CanGetInstance() As Boolean
            Return ApplicationContext.User.Identity.Name.Equals(mUserName) Or CanGetObject()
        End Function

Anyone comment on this as good OO design, or is passing an instance into a shared function better?

Ian

Copyright (c) Marimer LLC