Introduction
Maintaining code with complex permissions tends to be
difficult, because the code can be distributed across multiple classes. By embedding permissions directly on methods and
properties within a class, code is reduced and maintainability is simplified.
Table of Contents
Introduction
Architecture
Jazz Features
Applying Jazz to your
Project
Example with Code walk
thru
Windows Form Project
What is Jazz?
Jazz is an open source project available from codeplex.
It represents a compact, modular framework that can easily be added to
your development projects to build secure applications. Jazz allows complete workflows to be
implemented in a small footprint of C# or VB code.
Jazz may be used as an add-on to your project that
implements secure access to objects, properties and methods.
How easy is it to use Jazz?
Here are the steps to adding Jazz to your project:
- Ensure
that the
targetObject
class inherits from jObject, where MyFirstJazzClass
is the name of your class.
[Jazz]
public class MyFirstJazzClass : jObject
{
}
- Decorate
your code with any of the Grant construct attributes, where:
"Started" is the name of a state in
MyFirstJazzClass
,
"Manager
" is the name of a Role in MyFirstJazzClass
,
"IsValid
" is the name of a property in MyFirstJazzClass
.
[GrantStates("Started"), GrantRoles("Manager"), GrantProperty("IsValid")]
publicvoid Complete()
{
this.EnterState("Completed");
}
- Create
a Client Nexus to perform secure operations, such as method calls and property
access on the myObject., where "
myUser
" is the name of the
user.
NexusConfig config = newNexusConfig()
{
LoginProfile = jUser.Create("myUser"),
BindUnits = newjObject[] { new MyFirstJazzClass() }
};
using (ClientNexus nexus = ClientNexus.Create(config))
{
}
Who would use Jazz?
Jazz is targeted at applications where:
- Security
Permissions are a key requirement
- It is important that only specific Users and/or
Roles are permitted to access specific members in the business objects.
- Security rules are dynamic – i.e. rules are a
function of the data that itself is changing.
- The
developer wants improved code maintainability by embedding permission
statements directly on the properties and methods.
- The
application has many users, roles and complex actions.
- The
application has workflow attributes. It
is required that certain methods and properties are only available in specific
states.
- The
user interface is cleanly separated from business objects.
The name: where did it come from?
John Hansen and Charles Wiebe developed Jetfire, a persistent scripting language, and
built many applications based on it from 2007 to the present. "We wanted to build the evolution of Jetfire
based on.NET 4.0 and all the ‘what-ifs’ that we had encountered in the past two
years into a lighter, more flexible platform." That amounted to a lot of ‘jazz’.
For further information, check out the wiki at http://wiki.jazzfire.net.
Architecture
The architecture consists of:
- jClass and jObject
- Built-in members of jObject
- First Class Objects
- The Client Nexus
- Permission attributes
jClass
The jClass is an object that provides schema information
about the jObject type.
jObject
The
jObject, also known as the Jazz object, is
the common building block object in Jazz providing many of the features. All
first class
objects inherit from jObject.
Built-in Members of jObject
- The
jObject
class includes the following public members: - Class: Returns the Jazz Workflow Class that
provides Jazz workflow type operations and data. The Class is set during the
Bind process.
- ClientNexus: Returns the nexus
object.EnterState
: Causes 'this' jObject
to enter the
state as specified by 'stateName
'.
- Flags: The following flags provide information
about the
jObject
.
IsAccessible
:
Returns true when this instance is accessible. False may be returned because
access is not allowed due to the ACL or the jObject with this.ID does not
exist. An 'AccessViolationException
'
will be generated any access is attempted when IsAccessible
returns false.
IsBound
:
Returns true if this object has been bound ("Bind" operation) to the
client nexus. See http://wiki.jazzfire.net/Bind.ashx.
IsEmpty
:
Returns true if this is the empty object for this 'jObject
'
class. Empty objects are 'ReadOnly
'.
IsReadOnly
:
Returns true when this instance is readonly. When the instance is readonly
public properties and fields can be read.
Fields and properties cannot be set.
Methods cannot be executed. An 'AccessViolationException
' is generated if write or
execute access is attempted.
IsRoot
:
Returns true if this object is a 'root' object. 'IsRoot
'
is only applicable when Jazz objects are persisted A Root object, even if it has no references,
is not removed by the Server GC unless it is deleted.
- FullName: Returns a name in the format of 'server name'.'workspace name'.'this name'.
- ID: Gets the Guid value for jObjects from the
nexus. IDs are assigned during the 'Bind' process.
Before the initial bind, the ID is empty.
IsNameUnique
: indicates that the name is unique
for all instances of this 'Class'. For example
all 'jRoleBase
' instances will have unique
names.
- Name: Returns the name of this '
jObject
'. If
your 'jObject
' class requires a unique name then
use 'jNameUnique
' as a base class. - StorageStatus: Indicates whether an object's
data is pending or confirmed. 'Pending' indicates
that an object is changed but the server.
Nexus has not yet '
Confirmed
' the
operation.
- Status: Indicates whether this
jObject
instance
'Valid
', 'Deleted
',
'Broken' or 'Disconnected'.
IsDeleted
:
indicates that the jObject is deleted.
IsValid
:
indicates that the jObject is valid.
- StorageType: type of storage unit
- Timestamp:
Returns the
timeStamp
that is issued by server nexus. When blank the
object has been created but not written for the first time. The time stamp is
only valid if 'StorageStatus
’ is confirmed. Time stamps are unique within a
single server nexus.
- Workspace: Returns the 'Workspace'
that this ‘
jObject
’
resides in. All jObject
instances must reside in 1 workspace and only 1
workspace.
Protected Members include:
- _ACL: The ACL (Access Control List) controls how
this
jObject
instance is accessed. Note: If the ACL is empty then there are no
access restrictions.
- _Add(jAclItem): Add a '
jAclItem
'
to this jObject's ACL.
- _Delete: Deletes the jObject.
- _Remove(jAclItem): Remove a '
jAclItem
' from this jObject
's ACL.
- OnAssign: Allows the assignment of predefined
IDs. This is typically used for defining
empty objects for the class.
OnBind
: Executed before this instance is bound
to the client nexus. This method will be
executed every time this instance is bound to a client nexus.
When this
method is executed the 'Grant' attributes are not active.
OnInitialBind
: Executed before this instance is
bound to the client nexus for the first time. When this method is executed the base Jazz
object fields will have been set (except the Flag 'Bound' and 'Initialized'
bits). When
this method is executed the 'Grant' constructs are not active nor is the ACL
active.
Jazz First class objects
First class objects can be fully customized by the
developer. Within Jazz, first class
objects include:
- jRole
- jUser
- jProfile
- jWorkspace
jRole
Roles provide the logged-in user with permissions for accessing
workflows, workspace contents, executing methods, accessing properties and
changing permissions. Roles can contain other roles.
- jRoles
are independent from Roles found in .NET such as Forms Authentication and
Active Directory. However, developers
can customize jRole to wrap .NET roles.
Roles can be assigned to a Jazz
user. Methods and
properties can be permitted based on Roles by using the
'GrantRole
construct'.
jObjects can be assigned roles programmatically (see
ACL).
Roles, used in ACL items, work by
restricting access to the jObjects. Within the
client
nexus, jObjects that are not accessible to the user are marked as
inaccessible.
Role(s) can give a
user special abilities
that may be denied to other users such as:
- The ability to execute methods that have been
declared with the 'GrantRole'
construct.
- The ability to set and get properties that have
been declared with the 'GrantRole'
construct.
- The ability to set a property that has private
getter and public setter.
- Restricting access to
workspaces
(and their objects) when the
workspace has its 'Access Control List' set.
- Restricting access to a
jObject
when its 'Access Control List' is set.
Roles are identified by a unique name which is assigned to
the role when it is created. Once created the name of role may not
change.
jUser
A Jazz user class, jUser, describes a physical user,
typically a person. The jazz user consists of a unique name and a collection of
profiles. A
Jazz user must have at least 1 profile. The jazz user can not contain
roles directly;
profiles contain roles.
jUsers are independent from Users found in .NET
such as Forms Authentication and Active Directory. However, developers can customize the jUser
class to wrap .NET users.
A jUser may have one or more profiles. These are used to login as different aspects of
the user’s life.
- e.g. Joe, the Plumber, Joe, the soccer coach,
Joe, the dad.
- e.g. Jane, the coach, Jane, the player, Jane,
the referee
jWorkspace
jWorkspace is a Jazz directory that allows the user to
organize objects. When an Access Control
List (ACL) is assigned to a jWorkspace, all objects assigned to this workspace
are secured the same way as the workspace.
If a user cannot access the workspace, then the
user cannot access the objects within the workspace.
If a user can only read the workspace, then the
user can only read the objects within the workspace.
ClientNexus
Wikipedia
defines Nexus as a connection, usually where
multiple elements meet, as for example spokes at a hub, originally from a Latin
verb meaning "connect, bind."
The Client Nexus encapsulates objects for a user
and performs security operations on bound objects.
What does ‘fully customized’ mean?
The developer can inherit from the base classes to customize
all or any of the first class objects.
This is done by inheriting the ClientNexusGeneric
class and
passing in the inherited first classes.
Note: jProfile
, jUser
, jRole
and jWorkspace
are classes
designed by the developer. The developer
may choose any name for these classes as long as they meet each interface.
The first class objects contain the features required to
implement security and the code used matches standard classes used in the
development project. Customization
allows the developer to write user and role classes based on Windows or Forms
Authentication.
Jazz Features
States are built into jObject.
jObjects can define a set of states allowing a
jObject
instance to be in one given state. The jObject changes state by executing the 'EnterState
' method.
Operations can be performed upon entering a
state by inserting code in the state method. Instance methods and properties can have their
accessibility changed as the state of the jObject
changes by using the 'GrantState
' construct.
Defining a State
A state is declared in Jazz as a method with a void return
type. For example:
[State]
void Completed()
{
}
When the 'StartState
' attribute is used then the jObject is
placed in that state when instantiated.
[StartState]
void Started()
{
}
Entering a State
To enter a state the "EnterState
" method is used.
this.EnterState("Completed");
ACL
Every
jObject
has an Access Control List (ACL). If the ACL is empty, then there are no access
restrictions on the jObject. ACL items can be added programmatically, such that:
- Certain roles have read only access to the
jObject.
- Certain roles have full access to the jObject.\
- The jObject is totally inaccessible to certain
roles.
- Certain users have read-only access to the
jObject.
- Certain users have full access to the jObject.
- The jObject is totally inaccessible to certain
users.
The ACL is
enforced
by internal mechanisms in Jazz.
ACL Examples
In this example, "Manager"
and "Boss" are Roles defined in the code.
Logged in User
Roles | Object ACL
Items | Actions
Allowed |
"Manager" | empty ACL | Allows full
access of the object. |
no roles | empty ACL | Allows full
access of the object. |
"Manager" | [1] "Manager" - 'Accessible' | Allows full
access of the object. |
no roles | [1] "Manager" - 'Accessible' | No access of the
object allowed. |
"Manager" | [1] "Manager" - 'ReadOnly' | ReadOnly access
of the object. |
no roles | [1] "Manager" - 'ReadOnly' | No access of the
object allowed. |
"Manager" | [1] "Manager" - 'ReadOnly'
[2] "Boss" - 'Accessible' | ReadOnly access
of the object. |
"Manager", "Boss" | [1] "Manager" - 'ReadOnly'
[2] "Boss" - 'Accessible' | Allows full
access of the object. |
Access Enforcement
The grant construct consists of three C# attributes:
GrantProperty
The ‘GrantProperty
’ attribute
grants access to a instance method, property or property setter when the ‘Property
’
value:
is a boolean type returning ‘true’, or
is a role that the logged in user possesses, orreturns a state that is the current state.
GrantState
The ‘GrantState
’ attribute grants
access to an instance method, property or property setter only when the
jObject's
current state is
one of the states specified by the attribute.
GrantRole
The ‘GrantRole
’ attribute grants
access to an instance method, property or property setter only when the logged
in profile has one of the
roles specified by the attribute.
When multiple Grant constructs are used, then the access
granted will be a logical And of the
'grant constructs'. For example if both a 'GrantState
'
and a 'GrantRole
' are used on a member, then
access is granted when the jObject's current state is specified by the 'GrantState
' attribute and the user's login roles are
specified by 'GrantRole
' attribute, otherwise no
access is permitted.
Access Violations
| Applies To: | Access Violation Exception occurs when: |
GrantState | Instance Method
Instance Property
Instance Setter | Attempting an
operation when the jObject's current state is not a
state declared by ‘GrantState’ |
GrantRole | Instance Method
Instance Property
Instance Setter | Attempting an
operation when the logged in user/profile does not possess a
role specified by ‘GrantRole’. |
GrantProperty | Instance Method
Instance Property
Instance Setter | Attempting an
operation when the ‘GrantProperty’ value
is ‘false’ oris a role that the logged in user does not possess
orreturns a state that is not the current state. |
ReadOnly ACL | All methods of an
Jazz object | Attempting to
execute a method when the logged in profile only possesses a role that
supports ‘ReadOnly’ ACL access. |
No Access ACL | All methods, fields
and properties of a Jazz object. | Attempting any access
when the logged in profile does not possess a role that supports access (i.e.
‘Accessible’ ACL item). |
The Difference
How is Jazz different than .NET security?
Let’s look at how Jazz security is different from .NET
security.
Function
| .NET Role-based
security
| Jazz security
|
Access to methods
|
PrincipalPermission
attribute.
Unauthorized access to the method results in an exception.
| Use Grant constructs.
Unauthorized access to the method results in an exception.
|
In-line assertions
| .NET imperative pattern implements in-line assertions for
access to a specific portion of a method.
| Use ‘IsAccessible’ for the
object.
Use ‘IsExecutable’ property
to see if method is accessible.
Use ‘CanRead’ and/or ‘CanWrite’ for properties.
|
Access attributes
| PrincipalPermission supports:
Members of a Role(s) Named UserAny Authenticated user | GrantRoles supports:
Members of a Role(s)GrantStates supports:
Any state in the objectGrantProperty supports:
Any instance property in the object
Named User
Any Authenticated user |
Access to Constructors
| Not supported
| Future
|
Access to Properties
| Not supported
| Use Grant construct to decorate properties.
|
Access to Property Setter
| Not supported
| Use Grant construct to decorate property setters.
|
A Second Level of Security
A second level of security included in Jazz is Access
Control Lists.
Function
| .NET Role-based
security
| Jazz security
|
Access Control List for an object
| Not supported
| jObject have an Access Control List that allow Roles,
Read-only Roles, Users and Read-only Users.
|
Access Control List for a workspace
| Not supported
| jWorkspace has an Access Control List that allow Roles,
Read-only Roles, Users and Read-only Users.
jWorkspace is a container for jObjects.
|
Nexus Binding
jObjects may be used with or without the Nexus. When used
without the Nexus, no security operations take place.
Let’s look at the life of a
jObject
:
- Instantiation
of the
jObject
occurs when a new object is created or data retrieved from the
database is converted into jObjects
. The
object is a typical .NET object.
ToDoItem item = new ToDoItem();
- Bind
the
jObject
to the Nexus. The object now
belongs to the nexus. Secure operations can now be performed on the jObject
and
changes stored.
The jObject
features lie dormant until the 'Bind' operation
binds it to client nexus. A single physical 'jObject
'
may only be contained in a single
Client Nexus;
however an instance of jObject
, copied through serialization, may be in
multiple client nexuses simultaneously.
jObjects are standard .NET objects. jObjects are not
activated until a 'Bind'
operation binds them to
client nexus.
Steps 1 and 2 can be combined:
ToDoItem item = new ToDoItem(nexus);
jObjects may be bound to the nexus at any time. Another
approach is to create the nexus when it is required – this is shown in the code
snippet in the introduction, where a profile is defined as the login profile
and a role and target object are passed into the BindUnits array. Once the
Nexus is instantiated, secure operations can be performed on the target object(s).
Postsharp attributes
Postsharp is
used to implement Jazz attributes. To
use the source code ‘Postsharp’ is required. –It is available at:
http://www.sharpcrafters.com/postsharp/download
Applying Jazz to your Project
To use Jazz in your project, objects that are Jazz targets
must inherit from the jObject and have the Jazz attribute decorate the class.
To do this, make sure that projects referenced include
TrackerRealm.Jazz.ClientTrackerRealm.Jazz.CommonPostsharp
Note: TrackerRealm.Jazz.Client.Custom
is shown in the using
statements, because jUser, jRole, jProfile, jWorkspace and ClientNexus use
these classes in this namespace for this example.
using TrackerRealm.Jazz.Client.Custom;
using TrackerRealm.Jazz.Client;
using TrackerRealm.Jazz.Common;
[Jazz]
publicclass MyFirstJazzClass : jObject
{
}
Creating a Role
A jRole can be created programmatically
just like a .NET object. The following
factory method creates a jRole that is bound to the nexus.
jRole managerRole = jRole.Create(nexus, "Manager");
Creating a User
A jUser can be created
programmatically just like a .net object.
The following code snippet:
- Creates a jUser and default jProfile (implicitly
as part of the jUser factory method)
- Adds the "Manager"
Role to the user’s Profile
- Binds the jUser and jProfile objects to the nexus.
jUser user = jUser.Create(nexus, "Fred", "Manager");
Creating a jObject
The easiest way to control instantiation is to use a
singleton that wraps the object creation process.
Assigning a Role to a Method or Property
A role can be assigned to a
method or property using the
grant construct.
[GrantRoles("Manager")]
public void Approve()
{
this.IsApproved = true;
}
Access Violation Example
For example, given the
following
jObject property:
public string Request
{
get { return this.data; }
[GrantStates("Started")]
set
{
this.data = value;
this.EnterState("Approved");
}
}
The first set of Request demonstrates
a valid access to the property.
Subsequent sets of the Request
property demonstrate an access violation.
myFlow.Request = "May I leave early on Friday?";
try
{
myFlow.Request = "I changed my mind, I want to leave Friday at noon.";
Assert.Fail("should not reach this line!");
}
catch (Exception ex)
{
Assert.IsTrue(ex is AccessViolationException);
}
Using ‘Grant State’
Using the ‘GrantState’
construct, the accessibility of a method or property can change dynamically as
the state of the workflow changes.
[GrantStates("Approved")]
public void Approve()
{
this.IsApproved = true;
this.EnterState("Completed");
}
In this example only the
property setter accessibility is controlled by the 'State' construct.
public string Request
{
get { return this.data; }
[GrantStates("Started")]
set
{
this.data = value;
this.EnterState("Approved");
}
}
Adding an ACL Item
This example shows the instantiation of a new jWorkspace and
jRole. When the role is added to the workspace using ‘AclItem.Accessible’
,
the workspace is now secured, only allowing access to users that have the ‘Manager
’ jRole.
The last line of the code snippet is the syntax for how to
add an ACL to any jObect.
jWorkspace mySpace = jWorkspace.Create(nexus, "MySpace");
jRole manager = jRole.Create(nexus, "Manager");
mySpace.Add(jAclItem.Accessible(manager));
Example with Code walk thru
A group of
users collectively creates ToDo items where any user can volunteer to be the 'AssignedTo' worker for specific ToDo items and when
the items are completed they are approved by a manager.
Figure 1: State Diagram of the To Do Item
- Blue ellipses denote states.
- Text on the connector lines shows the command
and the Grant Role or Grant Property.
A Windows Form project is used to demonstrate the
example. The form code shows how to
design simple forms without knowing very much about the actual jObject.
Make changes to the ‘GroupToDo’
properties and methods and see them reflected on the form without changing the form code.
Users, Roles and Workspaces
There are a number of users and roles included in the demo.
Roles include:
- ‘ToDo’: this Role
allows the user to see any public To Do Item.
- ‘UserAdmin’: this
Role allows the user to see the users and roles that are defined.
Users include:
- ‘Guest’ with no
roles. Guest has no access to anything.
- ‘George’ with
Role: ‘ToDo’ – George can access the To Do
Items.
- ‘Sarah’ with
Role: ‘ToDo’ – Sarah can access the To Do Items.
- ‘Admin’ with
Role: ‘UserAdmin’ – Admin can access the Users
and Roles.
- ‘Manager’ with
Roles: ‘ToDo’ and ‘Manager’ – Manager can access the To Do Items.
Note: Users and Roles
are public jObjects that can be accessed by anyone. The demo uses UI logic to prevent users and
roles from being displayed to anyone except the ‘Admin’
user.
Workspaces include:
- ‘
Public
’: this
workspace holds the classes, users, roles, and empty objects. ‘Public
’ means that the objects in this
workspace are accessible to all users.
Properties and Methods can be decorated with Grant constructs.
- ‘To Do Workspace’:
this workspace holds the To Do Items.
The ‘ToDo’ and ‘Manager’
Roles are assigned to the ‘To Do Workspace’
ACL. This means that the user MUST have
the ‘ToDo’ or ‘Manager’
Role to access the To Do Items.
Note: Users can ONLY see the ID of Inaccessible jObjects. To
check this out, put a breakpoint in the login event. Log into the Guest.
Check out the To Do Item. Each property in the ToDoItemExample class
shows ‘The workflow, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
is not accessible.’.
To Do Item spec outline
Here are the requirements for the To Do Item used in the
article.
-
To
Do Items are created and viewed by all users in the To Do workspace.
-
Users
must have the "ToDo" Role to see the To Do
workspace
-
Managers
have the "Manager" Role.
-
The
Guest and Admin users do not have access to the To Do workspace.
-
Properties
are displayed as edit fields if and only if the user can write to the property.
-
Users
can assign themselves as the assigned to person for the To Do Item. This allows
this user and the ‘Manager’ Role to edit the To
Do Item and others to read it.
- Users
with the ‘ToDo’ Role can create a To Do Item.
-
There
is a Name and Description.
-
There
is a Assigned To Comment that only the "AssignedTo"
person can edit.
-
There
is a Approver Comment that only the "Manager"
role can edit.
-
Tracking
to do items in the "Started", "Defined", "Finished", "Approved" and "Deleted" states.
-
A
‘Manager’ should be able to perform the same
tasks as the ‘AssignedTo’ of the item.
-
Only
a ‘Manager’ can "Accept"
or "Reject" a "Finished"
To Do Item.
- The
owner can "Delete" the To Do Item.
-
If
the To Do Item is "Completed", then nothing can
be changed.
-
If
the To Do Item is "Deleted", then nothing can be
changed.
Building the To Do Item
Inherited properties include:
- Class: the class
object that the jObject belongs to
- Workspace: the directory
that the jObject is in
IsValid
: tells
the user that the jObject is valid
IsDeleted
: tells
the user that the jObject has been deleted
Access related properties include:
CanDelete
– a
combination of states and property checks for when the To Do Item can be
deleted
Commands include:
Definition_Complete
:
allows a granted user to mark the To Do Item as being defined what to do.
Claim_Task
: allows
the granted user to assign the To Do Item to the logged in user
- Finish: allows a
granted user to mark the To Do Item as ‘Finished’
- Approve: allows
the ‘Manager’ to approve the To Do Item.
- Reject: allows
the ‘Manager’ to return the To Do Item to the ‘Assigned’ state.
- Delete: allows a
granted user to delete the jObject
States include:
- Started: this is
designated as the Start State
- Defined: The To
Do Item definition is complete.Finished: The assigned
to user sets this to indicate that the To Do Item is done.
- Approved: The
Manager sets this state. It is a final
state for the jObject.
- Deleted: A
granted user deletes the To Do Item.
Instantiation of the ToDoItemExample yields a jObject with:
- Name = the name
of the To Do Item added
- Description: some
additional comments added by the ‘Creator’
- Assigned To Comment:
a comment added by the ‘AssignTo’ person
- Approver Comment:
a comment added by the ‘Manager’
Note: Only granted users can see
and/or set the values of these properties.
Properties and Methods decorated
with the ‘UI’ attribute make it easy to
determine if the member should be presented on the User Interface.
Jazz Code Example
[Jazz]
public class GroupToDo : jName
{
# region Constructor
public GroupToDo(ClientNexus nexus, string name) : base(null, name)
{
this.Initialize(nexus);
nexus.Bind(this);
}
void Initialize(ClientNexus nexus)
{
if (!this.IsEmpty)
{
this.Creator = nexus.LoginProfile;
jWorkspace ws = nexus.Cache.OfType<jWorkspace>().SingleOrDefault(s => s.Name == "To Do Workspace");
if (ws != null)
ws.Move(this);
}
}
# endregion
# region Properties
private ClientNexus Nexus
{
get { return (ClientNexus)this.ClientNexus; }
}
publicjProfile Creator
{
get;
privateset;
}
publicjProfile AssignedTo
{
get;
privateset;
}
publicoverride string Name
{
get { return base.Name; }
protectedset { base.Name = value; }
}
publicstring Description
{
get;
[GrantStates("Started"), GrantProperty("Creator")]
set;
}
publicstring AssignedTo_Comments
{
get;
[GrantStates("Assigned"), GrantProperty("AssignedTo")]
set;
}
publicstring Approver_Comments
{
get;
[GrantStates("Finished"), GrantRoles("Manager")]
set;
}
# endregion
# region Properties used in Grant Constructs
bool IsCreator
{
get { return this.Creator == this.Nexus.LoginProfile; }
}
bool IsMyItem
{
get
{
if (this.AssignedTo == this.Nexus.LoginProfile.Class.EmptyInstance)
returntrue;
returnthis.AssignedTo == this.Nexus.LoginProfile;
}
}
bool CanDelete
{
get
{
if (!this.IsValid)
returnfalse;
if (this.Nexus.LoginRoles.Contains("Manager"))
returnthis.CurrentState.Name != "Approved";
switch (this.CurrentState.Name)
{
case"Started":
if (this.Nexus.LoginProfile == this.Creator)
return true;
break;
case"Defined":
if (this.Nexus.LoginProfile == this.AssignedTo)
return true;
break;
}
returnfalse;
}
}
# endregion
# region Public Methods
[GrantStates("Started"), GrantProperty("IsCreator")]
publicvoid Definition_Complete()
{
this.EnterState("Defined");
}
[GrantStates("Defined"), GrantProperty("IsMyItem")]
publicvoid Claim_Task()
{
this.EnterState("Assigned");
this.AssignedTo = this.Nexus.LoginProfile;
}
[GrantStates("Assigned"), GrantProperty("IsMyItem")]
publicvoid Finish()
{
this.EnterState("Finished");
}
[GrantStates("Finished"), GrantRoles("Manager")]
publicvoid Approve()
{
this.EnterState("Approved");
}
[GrantStates("Finished"), GrantRoles("Manager")]
publicvoid Reject()
{
this.EnterState("Assigned");
}
[GrantProperty("CanDelete")]
publicvoid Delete()
{
this.EnterState("Deleted");
}
# endregion
# region States
[StartState]
void Started() { }
[State]
void Defined() { }
[State]
void Assigned() { }
[State]
void Finished() { }
[FinalState]
void Approved() { }
[FinalState]
void Deleted()
{
base._Delete();
}
# endregion
}
Windows Form Project
The Windows Form Project contains the Jazz code file plus three
forms:
- Form
Jazz: the driver form used to display navigation and selected objects
- Form
Name Prompt: prompts for the name needed for named objects
- Form
Help: displays help for the project
Displaying a jObject
Figure 2 (below) shows a ‘GroupToDo’ jObject
in form format. The form allows the user
to change the contents of the properties and save the changes.
The navigation pane of the form shows the default data that
is created when the form is opened.
There is no database, so the demo works as a standalone single user demo
that updates data in RAM. Default data includes:
Roles: roles are used in Grant constructs,
assigned to users.
Users: each user can log in to get a unique view
of the data. Just click the Login button in the tool strip and select a user.
GroupToDo: the to do item is used to explore the
security aspects of Jazz.
Click on a Role, User or ToDoItem to see the jObject
displayed in Form format.
To create a new Role or User, login as the Admin, click New
in the tool strip menu and select the item that you wish to create.
To create a new To Do Item, login as George,
Sarah or Manager, click New in the tool strip menu and select the item that you
wish to create.
Figure 2: Displaying
the jObject
Formal Use Case
Use Case Name: Complete the To Do Item
Actors:
- To Do Item Creator: Person who creates the to do
item
- To Do Item Assignee: Person assigned to the to
do item
- To Do Item Manager: Person who has a ‘Manager’ Role
- Guest: Person unassociated with To Do Items
Triggers:
- ‘George’ wants to
complete the ‘To Do Item for George’.
Preconditions:
- ‘George’ created
the ‘To Do Item for George’.
- Visual Studio is started and the JazzWindowsForm
project is selected as the Startup project.
- In Visual Studio, hit F5 to start
debugging. The form will open.
Post-conditions:
- The ‘To Do Item for
George’; is completed.
Normal Flow [A]:
- Click
Login at the top of the form and select ‘George’
as the Login name.
- ‘George’ is now
logged in (see top right of form).
- Select
the ‘[Assigned] To Do Item for George’ in the
navigation panel.
- As
the ‘Creator’, ‘George’
can still edit the ‘Description’.
Add "Enter additional
notes to complete the description" to the ‘Description’
field and click ‘Save’.
-
Now
that the definition is complete, ‘George’ clicks
‘Definition_Complete’.
- The To Do Item is promoted into the ‘Defined’ state and the form is a read-only view of the
To Do Item.
- Let’s
assign this item to George.
-
Add
a comment to the ‘AssignedTo_Comments’ field – "This is the task that I have finished." and click ‘Save’.
This
displays the jObject as displayed in Figure 1 above.
-
Click
the ‘Finish’ button.
- The To Do Item is promoted into the ‘Finished’ state and the form is a read-only view of
the To Do Item.
-
Click
Login As at the top of the form and select ‘Manager’
as the Login name.
-
The
Manager sees that they can include a comment and click either ‘Accept’ or ‘Reject’.
-
Add
a comment to the ‘Approver_Comments’ – "this is good" and click ‘Save’.
-
Click
the ‘Accept’ button. The To Do Item is now ‘Completed’ and the form reverts to a read-only view of
the data.
Alternate Flows [A]
3A1: The definition for the To Do Item is not completed.
3. Click Login As at the top of the form and select ‘Sarah’ as the Login name.
- When ‘Sarah’ logs
in, you see a read-only view of the To Do Item.
4. Click Login As at the top of the form and select ‘Manager’ as the Login name.
-
When ‘Manager’
logs in, you see a read-only view of the To Do Item.
5.
Click
Login As at the top of the form and select ‘Guest’
as the Login name.
-
‘Guest’ does not
have access to the To Do Item.
5A1: The definition for the To Do Item is now completed.
5. Click
Login As at the top of the form and select ‘Sarah’
as the Login name.
- When ‘Sarah’ logs
in, ‘Sarah’ sees the ‘Claim_Task'
button and can claim the To Do Item.
6.
Click
Login As at the top of the form and select ‘Manager’
as the Login name.
-
When ‘Manager’
logs in, ‘Manager’ sees the ‘Claim_Task' button and can claim the To Do Item.
8A1: ‘George’ has finished
the To Do Item.
8. Click
Login As at the top of the form and select ‘Sarah’
as the Login name.
- When ‘Sarah’ logs
in, you see a read-only view of the To Do Item.
Use Case Name: Create a To Do Item
Actors:
- To Do Item Creator: Person who creates the to do
item
Triggers:
- ‘George’ wants to
create ‘A new to do item’.
Preconditions:
- Visual Studio is started and the JazzWindowsForm
project is selected as the Startup project.
- In Visual Studio, hit F5 to start
debugging. The form will open.
Post-conditions:
- The ‘A new to do item’;
is created.
Normal Flow [B]:
-
Click
New at the top of the form and select ‘Create_To_Do_Item’.
- A form pops up prompting you for the name of the
To Do Item. (The name appears in the
navigation pane.)
- Enter
"A new to do item" and click ‘Ok’ or press the Enter key.
-
A
new to do item is added to the Navigation pane.
-
Select
the "A new to do item’ that is just added.
- It is displayed in the editor pane. Note that
the creator matches the name of the logged in user.
Displaying the Schema
Figure 3 (below) shows a GroupToDo jObject schema.
The schema view of the data shows properties and methods for
the To Do Item in its current state. This
is useful in getting a snap-shot of how the jObject is viewed by the logged-in
user.
Properties show the property name, the value, the ‘Can Read’ flag the ‘CanWrite’
and the Grant Constructs for States, Roles and Properties. Note that the Grant constructs for setters
are prefixed by ‘[Setter]’.
Methods show the method name, the ‘IsExecutable’
flag and the Grant Constructs for States, Roles and Properties.
Figure 3: Reviewing
jObject Schema
Code snippets used in the Demo
The demo is built based on a programmatic interface. Let’s
explore the Jazz API.
Get members of the jObject
For developers writing secure applications, it is not always
clear what properties and methods are available to the user. Jazz includes a jPropertyInfo and jMethodInfo
that mirrors PropertyInfo and MethodInfo.
These two classes provide properties that take into account the grant
constructs used in your code.
To get all of the properties used in the jObject, call
GetWrappedMembers()
and filter using the Linq ‘OfType<jPropertyInfo>()
’
method.
The methods are retrieved by replacing jPropertyInfo
with
jMethodInfo
.
IEnumerable<jPropertyInfo> properties = obj.GetWrappedMembers().OfType<jPropertyInfo>();
To get just the members of the sub-class of a jObject
, use
the following code. The difference is in
how GetMembers filters the members of the jObject
. Note that ‘ClassMethods
’ is an extension method.
IEnumerable<jMethodInfo> methods = obj.ClassMethods();
Read the value of the property
Use the jPropertyInfo
to check of the property can be read.
Whereas PropertyInfo.CanRead indicates whether a property has a getter,
jPropertyInfo.CanRead
takes Grant Constructs on the getter into account.
jPropertyInfo pi = properties.Single(p => p.Name == "Subject");
...if (pi.CanRead) {
object value = pi.Read(this.obj);
where pi is the jPropertyInfo
for a property.
Write the value of the property
Use the jPropertyInfo
to check of the property can be
written and to write it. Whereas PropertyInfo.CanWrite
indicates whether a
property has a setter, jPropertyInfo.CanWrite
takes Grant Constructs on the
setter into account.
jPropertyInfo pi = properties.Single(p => p.Name == "Subject");
...if (pi.CanWrite)
pi.Write(this.obj, value);
where pi is the jPropertyInfo
for a property.
Execute a command
Use the jMethodInfo
object to check if the method is
executable.
Note: The code checking for the correct signature is not
shown.
jMethodInfo mi = methods.FirstOrDefault(m => m.Name == command);
if (mi.IsExecutable)
mi.Execute(this.obj);
where command is the name of the method to be called.
Create a jObject
Instantiation of the jObject
is identical to creating a .NET
object. By passing the nexus into the
constructor, the jObject
is bound to the nexus and security-ready.
GroupToDo.Create(nexus);
The demo contains a pattern for providing granted users with
access to the instantiation of Users, Roles and To Do Items. The class is Creator and contains a method
for each instantiation. E.g. to create a To Do Item, the user must have the ‘ToDo
’ Role.
[UI, GrantRoles("ToDo")]
publicGroupToDo Create_To_Do_Item(string subject)
{
returnGroupToDo.Create(this.nexus, subject);
}
Points of Interest
Workflows can be built simply when a powerful technique like 'Grant constructs' are used.