/*
* @(#)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 EvaluationCtx
that is created from
* an XACML Request and falls back on an AttributeFinder if a requested
* value isn't available in the Request.
*
* 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
* 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.
*
* 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.
*
* 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) {
SetSet
s of
* RequestElement
s keyed by the attribute category
* String
s.
*/
private HashMapSet
of RequestElements
containing
* only the attributes that should be included in the result.
*/
private SetPolicySet
s.
*/
private StackReductionGraph
s.
*/
private StackBasicEvaluationCtx
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 BasicEvaluationCtx
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 BasicEvaluationCtx
based on the given
* request, and supports looking outside the original request for attribute
* values using the AttributeFinder
. 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 AttributeFinder
to use in looking for
* attributes that aren't in the request
* @param rFinder an RevocationFinder
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 BasicEvaluationCtx
based on the given
* request, and supports looking outside the original request for attribute
* values using the AttributeFinder
.
*
* @param request the request
* @param aFinder an AttributeFinder
to use in looking for
* @param rFinder an RevocationFinder
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 HashMapResult
class.
* @param delegate The delegate in this request (a set containing a
* single RequestElement
).
*
* @return An administrative context for this context.
*/
public EvaluationCtx createAdminCtx(int decision, SetMap
keyed by attribute id
* URI
s, containing Set
s
* of Attribute
s.
* @param xacmlVersion The XACML version number.
*
* @throws ParsingException
*
*/
private void checkResource(MapSet
of RequestElement
s
* representing the included attributes.
*/
public SetRequestElement
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, SetRequestElement
s to search for the
* requested attribute.
*
* @return an EvaluationResult
containing the requested
* attribute or an empty bag.
*/
protected EvaluationResult callHelper(URI category, URI type, URI id,
URI issuer, Setint
value of the decision according to
* the Result
class.
*/
public int getDecision() {
return this.delegationDecision;
}
/**
* Get the delegation depth.
*
* @return The int
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 Set
of RequestElement
s with
* the matching category.
*/
public SetSet
of RequestElements
* containing the attributes that should be included
* in the result.
*/
public SetMap
of RequestElements
* defining this request.
*/
public MapPolicySet
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 ReductionGraph
from the stack.
*/
public void popReductionGraph() {
if (!this.reductionGraphs.isEmpty()) {
this.reductionGraphs.pop();
}
}
/**
* Returns the root PolicySet
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 PolicySet
from the stack
* of parent policy sets.
*/
public void popParentPolicySet() {
if (!this.parentPolicySets.isEmpty()) {
this.parentPolicySets.pop();
}
}
/**
* Checks whether a Policy
or PolicySet
* 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 Set
of URI
s of
* inactive policies
* @return the inactive policies
*/
public SetEvaluationResult
* 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 String
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.
}
}