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.


Advertisements

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.

Simple Authorization in RIA Services and Silverlight using SAF-Framework

SAF is designed with simplicity and extensibility in mind. All you have to do to use it in RIA-Services and Silverlight  to manage end-to-end security is not much. The following example shows how to use SAF to manage Authorization in a project with RIA services and Silverlight.

We have an Australia wide shop. We use ASP.Net membership provider. And three Roles: Customer, StateAdmin, and NationalAdmin.

1. In your data model:

Assign security attributes:

	[MetadataType(typeof(Metadata)]
	public partial class ShoppingItem
	{
		[Grant(Roles=new[]{"StateAdmin"}, Permissions = Permission.All)]
		[Grant(Roles=new[]{"NationalAdmin"}, Permissions = Permission.All)]
		[Deny(Roles=new[]{"StateAdmin"}, Permissions = Permission.All, ConditionMethod = "NotInState")]
                [Grant(Roles=new[]{"Customer"}, Permissions = Permission.View)]
		[Deny(Roles=new[]{"Customer"}, Permissions = Permission.View, ConditionMethod = "NotInState")]
		public class Metadata
		{
			public static bool NotInState(object instance, IPrincipal principal)
			{
				var user = principal as CustomUser;
				var shoppingItem = instance as ShoppingItem;
				//You should check for null in production code
				if (!user.ActivityStates.Contains(instance.State))
					return true;
				return false;
			}

			[Deny(Roles=new[]{"Customer"}, Permission.View)]
			public decimal PriceThatWeBought { get; set; };

			[Deny(Roles=new[]{"StateAdmin"}, Permission.Edit)]
			public decimal PriceThatWeSell {get; set; };
		}
	}

Above code means that you have a ShoppingItem in your model, and following security rules apply:

  1. Customers only can view items and can not change anything.
  2. State admins have all the rights over items in their states of activity. They have no access to anything in other states. (They can view other state items only if they have Customer role as well and this is because the grant view to customer if after deny to the StateAdmin)
  3. NationalAdmins can do anything
  4. Customers can not see the PriceThatWeBought
  5. SateAdmins can not change the PriceThatWeSell because CEO only trusts himself.

As you can see writing the above rules in code is as easy as describing them in English.

2. At your Service Level:

You have three methods. One to get a specific shopping item. Second to get all shopping items. And third to save a shopping item. Here is all the code you have to write:

	public partial class MyDomainService
	{
		public Tuple<ShoppingItem, AuthorizationToken> GetShoppingItemById(int id)
		{
			DomainContext.ShoppingItems
				.Where( s => s.Id = id)
				.Select(s => s)
				.FilterUnAtuthorizedWithToken()  //What user should not see will be scapped here.
				.First();
		}

		public IEnumerable<Tuple<ShoppingItem, AuthorizationToken>> GetShoppingItems()
		{
			DomainContext.ShoppingItems
				.Select(s => s)
				.FilterUnAtuthorizedWithToken()  //What user should not see will be scapped here.
				.First();
		}

		[RequiresPermission(Permissions.Write)]
		public SaveShoppingItem(ShoppingItem item)
		{
			DmoainContext.SaveChanges();
		}
	}

We have only written three lines of code here to do all the authorization:
First and Second are the FilterUnAuthorizedWithToken() extension method that filters everything based on the user’s privileges and scraps fields that user should not see like PriceThatWeBought.
Third is the RequresPremission attribute which infers only someone can run this method that has Write permission over ShoppingItem object. For example an StateAdmin of Queensland can not edit a shopping item from NSW.

You can see the return types are Tuple<ShoppingItem, AuthorizationToken>. This is because the FilterUnAuthorizedWithToken() will attach an authorization token to any item returned. You can use FilterUnAuthorized() method if you don’t want an authorization token.

3. At the View (and ViewModel)

In you silverlight, the data context for your view is your view model.

	public class ShoppingItemViewModel
	{
		private ShoppingItem _shoppingItem;
		private AuthorizationToken _authorizationToken;

		public void Load()
		{
			//Load the data and authorization tokens here.
		}

		public string ItemName {get{...}set{...}}
		public bool ItemNameVisible {get{return _authorizationToken.IsVisible("ItemName");}}
		public bool ItemNameEditable {get{return _authorizationToken.IsEditable("ItemName");}}

		//Do the same for all other properties.
	}

The only change here is the AuthorizationToken which we saw is return by the FilterUnAuthorizedWithToken() method in the previous section.
Now you have to specify the visibility and editability properties and bind them to the visibility and editability of you XAML controls.
If you want to be more smart in the client side you can write a converter for visibility and editability. And instead of using strings in your code for “ItemName” you can use the property.Name to be type safe.
The magic here is that you can change anything in your authorization rules and policies, but the only place you have to change is your model. You should not worry about the service or view, they get updated auto-magically.

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.