298 lines
12 KiB
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);
|
|
}
|
|
|
|
|
|
}
|