/*
* @(#)PolicyReference.java
*
* Copyright 2003-2005 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 org.jboss.security.xacml.sunxacml;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.security.xacml.sunxacml.combine.CombiningAlgorithm;
import org.jboss.security.xacml.sunxacml.ctx.Result;
import org.jboss.security.xacml.sunxacml.ctx.Status;
import org.jboss.security.xacml.sunxacml.finder.PolicyFinder;
import org.jboss.security.xacml.sunxacml.finder.PolicyFinderResult;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* This class is used as a placeholder for the PolicyIdReference and
* PolicySetIdReference fields in a PolicySetType. When a reference is used
* in a policy set, it is telling the PDP to use an external policy in
* the current policy. Each time the PDP needs to evaluate that policy
* reference, it asks the policy finder for the policy. Typically the policy
* finder will have cached the referenced policy, so this isn't too slow.
* <p>
* NOTE: all of the accessor methods, the match method, and the evaluate method
* require this class to ask its <code>PolicyFinder</code> for the referenced
* policy, which can be a slow operation. Care should be taken, therefore in
* calling these methods too often. Also note that it's not safe to cache the
* results of these calls, since the referenced policy may change.
*
* @since 1.0
* @author Seth Proctor
*/
public class PolicyReference extends AbstractPolicy
{
/**
* Identifies this as a reference to a <code>Policy</code>
*/
public static final int POLICY_REFERENCE = 0;
/**
* Identifies this as a reference to a <code>PolicySet</code>
*/
public static final int POLICYSET_REFERENCE = 1;
// the reference
private URI reference;
// the reference type
private int policyType;
// and version constraints on this reference
private VersionConstraints constraints;
// the finder to use in finding the referenced policy
private PolicyFinder finder;
// the meta-data for the parent policy
private PolicyMetaData parentMetaData;
// the logger we'll use for all messages
private static final Logger logger =
Logger.getLogger(PolicyReference.class.getName());
/**
* Creates a new <code>PolicyReference</code> instance. This has no
* constraints on version matching. Note that an XACML 1.x reference may
* not have any constraints.
*
* @param reference the reference to the policy
* @param policyType one of the two fields in this class
* @param finder the <code>PolicyFinder</code> used to handle the reference
* @param parentMetaData the meta-data associated with the containing
* (parent) policy
*
* @throws IllegalArgumentException if the input policyType isn't valid
*/
public PolicyReference(URI reference, int policyType, PolicyFinder finder,
PolicyMetaData parentMetaData)
throws IllegalArgumentException
{
this(reference, policyType, new VersionConstraints(null, null, null),
finder, parentMetaData);
}
/**
* Creates a new <code>PolicyReference</code> instance with version
* constraints. Note that an XACML 1.x reference may not have any
* constraints.
*
* @param reference the reference to the policy
* @param policyType one of the two fields in this class
* @param constraints any optional constraints on the version of the
* referenced policy (this is never null, but
* it may impose no constraints, and in fact will
* never impose constraints when used from a pre-2.0
* XACML policy)
* @param finder the <code>PolicyFinder</code> used to handle the reference
* @param parentMetaData the meta-data associated with the containing
* (parent) policy
*
* @throws IllegalArgumentException if the input policyType isn't valid
*/
public PolicyReference(URI reference, int policyType,
VersionConstraints constraints, PolicyFinder finder,
PolicyMetaData parentMetaData)
throws IllegalArgumentException {
// check if input policyType is a valid value
if ((policyType != POLICY_REFERENCE) &&
(policyType != POLICYSET_REFERENCE))
throw new IllegalArgumentException("Input policyType is not a" +
"valid value");
this.reference = reference;
this.policyType = policyType;
this.constraints = constraints;
this.finder = finder;
this.parentMetaData = parentMetaData;
}
/**
* Creates an instance of a <code>PolicyReference</code> object based on
* a DOM node.
*
* @deprecated As of 2.0 you should avoid using this method and should
* instead use the version that takes a
* <code>PolicyMetaData</code> instance. This method will
* only work for XACML 1.x policies.
*
* @param root the DOM root of a PolicyIdReference or a
* PolicySetIdReference XML type
* @param finder the <code>PolicyFinder</code> used to handle the reference
*
* @exception ParsingException if the node is invalid
*/
public static PolicyReference getInstance(Node root, PolicyFinder finder)
throws ParsingException
{
return getInstance(root, finder, new PolicyMetaData());
}
/**
* Creates an instance of a <code>PolicyReference</code> object based on
* a DOM node.
*
* @param root the DOM root of a PolicyIdReference or a
* PolicySetIdReference XML type
* @param finder the <code>PolicyFinder</code> used to handle the reference
* @param metaData the meta-data associated with the containing policy
*
* @exception ParsingException if the node is invalid
*/
public static PolicyReference getInstance(Node root, PolicyFinder finder,
PolicyMetaData metaData)
throws ParsingException
{
URI reference = null;
int policyType;
// see what type of reference we are
String name = SunxacmlUtil.getNodeName(root);
if (name.equals("PolicyIdReference")) {
policyType = POLICY_REFERENCE;
} else if (name.equals("PolicySetIdReference")) {
policyType = POLICYSET_REFERENCE;
} else {
throw new ParsingException("Unknown reference type: " + name);
}
// next get the reference
try {
reference = new URI(root.getFirstChild().getNodeValue());
} catch (Exception e) {
throw new ParsingException("Invalid URI in Reference", e);
}
// now get any constraints
NamedNodeMap map = root.getAttributes();
String versionConstraint = null;
Node versionNode = map.getNamedItem("Version");
if (versionNode != null)
versionConstraint = versionNode.getNodeValue();
String earlyConstraint = null;
Node earlyNode = map.getNamedItem("EarliestVersion");
if (earlyNode != null)
earlyConstraint = earlyNode.getNodeValue();
String lateConstraint = null;
Node lateNode = map.getNamedItem("LatestVersion");
if (lateNode != null)
lateConstraint = lateNode.getNodeValue();
VersionConstraints constraints =
new VersionConstraints(versionConstraint, earlyConstraint,
lateConstraint);
// finally, create the reference
return new PolicyReference(reference, policyType, constraints, finder,
metaData);
}
/**
* Returns the refernce identitfier used to resolve the policy.
*
* @return the reference <code>URI</code>
*/
public URI getReference() {
return reference;
}
/**
* Returns the version constraints associated with this reference. This
* will never be null, though the constraints may be empty.
*
* @return the version constraints
*/
public VersionConstraints getConstraints() {
return constraints;
}
/**
* Returns whether this is a reference to a policy or to a policy set.
*
* @return the reference type, either <code>POLICY_REFERENCE</code>
* or <code>POLICYSET_REFERENCE</code>
*/
public int getReferenceType() {
return policyType;
}
/**
* Returns the id of this policy. If the policy is invalid or can't be
* retrieved, then a runtime exception is thrown.
*
* @return the policy id
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public URI getId() {
return resolvePolicy().getId();
}
/**
* Returns the version of this policy. If the policy is invalid or can't
* be retrieved, then a runtime exception is thrown.
*
* @return the policy version
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public String getVersion() {
return resolvePolicy().getVersion();
}
/**
* Returns the combining algorithm used by this policy. If the policy is
* invalid or can't be retrieved, then a runtime exception is thrown.
*
* @return the combining algorithm
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public CombiningAlgorithm getCombiningAlg() {
return resolvePolicy().getCombiningAlg();
}
/**
* Returns the given description of this policy or null if there is no
* description. If the policy is invalid or can't be retrieved, then a
* runtime exception is thrown.
*
* @return the description or null
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public String getDescription() {
return resolvePolicy().getDescription();
}
/**
* Returns the target for this policy. If the policy is invalid or can't be
* retrieved, then a runtime exception is thrown.
*
* @return the policy's target
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public Target getTarget() {
return resolvePolicy().getTarget();
}
/**
* Returns the default version for this policy. If the policy is
* invalid or can't be retrieved, then a runtime exception is thrown.
*
* @return the policy's default version
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public String getDefaultVersion() {
return resolvePolicy().getDefaultVersion();
}
/**
* Returns the child policy nodes under this node in the policy tree. If
* the policy is invalid or can't be retrieved, then a runtime exception
* is thrown.
*
* @return the <code>List</code> of child policy nodes
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public List getChildren() {
return resolvePolicy().getChildren();
}
/**
* Returns the child policy nodes and their associated parameters. If
* the policy is invalid or can't be retrieved, then a runtime exception
* is thrown.
*
* @return a <code>List</code> of <code>CombinerElement</code>s
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public List getChildElements() {
return resolvePolicy().getChildElements();
}
/**
* Returns the Set of obligations for this policy, which may be empty if
* there are no obligations. If the policy is invalid or can't be
* retrieved, then a runtime exception is thrown.
*
* @return the policy's obligations
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public Set getObligations() {
return resolvePolicy().getObligations();
}
/**
* Returns the meta-data associated with this policy. If the policy is
* invalid or can't be retrieved, then a runtime exception is thrown.
* Note that this is the meta-data for the referenced policy, not the
* meta-data for the parent policy (which is what gets provided to the
* constructors of this class).
*
* @return the policy's meta-data
*
* @throws ProcessingException if the referenced policy can't be retrieved
*/
public PolicyMetaData getMetaData() {
return resolvePolicy().getMetaData();
}
/**
* Given the input context sees whether or not the request matches this
* policy. This must be called by combining algorithms before they
* evaluate a policy. This is also used in the initial policy finding
* operation to determine which top-level policies might apply to the
* request. If the policy is invalid or can't be retrieved, then a
* runtime exception is thrown.
*
* @param context the representation of the request
*
* @return the result of trying to match the policy and the request
*/
public MatchResult match(EvaluationCtx context) {
try {
return getTarget().match(context);
} catch (ProcessingException pe) {
// this means that we couldn't resolve the policy
ArrayList code = new ArrayList();
code.add(Status.STATUS_PROCESSING_ERROR);
Status status = new Status(code, "couldn't resolve policy ref");
return new MatchResult(MatchResult.INDETERMINATE, status);
}
}
/**
* Private helper method that tried to resolve the policy
*/
private AbstractPolicy resolvePolicy() {
// see if this reference was setup with a finder
if (finder == null) {
if (logger.isLoggable(Level.WARNING))
logger.warning("PolicyReference with id " +
reference.toString() + " was queried but was " +
"not configured with a PolicyFinder");
throw new ProcessingException("couldn't find the policy with " +
"a null finder");
}
PolicyFinderResult pfr =
finder.findPolicy(reference, policyType, constraints,
parentMetaData);
if (pfr.notApplicable())
throw new ProcessingException("couldn't resolve the policy");
if (pfr.indeterminate())
throw new ProcessingException("error resolving the policy");
return pfr.getPolicy();
}
/**
* Tries to evaluate the policy by calling the combining algorithm on
* the given policies or rules. The <code>match</code> method must always
* be called first, and must always return MATCH, before this method
* is called.
*
* @param context the representation of the request
*
* @return the result of evaluation
*/
public Result evaluate(EvaluationCtx context) {
// if there is no finder, then we return NotApplicable
if (finder == null)
return new Result(Result.DECISION_NOT_APPLICABLE,
context.getResourceId().encode());
PolicyFinderResult pfr = finder.findPolicy(reference, policyType,
constraints,
parentMetaData);
// if we found nothing, then we return NotApplicable
if (pfr.notApplicable())
return new Result(Result.DECISION_NOT_APPLICABLE,
context.getResourceId().encode());
// if there was an error, we return that status data
if (pfr.indeterminate())
return new Result(Result.DECISION_INDETERMINATE, pfr.getStatus(),
context.getResourceId().encode());
// we must have found a policy
return pfr.getPolicy().evaluate(context);
}
/**
* Encodes this <code>PolicyReference</code> into its XML representation
* and writes this encoding to the given <code>OutputStream</code> with
* no indentation.
*
* @param output a stream into which the XML-encoded data is written
*/
public void encode(OutputStream output) {
encode(output, new Indenter(0));
}
/**
* Encodes this <code>PolicyReference</code> into its XML representation
* and writes this encoding to the given <code>OutputStream</code> with
* indentation.
*
* @param output a stream into which the XML-encoded data is written
* @param indenter an object that creates indentation strings
*/
public void encode(OutputStream output, Indenter indenter) {
PrintStream out = new PrintStream(output);
String encoded = indenter.makeString();
if (policyType == POLICY_REFERENCE) {
out.println(encoded + "<PolicyIdReference" + encodeConstraints() +
">" + reference.toString() + "</PolicyIdReference>");
} else {
out.println(encoded + "<PolicySetIdReference" +
encodeConstraints() + ">" + reference.toString() +
"</PolicySetIdReference>");
}
}
/**
* Private helper method that encodes the variable constraints info. Note
* that if this is a pre-2.0 policy the constraints are always null, so
* nothing will be added here.
*/
private String encodeConstraints() {
String str = "";
VersionConstraints version = getConstraints();
String v = version.getVersionConstraint();
if (v != null)
str += " Version=\"" + v + "\"";
String e = version.getEarliestConstraint();
if (e != null)
str += " EarliestVersion=\"" + e + "\"";
String l = version.getLatestConstraint();
if (l != null)
str += " LatestVersion=\"" + l + "\"";
return str;
}
}