/*
* @(#)ReductionGraph.java
*
* Copyright 2007 Swedish Institute of Computer Science 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 Swedish Institute of Computer Science 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. THE SWEDISH INSTITUE OF COMPUTER
* SCIENCE ("SICS") 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 SICS 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 SICS 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.reduction;
import java.net.URI;
import java.util.ArrayList;
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 com.sun.xacml.AbstractPolicy;
import com.sun.xacml.Constants;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.Obligation;
import com.sun.xacml.PolicyTreeElement;
import com.sun.xacml.ctx.RequestElement;
import com.sun.xacml.ctx.Result;
import com.sun.xacml.ctx.Status;
/**
* This class saves a graph structure for reducing untrusted policies.
*
* @author Ludwig Seitz
*
*/
public class ReductionGraph implements Cloneable {
/**
* <code>Set</code>s of <code>ReductionGraphEdge</code>s of the graph
* keyed by the nodeIds (i.e. policyId <code>URI</code>s) they go out from.
*/
private Map<URI, Set<ReductionGraphEdge>> fromEdges;
/**
* <code>Set</code>s of <code>ReductionGraphEdge</code>s of the graph
* keyed by the nodeIds (i.e. policyId <code>URI</code>s) they go in to.
*/
private Map<URI, Set<ReductionGraphEdge>> toEdges;
/**
* The <code>ReductionGraphNode</code>s keyed by the id.
*/
private Map<URI, ReductionGraphNode> nodes;
/**
* Constructor, starts a new ReductionGraph.
*
* @param pps The parent policy set, from which we gather the nodes
* of the graph.
*/
public ReductionGraph(AbstractPolicy pps) {
this.fromEdges = new HashMap<URI, Set<ReductionGraphEdge>>();
this.toEdges = new HashMap<URI, Set<ReductionGraphEdge>>();
this.nodes = new HashMap<URI, ReductionGraphNode>();
if (pps != null) {
Iterator<PolicyTreeElement> iter = pps.getChildren().iterator();
while(iter.hasNext()) {
AbstractPolicy policy = (AbstractPolicy)iter.next();
this.nodes.put(policy.getId(), new ReductionGraphNode(policy));
}
}
}
/**
* The clone method.
* FIXME: this does no deep copy on the content of the Maps.
*
* @return a copy of this object.
*/
public Object clone() {
try {
ReductionGraph clone = (ReductionGraph) super.clone();
clone.fromEdges = new HashMap<URI, Set<ReductionGraphEdge>>(this.fromEdges);
clone.toEdges = new HashMap<URI, Set<ReductionGraphEdge>>(this.toEdges);
clone.nodes = new HashMap<URI, ReductionGraphNode>(this.nodes);
return clone;
} catch (CloneNotSupportedException e) {//this should never happen
throw new RuntimeException("Couldn't clone ReductionGraph");
}
}
/**
* A private helper that does the reduction of an untrusted policy or
* policyset.
*
* @param ctx The evaluation context.
* @param result The evaluation result of this policy.
* @param policyId The id of the policy being reduced.
*
* @return The adapted evaluation result of this policy
* (see standard).
*/
public Result reduce(EvaluationCtx ctx, Result result, URI policyId) {
int allowedEdges = 0;
int extendedAllowedEdges = 0;
if (result.getDecision() == Result.DECISION_PERMIT) {
allowedEdges = ReductionGraphEdge.PP;
extendedAllowedEdges
= ReductionGraphEdge.PP | ReductionGraphEdge.PI;
} else if (result.getDecision() == Result.DECISION_DENY) {
allowedEdges = ReductionGraphEdge.DP;
extendedAllowedEdges
= ReductionGraphEdge.DP | ReductionGraphEdge.DI;
} else if (result.getDecision() == Result.DECISION_INDETERMINATE) {
allowedEdges
= ReductionGraphEdge.PP | ReductionGraphEdge.PI;
extendedAllowedEdges
= ReductionGraphEdge.DP | ReductionGraphEdge.DI;
}
Result reductionResult = graphSearch(allowedEdges, ctx, policyId);
if(reductionResult == null) {
reductionResult = graphSearch(extendedAllowedEdges, ctx, policyId);
if (reductionResult == null) {
return null;
}
List<String> codes = new ArrayList<String>();
codes.add(Status.STATUS_PROCESSING_ERROR);
return new Result(Result.DECISION_INDETERMINATE,
new Status(codes, "Error while reducing"));
}
return reductionResult; // Permit because reductionResult != null
}
private Result graphSearch(int allowedEdges, EvaluationCtx ctx,
URI policyId) {
// Get the node.
ReductionGraphNode node = (ReductionGraphNode)this.nodes.get(policyId);
// Prepare the 'discarded' result (see standard).
Result nullResult = null;
// Check if there is a parent policy set
if (node == null) {
ctx.newEvent(Integer.valueOf(allowedEdges));
ctx.closeCurrentEvent(nullResult);
return null;
}
// Recursion end clause
if (node.isTrusted()) {
return new Result(Result.DECISION_PERMIT, node.getObligations());
}
// Signal the reduction event
ctx.newEvent(Integer.valueOf(allowedEdges));
Result result = node.previousReduction(allowedEdges);
if (result == null || result.getDecision() != Result.DECISION_DENY) {
//DENY means that the node has not yet been reduced.
//Its a fix so we can just transmit the result.
ctx.closeCurrentEvent(result);
return result;
}
// create an administrative context
int newDecision = ctx.getDecision();
//Figure out decision if necessary
if (newDecision == Result.INVALID_DECISION) {
if ((allowedEdges & ReductionGraphEdge.PP)
== ReductionGraphEdge.PP) {
newDecision = Result.DECISION_PERMIT;
} else if ((allowedEdges & ReductionGraphEdge.DP)
== ReductionGraphEdge.DP) {
newDecision = Result.DECISION_DENY;
} else { // this should never happen because decision should be
// permit or deny.
//close the reduction event
ctx.closeCurrentEvent(nullResult);
return null;
}
}
Set<RequestElement> newDelegate = null;
// btw: we can safely assume that the policyIssuer is not
// null here otherwise this function would not have been called.
newDelegate = new HashSet<RequestElement>();
RequestElement delegate = new RequestElement(Constants.DELEGATE,
node.getIssuer().getAttributes());
newDelegate.add(delegate);
EvaluationCtx admCtx = ctx.createAdminCtx(newDecision,
newDelegate);
// deactivate this policy, we don't want to search it again.
admCtx.addInactivePolicyId(node.getNodeId());
// Create edgeExplorer to go through the graph edges from this node
EdgeExplorer potentialEdges = new EdgeExplorer(allowedEdges, admCtx,
node.getNodeId());
while (potentialEdges.hasNext()) {
URI nextPolicyId = potentialEdges.next();
if (nextPolicyId == null) {
ctx.closeCurrentEvent(nullResult);
node.setState(null, allowedEdges,
admCtx.getDelegationDepth());
return null;
}
result = graphSearch(allowedEdges, admCtx, nextPolicyId);
if (result != null &&
result.getDecision() == Result.DECISION_PERMIT) {
ctx.closeCurrentEvent(result);
Set<Obligation> obligations = new HashSet<Obligation>(node.getObligations());
obligations.addAll(result.getObligations());
node.setState(result, allowedEdges,
admCtx.getDelegationDepth());
//now return the result
return new Result(Result.DECISION_PERMIT, obligations);
}
}
ctx.closeCurrentEvent(nullResult);
node.setState(nullResult, allowedEdges, admCtx.getDelegationDepth());
return null;
}
/**
* Return the type of edge there is for two policies identified
* by their PolicyId <code>URI</code>s.
*
* @param from The policy from which the edge originates.
* @param to The policy to which the edge goes.
*
* @return the type of edge as in the static list above.
*/
public int getEdge(URI from, URI to) {
Set<ReductionGraphEdge> rteSet = this.fromEdges.get(from);
if (rteSet == null) {
return ReductionGraphEdge.NOT_EVALUATED;
}
Iterator<ReductionGraphEdge> iter = rteSet.iterator();
while (iter.hasNext()) {
ReductionGraphEdge rte = (ReductionGraphEdge)iter.next();
if (rte.getTo().equals(to)) {
return rte.getType();
}
}
//if we arrived here, no edges between the two policies where found
return ReductionGraphEdge.NOT_EVALUATED;
}
/**
* Set an edge in the ReductionGraph.
*
* @param edge The edge to be set.
*/
public void setEdge(ReductionGraphEdge edge) {
Set<ReductionGraphEdge> fromSet = this.fromEdges.get(edge.getFrom());
if (fromSet == null) {
fromSet = new HashSet<ReductionGraphEdge>();
this.fromEdges.put(edge.getFrom(), fromSet);
}
fromSet.add(edge);
Set<ReductionGraphEdge> toSet = this.toEdges.get(edge.getTo());
if (toSet == null) {
toSet = new HashSet<ReductionGraphEdge>();
this.toEdges.put(edge.getTo(), toSet);
}
toSet.add(edge);
}
/**
* Returns the <code>Set</code> of <code>ReductionGraphEdge</code>s
* that go out from a specific policy.
*
* @param from The policy identified its PolicyId <code>URI</code>.
*
* @return A <code>Set</code> of <code>ReductionGraphEdge</code>s.
*/
public Set<ReductionGraphEdge> getFromEdges(URI from) {
Set<ReductionGraphEdge> result = this.fromEdges.get(from);
if (result == null) {
result = new HashSet<ReductionGraphEdge>();
this.fromEdges.put(from, result);
}
return result;
}
/**
* Returns the <code>Set</code> of <code>ReductionGraphEdge</code>s
* that go to a specific policy.
*
* @param to The policy identified its PolicyId <code>URI</code>.
*
* @return A <code>Set</code> of <code>ReductionGraphEdge</code>s.
*/
public Set<ReductionGraphEdge> getToEdges(URI to) {
Set<ReductionGraphEdge> result = this.fromEdges.get(to);
if (result == null) {
result = new HashSet<ReductionGraphEdge>();
this.fromEdges.put(to, result);
}
return result;
}
/**
* Get a node from the graph.
*
* @param nodeId The node's id.
*
* @return The node.
*/
public ReductionGraphNode getNode(URI nodeId) {
return (ReductionGraphNode)this.nodes.get(nodeId);
}
/**
* Encode to string for debugging
*
* @return String representation of the graph.
*/
public String toString() {
String result = "";
Iterator<Map.Entry<URI, ReductionGraphNode>> iter = this.nodes.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<URI, ReductionGraphNode> entry = iter.next();
ReductionGraphNode node = (ReductionGraphNode) entry.getValue();
result += node.toString();
if (this.fromEdges.get(entry.getKey()) != null) {
Iterator<ReductionGraphEdge> iter2 = (this.fromEdges.get(entry.getKey())).iterator();
while (iter2.hasNext()) {
ReductionGraphEdge edge = iter2.next();
result += "[" + edge.toString() + "] ";
}
}
result += Constants.nl;
}
return result;
}
}