This repository has been archived on 2024-04-22. You can view files and clone it, but cannot push or open issues or pull requests.
GenericBreakGlass-XACML/src/eu.aniketos.securebpmn.xacm.../src/main/java/eu/aniketos/securebpmn/xacml/support/comb/LatticeCombiningAlgorithm.java

298 lines
12 KiB
Java

/* Copyright 2012-2015 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.aniketos.securebpmn.xacml.support.comb;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import eu.aniketos.securebpmn.xacml.support.AttributeResolver;
import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.Obligation;
import com.sun.xacml.PolicyTreeElement;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.combine.CombinerElement;
import com.sun.xacml.combine.CombinerParameter;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.ctx.Result;
public class LatticeCombiningAlgorithm extends PolicyCombiningAlgorithm {
public static final String algId =
"urn:policy-combining-algorithm:policy-lattice";
private static URI identifierURI = URI.create(algId);
private static final String LATTICE_IDENTIFIER = "urn:policyLattice:latticeIdentifier";
private static Logger logger = Logger.getLogger(LatticeCombiningAlgorithm.class);
private Map<String, Map<Long, PolicyLattice>> latticeCache = new HashMap<String, Map<Long, PolicyLattice>>();
public LatticeCombiningAlgorithm() {
super(identifierURI);
}
@Override
public Result combine(EvaluationCtx evalCtx, List<CombinerParameter> combParam,
List<CombinerElement> combElem) {
// store the first deny we get, we will return it in case we do not find a permit
Result firstDeny = null;
// store the first indeterminate
Result firstIndeterminate = null;
// create a cache where all permits are stored;
// we may need the result itself to get the obligations
Map<String, Result> permitCache = new HashMap<String, Result>();
// create a cache where the results of all deny policies are stored
// deny policies could be executed several time, i.e.,
// remember both the result and if we executed it
Map<String, Result> denyCache = new HashMap<String, Result>();
// get the active policies from the context once
Set<String> activePolicies = getActivePolicies(evalCtx);
// create (or get from cache) the policy lattice we want to evaluate
PolicyLattice lattice = getLattice(evalCtx, combParam, combElem);
// iterate over the lattice (i.e., level per level)
for ( LatticeElem elem : lattice ) {
// check if current element is an active policy
if ( isActive(elem, lattice, activePolicies) ) {
boolean permitted = false, denied = false;
// store a permit if we find it in this policy
Result permit = null;
// first, check for an inherited permit, then for a permit
LatticeElem inhPermit = getInheritedPermit(elem, permitCache);
if ( inhPermit != null ) {
permitted = true;
} else if (elem.getPermitPolicy() != null ) {
// no inherited permit, we have to evaluate the current permit policy
Result res = evaluate(elem.getPermitPolicy(), evalCtx);
if ( res.getDecision() == Result.DECISION_INDETERMINATE ) {
if ( firstIndeterminate == null ) {
firstIndeterminate = res;
}
continue; // this policy will not provide a result, go to next
} else if ( res.getDecision() == Result.DECISION_PERMIT ) {
// we found a permit, store it to cache (in the current policy it may be overwritten by a deny)
permitCache.put(elem.getIdentifier(), res);
permit = res;
permitted = true;
} else if ( res.getDecision() == Result.DECISION_DENY && firstDeny == null) {
firstDeny = res;
continue; // we do not need to evaluate the deny policies if we do not have a permit
}
}
if ( permitted ) {
// check if there is an inherited deny: iterate over all extending policies, i.e, downwards
for ( LatticeElem extending : elem.downwards() ) {
// check if extending policy has a deny policy defined
if ( extending.getDenyPolicy() != null ) {
Result res;
// check if we already evaluated this policy
if ( denyCache.containsKey(extending.getIdentifier()) ) {
res = denyCache.get(extending.getIdentifier());
} else {
// else, evaluate it and store it into the cache
res = evaluate(extending.getDenyPolicy(), evalCtx);
denyCache.put(extending.getIdentifier(), res);
}
// check if the current deny policy denies this request
if ( res.getDecision() == Result.DECISION_INDETERMINATE ) {
if ( firstIndeterminate == null ) {
firstIndeterminate = res;
}
} else if ( res.getDecision() == Result.DECISION_DENY ) {
denied = true;
if ( firstDeny == null ) {
firstDeny = res;
}
break; // we have found a deny, do not need to look further
}
}
}
// we end up here only if we found a permit, check if we also found a deny
if ( ! denied) {
// if we did not find a permit for this policy
if ( permit == null ) {
// we got an inherited permit: we have to remove the lattice obligations
// from this permit and attach the lattice obligations of the current policy
permit = permitCache.get(inhPermit.getIdentifier());
AbstractPolicy curPolicy = (AbstractPolicy) elem.getPermitPolicy();
AbstractPolicy inhPolicy = (AbstractPolicy) inhPermit.getPermitPolicy();
Set<Obligation> obligations = permit.getObligations();
// remove obligations defined by inhPolicy
for ( Obligation oblgRm : inhPolicy.getObligations() ) {
for ( Obligation oblg : obligations ) {
if ( oblg.getId().equals(oblgRm) ) {
obligations.remove(oblg);
break;
}
}
}
// add obligations from current policy
for ( Obligation oblg : curPolicy.getObligations() ) {
obligations.add(oblg.evaluate(evalCtx));
}
permit = new Result(permit.getDecision(), evalCtx, obligations);
}
return permit;
}
}
}
}
if ( firstIndeterminate != null ) {
return firstIndeterminate;
} else if ( firstDeny != null ) {
return firstDeny;
} else {
return new Result(Result.DECISION_DENY, evalCtx);
}
}
private Result evaluate(PolicyTreeElement policy, EvaluationCtx evalCtx) {
evalCtx.newEvent(policy);
MatchResult match = policy.match(evalCtx);
Result res = null;
if (match.getResult() == MatchResult.INDETERMINATE) {
res = new Result(Result.DECISION_INDETERMINATE, evalCtx);
} else if (match.getResult() == MatchResult.NO_MATCH) {
res = new Result(Result.DECISION_NOT_APPLICABLE, evalCtx);
} else if (match.getResult() == MatchResult.MATCH) {
res = policy.evaluate(evalCtx);
}
evalCtx.closeCurrentEvent(res);
return res;
}
private boolean isActive(LatticeElem elem, PolicyLattice lattice, Set<String> activePolicies) {
if ( activePolicies.contains(elem.getIdentifier())) {
return true;
} else {
for ( LatticeElem extending : elem.downwards() ) {
if ( activePolicies.contains(extending.getIdentifier())) {
return true;
}
}
}
return false;
}
private LatticeElem getInheritedPermit(LatticeElem elem, Map<String, Result> permitCache) {
for ( LatticeElem extended : elem.upwards() ) {
if ( permitCache.containsKey(extended)) {
return extended;
}
}
return null;
}
private PolicyLattice getLattice(EvaluationCtx evalCtx, List<CombinerParameter> combParam,
List<CombinerElement> combElem) {
String latticeId = getLatticeIdentifier(combParam);
PolicyLattice lattice = null;
if ( latticeId != null) {
Map<Long, PolicyLattice> latticeVerCache = null;
if ( latticeCache.containsKey(latticeId) ) {
latticeVerCache = latticeCache.get(latticeId);
} else {
latticeVerCache = new HashMap<Long, PolicyLattice>();
latticeCache.put(latticeId, latticeVerCache);
}
Long policyVers = getPolicyVersion(evalCtx);
if ( latticeVerCache.containsKey(policyVers)) {
return latticeVerCache.get(policyVers);
} else {
lattice = new PolicyLattice(latticeId, combElem);
latticeVerCache.put(policyVers, lattice);
return lattice;
}
} else {
return new PolicyLattice(null, combElem);
}
// if ( latticeCache.containsKey(policyVers) &&
// latticeCache.get(policyVers).containsKey(latticeId) ) {
// lattice = latticeCache.get(policyVers).get(latticeId);
// } else {
// Map<String, PolicyLattice> latticeVerCache = null;
// if ( latticeCache.containsKey(policyVers) ) {
// latticeVerCache = latticeCache.get(policyVers);
// } else {
// latticeVerCache = new HashMap<String, PolicyLattice>();
// latticeCache.put(policyVers, latticeVerCache);
// }
// lattice = new PolicyLattice(latticeId, combElem);
// latticeVerCache.put(latticeId, lattice);
// }
// return lattice;
}
private String getLatticeIdentifier(List<CombinerParameter> combParam) {
if ( combParam != null && combParam.size() > 0 ) {
for ( CombinerParameter param : combParam ) {
if ( LATTICE_IDENTIFIER.equals(param.getName()) ) {
if ( param.getValue() instanceof StringAttribute ) {
return ((StringAttribute)param.getValue()).getValue();
} else {
logger.warn("Found " + LATTICE_IDENTIFIER + " which is not of type string (" + param.getValue().encode() + ")");
}
}
}
}
logger.warn("Did not find a " + LATTICE_IDENTIFIER + " for evaluating the LatticeCombiningAlgorithm " +
(getRuntimeInfo() == null ? "" : getRuntimeInfo().getLocationMsgRuntime()));
return null;
}
private Long getPolicyVersion(EvaluationCtx evalCtx) {
return new Long(AttributeResolver.getPDPStatePolicyVersion(evalCtx));
}
private Set<String> getActivePolicies(EvaluationCtx evalCtx) {
return AttributeResolver.getActivePolicies(evalCtx);
}
}