377 lines
15 KiB
Java
377 lines
15 KiB
Java
|
|
/*
|
|
* @(#)PolicyCollection.java
|
|
*
|
|
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistribution of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistribution in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
|
|
* be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* This software is provided "AS IS," without a warranty of any kind. ALL
|
|
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
|
|
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
|
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
|
|
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
|
|
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
|
|
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
|
|
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
|
|
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
|
|
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
|
|
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
*
|
|
* You acknowledge that this software is not designed or intended for use in
|
|
* the design, construction, operation or maintenance of any nuclear facility.
|
|
*/
|
|
|
|
package com.sun.xacml.support.finder;
|
|
|
|
import com.sun.xacml.AbstractPolicy;
|
|
import com.sun.xacml.EvaluationCtx;
|
|
import com.sun.xacml.MatchResult;
|
|
import com.sun.xacml.Policy;
|
|
import com.sun.xacml.PolicyReference;
|
|
import com.sun.xacml.PolicySet;
|
|
import com.sun.xacml.PolicyTreeElement;
|
|
import com.sun.xacml.Target;
|
|
import com.sun.xacml.TargetSection;
|
|
import com.sun.xacml.VersionConstraints;
|
|
|
|
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
|
|
|
|
import com.sun.xacml.ctx.Result;
|
|
import com.sun.xacml.ctx.Status;
|
|
|
|
import java.io.Serializable;
|
|
import java.net.URI;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.StringTokenizer;
|
|
import java.util.TreeSet;
|
|
|
|
|
|
/**
|
|
* This class handles collections of <code>AbstractPolicy</code> instances,
|
|
* and provides some commonly useful operations. Specifically, it lets you
|
|
* retrieve matching policies (based on reference or context), it optionally
|
|
* handles wrapping multiple matches under a single PolicySet, and it manages
|
|
* different versions of policies correctly. This class is intended for use
|
|
* as a backing store to <code>PolicyFinderModule</code>s, but in practice
|
|
* may have many uses.
|
|
* <p>
|
|
* Note that this class will accept multiple versions of the same policy. This
|
|
* means that when you retieve a policy by reference, you will get the
|
|
* correct version. It also means that when you retrieve a policy based on
|
|
* context, there may be multiple revisions of the same policy, any number
|
|
* of which may apply. Generally speaking, the correct behavior here is not
|
|
* to return all of these policies, since they are (virtually speaking) the
|
|
* same policy, but may have conflicting rules. So, as a simplification, and
|
|
* to handle the most common cases, only the most recent version of a policy
|
|
* is returned in these cases. If you need a more complex solution, you
|
|
* will need to implement it yourself. Because the support modules use this
|
|
* class as their backing store, this is true also of those modules.
|
|
* <p>
|
|
* Note that this is not a heavily optimized class. It is intended more as
|
|
* an example, support code for the finder modules, and a starting utility
|
|
* for other programmers than as an enterprise-quality implementation. That
|
|
* said, it is fully functional, and should be useful for many applications.
|
|
*
|
|
* @since 2.0
|
|
* @author Seth Proctor
|
|
*/
|
|
public class PolicyCollection
|
|
{
|
|
|
|
// the actual collection of policies
|
|
private HashMap<String, TreeSet<AbstractPolicy> > policies;
|
|
|
|
// the single instance of the comparator we'll use for managing versions
|
|
private VersionComparator versionComparator = new VersionComparator();
|
|
|
|
// the optional combining algorithm used when wrapping multiple policies
|
|
private PolicyCombiningAlgorithm combiningAlg;
|
|
|
|
// the optional policy id used when wrapping multiple policies
|
|
private URI parentId;
|
|
|
|
// default target that matches anything, used in wrapping policies
|
|
private static final Target target;
|
|
|
|
/**
|
|
* This static initializer just sets up the default target, which is
|
|
* used by all wrapping policy sets.
|
|
*/
|
|
static {
|
|
target =
|
|
new Target(new ArrayList<TargetSection>());
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>PolicyCollection</code> that will return errors
|
|
* when multiple policies match for a given request.
|
|
*/
|
|
public PolicyCollection() {
|
|
this.policies = new HashMap<String, TreeSet<AbstractPolicy>>();
|
|
this.combiningAlg = null;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>PolicyCollection</code> that will create a new
|
|
* top-level PolicySet when multiple policies match for a given request.
|
|
*
|
|
* @param combiningAlg the algorithm to use in a new PolicySet when more
|
|
* than one policy applies
|
|
* @param parentPolicyId the identifier to use for the new PolicySet
|
|
*/
|
|
public PolicyCollection(PolicyCombiningAlgorithm combiningAlg,
|
|
URI parentPolicyId) {
|
|
this.policies = new HashMap<String, TreeSet<AbstractPolicy> >();
|
|
|
|
this.combiningAlg = combiningAlg;
|
|
this.parentId = parentPolicyId;
|
|
}
|
|
|
|
/**
|
|
* Adds a new policy to the collection, and uses the policy's identifier
|
|
* as the reference identifier. If this identifier already exists in the
|
|
* collection, and this policy does not represent a new version of the
|
|
* policy, then the policy is not added.
|
|
*
|
|
* @param policy the policy to add
|
|
*
|
|
* @return true if the policy was added, false otherwise
|
|
*/
|
|
public boolean addPolicy(AbstractPolicy policy) {
|
|
return addPolicy(policy, policy.getId().toString());
|
|
}
|
|
|
|
/**
|
|
* Adds a new policy to the collection using the given identifier as
|
|
* the reference identifier. If this identifier already exists in the
|
|
* collection, and this policy does not represent a new version of the
|
|
* policy, then the policy is not added.
|
|
*
|
|
* @param policy the policy to add
|
|
* @param identifier the identifier to use when referencing this policy
|
|
*
|
|
* @return true if the policy was added, false otherwise
|
|
*/
|
|
public boolean addPolicy(AbstractPolicy policy, String identifier) {
|
|
if (this.policies.containsKey(identifier)) {
|
|
// this identifier is already is use, so see if this version is
|
|
// already in the set
|
|
TreeSet<AbstractPolicy> set = this.policies.get(identifier);
|
|
return set.add(policy);
|
|
}
|
|
// this identifier isn't already being used, so create a new
|
|
// set in the map for it, and add the policy
|
|
TreeSet<AbstractPolicy> set = new TreeSet<AbstractPolicy> (this.versionComparator);
|
|
this.policies.put(identifier, set);
|
|
return set.add(policy);
|
|
}
|
|
|
|
/**
|
|
* Attempts to retrieve a policy based on the given context. If multiple
|
|
* policies match then this will either throw an exception or wrap the
|
|
* policies under a new PolicySet (depending on how this instance was
|
|
* constructed). If no policies match, then this will return null. See
|
|
* the comment in the class header about how this behaves when multiple
|
|
* versions of the same policy exist.
|
|
*
|
|
* @param context representation of a request
|
|
*
|
|
* @return a matching policy, or null if no policy matches
|
|
*
|
|
* @throws TopLevelPolicyException if multiple policies match but this
|
|
* instance wasn't setup to wrap policies
|
|
*/
|
|
public AbstractPolicy getPolicy(EvaluationCtx context)
|
|
throws TopLevelPolicyException
|
|
{
|
|
// setup a list of matching policies
|
|
ArrayList<PolicyTreeElement> list = new ArrayList<PolicyTreeElement>();
|
|
// get an iterator over all the identifiers
|
|
Iterator<TreeSet<AbstractPolicy>> it = this.policies.values().iterator();
|
|
|
|
while (it.hasNext()) {
|
|
// for each identifier, get only the most recent policy
|
|
AbstractPolicy policy = it.next().first();
|
|
|
|
// see if we match
|
|
context.newEvent(policy);
|
|
MatchResult match = policy.match(context);
|
|
int result = match.getResult();
|
|
|
|
// if there was an error, we stop right away
|
|
if (result == MatchResult.INDETERMINATE) {
|
|
context.closeCurrentEvent(
|
|
new Result(Result.DECISION_INDETERMINATE,
|
|
context));
|
|
throw new TopLevelPolicyException(match.getStatus());
|
|
}
|
|
|
|
if (result == MatchResult.NO_MATCH) {
|
|
context.closeCurrentEvent(
|
|
new Result(Result.DECISION_NOT_APPLICABLE));
|
|
}
|
|
|
|
// if we matched, we keep track of the matching policy...
|
|
if (result == MatchResult.MATCH) {
|
|
context.closeCurrentEvent();
|
|
// ...first checking if this is the first match and if
|
|
// we automaticlly nest policies
|
|
if ((this.combiningAlg == null) && (list.size() > 0)) {
|
|
ArrayList<String> code = new ArrayList<String>();
|
|
code.add(Status.STATUS_PROCESSING_ERROR);
|
|
Status status = new Status(code, "too many applicable"
|
|
+ " top-level policies");
|
|
throw new TopLevelPolicyException(status);
|
|
}
|
|
|
|
list.add(policy);
|
|
}
|
|
}
|
|
|
|
// no errors happened during the search, so now take the right
|
|
// action based on how many policies we found
|
|
switch (list.size()) {
|
|
case 0:
|
|
return null;
|
|
case 1:
|
|
return ((AbstractPolicy)(list.get(0)));
|
|
default:
|
|
return new PolicySet(this.parentId, this.combiningAlg, target, list);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to retrieve a policy based on the given identifier and other
|
|
* constraints. If there are multiple versions of the identified policy
|
|
* that meet the version constraints, then the most recent version is
|
|
* returned.
|
|
*
|
|
* @param identifier an identifier specifying some policy
|
|
* @param type type of reference (policy or policySet) as identified by
|
|
* the fields in <code>PolicyReference</code>
|
|
* @param constraints any optional constraints on the version of the
|
|
* referenced policy (this will never be null, but
|
|
* it may impose no constraints, and in fact will
|
|
* never impose constraints when used from a pre-2.0
|
|
* XACML policy)
|
|
*
|
|
* @return the policy/policy set that was retrieved.
|
|
*/
|
|
public AbstractPolicy getPolicy(String identifier, int type,
|
|
VersionConstraints constraints) {
|
|
TreeSet<AbstractPolicy> set = this.policies.get(identifier);
|
|
|
|
// if we don't know about this identifier then there's nothing to do
|
|
if (set == null) {
|
|
return null;
|
|
}
|
|
// walk through the set starting with the most recent version, looking
|
|
// for a match until we exhaust all known versions
|
|
Iterator<AbstractPolicy> it = set.iterator();
|
|
while (it.hasNext()) {
|
|
AbstractPolicy policy = it.next();
|
|
if (constraints.meetsConstraint(policy.getVersion())) {
|
|
// we found a valid version, so see if it's the right kind,
|
|
// and if it is then we return it
|
|
if (type == PolicyReference.POLICY_REFERENCE) {
|
|
if (policy instanceof Policy) {
|
|
return policy;
|
|
}
|
|
} else {
|
|
if (policy instanceof PolicySet) {
|
|
return policy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// we didn't find a match
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* A <code>Comparator</code> that is used within this class to maintain
|
|
* ordering amongst different versions of the same policy. Note that
|
|
* it actually maintains reverse-ordering, since we want to traverse the
|
|
* sets in decreasing, not increasing order.
|
|
* <p>
|
|
* Note that this comparator is only used when there are multiple versions
|
|
* of the same policy, which in practice will probably happen far less
|
|
* (from this class' point of view) than additions or fetches.
|
|
*/
|
|
static class VersionComparator implements Comparator<AbstractPolicy>, Serializable {
|
|
|
|
/**
|
|
* Serial version UID.
|
|
*/
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
public int compare(AbstractPolicy o1, AbstractPolicy o2) {
|
|
// we swap the parameters so that sorting goes largest to smallest
|
|
String v1 = o2.getVersion();
|
|
String v2 = o1.getVersion();
|
|
|
|
// do a quick check to see if the strings are equal (note that
|
|
// even if the strings aren't equal, the versions can still
|
|
// be equal)
|
|
if (v1.equals(v2)) {
|
|
return 0;
|
|
}
|
|
|
|
// setup tokenizers, and walk through both strings one set of
|
|
// numeric values at a time
|
|
StringTokenizer tok1 = new StringTokenizer(v1, ".");
|
|
StringTokenizer tok2 = new StringTokenizer(v2, ".");
|
|
|
|
while (tok1.hasMoreTokens()) {
|
|
// if there's nothing left in tok2, then v1 is bigger
|
|
if (! tok2.hasMoreTokens()) {
|
|
return 1;
|
|
}
|
|
|
|
// get the next elements in the version, convert to numbers,
|
|
// and compare them (continuing with the loop only if the
|
|
// two values were equal)
|
|
int num1 = Integer.parseInt(tok1.nextToken());
|
|
int num2 = Integer.parseInt(tok2.nextToken());
|
|
|
|
if (num1 > num2) {
|
|
return 1;
|
|
}
|
|
|
|
if (num1 < num2) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// if there's still something left in tok2, then it's bigger
|
|
if (tok2.hasMoreTokens()) {
|
|
return -1;
|
|
}
|
|
|
|
// if we got here it means both versions had the same number of
|
|
// elements and all the elements were equal, so the versions
|
|
// are in fact equal
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|