Prevent Un-authorized modifications in SAF

There is an issue currently with SAF (Simple Authorization Framework) when it is used for entities in Entity Framework. The issue is that when SAF reads an entity and tests it against the user principal, it would modify the properties which user has no read access to null. There are also properties that uses hos no edit access which Saf can notify the client about it but does not do anything at this stage to prevent un authorized edit on objects with partial permission.

For example if you modify a book object, which you can edit the title, but are not allowed to edit the price, there is no check on the server to make sure you haven’t done so. I call this partial edit access. If you don’t have edit access to the book object, then Saf prevents you from changing any thing.

Supporting partial edit access for entity framework is not hard, but you should be very cautious of doing so due to possible performance implications. In a 3 tier architecture, you don’t keep the object context alive for the whole session, thus, the original object is not kept anywhere when the service is called back for an update. The way EF ensures optimistic concurrency is that it compares all values inside the sql update statement for the fields with CuncurrencyMode set to Fixed to ensure data is not modified in the background, if there has been any modification to the data, a conflict exception is thrown and user has the choice to resolve the conflict.

However, we don’t have the luxury of implementing the authorization checks to the SQL statement, because Saf does not know how to create Sql queries. Instead, we have to load the actual object in the datastore and ensure that modifications are valid. This is possible by using GetObjectByKey method on the ObjectContext:


var oc = ...// the object context
var storeObject = oc.GetObjectByKey(((IEntityWithKey)modifiedObejct).EntityKey);
//Compare properties of the storeObject with modifiedObject to ensure no un- authorized
//change has happened

Above code can compare the store values with current values for properties of which user has no edit permission. Obviously there is a performance hit problem with this approach because an extra query is sent to the database for each modification that involves partial edit or view access on an object. There is also another issue with this approach; the logic here conflicts with the logic of optimistic concurrency check. For example if user has no view access to the price of the book, Saf will put null in the price field. When data is sent back to the server, context assumes that the object is modified and tries to write back the null value. If we replace the null with current store value, un-authorized properties are changed back, but what happens if someone else has changed these properties before? There should be a conflict, but we have overwritten that conflict with our authorization logic.

Therefore, I am still no sure if I should implement this as part of the Saf solution.

Alternative solution is to check the modified properties using some code similar to this:

var unauthorizedProps = ...; //List of unauthorized properties
var modiProps = objContext.ObjectStateManager.GetObjectStateEntry(modifiedObj).GetModifiedProperties().ToList();
//Mark the object unchanged
objContext.ObjectStateManager.ChangeObjectState(modifedObj, EntityState.Unchanged);
foreach(var prop in modiProps.Except(unauthorizedProps))
{
objContext.ObjectStateManager.GetObjectStateEntry(modifiedObj).SetModifiedProperty(prop);
}

Above code would find all the modified properties, filters out just authorized ones, sets the entity as unmodified and markes the authorized props to be modified. This is much better performer than the previous approach. One of these approaches may eventually find their way through Saf if there is no better way to manage this issue.

Advertisement

Quick guild to extend SAF (Simple Authorization Framework)

This post is a quick guide to extend SAF.

1. To create new authorizer (e.g. Authorization Attribute):

Easiest way to create new attributes is to inherit from Grand, Deny, or Custom attributes.

Authorizers are not limited to attributes. In general any authorizer should implement IAuthorizationContainer which contains an IEnumerable of IPrincipleAuthorizer.

All you need to extent the authorizers is to implement IPrincipleAuthorizer with your custom authorization logic.

2. Extend Authorization Rule Provider. If you don’t want to use the provided attribute based authorization rules, you can implement IAuthorizationRuleProvide with your own rules. For example, you may want to use database to get your authorization rules from it. In this case you implement the GetAuthorizers() and GetPropertyAuthorizers() methods of the authorization to generate your IPrincipalAuthorizers from database.

3. Extend AuthorizationContext. Authorization context depends to two providers: It needs 1) a rule provider, and 2) a principal provider. The principal provider. You can extend the context by writing these providers.


Authorization Rule Library in SAF and how to use it with RIA Services and Silverlight

A problem of applying rules by decorating objects using attributes is that there is no concept of re-usability. For example, we want NationalAdmins, StateAdmins, and Managers to be have all permissions over everything. Also permissions for my customer, customer_address, customer_emails, etc. are all similar. Normally you should apply all authorization attributes to every part of the model.

SAF provides an ImportAuthorizationAttribute that solves this problem.

public class AuthorizationMetadata
{
    [Grant...]
    [Grant...]
    public class AdminGroup { }

    [Deny...]
    [Deny...]
    [Deny...]
    public class RestrictedWriteGroup {}

    [Grant...]
    public class TitleViewersGroup {}
}

You can use AuthorizationMetadata class to centralize all your authorization rules. You can then use it in your model as follows:

[ImportAuthorization(SourceType=AuthorizationMetadata.AdminGroup)]
public partial class Model
{
}

What else do you want?

Refactoring SAF to be de coupled from the Authorization Attributes

SAF framework look at the metadata type to figure out the authorization rules. Although this is exactly what we want, but is a design flow. The reason I think it is a design flow and needs re-factoring is that it makes the rules to be defined in code.

Let’s imagine we want to seperate the role of Authorization rule definers from the programmers. Although, it is possible now, to implement IMetadataTypeProvider to load some external dll, but it is still bound to types which is not really necessary.

This means, the Grant and Deny attributes should not be limited to be attributes. It is possible with a small re factoring of the current code.

Let’s define a rule provider: IAuthorizationRuleProvider which retrieves rules for a given object. Grant and Deny are IPrincipalAuthorizer. We can give the IAuthorizationRuleProvider instead of to IMetadataProvier to the methods in PermissionHelper.

Then we need to define the AttributeAuthorizationRuleProvider which has a method GetAuthorizers and relies on an IMetadataProvider.

	public class AttributeAuthorizationRuleProvider<T> : IAuthorizationRuleProvider
	{
		private MetadataProvider _metadataProvider;

		public AttributeAuthorizatonRuleProvider(MetadataProvider meta)
		{
			_metadataProvider = meta;
		}

		public IEnumerable<IPrincipalAuthorizer<T>> GetAuthorizers(Type type)
		{
			var meta = metadataProvider.GetMetadataType(type);

            //Get all attributes for the type
            return meta.GetCustomAttributes(false).OfType<IPrincipalAuthorizer<Permission>>();
		}
	}

We later need an AuthorizationContext as a singleton like:

	public class AuthorizationContext
	{
		IMetadataProvider _metadataProvider;
		IAuthorizationRuleProvider _authorizationRuleProvider;
		public AuthorizationContext(IMetadataProvider meta, IAuthorizationRuleProvider rule)
		{
			_metadataProvider = meta;
			_authorizationRuleProvider = rule;
		}

		//Wrap all the methods in Permission Helper.
	}

Later, we can define a new authorizationRuleProvider called SqlAurhorizationRuleProvider that uses database for authorization rule and in database we can have a table like:
TABLE Authorizations (AuthID, Type, Property)
TABLE AuthorizationRules (RuleID, AuthID, RuleType (Grand/Deny/Custom), ConditionCode, ConditionQuery)
TABLE AuthorizationRuleAssignments (RuleID, Role)
In my providers I avoid using the traditional Provider model because the complexity is not required here and using IoC and a Context object, eliminates the role of Manager for every single provider.

Automatic End to End Authorization (Part 3)

I promised to show you how does the SAF framework really do the magic. I changed my test model from my last post a little bit so I post it back again here:

<pre> [Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Grant(Roles = new[] { "QLDAdmin", "NSWAdmin" }, Permission = Permission.View | Permission.Edit | Permission.Create)]
[Grant(Roles = new[] { "Deleter" }, Permission = Permission.Delete)]
[Grant(Roles = new[] { "God" }, Permission = Permission.View | Permission.Own)]
[Deny(Roles = new[] { "NSWAdmin" }, Permission = Permission.Edit)]
[AuthorizationCustom(CustomType = typeof(TestObject), Method = "OnlyQld")]
public class TestObject
{
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Deny(Roles = new[] { "God" }, Permission = Permission.View)]
public int YouCanSeeMe { get; set; }

[Grant(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
[Deny(Roles = new[] { "God" }, Permission = Permission.Edit | Permission.View)]
public string YouCanEditMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.View)]
public string YouCanNotSeeMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
public string YouCanSeeMeButNotEditMe { get; set; }

[Grant(Roles = new[] { "*" }, Permission = Permission.View)]
[Deny(Roles = new[] { "QLDMan" }, Permission = Permission.All)]
public string EverybodyCanSeeMe { get; set; }

public string[] States { get; set; }

public static Permission? OnlyQld(IPrincipal pri, object instance)
{
var user = pri as TestUser;
if (user.Roles.Any(r => r.Contains("QLD"))
&&
(((TestObject)instance).States != null && ((TestObject)instance).States.Contains("QLD"))
)
return Permission.All;
return null;
}
}</pre>

Note that the Permission for the custom authorization method is nullable this time. It actually won’t work if the return type is not nullable.

Now the magic comes:

  1. Filter what user should not see automatically using FilterAuthorized() extension method.
  2. Send authorization tokens to client. Client can use AuthorizationToken.Visible or AuthorizationToken.Editable methods to bind view parts to data.

Below is an example case for more clarity:

<pre>[TestMethod()]
public void FilterAuthorizedTest()
{
IMetadataClassProvider sm = new SelfMetadata();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var qldMan = new TestUser() { Roles = new[] { "QLDMan" } };

var coll = new[]
{
new TestObject() {YouCanSeeMe = 0, States = new[] {"QLD", "NSW"}},
new TestObject() {YouCanSeeMe = 1},
new TestObject() {YouCanSeeMe = 2, YouCanNotSeeMe = "a", States = new[] {"QLD"}},
new TestObject() {YouCanSeeMe = 3},
new TestObject() {YouCanSeeMe = 4, YouCanNotSeeMe = "b"}
};
var filtered = coll.FilterAuthorized(sm, everyone).ToList();
Assert.AreEqual(5, filtered.Count());

filtered = coll.FilterAuthorized(sm, qldMan).ToList();
Assert.AreEqual(2, filtered.Count());
}</pre>

The framework auto-magically cares about what user can see and what he can’t see.

In case you were not following me, here is the link to SAF Framework.

The major hole here is that the framework does not scrap properties that are not available to user at this stage. This should be a quick fix once I get some more time. Feel free, if you like to contribute. The architecture is quiet extensible and easy to understand.

Automatic End to End Authorization (Part 2)

In my previous post, I talked about the importance of an end to end authorization system. I had an open source project here a while ago for this purpose. I describe the framework with some example here. An old saying says that a piece of code talk more than 1000 words:

Below is a sample class annotated with different authentication attributes.

<pre>
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Grant(Roles = new[] { "QLDAdmin", "NSWAdmin" }, Permission = Permission.View | Permission.Edit | Permission.Create)]
[Grant(Roles = new[] { "Deleter" }, Permission = Permission.Delete)]
[Grant(Roles = new[] { "God" }, Permission = Permission.View | Permission.Own)]
[Deny(Roles = new[] { "NSWAdmin" }, Permission = Permission.Edit)]
[AuthorizationCustom(CustomType = typeof(TestObject), Method = "OnlyQld")]
public class TestObject
{
[Grant(Roles = new[] { "Everyone" }, Permission = Permission.View)]
[Deny(Roles = new[] { "God" }, Permission = Permission.View)]
public int YouCanSeeMe { get; set; }

[Grant(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
[Deny(Roles = new[] { "God" }, Permission = Permission.Edit | Permission.View)]
public string YouCanEditMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.View)]
public string YouCanNotSeeMe { get; set; }

[Deny(Roles = new[] { "Everyone" }, Permission = Permission.Edit)]
public string YouCanSeeMeButNotEditMe { get; set; }

[Grant(Roles = new[] { "*" }, Permission = Permission.View)]
public string EverybodyCanSeeMe { get; set; }

public string[] States { get; set; }

public static Permission OnlyQld( IPrincipal pri, object instance )
{
var user = pri as TestUser;
if (user.Roles.Any(r => r.Contains("QLD"))
&&
(((TestObject)instance).States != null && ((TestObject)instance).States.Contains("QLD"))
)
return Permission.All;
return Permission.None;
}
}

</pre>

Next step is to check the permission for them in different scenarios:

<pre>[TestMethod()]
public void GetObjectLevelPremissionTest()
{
IMetadataClassProvider metadataProvider = new SelfMetadata();
var to = new TestObject();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var god = new TestUser() { Roles = new[] { "God" } };
var adminNsw = new TestUser() { Roles = new[] { "NSWAdmin" } };
var adminQld = new TestUser() { Roles = new[] { "QLDAdmin" } };
var userWA = new TestUser() { Roles = new[] { "WAuser" } };
var userQLD = new TestUser() { Roles = new[] { "QLDuser" } };

var actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Create));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, god);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Own));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminNsw);
Assert.AreEqual(false, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminQld);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Edit));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));

to = new TestObject() { States = new[] { "QLD", "NSW" } };
actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, userWA);
Assert.AreEqual(false, actual.Key.HasFlag(Permission.View));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, userQLD);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Create));

actual = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, adminQld);
Assert.AreEqual(true, actual.Key.HasFlag(Permission.Delete));
Assert.AreEqual(true, actual.Key.HasFlag(Permission.View));
}</pre>

Above code tests the permissions at the object level. We can get permissions at property level also in an array:

<pre>[TestMethod()]
public void GetPropertyLevelPremissionsTest()
{
IMetadataClassProvider metadataProvider = new SelfMetadata();
var to = new TestObject();
var everyone = new TestUser() { Roles = new[] { "Everyone" } };
var god = new TestUser() { Roles = new[] { "God" } };
var anon = new TestUser() { Roles = new string[0] };

var parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, everyone);
var actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.Create));
Assert.AreEqual(false, actual["YouCanNotSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(true, actual["YouCanSeeMeButNotEditMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanSeeMeButNotEditMe"].Key.HasFlag(Permission.Edit));

parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, god);
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, god);
Assert.AreEqual(false, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanEditMe"].Key.HasFlag(Permission.View));
Assert.AreEqual(false, actual["YouCanEditMe"].Key.HasFlag(Permission.Edit));
Assert.AreEqual(false, actual.ContainsKey("YouCanNotSeeMe"));

parent = null;
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, everyone);
Assert.AreEqual(true, actual["YouCanSeeMe"].Key.HasFlag(Permission.View));

parent = PermissionHelper.GetObjectLevelPremission(metadataProvider, typeof(TestObject), to, anon);
actual = PermissionHelper.GetPropertyLevelPremissions(metadataProvider, parent, typeof(TestObject), to, anon);
Assert.AreEqual(true, actual["EverybodyCanSeeMe"].Key.HasFlag(Permission.View));
}</pre>

Don’t get too excited. So far we haven’t seen much. The real beauty is when we get to automatically filter collections and set the client view visibility using the framework with one line of code.

To be continued…

Automatic End to End Authorization (Part 1)

Authorization is still a dilemma in development of business applications. ASP.Net provides the membership provider which supports user/role membership and domain service also supports it so you can use UserContext in the client side and User.IsInRole() gives you the user’s role memberships.

This looks good, but is certainly not enough. First of all authorization can be quiet complex such that authorization rules should be applied on a record-by-record basis and also the authorization rules can be more complicated than a single IsInRole check. For example a use in certain rolls can access a filed of a record only if the state of the user (e.g. Queensland) is similar to the state of the object (e.g. Pharmacy in Queensland).

Currently, you have to implement authorization on the server-side. Then you have to implement the same thing in your database using SQL techniques and you have to implement all the scraps again in the client side to figure out which filed or views are visible and which are read only!!! This is owe-full, isn’t it?

The way I would like to do all my authorization is as follows:

  1. In the service side I want to be able to assign authorization attributes to the model’s metadata, (similar to authorized attribute in MVC which you can apply to controllers) . I should be able to have custom authorizations.
  2. In the service side for Queries, I want to have a filter athorized method, which get’s an IEnumerable of roles and/or users and filters the records, it should remove the records that user can not have access to, scrap the fileds that user has no access to (if he has access to the record) and attach an access attribute to the whole collection and each individual field (if different from the collection).
  3. In the service side for Commands, I want to have a check authorized method, which get’s an IEnumerable of roles and/or users, and the object which is supposed to be changed and returns a boolean identifying if is allowed or not.
  4. It would be great if in the service side, exists an IsAllowed method which get’s the data context and a list of users and roles and returns if the modifications in the context can be authorized.
  5. In the client side, there should be an attribute attached to each returned object from the service. The attribute contains the fields the accessible fields. For example “Name” is visible and read-only, “DateOfBirth” is visible and editable, etc. This attribute can be used to change the view appearance for visible and read-only fields. For MVVM, the visibility and edit-ability of parts of a view can be bound to these attributes (of course through a method or using a converter).
  6. In the client side, there should be a service that generates a dummy empty object with it’s authorization attributes to be bound o the view for creating a new object.
  7. In the Database side,  a function should be automatically generated, to do the authorization. Although this may look impossible in presence of custom authorization, there are ways to do that: First, to use a provider model and force user to implement the same custom authorization as SQL function. Second, (only for SQL Server) use CLR and create a CLR class that uses the same service side methods for this reason.

Extra benefit of using this approach is that, the authorization rules can be changed without any change to the client side. Full loose coupling!

Note that although this approach provide full end to end authorization by only defining rules at one place (if implemented completely) effectively, it can not be efficient (specially in DB side). User still should use their own personalized queries to query large datasets. However, his job can be simplified by shortlisting the results by more general rules, and fine tune authorization by filtering using the generated authorization functions.

I  try to implement this in my spare time and update implementation details in the blog.