/* * @(#)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. } }