This repository has been archived on 2018-08-08. You can view files and clone it, but cannot push or open issues or pull requests.
SecureBPMN/GenericBreakGlass-XACML/src/com.sun.xacml/src/main/java/com/sun/xacml/BasicEvaluationCtx.java

1154 lines
44 KiB
Java

/*
* @(#)BasicEvaluationCtx.java
*
* Copyright 2004-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;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.attr.DateAttribute;
import com.sun.xacml.attr.DateTimeAttribute;
import com.sun.xacml.attr.DecisionAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.attr.TimeAttribute;
import com.sun.xacml.cond.EvaluationResult;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.RequestElement;
import com.sun.xacml.ctx.Result;
import com.sun.xacml.finder.AttributeFinder;
import com.sun.xacml.finder.RevocationFinder;
import com.sun.xacml.reduction.ReductionGraph;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
/**
* A basic implementation of <code>EvaluationCtx</code> that is created from
* an XACML Request and falls back on an AttributeFinder if a requested
* value isn't available in the Request.
* <p>
* Note that this class can do some optional caching for current date, time,
* and dateTime values (defined by a boolean flag to the constructors). The
* XACML specification requires that these values always be available, but it
* does not specify whether or not they must remain constant over the course
* of an evaluation if the values are being generated by the PDP (if the
* values are provided in the Request, then obviously they will remain
* constant). The default behavior is for these environment values to be
* cached, so that (for example) the current time remains constant over the
* course of an evaluation.
*
* @since 1.2
* @author Seth Proctor
* @author Ludwig Seitz
*/
public class BasicEvaluationCtx implements EvaluationCtx, Cloneable
{
/**
* the finder to use if a value isn't in the request
*/
private AttributeFinder aFinder;
/**
* flag that indicates if the attribute finder is deactivated.
*/
private boolean afinderActive = true;
/**
* the revocation finder to use
*/
private RevocationFinder rFinder;
/**
* flag that indicates if the revocation finder is deactivated.
*/
private boolean rfinderActive = true;
/**
* the DOM root the original RequestContext document.
*/
private Node requestRoot;
/**
* The request data. A map of <code>Set</code>s of
* <code>RequestElement</code>s keyed by the attribute category
* <code>String</code>s.
*/
private HashMap<URI, Set<RequestElement>> requestElements = null;
/**
* the cached delegation decision value
*/
private int delegationDecision;
/**
* the delegation depth value
*/
private int delegationDepth;
/**
* the resource id
*/
private AttributeValue resourceId;
/**
* the <code>Set</code> of <code>RequestElements</code> containing
* only the attributes that should be included in the result.
*/
private Set<RequestElement> includedAttributes;
/**
* the resource scope
*/
private int scope;
/**
* the cached current date, which we may or may
* not be using depending on how this object was constructed
*/
private DateAttribute currentDate;
/**
* the cached current time, which we may or may
* not be using depending on how this object was constructed
*/
private TimeAttribute currentTime;
/**
* the cached current dateTime, which we may or may
* not be using depending on how this object was constructed
*/
private DateTimeAttribute currentDateTime;
/**
* a flag indicating that we should use the cached date/time values.
*/
private boolean useCachedEnvValues;
/**
* a stack of parent <code>PolicySet</code>s.
*/
private Stack<PolicyTreeElement> parentPolicySets;
/**
* a stack of <code>ReductionGraph</code>s.
*/
private Stack<ReductionGraph> reductionGraphs;
/**
* the set of deactivated policies
*/
private Set<URI> inactivePolicyIds;
/**
* the logger we'll use for all messages
*/
private static final Logger logger =
Logger.getLogger(BasicEvaluationCtx.class.getName());
/**
* Constructs a new <code>BasicEvaluationCtx</code> based on the given
* request. The resulting context will cache current date, time, and
* dateTime values so they remain constant for this evaluation.
*
* @param request the request
*
* @throws ParsingException if a required attribute is missing, or if there
* are any problems dealing with the request data
*/
public BasicEvaluationCtx(RequestCtx request)
throws ParsingException {
this(request, null, null, true);
}
/**
* Constructs a new <code>BasicEvaluationCtx</code> based on the given
* request.
*
* @param request the request
* @param cacheEnvValues whether or not to cache the current time, date,
* and dateTime so they are constant for the scope
* of this evaluation
*
* @throws ParsingException if a required attribute is missing, or if there
* are any problems dealing with the request data
*/
public BasicEvaluationCtx(RequestCtx request, boolean cacheEnvValues)
throws ParsingException {
this(request, null, null, cacheEnvValues);
}
/**
* Constructs a new <code>BasicEvaluationCtx</code> based on the given
* request, and supports looking outside the original request for attribute
* values using the <code>AttributeFinder</code>. The resulting context
* will cache current date, time, and dateTime values so they remain
* constant for this evaluation.
*
* @param request the request
* @param aFinder an <code>AttributeFinder</code> to use in looking for
* attributes that aren't in the request
* @param rFinder an <code>RevocationFinder</code> to use in looking for
* revocations
*
* @throws ParsingException if a required attribute is missing, or if there
* are any problems dealing with the request data
*/
public BasicEvaluationCtx(RequestCtx request, AttributeFinder aFinder,
RevocationFinder rFinder)
throws ParsingException {
this(request, aFinder, rFinder, true);
}
/**
* Constructs a new <code>BasicEvaluationCtx</code> based on the given
* request, and supports looking outside the original request for attribute
* values using the <code>AttributeFinder</code>.
*
* @param request the request
* @param aFinder an <code>AttributeFinder</code> to use in looking for
* @param rFinder an <code>RevocationFinder</code> to use in looking for
* revocations
* attributes that aren't in the request
* @param cacheEnvValues whether or not to cache the current time, date,
* and dateTime so they are constant for the scope
* of this evaluation
*
* @throws ParsingException if a required attribute is missing, or if there
* are any problems dealing with the request data
*/
public BasicEvaluationCtx(RequestCtx request, AttributeFinder aFinder,
RevocationFinder rFinder,
boolean cacheEnvValues)
throws ParsingException {
// keep track of the attribute finder
this.aFinder = aFinder;
// keep track of the revocation finder
this.rFinder = rFinder;
// remember the root of the DOM tree for XPath queries
this.requestRoot = request.getDocumentRoot();
// initialize the cached date/time values so it's clear we haven't
// retrieved them yet
this.useCachedEnvValues = cacheEnvValues;
this.currentDate = null;
this.currentTime = null;
this.currentDateTime = null;
// parse the request elements into a map
// and collect the IncludeInResult attributes
this.requestElements = new HashMap<URI, Set<RequestElement>>();
this.includedAttributes = new HashSet<RequestElement>();
Iterator<RequestElement> iter = request.getRequestElements().iterator();
while (iter.hasNext()){
RequestElement re = iter.next();
URI category = re.getCategory();
if (!re.getIncludeInResultSet().isEmpty()) {
this.includedAttributes.add(new RequestElement(
re.getCategory(), re.getIncludeInResultSet()));
}
if (this.requestElements.containsKey(category)) {
this.scope = Constants.SCOPE_MULTIPE_ELEMENTS;
Set<RequestElement> set = this.requestElements.get(category);
set.add(re);
} else {
Set<RequestElement> set = new HashSet<RequestElement>();
set.add(re);
this.requestElements.put(category, set);
}
}
//Compatibility code for XACML 2.0 requestId handling
if (request.getXACMLVersion() < Constants.XACML_VERSION_3_0
&& !this.includedAttributes.isEmpty()) {
throw new ParsingException("Can't have included attributes"
+ " in XACML < v 3.0");
}
//Compatibility code for old Resource handling.
if (this.requestElements.containsKey(Constants.RESOURCE_CAT)) {
Iterator<RequestElement> reIt = this.requestElements.get(
Constants.RESOURCE_CAT).iterator();
while(reIt.hasNext()) {
RequestElement re = (RequestElement)reIt.next();
checkResource(re.getAttributes());
}
}
//this will only be set in administrative requests.
this.delegationDecision = Result.INVALID_DECISION;
this.delegationDepth = 0;
this.parentPolicySets = new Stack<PolicyTreeElement>();
this.reductionGraphs = new Stack<ReductionGraph>();
// this is empty when the evaluation context is first created
// it is used during the reduction of untrusted policies
this.inactivePolicyIds = new HashSet<URI>();
}
/**
* The clone method
*
* @return a copy of this object.
*
*/
public Object clone() {
try {
BasicEvaluationCtx clone = (BasicEvaluationCtx)super.clone();
if (this.aFinder != null) {
clone.aFinder = (AttributeFinder)this.aFinder.clone();
}
clone.afinderActive = this.afinderActive;
if(this.rFinder != null) {
clone.rFinder = (RevocationFinder)this.rFinder.clone();
}
clone.rfinderActive = this.rfinderActive;
if(this.requestRoot != null) {
clone.requestRoot = this.requestRoot.cloneNode(true);
} else {
clone.requestRoot = null;
}
// deep copy of the requestElements
clone.requestElements = new HashMap<URI, Set<RequestElement>>();
Iterator<URI> iter = this.requestElements.keySet().iterator();
while(iter.hasNext()) {
URI key = URI.create(iter.next().toString());
Set<RequestElement> res = new HashSet<RequestElement>();
Iterator<RequestElement> iter2 = this.requestElements.get(key).iterator();
while(iter2.hasNext()) {
res.add((RequestElement)iter2.next().clone());
}
clone.requestElements.put(key, res);
}
clone.delegationDecision = this.delegationDecision;
clone.delegationDepth = this.delegationDepth;
clone.resourceId = this.resourceId;
//deep copy of the included attributes
clone.includedAttributes = new HashSet<RequestElement>();
Iterator<RequestElement> iter3 = this.includedAttributes.iterator();
while(iter3.hasNext()) {
RequestElement element = iter3.next();
clone.includedAttributes.add( (RequestElement) element.clone());
}
synchronized (clone) {
clone.scope = this.scope;
clone.currentDate = this.currentDate;
clone.currentTime = this.currentTime;
clone.currentDateTime = this.currentDateTime;
clone.useCachedEnvValues = this.useCachedEnvValues;
}
//clone.parentPolicySets = (Stack<PolicyTreeElement>) this.parentPolicySets.clone(); //
clone.parentPolicySets = new Stack<PolicyTreeElement>();
Collections.copy(parentPolicySets, this.parentPolicySets);
//clone.reductionGraphs = (Stack<ReductionGraph>) this.reductionGraphs.clone();
clone.reductionGraphs = new Stack<ReductionGraph>();
Collections.copy(reductionGraphs, this.reductionGraphs);
clone.inactivePolicyIds = new HashSet<URI>(this.inactivePolicyIds);
return clone;
} catch (CloneNotSupportedException e) {//This should never happen
throw new RuntimeException("Couldn't clone BasicEvaluationCtx");
}
}
/**
* Create a context for an administrative request from this context.
*
* @param decision The decision code corresponding to those in the
* <code>Result</code> class.
* @param delegate The delegate in this request (a set containing a
* single <code>RequestElement</code>).
*
* @return An administrative context for this context.
*/
public EvaluationCtx createAdminCtx(int decision, Set<RequestElement> delegate) {
if (decision == Result.INVALID_DECISION
|| decision == Result.DECISION_INDETERMINATE
|| decision == Result.DECISION_NOT_APPLICABLE) {
throw new ProcessingException("Invalid decision value for an"
+ " administrative request");
}
//First copy the old context.
BasicEvaluationCtx adminCtx = null;
adminCtx = (BasicEvaluationCtx)this.clone();
//Now set the new values ...
adminCtx.delegationDecision = decision;
adminCtx.delegationDepth = this.delegationDepth + 1;
//... and modify the requestElements accordingly
HashMap<URI, Set<RequestElement>> newREmap = new HashMap<URI, Set<RequestElement>>();
//get out the delegation relevant information
// and remove the old
adminCtx.requestElements.remove(Constants.DELEGATION_INFO);
adminCtx.requestElements.remove(Constants.DELEGATE);
//modify the rest so category * becomes category
// urn:oasis:names:tc:xacml:3.0:attribute-category:delegated:*
Iterator<Map.Entry<URI, Set<RequestElement>>> iter = adminCtx.requestElements.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry<URI, Set<RequestElement>> entry = iter.next();
Set<RequestElement> requestElementCategory = entry.getValue();
Iterator<RequestElement> iter2 = requestElementCategory.iterator();
HashSet<RequestElement> newREset = new HashSet<RequestElement>();
URI category = null;
while(iter2.hasNext()) {
RequestElement oldRe = iter2.next();
category = oldRe.getCategory();
if (category.toString().startsWith(Constants.DELEGATED)) {
newREset.add(oldRe);
} else { // add the delegated part to the category
category = URI.create(Constants.DELEGATED
+ category.toString());
// this URI must be valid, since the old category was
RequestElement newRe = new RequestElement(category,
oldRe.getAttributes());
newREset.add(newRe);
}
}
newREmap.put(category, newREset);
}
//put in the new delegate
newREmap.put(Constants.DELEGATE, delegate);
//put in the new delegation info
// then create the correct DelegationInfo category
DecisionAttribute delegationDecision = new DecisionAttribute(decision);
Set<Attribute> delegationInfoAttrs = new HashSet<Attribute>();
delegationInfoAttrs.add(delegationDecision);
RequestElement delegationInfo = new RequestElement(
Constants.DELEGATION_INFO, delegationInfoAttrs);
Set<RequestElement> delegationInfoSet = new HashSet<RequestElement>();
delegationInfoSet.add(delegationInfo);
// and put it into the new request elements map
newREmap.put(Constants.DELEGATION_INFO, delegationInfoSet);
//now we are done, copy the newREmap to the requestElements
adminCtx.requestElements = newREmap;
return adminCtx;
}
/**
* Creates a copy of this context with disabled attribute finder.
*
* @return A copy of this context with disabled attribute finder.
*/
public EvaluationCtx copyWithoutAttributeFinder() {
BasicEvaluationCtx ctx = (BasicEvaluationCtx)this.clone();
ctx.aFinder = null;
ctx.afinderActive = false;
return ctx;
}
/**
* This basically does the same thing that the other types need
* to do, except that we also look for a resource-id attribute, not
* because we're going to use, but only to make sure that it's not
* double, and for the optional scope attribute, to see what the scope
* of the attribute is.
*
* @param resourceAttr A <code>Map</code> keyed by attribute id
* <code>URI</code>s, containing <code>Set</code>s
* of <code>Attribute</code>s.
* @param xacmlVersion The XACML version number.
*
* @throws ParsingException
*
*/
private void checkResource(Map<URI, Set<Attribute>> resourceAttr)
throws ParsingException {
// make sure there's only one value for resource-id
Set<Attribute> rSet = resourceAttr.get(Constants.RESOURCE_ID);
if (rSet != null) {
if (rSet.size() > 1) {
System.err.println("Resource may contain "
+ "only one resource-id Attribute");
throw new ParsingException("too many resource-id attrs");
}
// keep track of the resource-id attribute
this.resourceId = ((Attribute)(rSet.iterator().next()))
.getValue();
}
// see if a resource-scope attribute was included
if (resourceAttr.containsKey(Constants.RESOURCE_SCOPE)) {
Set<Attribute> set = resourceAttr.get(Constants.RESOURCE_SCOPE);
// make sure there's only one value for resource-scope
if (set.size() > 1) {
System.err.println("Resource may contain "
+ "only one resource-scope Attribute");
throw new ParsingException("too many resource-scope attrs");
}
Attribute attr = (Attribute)(set.iterator().next());
AttributeValue attrValue = attr.getValue();
// scope must be a string, so throw an exception otherwise
if (! attrValue.getType().toString().
equals(StringAttribute.identifier)) {
throw new ParsingException("scope attr must be a string");
}
String value = ((StringAttribute)attrValue).getValue();
if (value.equals("Immediate")) {
this.scope = Constants.SCOPE_IMMEDIATE;
} else if (value.equals("Children")) {
this.scope = Constants.SCOPE_CHILDREN;
} else if (value.equals("Descendants")) {
this.scope = Constants.SCOPE_DESCENDANTS;
} else if (value.equals("XPath-expression")) {
this.scope = Constants.SCOPE_XPATH_EXPRESSION;
} else if (value.equals("EntireHierarchy")) {
this.scope = Constants.SCOPE_ENTIRE_HIERARCHY;
} else {
System.err.println("Unknown scope type: " + value);
throw new ParsingException("invalid scope type: " + value);
}
} else {
// by default, the scope is always Immediate
this.scope = Constants.SCOPE_IMMEDIATE;
}
}
/**
* Returns the DOM root of the original RequestType XML document.
*
* @return the DOM root node
*/
public Node getRequestRoot() {
return this.requestRoot;
}
/**
* Returns the resource scope of the request, which will be one of the
* three fields denoting Immediate, Children, or Descendants.
*
* @return the scope of the resource in the request
*/
public int getScope() {
return this.scope;
}
/**
* Returns the resource named in the request as resource-id.
*
* @return the resource
*/
public AttributeValue getResourceId() {
return this.resourceId;
}
/**
* @return The <code>Set</code> of <code>RequestElement</code>s
* representing the included attributes.
*/
public Set<RequestElement> getIncludedAttributes() {
return this.includedAttributes;
}
/**
* Changes the value of the resource-id attribute in this context. This
* is useful when you have multiple resources (ie, a scope other than
* IMMEDIATE), and you need to keep changing only the resource-id to
* evaluate the different effective requests.
*
* @param resourceId the new resource-id value
*/
public void setResourceId(AttributeValue resourceId) {
this.resourceId = resourceId;
// there will always be exactly one value for this attribute
Iterator<RequestElement> rIt = this.requestElements.get(Constants.RESOURCE_CAT).iterator();
while (rIt.hasNext()) {
RequestElement re = rIt.next();
Map<URI, Set<Attribute>> attrMap = re.getAttributes();
Set<Attribute> resId = attrMap.get(Constants.RESOURCE_ID);
Attribute attr = (Attribute)(resId.iterator().next());
// remove the old value...
resId.remove(attr);
Attribute newAttr = new Attribute(attr.getId(), attr.getIssuer(),
resourceId,
attr.getVersion(),
attr.includeInResult());
// if it was marked to be included in the result remove this too
if (attr.includeInResult()) {
Iterator<RequestElement> iter = this.includedAttributes.iterator();
while (iter.hasNext()) {
RequestElement ire = iter.next();
if (ire.getCategory().equals(Constants.RESOURCE_CAT)) {
this.includedAttributes.remove(ire);
Map<URI, Set<Attribute>> newAttrs = new HashMap<URI, Set<Attribute>>();
newAttrs.putAll(ire.getAttributes());
newAttrs.remove(Constants.RESOURCE_ID);
Set<Attribute> newResourceId = new HashSet<Attribute>();
newResourceId.add(newAttr);
newAttrs.put(Constants.RESOURCE_ID, newResourceId);
this.includedAttributes.add(new RequestElement(
ire.getCategory(), newAttrs));
}
}
}
// ...and insert the new value
resId.add(newAttr);
}
}
/**
* Returns the value for the current time. The current time, current
* date, and current dateTime are consistent, so that they all
* represent the same moment. If this is the first time that one
* of these three values has been requested, and caching is enabled,
* then the three values will be resolved and stored.
* <p>
* Note that the value supplied here applies only to dynamically
* resolved values, not those supplied in the Request. In other words,
* this always returns a dynamically resolved value local to the PDP,
* even if a different value was supplied in the Request. This is
* handled correctly when the value is requested by its identifier.
*
* @return the current time
*/
public synchronized TimeAttribute getCurrentTime() {
long millis = dateTimeHelper();
if (this.useCachedEnvValues) {
return this.currentTime;
}
return new TimeAttribute(new Date(millis));
}
/**
* Returns the value for the current date. The current time, current
* date, and current dateTime are consistent, so that they all
* represent the same moment. If this is the first time that one
* of these three values has been requested, and caching is enabled,
* then the three values will be resolved and stored.
* <p>
* Note that the value supplied here applies only to dynamically
* resolved values, not those supplied in the Request. In other words,
* this always returns a dynamically resolved value local to the PDP,
* even if a different value was supplied in the Request. This is
* handled correctly when the value is requested by its identifier.
*
* @return the current date
*/
public synchronized DateAttribute getCurrentDate() {
long millis = dateTimeHelper();
if (this.useCachedEnvValues) {
return this.currentDate;
}
return new DateAttribute(new Date(millis));
}
/**
* Returns the value for the current dateTime. The current time, current
* date, and current dateTime are consistent, so that they all
* represent the same moment. If this is the first time that one
* of these three values has been requested, and caching is enabled,
* then the three values will be resolved and stored.
* <p>
* Note that the value supplied here applies only to dynamically
* resolved values, not those supplied in the Request. In other words,
* this always returns a dynamically resolved value local to the PDP,
* even if a different value was supplied in the Request. This is
* handled correctly when the value is requested by its identifier.
*
* @return the current dateTime
*/
public synchronized DateTimeAttribute getCurrentDateTime() {
long millis = dateTimeHelper();
if (this.useCachedEnvValues) {
return this.currentDateTime;
}
return new DateTimeAttribute(new Date(millis));
}
/**
* Private helper that figures out if we need to resolve new values,
* and returns either the current moment (if we're not caching) or
* -1 (if we are caching)
*
* @return returns the current moment or -1.
*/
private long dateTimeHelper() {
// if we already have current values, then we can stop (note this
// always means that we're caching)
if (this.currentTime != null) {
return -1;
}
// get the current moment
Date time = new Date();
long millis = time.getTime();
// if we're not caching then we just return the current moment
if (! this.useCachedEnvValues) {
return millis;
}
// we're caching, so resolve all three values, making sure
// to use clean copies of the date object since it may be
// modified when creating the attributes
this.currentTime = new TimeAttribute(time);
this.currentDate = new DateAttribute(new Date(millis));
this.currentDateTime = new DateTimeAttribute(new Date(millis));
return -1;
}
/**
* Return available attribute values of the selected category.
*
* @param category the category the attribute value(s) must be in
* @param type the type of the attribute value(s) to find
* @param id the id of the attribute value(s) to find
* @param issuer the issuer of the attribute value(s) to find or null
*
* @return a result containing a bag either empty because no values were
* found or containing at least one value, or status associated with an
* Indeterminate result
*/
public EvaluationResult getAttribute(URI category, URI type, URI id,
URI issuer) {
Set<RequestElement> elements = this.requestElements.get(category);
if (elements == null) {
elements = RequestElement.EMPTY_SET;
}
return getGenericAttributes(category, type, id, issuer,
elements, false);
}
/**
* Helper function for the resource, action, environment and delegate
* methods to get an attribute.
*
* @param category the category the attribute value(s) must be in
* @param type the type of the attribute value(s) to find
* @param id the id of the attribute value(s) to find
* @param issuer the issuer of the attribute value(s) to find or null
* @param elements a set of <code>RequestElement</code>s to search for
* the requested attribute.
* @param local get only local attributes, don't call the
* AttributeFinder.
*
* @return a result containing a bag either empty because no values were
* found or containing at least one value, or status associated with an
* Indeterminate result.
*
*/
private EvaluationResult getGenericAttributes(URI category, URI type,
URI id, URI issuer, Set<RequestElement> elements, boolean local) {
Iterator<RequestElement> iter = elements.iterator();
Set<Attribute> attrSet = new HashSet<Attribute>();
while (iter.hasNext()) {
RequestElement element = iter.next();
// try to find the id
Map<URI, Set<Attribute>> map = element.getAttributes();
if (map.containsKey(id)) {
attrSet.addAll(map.get(id));
}
}
if (attrSet.isEmpty()) {
if (local) {
return new EvaluationResult(BagAttribute.createEmptyBag(type));
}
// the request didn't have an attribute with that id, so we should
// try asking the attribute finder
return callHelper(category, type, id, issuer, elements);
}
// now go through each, considering each Attribute object
List<AttributeValue> attributes = new ArrayList<AttributeValue>();
Iterator<Attribute> it = attrSet.iterator();
while (it.hasNext()) {
Attribute attr = it.next();
// make sure the type and issuer are correct
if ((attr.getValue().getType().equals(type)) &&
((issuer == null) ||
((attr.getIssuer() != null) &&
(attr.getIssuer().equals(issuer.toString()))))) {
// if we got here, then we found a match, so we want to pull
// out the values and put them in out list
attributes.add(attr.getValue());
}
}
// see if we found any acceptable attributes
if (attributes.size() == 0) {
if (local) {
return new EvaluationResult(BagAttribute.createEmptyBag(type));
}
// we failed to find any that matched the type/issuer, or all the
// Attribute types were empty...so ask the finder
// if (logger.isLoggable(Level.FINE)) {
// logger.fine("Attribute not in request: " + id.toString() +
// " ... querying AttributeFinder");
// }
if (logger.isDebugEnabled()) {
logger.debug("Attribute not in request: " + id.toString() + " category " + category +
" ... querying AttributeFinder");
}
return callHelper(category, type, id, issuer, elements);
}
// if we got here, then we found at least one useful AttributeValue
return new EvaluationResult(new BagAttribute(type, attributes));
}
/**
* Protected helper that calls the finder if it's non-null, or else returns
* an empty bag. (Proctected so it can be overridden by subclasses).
*
* @param category the category the attribute value(s) must be in or null
* @param type the type of the attribute value(s) to find or null
* @param id the id of the attribute value(s) to find or null
* @param issuer the issuer of the attribute value(s) to find or null
* @param elements a set of <code>RequestElement</code>s to search for the
* requested attribute.
*
* @return an <code>EvaluationResult</code> containing the requested
* attribute or an empty bag.
*/
protected EvaluationResult callHelper(URI category, URI type, URI id,
URI issuer, Set<RequestElement> elements) {
if (this.aFinder != null) {
return this.aFinder.findAttribute(category, type, id, issuer,
this);
}
if (this.afinderActive) {
logger.warn("Context tried to invoke AttributeFinder but was "
+ "not configured with one");
}
return new EvaluationResult(BagAttribute.createEmptyBag(type));
}
/**
* Returns the attribute value(s) retrieved using the given XPath
* expression.
*
* @param contextPath the XPath expression to search
* @param namespaceNode the DOM node defining namespace mappings to use,
* or null if mappings come from the context root
* @param type the type of the attribute value(s) to find
* @param xpathVersion the version of XPath to use
*
* @return a result containing a bag either empty because no values were
* found or containing at least one value, or status associated with an
* Indeterminate result
*/
public EvaluationResult getAttribute(String contextPath,
Node namespaceNode, URI type,
String xpathVersion) {
if (this.aFinder != null) {
return this.aFinder.findAttribute(contextPath, namespaceNode,
type, this, xpathVersion);
}
logger.warn("Context tried to invoke AttributeFinder but was " +
"not configured with one");
return new EvaluationResult(BagAttribute.createEmptyBag(type));
}
/**
* Get the decision.
*
* @return The <code>int</code> value of the decision according to
* the <code>Result</code> class.
*/
public int getDecision() {
return this.delegationDecision;
}
/**
* Get the delegation depth.
*
* @return The <code>int</code> value specifying the number of nodes
* in the reduction graph until now (not including this one).
*/
public int getDelegationDepth() {
return this.delegationDepth;
}
/**
* Get a whole category.
*
* @param category The name of the category. If the category does
* not exist return an empty set
*
* @return The <code>Set</code> of <code>RequestElement</code>s with
* the matching category.
*/
public Set<RequestElement> getCategory(URI category) {
if (this.requestElements.containsKey(category)) {
return new HashSet<RequestElement>(this.requestElements.get(category));
}
return RequestElement.EMPTY_SET;
}
/**
* @return the <code>Set</code> of <code>RequestElements</code>
* containing the attributes that should be included
* in the result.
*/
public Set<RequestElement> getIncludedAttrs() {
return this.includedAttributes;
}
/**
* @return the <code>Map</code> of <code>RequestElements</code>
* defining this request.
*/
public Map<URI, Set<RequestElement>> getRequestElements() {
return Collections.unmodifiableMap(this.requestElements);
}
/**
* Save the parent <code>PolicySet</code> in this evaluation context
* for doing reduction of delegated policies if that becomes necessary.
*
* @param pps the parent policy set
*/
public void saveParentPolicySet(AbstractPolicy pps) {
if (pps instanceof PolicySet) {
this.parentPolicySets.push(pps);
} else if (pps instanceof PolicyReference) {
PolicyReference pr = (PolicyReference)pps;
if (pr.getReferenceType() != PolicyReference.POLICYSET_REFERENCE) {
//this should never happen
throw new RuntimeException("Tried to save a Reference "
+ "to a Policy as a PolicySet");
}
this.parentPolicySets.push(pps);
} else {
throw new RuntimeException("Tried to save "
+ this.getClass().getName() + " as a PolicySet");
}
}
/**
* Create a reduction graph for the current parent PolicySet.
*
*/
public void createReductionGraph() {
this.reductionGraphs.push(new ReductionGraph(getParentPolicySet()));
}
/**
* @return The current reduction graph or null if there is none.
*/
public ReductionGraph getReductionGraph() {
if (this.reductionGraphs != null && !this.reductionGraphs.empty()) {
return (ReductionGraph)this.reductionGraphs.peek();
}
return null;
}
/**
* Remove the current <code>ReductionGraph</code> from the stack.
*/
public void popReductionGraph() {
if (!this.reductionGraphs.isEmpty()) {
this.reductionGraphs.pop();
}
}
/**
* Returns the root <code>PolicySet</code> for this evaluation context.
* If there is none, return null.
*
* @return the root policy set or null.
*/
public AbstractPolicy getParentPolicySet() {
if (!this.parentPolicySets.empty()) {
return (AbstractPolicy) this.parentPolicySets.peek();
}
return null;
}
/**
* Remove the current parent <code>PolicySet</code> from the stack
* of parent policy sets.
*/
public void popParentPolicySet() {
if (!this.parentPolicySets.isEmpty()) {
this.parentPolicySets.pop();
}
}
/**
* Checks whether a <code>Policy</code> or <code>PolicySet</code>
* supports a revocation of a specific Policy of PolicySet
* in this context.
*
* @param supporting The policy or policy set that could support
* a revocation.
* @param candidate The id of the policy or policy set that is candidate
* for revocation.
*
* @return true if the policy/policy set supports a revocation,
* false otherwise.
*/
public boolean supportsRevocation(AbstractPolicy supporting,
URI candidate) {
if (this.rFinder != null && this.rfinderActive) {
//deactivate revocation finder to avoid infinite loops
// of policies revoking themselves
this.rfinderActive = false;
boolean result = this.rFinder.supportsRevocation(supporting,
candidate, this);
this.rfinderActive = true;
return result;
}
// since there is no revocation finder, no policy can be
// revoked.
logger.warn("Context tried to invoke RevocationFinder but was "
+ "not configured with one");
return false;
}
/**
* Add new inactive PolicyId to the Map
* @param policyId the id of the new inactive policy
*/
public void addInactivePolicyId(URI policyId) {
this.inactivePolicyIds.add(policyId);
}
/**
* Return an unmodifiable <code>Set</code> of <code>URI</code>s of
* inactive policies
* @return the inactive policies
*/
public Set<URI> getInactivePolicyIds() {
return Collections.unmodifiableSet(
new HashSet<URI>(this.inactivePolicyIds));
}
/**
* Signal a new event to this EvaluationCtx.
* BasicEvaluationCtx does nothing with this signal.
*
* @param element The new event.
*/
public void newEvent(Object element) {
//BasicEvaluationCtx does nothing with this signal.
}
/**
* Signal that an event has finished and pass the result.
* BasicEvaluationCtx does nothing with this signal.
*
* @param result The result of the finished event.
*/
public void closeCurrentEvent(Result result) {
//BasicEvaluationCtx does nothing with this signal.
}
/**
* Signal that an event has finished and pass the result.
* BasicEvaluationCtx does nothing with this signal.
*
* @param result The result of the finished event.
*/
public void closeCurrentEvent(MatchResult result) {
//BasicEvaluationCtx does nothing with this signal.
}
/**
* Signal that an event has finished and pass the result
* which is a <code>EvaluationResult</code>
* BasicEvaluationCtx does nothing with this signal.
*
* @param result The result of the finished event.
*/
public void closeCurrentEvent(EvaluationResult result) {
//BasicEvaluationCtx does nothing with this signal.
}
/**
* Signal that an event has finished with a <code>String</code> message.
* BasicEvaluationCtx does nothing with this signal.
*
* @param message The message.
*/
public void closeCurrentEvent(String message) {
//BasicEvaluationCtx does nothing with this signal.
}
/**
* Signal that an event has finished with no result.
* BasicEvaluationCtx does nothing with this signal.
*/
public void closeCurrentEvent() {
//BasicEvaluationCtx does nothing with this signal.
}
}