» state machine
|
|
Using CAZE as a finite state machine (FSM)
At its core, CAZE is an implementation of a
finite state machine
(FSM, also called a finite automaton):
However, in order to use CAZE as a finite state machine, you should carefully consider the following CAZE
features that don't have counterparts in the FSM world:
Principals and roles
With CAZE, you'll represent state transitions with action authorization rules.
Because an
ActionRule
has to be associated with a role, you'll have to add at least one role to the
AuthorizationPolicy.Roles
collection. The role associated with an action rule is used by CAZE to determine whether
the rule is executable (i.e. whether the state transition defined by the rule is enabled).
If the
AuthorizationPolicy.CurrentPrincipal
is member of the role, the rule is executable (the state transition is enabled). Otherwise, the
rule isn't executable (from the FSM perspective, the state transition doesn't exist).
When using CAZE as a FSM, you obviously don't want the current principal to influence
the set of available state transition. The best way to do it is to use the
DynamicRole
class:
-
Create an instance of the
DynamicRole class and make sure
the
DynamicRole.IsMemberResult
property is set to true .
-
Add the instance to the
AuthorizationPolicy.Roles
collection.
-
Associate each
ActionRule (state transition) with the DynamicRole instance
you've added in the previous step.
Another approach you can use to ensure all action rules are allowed is to derive from
the
AuthorizationPolicy
class and override the
AuthorizationPolicy.IsRoleMember
method in such a way that it always returns true .
The sample code at the end of this page illustrates both approaches.
Initial state
The CAZE authorization policy doesn't have a notion of an initial or a final state.
All states are equivalent in this regard and you'll have to make sure that after
populating an instance of the AuthorizationPolicy class with states,
you set the
AuthorizationPolicy.CurrentState
property to the desired initial state.
State transitions
In CAZE, state transitions are invoked by executing actions. The
ActionRule
class associates an Action with two states:
-
The
ActionRule.State
property determines the state in which the action can be executed (the action represents the input symbol in FSM parlance).
-
The
ActionRule.TargetState
property determines the target state in which the state machine resides after the action is executed.
Sample code
The following code defines a simple voting state machine and illustrates how to
actually code around the above described CAZE features:
[Visual Basic]
' Let's name the authorization policy to
' make the FSM concepts more clear.
Dim fsmVoting As AuthorizationPolicy = New AuthorizationPolicy
' Add a DynamicRole instance making sure that any
' principal is member of the role. This will effectively
' make the policy (state-machine) principal agnostic.
' Note that an empty string is a valid role identifier).
fsmVoting.Roles.Add(New DynamicRole("", True))
' Define the states of the machine.
fsmVoting.States.AddNew("NotRunning")
fsmVoting.States.AddNew("InProgress")
fsmVoting.States.AddNew("Suspended")
fsmVoting.States.AddNew("Stopped")
' Define the actions (the machine's alphabet).
fsmVoting.Actions.AddNew("Start")
fsmVoting.Actions.AddNew("Stop")
fsmVoting.Actions.AddNew("Suspend")
fsmVoting.Actions.AddNew("Resume")
' Define the action rules, i.e. state transitions.
' Note: we're associating the rules with the previously
' defined DynamiRole with 'everyone' membership.
fsmVoting.ActionRules.AddNew("Start", "", "NotRunning", "InProgress")
fsmVoting.ActionRules.AddNew("Stop", "", "InProgress", "Stopped")
fsmVoting.ActionRules.AddNew("Suspend", "", "InProgress", "Suspended")
fsmVoting.ActionRules.AddNew("Resume", "", "Suspended", "InProgress")
' The first state added to the States collection is
' used as the initial value of the CurrentState property.
' Because it is the "NotRunning" state, we don't have to
' set the initial state explicitly. If the FSM should "start"
' with a different initial state, we'd use code like this:
' fsmVoting.CurrentState = fsmVoting.States["InProgress"];
' Now we're ready to invoke state transitions. We do it
' by executing actions; the TargetState property of the
' action determines the new state of the state machine.
Debug.Assert(fsmVoting.IsActionExecutable("Start"))
fsmVoting.ExecuteAction("Start")
Debug.Assert(fsmVoting.CurrentState.Id = "InProgress")
Debug.Assert(fsmVoting.IsActionExecutable("Suspend"))
fsmVoting.ExecuteAction("Suspend")
Debug.Assert(fsmVoting.CurrentState.Id = "Suspended")
Debug.Assert(fsmVoting.IsActionExecutable("Resume"))
fsmVoting.ExecuteAction("Resume")
Debug.Assert(fsmVoting.CurrentState.Id = "InProgress")
Debug.Assert(fsmVoting.IsActionExecutable("Stop"))
fsmVoting.ExecuteAction("Stop")
Debug.Assert(fsmVoting.CurrentState.Id = "Stopped")
[C#]
// Let's name the authorization policy to
// make the FSM concepts more clear.
AuthorizationPolicy fsmVoting = new AuthorizationPolicy();
// Add a DynamicRole instance making sure that any
// principal is member of the role. This will effectively
// make the policy (state-machine) principal agnostic.
// Note that an empty string is a valid role identifier).
fsmVoting.Roles.Add(new DynamicRole("", true));
// Define the states of the machine.
fsmVoting.States.AddNew("NotRunning");
fsmVoting.States.AddNew("InProgress");
fsmVoting.States.AddNew("Suspended");
fsmVoting.States.AddNew("Stopped");
// Define the actions (the machine's alphabet).
fsmVoting.Actions.AddNew("Start");
fsmVoting.Actions.AddNew("Stop");
fsmVoting.Actions.AddNew("Suspend");
fsmVoting.Actions.AddNew("Resume");
// Define the action rules, i.e. state transitions.
// Note: we're associating the rules with the previously
// defined DynamiRole with 'everyone' membership.
fsmVoting.ActionRules.AddNew("Start", "", "NotRunning", "InProgress");
fsmVoting.ActionRules.AddNew("Stop", "", "InProgress", "Stopped");
fsmVoting.ActionRules.AddNew("Suspend", "", "InProgress", "Suspended");
fsmVoting.ActionRules.AddNew("Resume", "", "Suspended", "InProgress");
// The first state added to the States collection is
// used as the initial value of the CurrentState property.
// Because it is the "NotRunning" state, we don't have to
// set the initial state explicitly. If the FSM should "start"
// with a different initial state, we'd use code like this:
// fsmVoting.CurrentState = fsmVoting.States["InProgress"];
// Now we're ready to invoke state transitions. We do it
// by executing actions; the TargetState property of the
// action determines the new state of the state machine.
Debug.Assert(fsmVoting.IsActionExecutable("Start"));
fsmVoting.ExecuteAction("Start");
Debug.Assert(fsmVoting.CurrentState.Id == "InProgress");
Debug.Assert(fsmVoting.IsActionExecutable("Suspend"));
fsmVoting.ExecuteAction("Suspend");
Debug.Assert(fsmVoting.CurrentState.Id == "Suspended");
Debug.Assert(fsmVoting.IsActionExecutable("Resume"));
fsmVoting.ExecuteAction("Resume");
Debug.Assert(fsmVoting.CurrentState.Id == "InProgress");
Debug.Assert(fsmVoting.IsActionExecutable("Stop"));
fsmVoting.ExecuteAction("Stop");
Debug.Assert(fsmVoting.CurrentState.Id == "Stopped");
The following code illustrates how you can inherit from
the
AuthorizationPolicy
class and override the
AuthorizationPolicy.IsRoleMember
method making sure that all action rules (state transitions) will be enabled
regardless of the current principal:
[Visual Basic]
' <summary>
' A simple state machine (all action rules are enabled).
' </summary>
Private Class StateMachine
Inherits AuthorizationPolicy
' <summary>
' Returns always <see langword="true"/> making the class
' principal-agnostics (i.e. any principal
' can execute the defined actions).
' </summary>
Protected Overrides Function IsRoleMember( _
ByVal principal As IPrincipal, _
ByVal role As Role) As Boolean
Return True
End Function
End Class
[C#]
/// <summary>
/// A simple state machine (all action rules are enabled).
/// </summary>
private class StateMachine : AuthorizationPolicy
{
/// <summary>
/// Returns always <see langword="true"/> making the class
/// principal-agnostics (i.e. any principal
/// can execute the defined actions).
/// </summary>
protected override bool IsRoleMember(IPrincipal principal, Role role)
{
return true;
}
}
|