Authorization grants access to activities and data service resources for certain users. For example, an activity removing a resource from a service may only be executed by an administrator whereas a database query may be executed by any user.
The authorization interface is an extensibility point where developers can plug in their own authorization classes.
In what follows we refer to example code. These examples can be compiled. The How to Compile and Run the Examples section of the Client Toolkit Tutorial contains information on how to compile all of our examples.
Access can be controlled by classes implementing the uk.org.ogsadai.service.authorization.AccessAuthorizer interface. This interface defines two methods which govern access to data service resources and activities:
See the javadoc documentation of uk.org.ogsadai.service.authorization.AccessAuthorizer for a detailed description of the interface.
In this example we will create a simple file-based authorizer which grants or denies access according to an access control list. The access control list is stored in an XML file similar to the following example:
All authorization classes must implement the AccessAuthorizer interface. As the authorizer object will be created by reflection when the container is started up, any class implementing the AccessAuthorizer interface must provide a constructor with a single java.lang.String parameter. The value of this parameter is set in the service configuration and may contain any information that an authorizer requires during construction.
Our authorizer implementation will read a configuration file upon construction and authorize access according to this configuration. The location of the configuration file is passed in the parameter to the constructor. We have to complete the following skeleton class:
package uk.org.ogsadai.examples.authorization; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import uk.org.ogsadai.activity.Activity; import uk.org.ogsadai.common.SecurityContext; import uk.org.ogsadai.common.xml.XMLUtilities; import uk.org.ogsadai.service.authorization.AccessAuthorizer; public class SimpleFileAuthorization implements AccessAuthorizer { public SimpleFileAuthorization(String config) { // add code here } public boolean authorizeResource(String resourceID, SecurityContext context) { // add code here } public boolean authorizeActivity(String resourceID, Activity activity, SecurityContext context) { // add code here } }
In the constructor we load an access control list from an XML file. To store this list we add the following member variables:
protected Map resourceAuthorization = new HashMap(); protected Map activityAuthorization = new HashMap();
The first map maps a resource ID to the set of user names which are authorized to access the resource. The second map does the same with activity names. For the sake of simplicity, in this example all activities have the same access control list on every resource.
The constructor populates the above maps with the information provided by the configuration file:
public SimpleFileAuthorization(String config) throws Exception { // Parse the XML configuration file into a DOM document Document doc = XMLUtilities.xmlFileToDOM(config, false); // Scroll through all resource elements NodeList resources = doc.getElementsByTagName("resource"); for (int j=0; jSince the access control lists are being populated upon construction the implementation of authorizeResource only has to check whether the user's distinguished name (as provided by the security context) is contained in the list of authorized users.
public boolean authorizeResource(String resourceID, SecurityContext context) { // deny access if no security context is available if (context == null) { return false; } Set users = (Set)resourceAuthorization.get(resourceID); if (users == null) { return false; } else { return users.contains(context.getDN()); } }Note that the security context may be null if security is not supported on a platform. Even if you write your authorization class only for platforms which supports security it is good practice to make sure that no NullPointerException can occur. For example, OGSA-DAI WSI on a pure Axis/Tomcat platform currently does not support security but it does when deployed on the OMII container.
The implementation of authorizeActivity is similar. It verifies whether the specified user name is contained in the set of authorized users for the given activity. In this (simplified) example, we ignore the resource ID and authorize access to activities regardless of the data service resource. A more appropriate approach would take the resource ID into account, authorizing access to activities depending on which data service resource they are to be processed on. This would, of course, require a different type of configuration file - one that specified the users that are allowed to execute specific activities within the context of specific resources.
The activity object is provided as a parameter allowing access to the activity name and other, possibly implementation-specific, properties. The activity name (which describes the type of activity) should be used to control access to an activity.
public boolean authorizeActivity(String resourceID, Activity activity, SecurityContext context) { // deny access if no security context is available if (context == null) { return false; } Set users = (Set)activityAuthorization.get(activity.getActivityName()); if (users == null) { return false; } else { return users.contains(context.getDN()); } }The complete code of the above example is available at OGSA-DAI/examples/src/uk/org/ogsadai/examples/authorization/SimpleFileAuthorizer.java.
Configuring the Service to Load a Non-Default AccessAuthorizer
At initialisation a data service will try to lookup a parameter which specifies the location of an authorization configuration file in the parameters of the service configuration file server-config.wsdd.
This parameter is optional. If it is not present the service will load the default access authorizer uk.org.ogsadai.service.authorization.NullAuthorizer which always grants access to all data service resources and activities, regardless of the security context.
To enable a non-default access authorizer (for example, the SimpleFileAuthorizer we have implemented above) the location of the authorization configuration must be specified by the value of the parameter dai.authorization.config. An example for an authorization config file:
Note that initialisation of the data service fails if:
- the authorization configuration file does not exist or cannot be read
- the content of the authorization configuration file is not well-formed XML or not of the expected format
- the authorizer class specified in the configuration file does not exist
- an authorizer object cannot be created
The initialisation of a data service does not fail if the property dai.authorization.config is not present in the service configuration. As has been mentioned above, in this case the default authorizer will be loaded.
Specification of the Authorization Configuration
The authorization configuration specifies an authorizer class to be loaded when the data service is initialised.
- Element authorizationConfig - root element of the configuration file. It has the following subelement:
- Element authorizer - specifies the implementation class and configuration information.
- Attribute implementation - specifies a class implementing the AccessAuthorizer interface.
- Attribute config (optional) - contains a string value that will be passed to the authorizer class upon construction.
Managing Authorization
Access authorizers can be accessed and manipulated directly by activities. For example, an activity which creates a new resource may want to specify users who are granted access to the new resource. It depends on the authorizer implementation whether updates of access control lists are supported.
The example SimpleFileAuthorizer above cannot be modified as the configuration file is read only once in the constructor. However, it could be extended to allow for adding or removing users from the lists, rewriting the configuration file where necessary.
synchronized public void grantResourceAccessToUser( String resourceID, String userDN) { Set users = (Set)resourceAuthorization.get(resourceID); if (users != null) { users.add(userDN); } else { users = new HashSet(); users.add(userDN); resourceAuthorization.put(resourceID, users); } // Store the new configuration to the file writeConfiguration(); }Similarly, another method grantActivityAccessToUser(activityName, resourceID, userDN) could be defined which updates the activity access control list.
The complete code of an example of such an updateable authorizer is available at OGSA-DAI/examples/src/uk/org/ogsadai/examples/authorization/UpdateableFileAuthorizer.java.
Within an activity the data service resource authorizer can be accessed through ActivityContext.getAuthorizer(). The returned object may then be cast to the appropriate type and manipulated as required. For example:
AccessAuthorizer auth = mContext.getAuthorizer(); if (auth instanceof UpdateableFileAuthorizer) { UpdateableFileAuthorizer authorizer = (UpdateableFileAuthorizer)auth; // do something // for example, update access control list authorizer.grantResourceAccessToUser(resourceID, userDN); authorizer.grantActivityAccessToUser(activityName, resourceID, userDN); }
Up: OGSA-DAI User Guide ? International Business Machines Corporation, 2002-2006. ? The University of Edinburgh, 2002-2006.