use of org.ow2.authzforce.core.pdp.api.combining.CombiningAlgParameter in project core by authzforce.
the class PolicyEvaluators method getInstanceGeneric.
/**
* Generic creation of PolicySet evaluator
*
* @param policySetRefChainWithArgIffRefTarget null/empty if {@code policyElement} is the root policySet; else it is the chain of top-level (as opposed to nested inline) PolicySets linked by PolicySetIdReferences from the root
* PolicySet up to (and including) the top-level (PolicySetIdReference-targeted) PolicySet that encloses or is {@code policyElement}
*/
private static <TLPEE extends TopLevelPolicyElementEvaluator, COMBINED_EVALUATOR extends PolicyEvaluator> TLPEE getInstanceGeneric(final PolicySetElementEvaluatorFactory<TLPEE, COMBINED_EVALUATOR> policyEvaluatorFactory, final PolicySet policyElement, final Deque<String> policySetRefChainWithArgIffRefTarget) throws IllegalArgumentException {
assert policyEvaluatorFactory != null && policyElement != null;
// final Set<PrimaryPolicyMetadata> enclosedPolicies = HashCollections.newUpdatableSet();
final String policyId = policyElement.getPolicySetId();
final BooleanEvaluator targetEvaluator = TargetEvaluators.getInstance(policyElement.getTarget(), policyEvaluatorFactory.expressionFactory, policyEvaluatorFactory.defaultXPathCompiler);
/*
* Elements defined in xs:choice of PolicySetType in XACML schema (Policy(Set)/Policy(Set)IdReference/CombinerParameters/Policy(Set)CombinerParameters
*/
final List<Serializable> jaxbPolicySetChoiceElements = policyElement.getPolicySetsAndPoliciesAndPolicySetIdReferences();
/*
* Prepare the list of evaluators combined by the combining algorithm in this PolicySet, i.e. Policy(Set)/Policy(Set)IdReference evaluators. combinedEvaluators.size() <=
* jaxbPolicySetChoiceElements.size() since combinedEvaluators does not include *CombinerParameter evaluators
*/
final List<COMBINED_EVALUATOR> combinedEvaluators = new ArrayList<>(jaxbPolicySetChoiceElements.size());
/*
* Why isn't there any VariableDefinition in XACML PolicySet like in Policy? If there were, we would keep a copy of variable IDs defined in this policy, to remove them from the global manager
* at the end of parsing this PolicySet. They should not be visible outside the scope of this.
* <p>
* final Set<String> variableIds = HashCollections.newUpdatableSet(jaxbPolicySetChoiceElements.size());
*/
/*
* Map to get child Policies by their ID so that we can resolve Policies associated with PolicyCombinerParameters Size cannot get bigger than jaxbPolicySetChoiceElements.size()
*/
final Map<String, COMBINED_EVALUATOR> childPolicyEvaluatorsByPolicyId = HashCollections.newUpdatableMap(jaxbPolicySetChoiceElements.size());
/*
* Map to get child PolicySets by their ID so that we can resolve PolicySets associated with PolicySetCombinerParameters Size cannot get bigger than jaxbPolicySetChoiceElements.size()
*/
final Map<String, COMBINED_EVALUATOR> childPolicySetEvaluatorsByPolicySetId = HashCollections.newUpdatableMap(jaxbPolicySetChoiceElements.size());
/*
* *CombinerParameters (combining algorithm parameters), size <= jaxbPolicySetChoiceElements.size()
*/
final List<CombiningAlgParameter<? extends COMBINED_EVALUATOR>> combiningAlgParameters = new ArrayList<>(jaxbPolicySetChoiceElements.size());
int childIndex = 0;
for (final Serializable policyChildElt : jaxbPolicySetChoiceElements) {
if (policyChildElt instanceof PolicyCombinerParameters) {
final String combinedPolicyId = ((PolicyCombinerParameters) policyChildElt).getPolicyIdRef();
final COMBINED_EVALUATOR childPolicyEvaluator = childPolicyEvaluatorsByPolicyId.get(combinedPolicyId);
if (childPolicyEvaluator == null) {
throw new IllegalArgumentException(policyEvaluatorFactory.policyMetadata + ": invalid PolicyCombinerParameters: referencing undefined child Policy #" + combinedPolicyId + " (no such policy defined before this element)");
}
final BaseCombiningAlgParameter<COMBINED_EVALUATOR> combiningAlgParameter;
try {
combiningAlgParameter = new BaseCombiningAlgParameter<>(childPolicyEvaluator, ((CombinerParametersType) policyChildElt).getCombinerParameters(), policyEvaluatorFactory.expressionFactory, policyEvaluatorFactory.defaultXPathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyEvaluatorFactory.policyMetadata + ": invalid child #" + childIndex + " (PolicyCombinerParameters)", e);
}
combiningAlgParameters.add(combiningAlgParameter);
} else if (policyChildElt instanceof PolicySetCombinerParameters) {
final String combinedPolicySetId = ((PolicySetCombinerParameters) policyChildElt).getPolicySetIdRef();
final COMBINED_EVALUATOR combinedPolicySetEvaluator = childPolicySetEvaluatorsByPolicySetId.get(combinedPolicySetId);
if (combinedPolicySetEvaluator == null) {
throw new IllegalArgumentException(policyEvaluatorFactory.policyMetadata + ": invalid PolicySetCombinerParameters: referencing undefined child PolicySet #" + combinedPolicySetId + " (no such policySet defined before this element)");
}
final BaseCombiningAlgParameter<COMBINED_EVALUATOR> combiningAlgParameter;
try {
combiningAlgParameter = new BaseCombiningAlgParameter<>(combinedPolicySetEvaluator, ((CombinerParametersType) policyChildElt).getCombinerParameters(), policyEvaluatorFactory.expressionFactory, policyEvaluatorFactory.defaultXPathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyEvaluatorFactory.policyMetadata + ": invalid child #" + childIndex + " (PolicySetCombinerParameters)", e);
}
combiningAlgParameters.add(combiningAlgParameter);
} else if (policyChildElt instanceof JAXBElement) {
final JAXBElement<?> jaxbPolicyChildElt = (JAXBElement<?>) policyChildElt;
final String eltNameLocalPart = jaxbPolicyChildElt.getName().getLocalPart();
if (eltNameLocalPart.equals(XacmlNodeName.POLICY_ID_REFERENCE.value())) {
final IdReferenceType policyChildIdRef = (IdReferenceType) jaxbPolicyChildElt.getValue();
final COMBINED_EVALUATOR childEvaluator = policyEvaluatorFactory.getChildPolicyRefEvaluator(childIndex, TopLevelPolicyElementType.POLICY, policyChildIdRef, null);
combinedEvaluators.add(childEvaluator);
final COMBINED_EVALUATOR duplicate = childPolicySetEvaluatorsByPolicySetId.putIfAbsent(childEvaluator.getPolicyId(), childEvaluator);
if (duplicate != null) {
throw new IllegalArgumentException("Duplicate PolicyIdReference's id = " + childEvaluator.getPolicyId());
}
} else if (eltNameLocalPart.equals(XacmlNodeName.POLICYSET_ID_REFERENCE.value())) {
final IdReferenceType policyChildIdRef = (IdReferenceType) jaxbPolicyChildElt.getValue();
final String policyChildId = policyChildIdRef.getValue();
/*
* Add this new reference to policyChildIdRef to the policyRef chain arg of getChildPolicyRefEvaluator(...). If policySetRefChainWithArgIffRefTarget is null/empty, policyElement is
* the root policy (no ancestor in the chain), therefore it should be added before policyChildIdRef, as the antecedent; Else non-empty policySetRefChainWithArgIffRefTarget's last
* item is either policyElement (iff it is a policy ref's target) or the top-level (as opposed to nested inline) PolicySet that encloses policyElement, in which either case we just
* add policyChildIdRef to the chain.
*/
final Deque<String> newPolicySetRefChainWithArgIffRefTarget = policySetRefChainWithArgIffRefTarget == null || policySetRefChainWithArgIffRefTarget.isEmpty() ? new ArrayDeque<>(Arrays.asList(policyId, policyChildId)) : policyEvaluatorFactory.joinPolicySetRefChains(policySetRefChainWithArgIffRefTarget, Collections.singletonList(policyChildId));
final COMBINED_EVALUATOR childEvaluator = policyEvaluatorFactory.getChildPolicyRefEvaluator(childIndex, TopLevelPolicyElementType.POLICY_SET, policyChildIdRef, newPolicySetRefChainWithArgIffRefTarget);
combinedEvaluators.add(childEvaluator);
final COMBINED_EVALUATOR duplicate = childPolicySetEvaluatorsByPolicySetId.put(policyChildId, childEvaluator);
if (duplicate != null) {
throw new IllegalArgumentException("Duplicate PolicySetIdReference's id = " + policyChildId);
}
} else if (eltNameLocalPart.equals(XacmlNodeName.COMBINER_PARAMETERS.value())) {
/*
* CombinerParameters that is not Policy(Set)CombinerParameters already tested before
*/
final BaseCombiningAlgParameter<COMBINED_EVALUATOR> combiningAlgParameter;
try {
combiningAlgParameter = new BaseCombiningAlgParameter<>(null, ((CombinerParametersType) jaxbPolicyChildElt.getValue()).getCombinerParameters(), policyEvaluatorFactory.expressionFactory, policyEvaluatorFactory.defaultXPathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyEvaluatorFactory.policyMetadata + ": invalid child #" + childIndex + " (CombinerParameters)", e);
}
combiningAlgParameters.add(combiningAlgParameter);
}
} else if (policyChildElt instanceof PolicySet) {
final PolicySet childPolicy = (PolicySet) policyChildElt;
/*
* XACML spec §5.1: "ensure that no two policies visible to the PDP have the same identifier"
*/
final String childPolicyId = childPolicy.getPolicySetId();
/*
* Create/Update the policySet ref chain if necessary. If policySetRefChainWithArgIffRefTarget is null/empty, this means policyElement is the root policyset (no antecedent), and we
* create a chain with its ID, to know the antecedent of the next encountered policyset ref (which may be found deep under multiple levels of nested PolicySets).; else
* policySetRefChainWithArgIffRefTarget's last item is either policyElement (iff it is a policy ref's target) or the top-level (as opposed to nested inline) PolicySet that encloses
* policyElement, in which either case we already have the info we need in the chain so keep it as is.
*/
final Deque<String> newPolicySetRefChain = policySetRefChainWithArgIffRefTarget == null || policySetRefChainWithArgIffRefTarget.isEmpty() ? new ArrayDeque<>(Collections.singletonList(policyId)) : policySetRefChainWithArgIffRefTarget;
final COMBINED_EVALUATOR childEvaluator = policyEvaluatorFactory.getChildPolicySetEvaluator(childIndex, childPolicy, newPolicySetRefChain);
combinedEvaluators.add(childEvaluator);
final COMBINED_EVALUATOR duplicate = childPolicySetEvaluatorsByPolicySetId.putIfAbsent(childPolicyId, childEvaluator);
if (duplicate != null) {
throw new IllegalArgumentException("Duplicate PolicySetId = " + childPolicyId);
}
} else if (policyChildElt instanceof Policy) {
final Policy childPolicy = (Policy) policyChildElt;
/*
* XACML spec §5.1: "ensure that no two policies visible to the PDP have the same identifier"
*/
final String childPolicyId = childPolicy.getPolicyId();
final COMBINED_EVALUATOR childEvaluator = policyEvaluatorFactory.getChildPolicyEvaluator(childIndex, childPolicy);
combinedEvaluators.add(childEvaluator);
final COMBINED_EVALUATOR duplicate = childPolicyEvaluatorsByPolicyId.putIfAbsent(childPolicyId, childEvaluator);
if (duplicate != null) {
throw new IllegalArgumentException("Duplicate PolicyId = " + childPolicyId);
}
}
/*
* Why isn't there any VariableDefinition in XACML PolicySet defined by OASIS XACML 3.0 spec, like in Policy? If there were, the following code would be used (same as in PolicyEvaluator
* class).
*/
// else if (policySetChildElt instanceof VariableDefinition)
// {
// final VariableDefinition varDef = (VariableDefinition)
// policyChildElt;
// final Deque<String> varDefLongestVarRefChain = new
// ArrayDeque<>();
// final VariableReference<?> var;
// try
// {
// var = expressionFactory.addVariable(varDef, defaultXPathCompiler,
// varDefLongestVarRefChain);
// } catch (IllegalArgumentException e)
// {
// throw new IllegalArgumentException(policyFriendlyId + ": invalid
// child #" + childIndex + " (VariableDefinition)", e);
// }
//
// if (var != null)
// {
// /*
// * Conflicts can occur between variables defined in this policy
// but also with others already in a wider scope, i.e. defined in
// * parent/ancestor policy
// */
// throw new IllegalArgumentException(policyFriendlyId + ":
// Duplicable VariableDefinition for VariableId=" +
// var.getVariableId());
// }
//
// localVariableIds.add(varDef.getVariableId());
// // check whether the longest VariableReference chain in the
// VariableDefinition is longer than what we've got so far
// final int sizeOfVarDefLongestVarRefChain =
// varDefLongestVarRefChain.size();
// if(sizeOfVarDefLongestVarRefChain >
// sizeOfPolicyLongestVarRefChain) {
// sizeOfPolicyLongestVarRefChain = sizeOfVarDefLongestVarRefChain;
// }
// }
childIndex++;
}
/*
* Why isn't there any VariableDefinition in XACML PolicySet like in Policy? If there were, the final following code would be used: We are done parsing expressions in this policy, including
* VariableReferences, it's time to remove variables scoped to this policy from the variable manager
*/
// for (final String varId : variableIds)
// {
// expFactory.remove(varId);
// }
final ObligationExpressions obligationExps = policyElement.getObligationExpressions();
final AdviceExpressions adviceExps = policyElement.getAdviceExpressions();
return policyEvaluatorFactory.getInstance(policyEvaluatorFactory.policyMetadata, targetEvaluator, ImmutableList.of(), policyElement.getPolicyCombiningAlgId(), ImmutableList.copyOf(combinedEvaluators), ImmutableList.copyOf(combiningAlgParameters), obligationExps == null ? null : obligationExps.getObligationExpressions(), adviceExps == null ? null : adviceExps.getAdviceExpressions());
}
use of org.ow2.authzforce.core.pdp.api.combining.CombiningAlgParameter in project core by authzforce.
the class PolicyEvaluators method getInstance.
/**
* Creates Policy handler from XACML Policy element
*
* @param policyElement Policy (XACML)
* @param parentDefaultXPathCompiler XPath compiler corresponding to parent PolicyDefaults/XPathVersion; undefined if this Policy has no parent Policy (root), or none defined in parent, or XPath disabled by PDP configuration
* @param namespacePrefixToUriMap namespace prefix-URI mappings from the original XACML Policy (XML) document, to be used for namespace-aware XPath evaluation; empty iff XPath support disabled or: {@code parentDefaultXPathCompiler.isPresent()} and they can be retrieved already from {@code parentDefaultXPathCompiler.get().getDeclaredNamespacePrefixToUriMap()}
* @param expressionFactory Expression factory/parser
* @param combiningAlgRegistry rule/policy combining algorithm registry
* @return instance
* @throws IllegalArgumentException if any argument is invalid
*/
public static StaticTopLevelPolicyElementEvaluator getInstance(final Policy policyElement, final ExpressionFactory expressionFactory, final CombiningAlgRegistry combiningAlgRegistry, final Optional<XPathCompilerProxy> parentDefaultXPathCompiler, final Map<String, String> namespacePrefixToUriMap) throws IllegalArgumentException {
if (policyElement == null) {
throw NULL_XACML_POLICY_ARG_EXCEPTION;
}
if (expressionFactory == null) {
throw NULL_EXPRESSION_FACTORY_EXCEPTION;
}
if (combiningAlgRegistry == null) {
throw NULL_XACML_COMBINING_ALG_ARG_EXCEPTION;
}
final String policyId = policyElement.getPolicyId();
final PolicyVersion policyVersion = new PolicyVersion(policyElement.getVersion());
final PrimaryPolicyMetadata policyMetadata = new BasePrimaryPolicyMetadata(TopLevelPolicyElementType.POLICY, policyId, policyVersion);
final BooleanEvaluator targetEvaluator = TargetEvaluators.getInstance(policyElement.getTarget(), expressionFactory, parentDefaultXPathCompiler);
/*
* Elements defined in xs:choice of XACML schema type PolicyType: Rules/(Rule)CombinerParameters/VariableDefinitions
*/
final List<Serializable> policyChoiceElements = policyElement.getCombinerParametersAndRuleCombinerParametersAndVariableDefinitions();
/*
* There are at most as many combining alg parameters as policyChoiceElements.size().
*/
final List<CombiningAlgParameter<? extends RuleEvaluator>> combiningAlgParameters = new ArrayList<>(policyChoiceElements.size());
/*
* Keep a copy of locally-defined variables defined in this policy, to remove them from the global manager at the end of parsing this policy. They should not be visible outside the scope of
* this policy. There are at most as many VariableDefinitions as policyChoiceElements.size().
*/
final List<VariableReference<?>> localVariables = new ArrayList<>(policyChoiceElements.size());
final DefaultsType policyDefaults = policyElement.getPolicyDefaults();
Optional<XPathCompilerProxy> childXpathCompiler;
/*
* Leave childXpathCompiler undefined if XPath support disabled globally.
*
* Else (XPath support enabled globally, but may be disabled locally for this specific Policy if XPathVersion undefined in it and any enclosing PolicySet)...
* Reminder: According to the XACML standard, the Policy(Set)Defaults/XPathVersion must be specified (non-null) in the current Policy(Set) or any of its enclosing/ancestor PolicySet for XPath expressions to be allowed (therefore a need for a XPathCompiler), e.g. in AttributeSelectors, XPath functions, etc.
*/
if (expressionFactory.isXPathEnabled()) {
/*
If both policyDefaults and parentDefaultXPathCompiler undefined, it means no Policy(Set)Defaults/XPathVersion defined (in current Policy and any enclosing PolicySet), i.e. XPath support is disabled for this Policy, so leave childXpathCompiler undefined like parentDefaultXPathCompiler.
We may reuse parentDefaultXPathCompiler if:
- parentDefaultXPathCompiler is defined
- AND policyDefaults/XPathVersion is undefined OR the XPath version matches the policyDefaults/XPathVersion
- AND namespacePrefixToUriMap is empty (i.e. only the ones from parentDefaultXPathCompiler apply)
*/
if (policyDefaults == null) {
if (parentDefaultXPathCompiler.isEmpty() || namespacePrefixToUriMap.isEmpty()) {
childXpathCompiler = parentDefaultXPathCompiler;
} else {
// parentDefaultXPathCompiler defined AND namespacePrefixToUriMap not empty -> new XPathCompiler to handle these new namespacePrefixToUriMap map
childXpathCompiler = Optional.of(new ImmutableXPathCompiler(parentDefaultXPathCompiler.get().getXPathVersion(), namespacePrefixToUriMap, List.of()));
}
} else {
// policyDefaults defined
final String xpathVersionUri = policyDefaults.getXPathVersion();
assert xpathVersionUri != null : "PolicyDefaults(non-null)/XPathVersion = null, which violates the XACML schema! Fix: enforce XACML schema validation.";
try {
final XPathVersion xPathVersion = XPathVersion.fromURI(xpathVersionUri);
if (parentDefaultXPathCompiler.isEmpty()) {
childXpathCompiler = Optional.of(new ImmutableXPathCompiler(xPathVersion, namespacePrefixToUriMap, List.of()));
} else // parentDefaultXPathCompiler defined, re-use it only if XPath version matches policyDefaults and namespacePrefixToUriMap empty
if (parentDefaultXPathCompiler.get().getXPathVersion().equals(xPathVersion) && namespacePrefixToUriMap.isEmpty()) {
childXpathCompiler = parentDefaultXPathCompiler;
} else {
childXpathCompiler = Optional.of(new ImmutableXPathCompiler(xPathVersion, namespacePrefixToUriMap, List.of()));
}
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyMetadata + ": Invalid PolicySetDefaults/XPathVersion or XML namespace prefix/URI undefined", e);
}
}
} else {
// XPath support disabled globally
childXpathCompiler = Optional.empty();
}
/*
childXpathCompiler is empty iff XPath support disabled globally (!expressionFactory.isXPathEnabled()) OR (policyDefaults ==null AND parentDefaultXPathCompiler.isEmpty()), in other words iff XPath support disabled for this Policy
*/
/*
If XPath support enabled, we can reuse the same XPathCompiler as long as there is no new VariableDefinition
*/
boolean isNewChildXpathCompilerRequired = false;
/*
* We keep a record of the size of the longest chain of VariableReference in this policy, and update it when a VariableDefinition occurs
*/
int sizeOfPolicyLongestVarRefChain = 0;
/*
* Map to get rules by their ID so that we can resolve rules associated with RuleCombinerParameters, and detect duplicate RuleId. We want to preserve insertion order, to get map.values() in
* order of declaration, so that ordered-* algorithms have rules in order. There are at most as many Rules as policyChoiceElements.size().
*/
final Map<String, RuleEvaluator> ruleEvaluatorsByRuleIdInOrderOfDeclaration = new LinkedHashMap<>(policyChoiceElements.size());
int childIndex = 0;
for (final Serializable policyChildElt : policyChoiceElements) {
/*
If and only if XPath enabled for this Policy (childXpathCompiler.isPresent()), XPath compiler needed for each child, can we reuse the same one as last time (it was used to create a child element evaluator) ?
*/
if (childXpathCompiler.isPresent() && isNewChildXpathCompilerRequired) {
/*
New Variables defined since last XPath compiler created -> we need to use a new one to handle the new XACML Variables as XPath variables
*/
childXpathCompiler = Optional.of(new ImmutableXPathCompiler(childXpathCompiler.get().getXPathVersion(), namespacePrefixToUriMap, localVariables));
isNewChildXpathCompilerRequired = false;
}
if (policyChildElt instanceof RuleCombinerParameters) {
final String combinedRuleId = ((RuleCombinerParameters) policyChildElt).getRuleIdRef();
final RuleEvaluator ruleEvaluator = ruleEvaluatorsByRuleIdInOrderOfDeclaration.get(combinedRuleId);
if (ruleEvaluator == null) {
throw new IllegalArgumentException(policyMetadata + ": invalid RuleCombinerParameters: referencing undefined child Rule #" + combinedRuleId + " (no such rule defined before this element)");
}
final BaseCombiningAlgParameter<RuleEvaluator> combiningAlgParameter;
try {
combiningAlgParameter = new BaseCombiningAlgParameter<>(ruleEvaluator, ((CombinerParametersType) policyChildElt).getCombinerParameters(), expressionFactory, childXpathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyMetadata + ": invalid child #" + childIndex + " (RuleCombinerParameters)", e);
}
combiningAlgParameters.add(combiningAlgParameter);
} else if (policyChildElt instanceof CombinerParametersType) {
/*
* CombinerParameters that is not RuleCombinerParameters already tested before
*/
final BaseCombiningAlgParameter<RuleEvaluator> combiningAlgParameter;
try {
combiningAlgParameter = new BaseCombiningAlgParameter<>(null, ((CombinerParametersType) policyChildElt).getCombinerParameters(), expressionFactory, childXpathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyMetadata + ": invalid child #" + childIndex + " (CombinerParameters)", e);
}
combiningAlgParameters.add(combiningAlgParameter);
} else if (policyChildElt instanceof VariableDefinition) {
final VariableDefinition varDef = (VariableDefinition) policyChildElt;
final Deque<String> varDefLongestVarRefChain = new ArrayDeque<>();
final VariableReference<?> var;
try {
var = expressionFactory.addVariable(varDef, varDefLongestVarRefChain, childXpathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyMetadata + ": invalid child #" + childIndex + " (VariableDefinition)", e);
}
if (var != null) {
/*
* Conflicts can occur between variables defined in this policy but also with others already in a wider scope, i.e. defined in parent/ancestor policy
*/
throw new IllegalArgumentException(policyMetadata + ": Duplicable VariableDefinition for VariableId = " + var.getVariableId());
}
localVariables.add(expressionFactory.getVariableExpression(varDef.getVariableId()));
/*
New Variables defined since last XPath compiler created -> we need to use a new one to handle the new XACML Variables as XPath variables in the subsequent child elements
*/
isNewChildXpathCompilerRequired = true;
/*
* check whether the longest VariableReference chain in the VariableDefinition is longer than what we've got so far
*/
final int sizeOfVarDefLongestVarRefChain = varDefLongestVarRefChain.size();
if (sizeOfVarDefLongestVarRefChain > sizeOfPolicyLongestVarRefChain) {
sizeOfPolicyLongestVarRefChain = sizeOfVarDefLongestVarRefChain;
}
} else if (policyChildElt instanceof Rule) {
final RuleEvaluator ruleEvaluator;
try {
ruleEvaluator = new RuleEvaluator((Rule) policyChildElt, expressionFactory, childXpathCompiler);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException(policyMetadata + ": Error parsing child #" + childIndex + " (Rule)", e);
}
final RuleEvaluator conflictingRuleEvaluator = ruleEvaluatorsByRuleIdInOrderOfDeclaration.putIfAbsent(ruleEvaluator.getRuleId(), ruleEvaluator);
if (conflictingRuleEvaluator != null) {
/*
* Conflict: 2 Rule elements with same RuleId -> violates uniqueness of RuleId within a Policy (XACML spec)
*/
throw new IllegalArgumentException(policyMetadata + ": Duplicate Rule with RuleId = " + conflictingRuleEvaluator.getRuleId());
}
}
childIndex++;
}
final ObligationExpressions obligationExps = policyElement.getObligationExpressions();
final AdviceExpressions adviceExps = policyElement.getAdviceExpressions();
final StaticTopLevelPolicyElementEvaluator policyEvaluator = new StaticBaseTopLevelPolicyElementEvaluator<>(RuleEvaluator.class, policyMetadata, Optional.empty(), targetEvaluator, ImmutableList.copyOf(localVariables), policyElement.getRuleCombiningAlgId(), ImmutableList.copyOf(ruleEvaluatorsByRuleIdInOrderOfDeclaration.values()), ImmutableList.copyOf(combiningAlgParameters), obligationExps == null ? null : obligationExps.getObligationExpressions(), adviceExps == null ? null : adviceExps.getAdviceExpressions(), expressionFactory, combiningAlgRegistry, childXpathCompiler);
/*
* We are done parsing expressions in this policy, including VariableReferences, it's time to remove variables scoped to this policy from the variable manager
*/
localVariables.forEach(var -> expressionFactory.removeVariable(var.getVariableId()));
return policyEvaluator;
}
use of org.ow2.authzforce.core.pdp.api.combining.CombiningAlgParameter in project core by authzforce.
the class DPUnlessPDCombiningAlg method getInstance.
/**
* {@inheritDoc}
*/
@Override
public CombiningAlg.Evaluator getInstance(final Iterable<CombiningAlgParameter<? extends T>> params, final Iterable<? extends T> combinedElements) throws UnsupportedOperationException, IllegalArgumentException {
// if no element combined -> decision is overridden Effect
if (combinedElements == null) {
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant decision {}", this, this.overriddenEffect);
return this.constantOverriddenEffectDecisionEvaluator;
}
final Iterator<? extends Decidable> combinedEltIterator = combinedElements.iterator();
if (!combinedEltIterator.hasNext()) {
// empty (no element to combine)
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant decision {}", this, this.overriddenEffect);
return this.constantOverriddenEffectDecisionEvaluator;
}
/*
* If combined elements are Rules, we can optimize
*/
if (!RuleEvaluator.class.isAssignableFrom(getCombinedElementType())) {
return new Evaluator(combinedElements, this.overridingEffect);
}
// combined elements are Rules, we can optimize
/*
* There is at least one Rule. Prepare to iterate over Rules.
*/
/*
* If we found any empty rule with overriding Effect, all others do not matter since the algorithm ends there with overriding Effect as decision -> ignore other rules. If there are non-empty
* rules with overriding Effect, for optimization, we separate them from others. If the overriding Effect is not returned as decision, the overridden Effect is always returned as decision,
* therefore the other rules (with overridden Effect) affect the decision result only if they have PEP action(s).
*/
final Deque<RuleEvaluator> nonEmptyRulesWithOverridingEffect = new ArrayDeque<>();
final Deque<RuleEvaluator> rulesWithOverriddenEffectAndPepActions = new ArrayDeque<>();
while (combinedEltIterator.hasNext()) {
final RuleEvaluator rule = (RuleEvaluator) combinedEltIterator.next();
if (rule.getEffect() == overridingEffect) {
/*
* If rule's effect is the overriding Effect, and it has no target/condition/pep_actions, then rule will always return this Effect -> {overriding_effect}-overrides alg always evaluates
* to ${overriding_effect} (ignore/remove all other rules). ({overriding_effect} = Permit if algorithm is deny-unless-permit, or Deny if algorithm is permit-unless-deny in this
* statement.)
*/
if (rule.isEmptyEquivalent()) {
LOGGER.warn("{}: {} with Effect={} is empty (no target/condition/pep_actions) => always returns {} => algorithm will always return {} => other combined rules have no effect => will be ignored/removed.", this, rule, this.overridingEffect, this.overridingEffect, this.overridingEffect);
return constantOverridingEffectDecisionEvaluator;
}
/*
* Rule is not empty, i.e. has a target/condition/actions, therefore may not necessarily return its (overriding) Effect as decision
*/
nonEmptyRulesWithOverridingEffect.addLast(rule);
continue;
}
/*
* Rule Effect = {overridden_Effect} (e.g. Permit if algorithm is deny-unless-permit)
*
* In the end, if there is no applicable rule with overriding Effect, we already know that the result is always the overridden Effect with PEP actions from all other rules with same
* (overridden) Effect and PEP action(s).
*/
if (!rule.hasAnyPepAction()) {
/*
* Ignore this new Rule with overridden Effect and no PEP action; it will have no effect.
*/
LOGGER.warn("{}: Ignoring/removing {} (Effect={}, no PEP action) because it does not affect the result.", this, rule, overriddenEffect);
// continue looking for rules with overriding Effect or with PEP actions
continue;
}
// rule has PEP action(s)
rulesWithOverriddenEffectAndPepActions.addLast(rule);
}
/*
* There is at least one rule and there is no empty Rule with overriding Effect.
*
* If there is no rule with overriding Effect and no rule with PEP action -> final result is always simple overridden Effect as decision.
*/
if (nonEmptyRulesWithOverridingEffect.isEmpty() && rulesWithOverriddenEffectAndPepActions.isEmpty()) {
LOGGER.warn("{}: the only combined rule(s) is/are {} Rule(s) without PEP action => algorithm will always return {} => optimization: replacing with equivalent evaluator returning constant {} decision", this, this.overriddenEffect, this.overriddenEffect, this.overriddenEffect);
return constantOverriddenEffectDecisionEvaluator;
}
/*
* (All rules have same overridden Effect, and) either there is no empty rule OR there is at least one with PEP action
*/
LOGGER.debug("{}: 'children may be processed in any order' (XACML). This implementation will process Rules with overriding Effect first, then the others (with PEP actions only, others without are ignored)", this);
return new OverridingEffectFirstRuleCombiningAlgEvaluator(nonEmptyRulesWithOverridingEffect, rulesWithOverriddenEffectAndPepActions);
}
use of org.ow2.authzforce.core.pdp.api.combining.CombiningAlgParameter in project core by authzforce.
the class DPOverridesCombiningAlg method getInstance.
/**
* {@inheritDoc}
*/
@Override
public CombiningAlg.Evaluator getInstance(final Iterable<CombiningAlgParameter<? extends T>> params, final Iterable<? extends T> combinedElements) throws UnsupportedOperationException, IllegalArgumentException {
// if no element -> NotApplicable
if (combinedElements == null) {
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant NotApplicable decision", this);
return CombiningAlgEvaluators.NOT_APPLICABLE_CONSTANT_EVALUATOR;
}
final Iterator<? extends Decidable> combinedEltIterator = combinedElements.iterator();
if (!combinedEltIterator.hasNext()) {
// empty (no element to combine)
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant NotApplicable decision", this);
return CombiningAlgEvaluators.NOT_APPLICABLE_CONSTANT_EVALUATOR;
}
/*
* If combined elements are Rules, we can optimize
*/
if (!RuleEvaluator.class.isAssignableFrom(getCombinedElementType())) {
return new OrderPreservingCombiningAlgEvaluator(combinedElements, this.overridingEffect);
}
// combined elements are Rules, we can optimize
/*
* There is at least one Rule. Prepare to iterate over Rules and collect them in a specific way (depending on whether it is "ordered-*-overrides" kind of algorithm or not).
*/
final RuleCollector ruleCollector = ruleCollectorFactory.newInstance();
/*
*
* If we find any empty Rule in overridden Effect (no target/condition/pep_action), we don't need to look at other rules with such Effect and no PEP action; because if there is no rule with
* overriding Effect, this rule is enough to return the overridden Effect as decision, and PEP actions come from all other rules with same Effect and PEP actions (e.g. if algorithm is
* deny-overrides, then if there is no applicable Deny rule, if there is any empty Permit rule, the result is Permit with PEP actions combined from all other Permit rules with PEP actions)
*/
RuleEvaluator firstEmptyRuleWithOverriddenEffect = null;
while (combinedEltIterator.hasNext()) {
final RuleEvaluator rule = (RuleEvaluator) combinedEltIterator.next();
if (rule.getEffect() == overridingEffect) {
/*
* If rule's effect is the overriding Effect, and it has no target/condition/pep_actions, then rule will always return this Effect -> {overriding_effect}-overrides alg always evaluates
* to ${overriding_effect} (ignore/remove all other rules). ({overriding_effect} = Permit if algorithm is Permit-overrides, or Deny if algorithm is Deny-overrides in this statement.)
*/
if (rule.isEmptyEquivalent()) {
LOGGER.warn("{}: {} with Effect={} is empty (no target/condition/pep_actions) => always returns {} => algorithm will always return {} => other combined rules have no effect => will be ignored/removed.", this, rule, this.overridingEffect, this.overridingEffect, this.overridingEffect);
return constantOverridingEffectDecisionEvaluator;
}
/*
* Rule is not empty, i.e. has a target/condition/actions, therefore may not necessarily return its (overriding) Effect as decision
*/
ruleCollector.addNonEmptyRuleWithOverridingEffect(rule);
continue;
}
/*
* Rule Effect = {overridden_Effect} (e.g. Permit if algorithm is deny-overrides)
*
* In the end, if there is no applicable rule with overriding Effect, and if there is an empty rule with such overridden Effect, we already know that the result is always the overridden
* Effect with PEP actions from all other rules with same Effect and PEP action(s).
*/
if (firstEmptyRuleWithOverriddenEffect != null) {
if (rule.hasAnyPepAction()) {
// rule has PEP action(s), therefore it matters if final result is overridden Effect
ruleCollector.addRuleWithOverriddenEffectAndPepActions(rule);
continue;
}
/*
* Rule has no PEP action Ignore this new Rule with overridden Effect and no PEP action; it will have no effect since we are sure the empty rule (that we already found) with overridden
* Effect will successfully evaluate.
*/
LOGGER.warn("{}: Ignoring/removing {} (Effect={}) because it does not affect the result, since it does no have any PEP action and we already found an empty Rule ({}) found with same Effect (always returns {}).", this, rule, overriddenEffect, firstEmptyRuleWithOverriddenEffect, overriddenEffect);
// continue looking for rules with overriding Effect
continue;
}
// No empty Rule with overridden Effect found yet (firstEmptyRuleWithOverriddenEffect == null)
if (rule.isEmptyEquivalent()) {
// rule has no PEP action -> firstEmptyRuleWithOverriddenEffect == null
/*
* This is the first declared empty Rule with overridden Effect -> always returns the overridden Effect as decision; we can ignore/remove other Rules with overridden Effect unless they
* have PEP actions (have no effect anymore)
*/
LOGGER.warn("{}: {} (Effect={}) is empty (no target/condition/pep_actions) => always returns {} => algorithm will always return {} unless some {} rule applies => other combined {} rules without any PEP action have no effect => will be ignored/removed.", this, rule, overriddenEffect, overriddenEffect, overriddenEffect, overridingEffect, overriddenEffect);
firstEmptyRuleWithOverriddenEffect = rule;
ruleCollector.addFirstEmptyRuleWithOverriddenEffect(rule);
// continue looking for rules with overriding Effect or with PEP actions
continue;
}
/*
* Non-empty Rule with overridden Effect found yet and current rule is not empty
*/
if (rule.hasAnyPepAction()) {
ruleCollector.addRuleWithOverriddenEffectAndPepActions(rule);
} else {
ruleCollector.addNonEmptyRuleWithOverriddenEffectButNoPepAction(rule);
}
}
/*
* There is at least one rule and there is no empty Rule with overriding Effect.
*
* If there is no rule with overriding Effect...
*/
if (!ruleCollector.hasRuleWithOverridingEffect()) {
/*
* No Rule with overriding Effect (whether empty or not) -> at least one Rule with overridden Effect and all rules have this same overridden Effect. If we found an empty rule with
* overridden Effect and no other with PEP action, the decision is the constant overridden Effect without PEP action.
*/
if (firstEmptyRuleWithOverriddenEffect != null && !ruleCollector.hasRuleWithOverriddenEffectAndPepAction()) {
/*
* no Rule with overriding Effect or PEP action, but one empty rule with overridden Effect -> final result is the overridden Effect as simple decision (no PEP action) always
*/
LOGGER.warn("{}: the only combined rule is empty {} Rule ({}) => algorithm will always return this {} => optimization: replacing with equivalent evaluator returning constant {} decision", this, this.overriddenEffect, firstEmptyRuleWithOverriddenEffect, this.overriddenEffect, this.overriddenEffect);
return constantOverriddenEffectDecisionEvaluator;
}
/*
* (All rules have same overridden Effect, and) either there is no empty rule OR there is at least one with PEP action
*/
return ruleCollector.getRuleCombiningAlgEvaluatorAssumingAllWithOverriddenEffect();
}
if (!ruleCollector.hasRuleWithOverriddenEffect()) {
/*
* No rule with overridden Effect -> only non-empty rules with same overriding Effect
*/
return ruleCollector.getRuleCombiningAlgEvaluatorAssumingAllWithOverridingEffect();
}
/*
* At least one Rule with overridden Effect and only non-empty rules with overriding Effect
*/
return ruleCollector.getDPOverridesRuleCombiningAlgEvaluator(overridingEffect);
}
use of org.ow2.authzforce.core.pdp.api.combining.CombiningAlgParameter in project core by authzforce.
the class FirstApplicableCombiningAlg method getInstance.
/**
* {@inheritDoc}
*/
@Override
public CombiningAlg.Evaluator getInstance(final Iterable<CombiningAlgParameter<? extends T>> params, final Iterable<? extends T> combinedElements) throws UnsupportedOperationException, IllegalArgumentException {
// if no element combined -> decision is overridden Effect
if (combinedElements == null) {
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant decision NotApplicable", this);
return CombiningAlgEvaluators.NOT_APPLICABLE_CONSTANT_EVALUATOR;
}
final Iterator<? extends Decidable> combinedEltIterator = combinedElements.iterator();
if (!combinedEltIterator.hasNext()) {
// empty (no element to combine)
LOGGER.warn("{}: no element to combine -> optimization: replacing with equivalent evaluator returning constant decision NotApplicable", this);
return CombiningAlgEvaluators.NOT_APPLICABLE_CONSTANT_EVALUATOR;
}
if (!RuleEvaluator.class.isAssignableFrom(getCombinedElementType())) {
// combined elements are not rules but policies
return new Evaluator(combinedElements);
}
// combined elements are Rules, we can optimize
/*
* There is at least one Rule. Prepare to iterate over Rules.
*/
/*
* If we found any empty rule
*/
final Deque<RuleEvaluator> finalRules = new ArrayDeque<>();
while (combinedEltIterator.hasNext()) {
final RuleEvaluator rule = (RuleEvaluator) combinedEltIterator.next();
finalRules.add(rule);
if (rule.isAlwaysApplicable()) {
/*
* The algorithm won't go further than that
*/
break;
}
}
/*
* if(combinedEltIterator.hasNext()), combinedElements has more elements than finalRules, so finalRules is a subset of combinedElements; else they have the same elements
*/
return new Evaluator(combinedEltIterator.hasNext() ? finalRules : combinedElements);
}
Aggregations