Personal tools
You are here: Home GRIA Documentation Documentation 5.1 Reference Manuals PBAC 2 Manual

PBAC 2 Manual

Note: Return to reference manual view.

PBAC (Process-Based Access Control) is the security system used in the GRIA services. This guide explains how to administer a service using PBAC, and how to use PBAC in your own programs and services.

1. Overview

What PBAC is and how it works.

Introduction

PBAC 2 is a dynamic access control mechanism. Its core is a PDP, which answers questions of the form Can <some user> perform <some action> on <some resource>?. The user is described as a collection of credentials. The action and the resource are strings chosen by the calling application. The answer depends on the roles the user has with the resource, the current state of the resource, and the resource's policy.

The diagram below shows a typical PBAC system. Service invocations from users are intercepted by a PEP. This collects information about the user, the resource being accessed (the service, or a resource managed by the service) and the action being performed, and asks the PDP whether the access is permitted. The information collection process may involve querying one or more PIPs. If permitted, the request is passed to the service, which may register new resources with the system, update a resource's state, or change the access control rules.

The system administrator may use a PAP (typically a web administration interface) to manage the resource policies.

PBAC components

The PEP acts as a connector between the PDP and the rest of the system and is typically very simple. New PEPs can be written as required, and PBAC comes with several, for common situations.

PBAC and web services

PBAC is often used for controlling access to web services. Web services create resources and allow users to perform operations with them. When a user tries to access a resource by invoking an operation on a web service, the PBAC system checks that the user is authorised to perform the requested operation on that resource.

The diagram below shows a SOAP service protected by the PBAC system.

  1. A SOAP message from a client is first processed by a Security Handler which checks the digital signature and records the identity of the sender.
  2. The SOAP PEP (provided with PBAC) extracts three pieces of information from the message context:
    • The operation name (from the SOAP body).
    • The primary resource context (from a SOAP header).
    • The X.509 certificate of the sender (from the the security handler).
  3. The PEP passes this information to the PBAC PDP, which checks whether the operation should be permitted given the state and policy of the resource.
  4. If the policy permits the operation, the PEP passes the request to the service itself.
  5. The service may update the resource's state as a result of the request.
PBAC deployment overview

Resources

Conceptually, a service controls a number of resources. Each resource is identified by a unique resource ID, which is a globally unique string (often a URI). Examples of resources include accounts, data stagers, reports and patient records.

Each resource has a process state and a resource type. Different operations on the resource are possible in different states. For example, it is not possible to perform the "read" operation on resources of "data stager" type which are in the "empty" state.

Each service is itself also a resource, with its own distict resource type ("account service" vs "account"). Operations which are not directed at a specific resource (for example, "openAccount") are treated as operations on the service resource, and thus are also subject to access control by PBAC.

Process roles

Not all users are able to invoke all the available operations on a resource. Each possible operation of a given state has a set of process roles which may invoke it. Each individual user has a set of process roles for the resource. The user can only invoke the operation if the intersection of these two sets is non-empty (i.e., if they have one of the permitted roles).

The rules for determining a user's process roles can be changed dynamically, allowing users to delegate access to a resource to others. Delegation is performed by invoking a delegation web service operation, and whether a user can delegate is therefore also controlled by PBAC.

This diagram shows a process type with four states. Inside each state is a list of operations which are valid in that state. After each operation is the set of process roles which are allowed to perform the operation in that state, and a set of states to which that operation may transition. The process roles a user may have are owner, read and readWrite.

Example state model for a data service.

Deployment

A typical in-process PBAC setup is shown below. When a client wishes to use a service, it sends a SOAP message to it. When the message arrives at the server, it is processed by a chain of handlers before the service itself sees it. These handlers ensure that the security policy is enforced, and provide additional information to the service.

A simple in-process PBAC deployment.

In slightly more detail than in the introduction, the steps are:

  1. A SOAP message from a client is first processed by a Security Handler (such as WSS4J) which checks the signatures in the WS-Security header against the message. For each correct signature, it records which elements were signed and which certificate signed them. This list of signatures is attached to the message context and both are passed on to the next handler. If any signature is incorrect the message is rejected. However, whether the signature is from a trusted party is not checked at this point.
  2. The PEP extracts three pieces of information from the message context:
    • The operation QName from the SOAP body.
    • The primary resource context from a SOAP header if present, or the resource context of the service itself otherwise.
    • The X.509 certificate of a signature covering both the body and the context header elements (from the list created by the security handler).
  3. The PEP passes this information to the PBAC PDP, which locks the resource and checks whether the operation should be permitted given the state and policy of the resource. The resource's policy includes cryptographic key material necessary to decide whether to trust the signature.
  4. If the policy permits the operation, the PEP passes the request to the service itself.
  5. The service may update the PBAC policy or the state of the resource as a result of the request. It may also create new resources and register them for protection by the PDP.
  6. When the operation is complete (or the access check fails), the PEP gets the PDP to unlock the resource.

The diagram above shows a simple deployment in which PBAC runs in the same process (JVM) as the service. Invocations in this case are simple Java method calls. It is also possible to deploy PBAC as a stand-alone service and invoke its operations using SOAP calls, as shown below:

A deployment with a separate PBAC service.

Note that in this case the standalone PBAC service has its own security handler and PEP. The adminstrator of the PBAC service sets the PBAC service's own access policy to determines which other services can use it to protect their resources. The check step (3) therefore becomes two checks: one to check that the target service is permitted to use PBAC, and one to check that the user is permitted to access the target service.

The full in-process PDP and the PDP Proxy components both present the same interface to the PEP and to the service, so no code needs to be changed for different types of deployment, only the configuration requires changing.

2. Service Administrator's Guide

This is a guide for service administrators who wish to deploy and manage services that support PBAC.

PBAC 2 : Service Administrator's Guide

This is a guide for service administrators who wish to deploy and manage services that support PBAC. The guide assumes that you have already read the introduction.

PBAC provides some web components which can be incorporated into a service's web administration interface. How these components are accessed will depend on your service; consult the service documentation for details.

The policies list

The screenshot below shows the PBAC policy list interface component, which lists each resource type registered with PBAC. The policy types shown in this example are:

Services
There are four service types shown: data-service, job-service, private-account-service and trade-account-service. There is a single resource (not shown here) of each type, representing each service itself (this server hosts once job service, one data service, etc).
Service-specific objects
Each service in this example manages a single type of object (data, job, private-account or trade-account, ). There will be many objects of each type (many accounts, many data stagers and many jobs). A service may manage any number of different types, or none at all, but this example is typical.
Groups
Group objects are not managed by a service in this example (although they could be, in which case there would be a group-service type as well). They are managed here only through the web interface, and their purpose is to group access control lists which should be shared between many resources.
The policy list web interface

The policies list provides access to the list of resources of each type (by clicking on a policy in the list), and it also allows policies to be deployed, undeployed or updated.

Policy deployment

Services will normally deploy a default policy for each type they require during installation. These policies state which roles can perform which actions (for example "A service-administrator may invoke suspend-account when an account is open"). Normally you will want to change the dynamic access control lists (which control who has the service-administrator role), not the policy itself.

However, if you do need to deploy a new policy:

  1. Enter the name for the resource type (a full URI like the ones shown above).
  2. Select the XML file containing the new policy which you want to upload.
  3. Click on Deploy policy.

For information about creating policy files, see the developer's guide.

To update an existing policy you will need to undeploy the old policy first:

  1. Click on the policy to select it.
  2. Click on the Undeploy button that appears beside it.

While a policy is undeployed, no resources of that type can be accessed (but existing resources of that type are not destroyed). If an invocation on a resource of the type is currently in progress the type is disabled but not undeployed; this allows the operations in progress to complete but prevents new ones from starting. Once all operations are complete, click on Undeploy again to actually undeploy the policy. Note that the disabled status is only held in memory; after restarting the web service container all deployed policies will be enabled.

The resource list

Clicking on a resource type in the policies list will select that type and display an additional table showing all resources of that type. The screenshot below has the private-account resource type selected. An Undeploy policy button appear beside the policy type, and each private account resource is shown in a new table below:

The account service resource

Each resource has a process state. The operations available given a set of process roles depends on the process state. Some resources have very simple state models; the account service has only an active state (besides the UNINITIALISED_STATE and DESTROYED_STATE states, which all resources have), while private accounts can be open or suspended.

Clicking on a resource (such as one of the accounts shown in the lower table) will usually show the dynamic policy for that resource. The dynamic policy controls which process roles a user has.

Setting the dynamic policy

Each resource has a set of match rules. For someone to have a particular process role, they must match at least one Sufficient rule, and no Deny rules. This list of rules is called the dynamic policy, because it is typically updated frequently during the lifetime of the resource.

The trade account service resource type defines a single process role, world, which will grant access to all of the service's public methods which don't take a context. This will usually include any operations that create resources (such as openAccount) and methods for fetching the service's identity or policy.

The figure below shows a typical default dynamic policy for a service resource, which grants anyone the world role.

The account service match rules

There are four types of match rule you can add using the web interface (select the type to add and then click on Add):

Subject DN is ...

Upload an X.509 certificate for a particular subject (a person or service) and the CA of their issuer. The rule will only match someone with that exact distinguished name (DN), signed by the CA. Note that only the distinguished name is taken from the user's certificate, not their public key. If the user generates a new key they can still get the role, provided it has the same DN and is signed by the same issuer. This allows the use of short-lived X.509 user certificates.

Certificate is signed by ...
Upload an X.509 certificate for a certificate authority to create a rule that will match anyone whose identity is vouched for by a particular CA. This could be used to ensure that people can't open new accounts under bogus names, for example. If you wish to trust multiple CA's, add a new rule for each one.
Has SAML attribute ...

Upload an X.509 certificate for a SAML issuer and an attribute (name, value) pair. The rule will match anyone with a SAML assertion signed by the issuer asserting that they have the specified attribute.

Member of group ...

Select a group from the list of groups. Anyone who is a member of the group will match. Each group is itself a resource, and has its own set of match rules to determine who is a member.

Anyone

Creates a match rule that matches anyone at all. This would create the default rule shown above.

The figure below shows the dynamic policy for an individual trade account (rather than for the service). This determines who can invokes operations on this account (such as closeAccount or getAccountStatement).

An account's match rules

Here we have four process roles, with a single rule for each:

service-admin
Granted to anyone who is a member of the account-service-admins group. Service administrators can suspend accounts.
billing-service
Granted to anyone who is a member of the account-billing-services group. A billing service can check whether someone has the user role, and can record charges on the account.
budget-holder
Granted to james@gria.org, if his certificate is signed by GRIA CA. Budget holders can get the account statement and control who gets the user role.
user
Granted to anyone with a SAML assertion from security@gria.org stating that their supervisor is james. Users can use other services and charge the use to this account.

The use of groups here allows the service administrator to affect the access control policy of many accounts at once. For example, when they deploy a new job service (which should be able to record usage on any client's account) then can make the job service a member of the account-billing-services group, rather than having to update the dynamic policy of every account individually.

Like the service administrator, the budget holder (James) also does not wish to list every permitted user against the account, because then when a new member of staff joined it would be necessary to update the dynamic policy at every service provider he uses. Instead, he grants anyone access if they have a SAML token stating that they work for him. He controls who can get these tokens, valid at many service providers, using his SecurityService.

3. Developer's Guide

PBAC is often used to protect web services. However, this introduces a lot of extra complexitiy, so we will instead start with a simple single process example.

PBAC 2 : Developer's Guide

PBAC is often used to protect web services. However, this introduces a lot of extra complexitiy, so we will instead start with a simple single process example.

Deploying a policy

We'll start with a simple resource we wish to protect - a standard Java Map. To begin, we need a policy for it (save this as map-policy.xml):

<?xml version="1.0" encoding="UTF-8"?>
<state-model description="My service"
xmlns="http://www.itinnovation.soton.ac.uk/uk/ac/soton/itinnovation/grid/pbac2/staticpolicy/types">

<state name="UNINITIALISED-STATE">
<transition>
<event name="init"/>
<to-state name="active"/>
</transition>
</state>

<state name="active">
<operation name="put">
<process-role name="writer"/>
</operation>
<operation name="get">
<process-role name="reader"/>
<process-role name="writer"/>
</operation>
<transition>
<event name="destroy"/>
<to-state name="DESTROYED-STATE"/>
</transition>
</state>

<state name="DESTROYED-STATE"/>

</state-model>

The policy states that a Map has three states: UNINITIALISED-STATE (before it is ready for use), active (the normal state) and DESTROYED-STATE (no longer available for use). UNINITIALISED-STATE and DESTROYED-STATE are special, and must have these names.

The policy also defines two state transitions: the init signal moves an uninitialised Map into the active state, and the destroy signal moves an active Map to DESTROYED-STATE. The state model is shown on the right.

As well as giving the state model, the policy defines which actions are available to each process role in each state. Here, we define two roles: in the active state, a reader can call the get operation, while a writer can call get or put.

Our program's first step is to deploy this policy into PBAC, giving it a globally unique name (http://example.com/map):

import uk.ac.soton.itinnovation.grid.pbac2.pep.DynamicPEP;
import uk.ac.soton.itinnovation.grid.types.TrustedAttribute;
import uk.ac.soton.itinnovation.grid.types.MatchRule;
import uk.ac.soton.itinnovation.grid.types.SubjectDescription;
import uk.ac.soton.itinnovation.grid.pbac2.pdp.PDP;
import uk.ac.soton.itinnovation.grid.pbac2.pdp.PBACUtils;
import uk.ac.soton.ecs.iam.grid.utils.ImplementationFactory;
import java.util.Map;
import java.util.HashMap;

public class PBACTutorial {
private final static String MAP_RESOURCE_TYPE = "http://example.com/map";

public static void main(String[] args) throws Exception {
PBACUtils.ensureDeployed(MAP_RESOURCE_TYPE, "map-policy.xml");
}
}

Building and running the examples

To compile and run this code, you will need the following jars in CLASSPATH (these are all supplied with PBAC):

  • antlr-2.7.6rc1.jar
  • axis-1.4.jar
  • axis-jaxrpc-1.4.jar
  • axis-saaj-1.4.jar
  • castor-0.9.7.jar
  • cglib-2.1.3.jar
  • commons-collections-2.1.1.jar
  • commons-discovery-0.2.jar
  • commons-logging-1.0.4.jar
  • dom4j-1.6.1.jar
  • geronimo-spec-jta-1.0.1B-rc4.jar
  • gria-infra-comms-5-SNAPSHOT.jar
  • gria-infra-service-5-SNAPSHOT.jar
  • gria-pbac-common-2.0.2-SNAPSHOT.jar
  • gria-pbac-pdp-5-SNAPSHOT.jar
  • gria-pbac-pep-5-SNAPSHOT.jar
  • hibernate-3.1.3.jar
  • hsqldb-1.7.3.3.jar
  • log4j-1.2.9.jar
  • opensaml-1.1-itinnov-1.jar
  • xercesImpl-2.7.1.jar
  • xmlsec-1.3.0-itinnov-1.jar

You will also need the following configuration files (also in CLASSPATH):

If you run the code now, it should create a few pbac-db.* files, recording that the policy has been registered. However, when running the program again the database will be wiped first. In a real system, you would edit hibernate.properties to specify hibernate.hbm2ddl.auto update rather than hibernate.hbm2ddl.auto create.

Adding resources

Now that we've defined an access policy for Maps, we need to register an actual Map object with PBAC. This is done with newProcess:

public class PBACTutorial {
private final static String MAP_RESOURCE_TYPE = "http://example.com/map";

public static void main(String[] args) throws Exception {
PBACUtils.ensureDeployed(MAP_RESOURCE_TYPE, "map-policy.xml");

PDP pdp = ImplementationFactory.getSingletonInstance(PDP.class);

Map map1 = new HashMap();
pdp.newProcess(MAP_RESOURCE_TYPE, "map-1");
try {
map1.put("name", "pbac-tutorial");
pdp.signal("map-1", "init");
} finally {
pdp.unlock("map-1");
}

System.out.println("Resources registered with PBAC:");
for (String resource : pdp.getResources(MAP_RESOURCE_TYPE, null, null)) {
System.out.println("Resource: " + resource);
}
} }

There are several points to note in the above code:

  • We use ImplementationFactory to get the PDP implementation from its interface. This uses implementationfactory.properties, and ensures that only a single instance is ever created. Future calls will return the same PDP object again.

  • When the new map-1 resource is registered it is initially locked. In a multi-threaded application, this prevents other threads from using this resource until we unlock it. You must always have a try...finally block immediately following a call to newProcess to ensure that the unlock always happens, even if initialisation fails. Note: locking does not use the database.

  • We send the init signal at the end of the initialisation so that it will be in the active state when unlocked. If the resource is unlocked while still in the UNINITIALISED-STATE it will be automatically removed from PBAC's database. Thus, no special clean-up code is needed to handle errors during initialisation.

Finally, we list all the resources known to PBAC to check that it worked. If you remove the call to signal, the list will be empty.

Checking for access

Once the resource is created and unlocked, we can check whether someone can call the get method using lockAndCheck:

    SubjectDescription nobody = new SubjectDescription();
pdp.lockAndCheck("map-1", nobody, "get");
try {
System.out.println("name = " + map1.get("name"));
} finally {
pdp.unlock("map-1");
}

Like newProcess, a successful call to lockAndCheck leaves the resource locked, and a try...finally block must follow immediately to ensure it is unlocked again afterwards.

Running the code now will give the exception No access to resource 'map-1' for user '<subject (no certificate)>', because we haven't granted anyone access to this resource yet.

Granting access

While initialising the resource, we'll add a rule granting anyone the reader role, which we defined above in our map-policy.xml:

    Map map1 = new HashMap();
pdp.newProcess(MAP_RESOURCE_TYPE, "map-1");
try {
map1.put("name", "pbac-tutorial");

MatchRule anyoneIsAReader = new MatchRule();
anyoneIsAReader.setSubjectDN("*");
anyoneIsAReader.setIssuerCertificate("*");
anyoneIsAReader.setProcessRole("reader");

pdp.addAccessControlRule("map-1", anyoneIsAReader);
pdp.signal("map-1", "init"); } finally { pdp.unlock("map-1"); }

If run, the program will now display name = pbac-tutorial, because even our nobody user with no credentials is allowed to call get.

If you change the action to put, you'll get User '<subject (no certificate)>' is not authorised to perform action 'put' on resource 'map-1'. This is a slightly different error message; nobody does have some access to this resource, but not enough to call put, since the anyoneIsAReader rules only makes them a reader, not a writer.

Subject credentials

If we have more information about our user, we can add it to the SubjectDescription. This allows more useful match rules. There are three types of information we can add:

An X.509 certificate

This is a certificate which you know belongs to the user (e.g., because the message you received was signed by the private key corresponding to the public key in the certificate).

For example, a servlet can get this from a request sent over https which has client authentication turned on using the javax.servlet.request.X509Certificate attribute. An Axis SOAP service can get this from a security handler such as WSS4J.

Note: you don't need to believe that the details in the certificate are genuine (you don't need to trust the issuer); you just need to know that the user has the private key. The PDP will check whether the certificate allows access, including checking the issuer's signature.

A signed SAML assertion

You may wish to let users pass in additional SAML tokens, which you should attach to the SubjectDescription. The subject of the assertion must be an X.509 certificate. You do not need to trust the token; PBAC will ensure that the subject of the token is the user's X.509 certificate, and that the signature is trusted.

A trusted attribute

Some other system may have already authenticated the user, or provided some other information which you trust. For example, a servlet container may have asked the user for a password and checked that it is correct. In that case, you can just add the user-name to the SubjectDescription. Obviously, you MUST NOT let users specify the trusted attributes!

PBAC's main use is in checking cryptographic tokens (X.509 certificates and SAML tokens), but we'll use trusted attributes for our example as they're simpler. We'll add a match rule stating that bob is a writer:

    pdp.newProcess(MAP_RESOURCE_TYPE, "map-1");
try {
map1.put("name", "pbac-tutorial");

MatchRule anyoneIsAReader = new MatchRule();
anyoneIsAReader.setSubjectDN("*");
anyoneIsAReader.setIssuerCertificate("*");
anyoneIsAReader.setProcessRole("reader");
pdp.addAccessControlRule("map-1", anyoneIsAReader);

MatchRule bobIsAWriter = new MatchRule();
bobIsAWriter.setAttributeName("example.username");
bobIsAWriter.setAttributeValue("bob");
bobIsAWriter.setProcessRole("writer");
pdp.addAccessControlRule("map-1", bobIsAWriter);
pdp.signal("map-1", "init"); } finally { pdp.unlock("map-1"); }

Now we'll assume we have authenticated the user already somehow, and add their user name before trying to call put:

    SubjectDescription bob = new SubjectDescription();
bob.setTrustedAttributes(new TrustedAttribute[] {
new TrustedAttribute("example.username", "bob"),
});

pdp.lockAndCheck("map-1", bob, "put");
try {
map1.put("verified", "true");
} finally {
pdp.unlock("map-1");
}

The DynamicPEP

It's annoying to have to write the action name twice each time (once as an argument to lockAndCheck and once as the method on map1. It would be easy to make a copy-and-paste error and end up doing a put when you only checked for get access! Also, the extra boiler-plate code is ugly.

Luckily, PBAC comes with a useful DynamicPEP class to solve these problems. We can use it to create a wrapper for map1 that enforces the access policy:

Map protectedMap = DynamicPEP.dynamicPEP(Map.class, map1, "map-1", bob);

System.out.println("name = " + protectedMap.get("name"));
protectedMap.put("verified", "true");

Here we performed two operations (get and put), locking, checking and unlocking the resource each time automatically. If you replace bob with nobody, the get will succeed but the put will fail.

In fact, the put will fail with an UndeclaredThrowableException, because Java's Map interface doesn't say that put can throw GridFailureException. When defining your own services or resources, you would declare each method to throw this exception.

There are pros and cons to the DynamicPEP. It saves a lot of code, and makes the code more readable and safer. However, you do need to design your interface so that each method corresponds directly to an access control decision.

Destroying a resource

To unregister a resource (so that PBAC forgets about it) you must unlock it while it is in DESTROYED-STATE. We can move it to this state using signal just as we moved it to the active state before:

    pdp.lockForAdmin("map-1", "destroy");
try {
pdp.signal("map-1", "destroy");
} finally {
pdp.unlock("map-1");
}

System.out.println("name = " + protectedMap.get("name"));

This program will give the error Resource does not exist (for resource map-1) when we try to invoke the get method after destroying it.

This fragment also demonstrates the lockForAdmin method, an alternative to lockAndCheck which doesn't perform the check part (the action given is for informational use only; it may appear in errors and log messages). This is useful when the system itself needs to perform an action. Of course, if a user requested the destruction of the map then we would use lockAndCheck("map-1", subject, "destroy") and pass the user's credentials as normal.

Conclusions

This guide has shown how to use PBAC within a single application. The PBAC database is stored in a file, and re-initialised on each restart to aid testing. We can make calls on the PDP directly, or we can wrap the target objects using a DynamicPEP to make the calls for us.

In a real deployment, you would probably:

  • Configure hibernate to use a real database, not a file.
  • Configure hibernate not to clear the tables on start-up.
  • Use cryptographic tokens such as X.509 certificates rather than trusted attributes, or get the trusted attribute from some other system.
  • Create multiple resources and possibly multiple resource types.
  • Provide an administration interface, allowing the XML policy to be updated by the administrator.
  • Allow multiple client connections in parallel, taking advantage of PBAC's resource locking system.

The SOAP developer's guide shows how to use PBAC to control access to a SOAP service, and the GRIA Service Developer Kit contains a sample service which uses PBAC to control access to its SOAP operations and a tutorial showing how to extend it.

4. Service Developer's Guide

PBAC can be used to protect all SOAP invocations on your web services.

PBAC 2 : Service Developer's Guide

PBAC can be used to protect all SOAP invocations on your web services.

Each service will have a PBAC policy for any operations which don't take a context (these are similar to static methods and constructors in Java). This is described in the Services section below.

SOAP requests may also take a resource identifier in the SOAP header to provide context for the operations. Operations which take a context work like normal non-static methods in Java. Each resource has a unique identifier and a unique resource type; there is one PBAC policy for each resource type. Creating and managing resources is described in the Resources section.

The Web administration section shows how to create a web-based adminstrator interface easily using JSP. This lets you deploy and undeploy policies, view the resources protected by each policy, and change the dynamic rules for assigning process roles.

The client

No special features should be required on the client except for WS-Security support. If the context ID is passed in the header instead of the body, the client will need to be able to create these headers (typically by supporting WS-Addressing).

The PEP

The PEP wraps the service and prevents unauthorised users from invoking any operations on resources to which they have not been granted access. The PEP is intended to be very light-weight, as most of the work is done in the PDP. Each web service platform requires its own implementation of the PEP.

The client's SOAP message is received by the service framework, which processes it with a series of inbound handlers before invoking the service itself (see the Deployment section in the overview).

The PEP passes the user's X.509 certificate, the resource ID and the SOAP operation name to the PDP (either a local PDP, or a PDP Invoker).

The PDP locks the resource and then checks that the user is authorised to perform the operation. If not, it unlocks the resource and throws an exception, stopping processing of the message. If the user is authorised, the check returns success with the resource still locked.

On successful completion of the check, the PEP invokes the service operation itself. The service operation may signal events to the PDP during its invocation; these events may change the process state of the resource, with the service policy deciding to which state the resource will transition on each event.

After the service operation has been invoked (whether successful or not), the PEP tells the PDP to unlock the resource.

Axis deployment

The PEP class is in the itinnov-grid-pbac-pep-VERSION.jar JAR file. To use PBAC, the service's server-config.wsdd file must list the PEP as the provider. For example:

<service name="DataService" provider="java:PBAC-PEP" use="literal" style="wrapped">
<requestFlow>
<handler type="SecurityInboundHandler"/>
</requestFlow>
<responseFlow>
<handler type="IntegrityEnforcementHandler"/>
</responseFlow>
<parameter name="className" value="java.package.MyServiceInterface/>
<parameter name="allowedMethods" value="*"/>
<parameter name="scope" value="Application"/>
</service>

Note: WSS4J does not provide a configuration setting to disable calling the vertifyTrust method, which checks that the signing certificate is in the service's keystore. Since PBAC roots of trust are per-resource rather than global, this check must be disabled. This can be done by setting SecurityInboundHandler to a subclass of WSDoAllReceiver which overrides this method to always return true.

Note 2: When using the PBAC-PEP provider, the service's className attribute must be set to a Java interface rather than to the implementing class. This removes the need to set allowedMethods to an actual list of operations (since only operations in the interface are exposed) and permits any web-based configuration interface access to the same singleton implementation object. The implementing class to use for the interface is specified in the implementationfactory.properties file:

java.package.MyServiceInterface = com.example.MyServiceImpl

.NET filters

The .NET filters are written in C# and make use of the WSE 2 libraries. Since the PDP requires a JVM it cannot be run in-process with .NET services, so the .NET filters communicate with the PDP using SOAP calls.

WSE 2 automatically includes the required WS-Security handlers, so this does not need to be listed. The service's Web.config file therefore only needs to list the PEP filter for both chains, eg:

  <microsoft.web.services2>

<filters>
<input>
<add type="PBAC.PEPInputFilter, PBAC" /> </input> <output> <add type="PBAC.PEPOutputFilter, PBAC" /> </output> </filters> ... </microsoft.web.services2>

Services

To write a service that uses PBAC, you should already be familiar with creating web services using your chosen service container (eg, tomcat/axis or .NET).

Creating a service policy

The service policy controls access to operations that don't act on an existing resource. We'll start with a service which has just an echo operation. A suitable PBAC policy for our service might look like this:

<?xml version="1.0" encoding="UTF-8"?>
<state-model description="My service"
xmlns="http://www.itinnovation.soton.ac.uk/uk/ac/soton/itinnovation/grid/pbac2/staticpolicy/types">

<state name="UNINITIALISED-STATE">
<transition>
<event name="init"/>
<to-state name="active"/>
</transition>
</state>

<state name="active">
<operation name="echo">
<process-role name="world"/>
</operation>
</state>

<state name="DESTROYED-STATE"/>

</state-model>

All resources start in the UNINITIALISED-STATE state. Once initialisation is complete, the service will send the init event to change it to the active state. The service will stay in this state for the whole time it is deployed.

While in the active state, anyone with the world role may invoke the echo operation.

Registering the service with PBAC

When your service is configured, it should deploy the XML policy, and then create a singleton resource named service:java.package.MyServiceInterface (the same interface that was specified in the wsdd file):

PBACUtils.ensureDeployed(MyServiceInterface.MY_SERVICE_RESOURCE_TYPE, "my-service-policy.xml");
PBACUtils.ensureServiceResource(this, new MatchRule[] {MatchRule.createAnyoneRule("world"));

The first argument to ensureServiceResource is your service object, which must have a PEPServiceResource annotation on its class. Alternatively, you may pass the PBAC type and Java interface as arguments in place of this.

The second argument to creates an initial dynamic policy which gives anyone at all the world role. This is useful for completely public methods. The service policy above permits users with the world role to invoke the echo operation once the service is in the active state. Alternatively, you might choose to leave the initial policy empty and have the service administrator set it through the web interface.

MY_SERVICE_RESOURCE_TYPE is simply a unique string identifying the new type. To ensure that it is unique, it is usual to pick an unused URL in a domain that you control. Including the year that the URL was coined means you only have to remember which names you've already used this year. For example:

public static final String MY_SERVICE_RESOURCE_TYPE =
"http://example.com/2006/MyServicePolicy";

Writing the echo opertion

The implementation of the echo operation doesn't require any interaction with PBAC. Add the prototype for echo to MyServiceInterface:

 public String echo(String message);

Add the implementation to MyServiceImpl:

public String echo(String message) {
return "Echo: " + message;
}

Anyone should now be able to invoke the echo operation, since we granted everyone the world role.

Creating a web-based administration interface

Services generally export operations that can be used to manage the dynamic policy (e.g., who gets access to the world role). However, it is useful (especially during development) to have a web interface for this. This can be done with some simple JSP code:

<... resources of that type.</p><%

PolicyAdmin admin = new PolicyAdmin(request);
String message = admin.processPOST();
if (message != null)
out.write("<p>" + message + "</p>");
admin.showAdmin(out);

The processPOST operation checks whether the user is submitting a form, and performs the requested action if so. It may return a message, which you should display to the user. The showAdmin operation displays the policy tables.

When you view this page you should see your new policy and, when you select it, you should see the singleton service object in the active state. Use of the administration interface is described in the PBAC administrator's guide.

Web-based policy admin interface

Clicking on a resource takes you to the resource's page. You can configure which JSP page to link to for each resource type using registerResourceTypeLink. By default, the page pbac_resource.jsp is assumed, but customising this allows you to show extra resource-specific information:

admin.registerResourceTypeLink(MyServiceInterface.MY_SERVICE_RESOURCE_TYPE, "my_service.jsp");

To include the dynamic policy component in your web interface in the resource's page (my_service.jsp), use this code:

ACLadmin acladmin = new ACLadmin(request);
String message = acladmin.processPOST();
if (message != null)
out.write("<p>" + message + "</p>");
acladmin.showRules(out);

This will produce the table shown below for our sample service (a groups section will also be shown if you have groups defined). Use of the dynamic policy administration interface is described in the PBAC administrator's guide.

A simple dynamic policy

Resources created by services

Some services only have operations with no context (like static methods on a Java class). Most services however will create other resources dynamically: an account service will create accounts, and a data service will create data stagers. Each such resource needs to have its own access control dynamic policy (every account will grant a different person the budget-holder process role, for example).

Creating a resource policy

For this tutorial, we'll extend our service to allow the creation of data stagers. Users will be able to create, write to, read from and destroy stagers. A simple initial policy might look like this:

<?xml version="1.0" encoding="UTF-8"?>
<state-model description="DataService description"
xmlns="http://www.itinnovation.soton.ac.uk/uk/ac/soton/itinnovation/grid/pbac2/staticpolicy/types">

<state name="UNINITIALISED-STATE">
<transition>
<event name="init"/>
<to-state name="empty"/>
</transition>
</state>

<state name="empty">
<operation name="save">
<process-role name="owner"/>
</operation>
<operation name="destroy">
<process-role name="owner"/>
</operation>

<transition>
<event name="write"/>
<to-state name="full"/>
</transition>
<transition>
<event name="destroy"/>
<to-state name="DESTROYED-STATE"/>
</transition>
</state>

<state name="full">
<operation name="read">
<process-role name="reader"/>
<process-role name="owner"/>
</operation>
<operation name="deleteContents">
<process-role name="owner"/>
</operation>

<transition>
<event name="delete"/>
<to-state name="empty"/>
</transition>
</state>

<state name="DESTROYED-STATE"/>
</state-model>

Apart from the special uninitialised and destroyed states, a stager can be either empty or full. When empty, anyone with the owner role can write to it or destroy it. When full, the owner can delete the contents and both the owner and any reader can read the data.

This model introduces some constraints on how a stager is used. For example, no-one (not even the owner) can destroy a stager which is in the full state. It must be moved to the empty state first.

Add a line to your initialisation code to deploy the resource policy:

PBACUtils.ensureDeployed(MyServiceInterface.MY_SERVICE_RESOURCE_TYPE, "my-service-policy.xml");
PBACUtils.ensureDeployed(MyServiceInterface.MY_RESOURCE_TYPE, "my-policy.xml"); PBACUtils.ensureServiceResource(this, new MatchRule[] {MatchRule.createAnyoneRule("world"));

The web interface should now list both resource types, although there won't be any objects listed for the new type because no data stagers have been created yet.

Creating a new resource

New resources are registered with PBAC using newProcess. Typically, there is a web service operation which does not take a resource ID as input (and hence is protected by the PBAC service policy above, rather than by a resource policy), but just creates new resources. Methods that create new resources generally take a MatchRule argument, allowing for future identification of the creator.

Our newDataStager operation is typical (it creates a new resource and initialises it with a dynamic policy that grants the caller of newDataStager the owner role):

public String newDataStager(MatchRule owner) {
PBACUtils.validateOwner(owner, getCurrentUser(), "owner");

// Create the new resource
DataStager resource = new DataStager();

// Register it with PBAC
pdp.newProcess(MY_RESOURCE_TYPE, resource.ID);
try {
// Give the caller the 'owner' role
pdp.addAccessControlRule(resourceID, owner);

// Signal successful initialisation
pdp.signal(resourceID, "init");

return resource.ID;
} finally {
pdp.unlock(resourceID);
}
}

The validateOwner method checks that the caller of newDataStager is matched by this policy as a sanity check (to prevent people from creating resources that they can't access). It also ensures that the rule grants the correct role (this is especially important if there are roles with greater access than owner, such as service-admin).

New resources start life in the locked state (preventing the user from doing anything with them until initialisation is complete). Since the PEP didn't lock it, it won't unlock it automatically, so the finally clause is required to do this. If the operation fails, the resource will be unlocked without receiving the "init" signal; the PDP will forget about it automatically in this case.

Before sending the "init" signal, we also need to give someone access to the resource. In this case, we grant the caller of newDataStager the owner process role. Note: If we sent the init signal first, and an error occured after that and before adding the new rule, we would get a new resource that no-one could access (except through the web admin interface). So, don't send the init signal until the point where the resource should continue to exist even if an exception is thrown.

If you try to invoke the newDataStager operation, PBAC will reject the request. To allow people to call it, edit the service's policy (not the data stagers's policy!) to add the new operation:

<state name="active">
<operation name="echo">
<process-role name="world"/>
</operation>]]> <operation name="newDataStager"> <process-role name="world"/> </operation> </state>

Undeploy the old version using the web interface and deploy the new one. You should now be able to create new data stager objects. Of course, you may not want everyone to be able to create new data stagers. In that case, specify a different role instead of world. You will have to add a new rule using the web interface to grant someone this role.

Operations using the resource

Whenever a user invokes an operation on a resource, the PEP will check that the user is authorised and lock the resource for you. When your operation returns, the PEP will unlock it. Therefore, no-one else can perform operations on the resource during this time (the PDP will queue requests for around a minute by default before giving up).

A typical operation looks like this:

 public void deleteContents() {
DataStager resource = getDataStagerFromContext();

resource.deleteContents();

pdp.signal(resource.ID, "delete");
}

Sending a signal may cause the state of the resource to change, affecting what operations can be called. In this case, the delete signal will cause the PDP to transition the resource to the empty state, from which the read operation is unavailable.

When a SOAP message arrives, the PBAC PEP extracts the resource ID from a WS-Addressing header element. It ensures that there is a signature covering both this header and the SOAP body, and passes the context and operation to the PDP to check that the signer is authorised to invoke the method. If the operation is authorised, the PEP stores the context in the Axis message context for use by the service.

The private getDataStagerFromContext method gets this resource context from the SOAP message context. There should always be a context by this point, because if the user didn't give one then PBAC would have used the service policy rather than the data stager policy to check the user's authorisation and the service policy should never allow deleteContents to be called. A typical implementation looks like this:

private DataStager getDataStagerFromContext() {
ProcessContext primaryContext = ProcessContextHelper.getProcessContext();
String resourceID = primaryContext.getConversation();
if (resourceID == null)
throw new RuntimeException("No resource ID in message context (policy enforcement error)");
return loadFromDatabase(resourceID);
}

Getting a resource's process state

Many services will maintain their own idea of a resource's state. However, sometimes it is useful to know the PBAC process state of a resource, or even make it available to clients. This operation does not send any signals, and thus does not change the state.

 public String getState() {
DataStager resource = getDataStagerFromContext();
return pdp.getCurrentState(resource.ID);
}

Destroying a resource

There are two special states: UNINITIALISED-STATE and DESTROYED-STATE. If a resource is unlocked while in either of these two states, it is destroyed (causing PBAC to forget all about it). The destroyed state is used to remove a resource which is no longer needed, while unlocking an uninitialised resource indicates a failure while the service was setting up the resource.

To destroy a resource, send a signal such as destroy. The policy should cause the resource to transition to DESTROYED-STATE. When the PEP unlocks the resource after the operation completes, PBAC will forget about the resource.

Other PBAC operations

For a complete list of available PBAC operations, consult the JavaDoc reference.

Unlocking a resource during an operation

A service's operation is invoked by Axis only once PBAC has checked that the client is authorised to perform the operation. The resource is locked at this point.

Before returning, the service may send any number of events to the PDP, which may choose to update the state of the resource in response.

The fetch operation unlocks the resource, fetches the data from a remote URL, and then sets the state to FULL and returns. The purpose of this example is to show how the context can be unlocked during a long-running operation. Note that the resource itself must not be modified while it is unlocked; instead, we download to a temporary file. Once the data is ready, we lock the context to actually copy it in. "completeFetch" is an internal (fake) operation, used to reacquire the lock during the fetch operations.

Since the resource is unlocked, another operation could change its state (such as making it read-only or finishing it) while we fetch the data. In that case, the lockAndCheck operation would throw an exception (which is safe and reasonable). An alternative approach is to transition into a 'fetching' state at the start, from which only getState can be called. However, this makes the error handling more complex, as you must take care that the resource is not left in the 'fetching' state on error.

public void fetch(URL source) {
DataStager resource = getDataStagerFromContext();

pdp.unlock(resource.ID);

File tempFile = source.fetchToTemp(); // Slow; resource unlocked

try {
pdp.lockAndCheck(resource.ID, "completeFetch");

resource.store(tempFile);

pdp.update(resource.ID, "fetch-complete");
} finally (
tempFile.unlink();
}
}

Another alternative would be use lockForAdmin instead of lockAndCheck. This locks the resource without checking that it is in a suitable state. In this case you don't need to pass the user's identity, and the operation name is only used for display purposes (e.g., a notice in the admin page saying why the resource is locked). Use lockAndCheck if you want the operation to fail if the resource is now in the wrong state, or the user may have lost their privileges.

Delegation

Delegation operations (granting additional users access to an existing context) are invoked by the service. The client therefore calls a normal service operation, subject to the normal PBAC access control rules, to perform these operations.

For example, a data service might provide an addReaderRule operation which controls access to the reader role. The PBAC policy will determine who can call the addReaderRule operation on the service.

public void addReaderRule(MatchRule rule) {
DataStager resource = getDataStagerFromContext();

delegate.checkRole("reader");
pdp.addAccessControlRule(resource.ID, rule);
}

You should also provide service operations that call PDP.removeAccessControlRule and PDP.getAccessControlRules to provide a full access control interface.

Discovering resources

Most services provide an operation to let users ask "Which resources can I access?". This is easily implemented using PBAC:

public String[] getResources() {
return pdp.getResources(MyServiceInterface.MY_RESOURCE_TYPE, getCurrentUser(), null);
}

You may wish to convert the resources to EndpointReferenceTypes, so that they include the service address and any other meta-data (such as the user's label for the resource).