|
Security / Application | org.jaffa.security |
Overview
This document details the Application Security architecture implemented in the Jaffa such that any
application using this framework can take advantage of this feature.
Introduction
Jaffa has three important configuration files used to model security (components.xml, business-functions.xml
and roles.xml). To build an application that uses the Application Security framework these files must be set
up to model the security rules of the application.
Below shows the basic overview of the JAFFA descriptors, each one being defined in its own XML file
Click on image for larger view
The rest of the document will explain the three different types of security which can be used via the
Jaffa framework and some sample code.
Notes On Modeling
The Security model is design to reflect the use-case model, but there are two ways to approach this mapping.
In a use case model we can have inheritance of actors, this implies that an actor that extends another also has
all the privileges of the actor they extend.
For example we take the example of a manager extends the supervisor, which extends the clerk. At each level this
actor has more access to functions in the system (use-cases!). The decision becomes when you want to map real users
to roles. To create a user who is a manager do you give them access to the 'Manager' role, or access to the
"Manager,Supervisor and Clerk" Roles.
The assumption we have made in our design, and in the Together Modeling Plug in, is that in the above case the
role clerk has access to some functions, and then the supervisor role is only defined to have access to the extra
functions beyond what the clerk has, and likewise the Manager role only has access to the addition functions that
the supervisor or clerk does not have. In this case to give a user access to the full set of manager functions
they must be defined to have access to the roles "Manager,Supervisor,Clerk".
We chose this approach as it allows the architecture to combine the roles and deal with inheritance, and it keeps
true to the encapsulation of inherited roles, just like object inheritance. If you prefer the other way, you'll need to
invent a way to 'denormalize' the current roles.xml file, so inherited function are explicitly defined in each role that
inherits another.
|
Role-based Security |
The concept for role-based security is that a role represents a profile of what a user can do.
These roles will govern access to components and business functions. As many users of a system
perform the same roles, users will be assigned to roles. In this way the need for setting up a
security profile for each specific user is greatly reduced. A role can be defined for each ‘Actor’
that is using the system, and each user will represent an instance of a particular actor.
In the use-case model, just as an actor can inherit from other actors, a user can be associated
with multiple roles. We may have a Clerk Role, and a Manager Role, and a Paul may be assign the
Clerk role, where as John, the manager may be assigned the Clerk and Manager Roles as he can play
in both roles.
The reason for ‘declarative’ security roles is to add flexibility to the application. A role is a
notional thing and many organizations want to define their roles in different ways with different
levels of granularity. From a use-case modeling perspective, this is like developing the use-cases,
and then at application implementation time allow the actor hierarchy to be defined for a specific
implementation of the application. This implies that the software should not be coded to make decisions
based on specific actors, or roles, it should use an abstract programming model that could then be
used to defined the scope of these roles
For this reason the architecture should assume that roles will be modified (create, update, delete)
while the application is operational.
|
Component Based Security |
Jaffa is based on a ‘component’ architecture, each component relates to all or part
of a use-case. For a given role we need to be able to decide whether this component
should be available. If a component is not available the security system should not
allow the user to execute that component.
It should be possible to ascertain whether or not a user has access to a given component
prior to executing it so that it could be removed from the user’s view. This capability
will be essential is a menu based user interface is to be used by the application, which
should hide access to components based on the users security.
|
Business Function Security |
In addition to component security, many applications need to control access to
various functions at a much lower level. This level of access will be referred to
as ‘Business Function’ level access. A business function can be as small as a
single line of code, or may represent a complete set of use cases. It is entirely
up to the scope of the application using Jaffa framework.
Again based on the abstract nature of the layered architecture, this ‘guarded’
business function maybe something in a domain object, service class, transaction
controller, component controller or even an action class.
The main principle is that a set of code will be encapsulated by a named business
function, and the encapsulated code will only be executable if the role of the current
user grants access to this business function
|
Writing Secured Web Pages with Jaffa |
With Jaffa's Tag Library we provide two ways to guard the generation of HTML content, either
by individual business function access, or by component access (which is based on access to
all the mandatory functions for that component). The two tags can be either to display the
contained HTML based on either access, or no-access to the specified function or component.
These guards are heavily used arround buttons which link to the execution of related components
such that the button is hidden if you don't have access to the component it executes.
For more details look at the Tag Library referece guide
|
Writing Secured Code with Jaffa |
This section explains how to use the Jaffa Security Model into your application to
guard different functions (business-functions) based on role-based access. It also
explains how to handle component access based on the business function access. There
also some sample code explaining the same.
- The following code shows how to guard a function and throws an exception from the secured method.
public FormKey do_Button1b_Clicked() {
System.out.println("Trying to do something protected by Function1");
try {
SecurityManager.runFunction("Function1", new PrivilegedExceptionAction()
{
public Object run() {
throw new UnsupportedOperationException("Test Exception");
}
});
} catch (AccessControlException e) {
((Page1Form)form).setMessage("You were not allowed access to Function1");
} catch (PrivilegedActionException e) {
((Page1Form)form).setMessage("Caught Exception From Secured Function : " +
e.getException().getClass().getName() + " - " + e.getException().getMessage());
}
return new FormKey(Page1Form.NAME, component != null ? component.getComponentId() : null);
}
|
- The following code handles the fact that security access is denied.
public FormKey do_Button2_Clicked() {
System.out.println("Trying to do something protected by Function2");
try {
SecurityManager.runFunction("Function2", new PrivilegedAction()
{
public Object run() {
String msg = "Doing Something Protected By Function2";
((Page1Form)form).setMessage(msg);
System.out.println(msg);
return null;
}
});
} catch (AccessControlException e) {
String msg = "You Were Not Allowed Access To Function 2";
((Page1Form)form).setMessage(msg);
System.out.println(msg);
}
return new FormKey(Page1Form.NAME, component != null ? component.getComponentId() : null);
}
|
- The following code tries to run a component that the user may, or may not have access to
public FormKey do_Button3_Clicked() {
System.out.println("Trying to run component Test.Security.Test2");
try {
IComponent c = ComponentManager.run("Test.Security.Test2", UserSession.getUserSession(request) );
String msg = "Running Component : Create Succeeded";
((Page1Form)form).setMessage(msg);
System.out.println(msg);
} catch (AccessControlException e) {
String msg = "No Access To Component";
((Page1Form)form).setMessage(msg);
System.out.println(msg);
} catch (ComponentCreationRuntimeException e) {
String msg = "Running Component : Create Failed";
((Page1Form)form).setMessage(msg);
System.out.println(msg);
}
return new FormKey(Page1Form.NAME, component != null ? component.getComponentId() : null);
}
|
|
Configuring XML descriptors |
As mentioned earlier the three configuration files needed to model security are
roles.xml, business-function.xml and components.xml. This section explains how you
can configure the three xml files.
Note: Here we take the example of the
UserMaintenance in SampleApp
Scenario: A ClientUser (role) does not have access to UserMaintenance while an
Administrator has access to UserMaintenance.
First of all , a component definition is defined in components.xml
Components.xml |
<component id="User.UserMaintenance">
<class>org.jaffa.applications.sample.modules.user.components.usermaintenance.ui.UserMaintenanceComponent</class>
<type>Skeleton</type>
<mandatory-function name="User.User.Maintenance"/>
</component>
|
Then a business function is defined in business-function.xml which is mentioned in components.xml
Busniess-Function.xml |
<business-function name="User.User.Maintenance">
<description>Create/Modify User Account</description>
</business-function>
|
Then the roles.xml is defined specifying which user/role has access to the business function.
Roles.xml |
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE roles PUBLIC "-//JAFFA//DTD Security Roles 1.0//EN"
"http://jaffa.sourceforge.net/DTD/security-roles_1_0.dtd">
<roles>
<role name="User">
<description>This is a User. Every other user needs to inherit from this User</description>
<grant-function-access name="User.User.SelfUpdate"/>
</role>
<role name="Client">
<description>This is a Client user</description>
<include name="User"/>
<grant-function-access name="User.User.Inquiry"/>
<grant-function-access name="User.User.Details"/>
</role>
<role name="Administrator">
<description>This is an administrator</description>
<include name="Client"/>
<grant-function-access name="User.User.Maintenance"/>
<grant-function-access name="User.User.Inquiry"/>
<grant-function-access name="User.User.Details"/>
</role>
</roles>
|
Note that the business function User.User.Maintenance is defined only for Administrator
You can alternatively generate these xml files from a UML model,
(if you have the Together Control Center) using the
Jaffa Security Plug in
Now, In addition to this we should guard this function in the transaction controller.
The code in transaction controller looks something like this. (Full source available in CVS)
private boolean hasMaintenanceAccess() {
return hasAccess("User.User.Maintenance");
}
private boolean hasAccess(String functionName) {
boolean access;
try {
SecurityManager.runFunction(functionName, new PrivilegedAction() {
public Object run() {
return null;
}
});
access = true;
} catch(Exception e) {
access = false;
}
return access;
}
|
And in the create function for UserMaintenance you would use it like this.
// ensure that the user has access to the business function - User.UserDetail.Maintenance
if (!hasMaintenanceAccess()) {
String str = "Create aborted. User has no access to the Business Function: 'User.User.Maintenance'";
log.error(str);
aes.add(new UserMaintenanceException(UserMaintenanceException.PROP_NO_MAINT_ACCESS));
throw aes;
}
|
|
Includes and Excludes |
New in v1.2 of Jaffa is two extra tags inside a role definition. These are <includes> and
<excludes> . This are generated by the Together Security Plug-In (or can be created manually).
<includes>
If a role inherits from another role, then an <includes> tag is put in <role>
tag. It is possible that a role can include multiple, or that and included role itself, includes other roles.
The jaffa security model does not use this information directly, but makes it available via its security API's.
This can then be used to validate that a user has selected a valid set of roles bases on these rules. If you look
at the UserMaintenace component in the sample app, you can see this in action.
<excludes>
If a role is mutually exclusive with other roles, then an <excludes> tag is put in <role>
tag. For example in a supply chain system you could have the roles, Supplier, Buyer and Customer, to represent a three part supply
chain, where you can only be one of these. In this case a each role, would be defined to exclude the other two.
Again, the UserMaintence component in the sample app will use this information to validate the list of Roles selected
for a user, and throw a validation error if any mutually exclusive role combination is selected.
|
|