/* * @(#)EdgeExplorer.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.Iterator; import java.util.List; import java.util.Set; import com.sun.xacml.AbstractPolicy; import com.sun.xacml.EvaluationCtx; import com.sun.xacml.MatchResult; import com.sun.xacml.PolicyReference; import com.sun.xacml.PolicySet; import com.sun.xacml.PolicyTreeElement; import com.sun.xacml.ctx.Result; /** * This class provides an iterator that goes through the support * edges of a policy. * * @author Ludwig Seitz * */ public class EdgeExplorer { /** * The node we are exploring from. */ private ReductionGraphNode node; /** * The type of edges we want to explore encoded as * bitwise or of the codes in * <code>ReductionGraphEdge</code>. */ private int allowedEdges; /** * The context we want to explore in. */ private EvaluationCtx ctx; /** * The List of neighboring nodes. */ private List<AbstractPolicy> neighbors; /** * Iterator going through the neighbors. */ private Iterator<AbstractPolicy> neighborIter; /** * Constructor. * * @param allowedEdges The type of edges we want to explore * as bitwise or of the corresponding * codes in <code>ReductionGraphEdge</code>. * @param ctx The context we want to explore in. * @param startPolicy The PolicyId we are exploring from. */ public EdgeExplorer(int allowedEdges, EvaluationCtx ctx, URI startPolicy) { this.node = ctx.getReductionGraph().getNode(startPolicy); this.ctx = ctx; this.allowedEdges = allowedEdges; // first get the parent policy set from the stack in context AbstractPolicy parentPolicySet = ctx.getParentPolicySet(); if (parentPolicySet != null) { this.neighbors = getActiveChilds(parentPolicySet.getChildren(), ctx); } else { this.neighbors = new ArrayList<AbstractPolicy>(); } this.neighborIter = this.neighbors.iterator(); } /** * @return true if there are potential edges left to explore, * false otherwise. */ public boolean hasNext() { return this.neighborIter.hasNext(); } /** * @return The next node that is reachable by one of the allowed * edge types, or null if there aren't any more. */ public URI next() { if (!this.neighborIter.hasNext()) { return null; } AbstractPolicy neighbor = (AbstractPolicy)this.neighborIter.next(); // First check if there are already matching edges Set<ReductionGraphEdge> fromEdges = this.ctx.getReductionGraph().getFromEdges( this.node.getNodeId()); Iterator<ReductionGraphEdge> edgeIt = fromEdges.iterator(); while (edgeIt.hasNext()) { ReductionGraphEdge edge = edgeIt.next(); if (edge.getTo().equals(neighbor.getId())) { // we use bitwise AND to see if the edge type is allowed if ((this.allowedEdges & edge.getType()) == edge.getType()) { //report this event this.ctx.newEvent(edge); this.ctx.closeCurrentEvent(); return neighbor.getId(); } else if (edge.getType() == ReductionGraphEdge.NOT_APPLICABLE) { // we don't need to process this any further // so we can go to the next element this.ctx.newEvent(edge); this.ctx.closeCurrentEvent(); return next(); } else if (((this.allowedEdges & ReductionGraphEdge.PP) == ReductionGraphEdge.PP) && edge.getType() == ReductionGraphEdge.PI) { // we don't need to process this any further // so we can go to the next element this.ctx.newEvent(edge); this.ctx.closeCurrentEvent(); return next(); } else if (((this.allowedEdges & ReductionGraphEdge.DP) == ReductionGraphEdge.DP) && edge.getType() == ReductionGraphEdge.DI) { // we don't need to process this any further // so we can go to the next element this.ctx.newEvent(edge); this.ctx.closeCurrentEvent(); return next(); } } } //This edge hasn't been evaluated before, so we do it now this.ctx.newEvent(neighbor); MatchResult matchResult = neighbor.match(this.ctx); switch (matchResult.getResult()) { case MatchResult.NO_MATCH: this.ctx.closeCurrentEvent( new Result(Result.DECISION_NOT_APPLICABLE)); this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.NOT_APPLICABLE)); return next(); case MatchResult.INDETERMINATE: this.ctx.closeCurrentEvent( new Result(Result.DECISION_INDETERMINATE, this.ctx)); if (this.ctx.getDecision() == Result.DECISION_PERMIT) { this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.PI, matchResult.getStatus())); if ((this.allowedEdges & ReductionGraphEdge.PI) == ReductionGraphEdge.PI) { return neighbor.getId(); } } else { // decision is DENY this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.DI, matchResult.getStatus())); if ((this.allowedEdges & ReductionGraphEdge.DI) == ReductionGraphEdge.DI) { return neighbor.getId(); } } return next(); case MatchResult.MATCH: //evaluate policy if (neighbor instanceof PolicySet) { this.ctx.saveParentPolicySet(neighbor); } else if (neighbor instanceof PolicyReference) { PolicyReference ref = (PolicyReference)neighbor; if (ref.getReferenceType() == PolicyReference.POLICYSET_REFERENCE) { this.ctx.saveParentPolicySet(neighbor); } } Result eval = neighbor.getCombiningAlg().combine(this.ctx, neighbor.getCombiningParameters(), neighbor.getChildElements()); // check for maxDelegationDepth if (this.ctx.getDelegationDepth() > neighbor.getMaxDelegationDepth()) { this.ctx.closeCurrentEvent("MaxDelegationDepth violated"); this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.NOT_APPLICABLE)); return null; } // Check if it supports the revocation of // a policy down the delegation chain if (this.ctx.supportsRevocation(neighbor, this.node.getNodeId())) { Result nullResult = null; this.ctx.closeCurrentEvent(nullResult); this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.NOT_APPLICABLE)); return null; } this.ctx.closeCurrentEvent(eval); if (neighbor instanceof PolicySet) { this.ctx.popParentPolicySet(); this.ctx.popReductionGraph(); } else if (neighbor instanceof PolicyReference) { PolicyReference ref = (PolicyReference)neighbor; if (ref.getReferenceType() == PolicyReference.POLICYSET_REFERENCE) { this.ctx.popParentPolicySet(); this.ctx.popReductionGraph(); } } switch (eval.getDecision()) { case Result.DECISION_PERMIT : if (this.ctx.getDecision() == Result.DECISION_PERMIT) { this.ctx.getReductionGraph().setEdge( new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.PP)); if ((this.allowedEdges & ReductionGraphEdge.PP) == ReductionGraphEdge.PP) { return neighbor.getId(); } } else if (this.ctx.getDecision() == Result.DECISION_DENY) { this.ctx.getReductionGraph().setEdge( new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.DP)); if ((this.allowedEdges & ReductionGraphEdge.DP) == ReductionGraphEdge.DP) { return neighbor.getId(); } } return next(); case Result.DECISION_INDETERMINATE : if (this.ctx.getDecision() == Result.DECISION_PERMIT) { this.ctx.getReductionGraph().setEdge( new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.PI, eval.getStatus())); if ((this.allowedEdges & ReductionGraphEdge.PI) == ReductionGraphEdge.PI) { return neighbor.getId(); } } else if (this.ctx.getDecision() == Result.DECISION_DENY) { this.ctx.getReductionGraph().setEdge( new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.DI, eval.getStatus())); if ((this.allowedEdges & ReductionGraphEdge.DI) == ReductionGraphEdge.DI) { return neighbor.getId(); } } return next(); default: this.ctx.getReductionGraph().setEdge(new ReductionGraphEdge( this.node.getNodeId(), neighbor.getId(), ReductionGraphEdge.NOT_APPLICABLE)); return next(); } } return next(); } /** * Private helper that builds the list of active child policies/policy sets * from the list of all childs and the list of deactivated childs. * Used in reduction steps of a delegation chain. * * @param allChilds a <code>List</code> of <code>CombinerElement</code>s. * @param context the context containing the deactivated childs. * * @return A list of <code>CombinerElemet</code>s. */ private List<AbstractPolicy> getActiveChilds(List<PolicyTreeElement> allChilds, EvaluationCtx context) { List<AbstractPolicy> activeChilds = new ArrayList<AbstractPolicy>(); Iterator<PolicyTreeElement> iter = allChilds.iterator(); while (iter.hasNext()) { AbstractPolicy policy = (AbstractPolicy)iter.next(); URI elementId = null; if (policy instanceof PolicyReference) { //PolicyReferences get special treatment here. //this is due to the fact that in certain cases we do not // want to run the resolvePolicy() method of a policy // reference here, which would be the case if we used getId(). // An example for such cases can be found in the XACML 2.0 // test cases, test group E, test 003. PolicyReference pr = (PolicyReference)policy; elementId = pr.getReference(); } else { elementId = policy.getId(); } if (!context.getInactivePolicyIds().contains(elementId)) { activeChilds.add(policy); } } return activeChilds; } }