Simple Workflow Engine, Re-designed and Simplified (even more)

Redesigning SWE, a simple workflow engine:

I totally redesigned the SWE project, as I had to use the state machines in one of my own projects, and learned from its experience. The API for SWE is highly simplified. Everything gets done with only one public class “SateMachine<SO, RT>”. This class implements  four interfaces:

  • IStateMachineBuilder
  • IStateMachineController
  • IStateMchine
  • IStableStateMachine

To create and use a state machine is very simple. Each state has also got a very simple concept.

All that matters is:
State: Has a name, does some stuff, and decides what the next step is.

Below is a simple Light; on – off example:

Var smBuilder = StateMachine<Lamp, Lamp>.Create("On")
.State("On", (s, l) =>
{
    l.IsOn = true;
    return StateMachine.NamedState("Off");
})
.State("Off", (s, l) =>
{
    l.IsOn = false;
    return StateMachine.NamedState("On");
});
Var sm = smBuilder.BindTo(theLamp);

//Usage: each call, alternates the lamp between on and off.
While(true){
    sm.Process();
    Thread.Sleep(10);
}

In another post, I show a more advanced use of swe, with a csv parser.

A simple and handy workflow framework

Workflow plays a critical role in business applications, but very few use it (me included). I never used WF in my dev even though I believe workflow is a critical tool for ensuring quality code. The reason is obvious.
In case of WF, usually pain is more than gain. For a simple workflow you’ve got to deal with lots of not so interesting fluff.
The solution is obvious: Cut the crap!
I want to implement a tiny state machine suitable for developing workflows. No crab. Just a little dll to reference with a flexible design. Here comes the story.

Everybody talks about the on-off switch to describe state machine, so do I. Assume the following state machine. A lamp can be in the off or on state and a click will change the state. If it is on will become off and vise versa.

Here comes the code that is required to be implemented with WF state machine. Well, I don’t write the code, its a pretty long story so check the msdn article yourself.

So much crap wouldn’t convince me to use the state machine at all. I also hate to define events and a bunch of codes and classes for such a simple task. This is what I want to do:

var onOffWf = WorkFlow.Start(ILamp)
.State("Off",
	l => l.IsOn, //Enterance condition
	l => l.MakeOff()) //What happens at the state
.Then("On")
.State("On",
	l => k.IsOff, //Enterance condition
	l => l.MakeOn()) //What happens at the state
.Then("Off")

I want all the code to be in one place and also easy to read and understand.

One more thing that I want is to be able to easily save the state of my lamp and its workflow in with my object. For example I want to add a column to my Lamp table (lampId, WorkFlowState) which holds the stat of my lamp. So each time I load my lamp, I continue from where I was before. This is the code that I want to go on to my button OnClick() event:


void OnClick(...)
{
	MyLamp.MoveNext();
}

I also would like to bind the status of my lamp to its state as:

<TextBox ItemSource="{Binding Path=StatusTitle}"/>

onOffWf has a property onOffWf.StatusTitle.

Is that possible? Not with WF. If you know a way let me know quick because I am going to implement this and some time would be spent.

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.

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.