/* * Copyright (c) 2010-2017 Evolveum * * 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 com.evolveum.midpoint.model.impl.lens; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.namespace.QName; import com.evolveum.midpoint.model.api.context.*; import com.evolveum.midpoint.model.common.expression.ItemDeltaItem; import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject; import com.evolveum.midpoint.model.common.mapping.Mapping; import com.evolveum.midpoint.model.common.mapping.PrismValueDeltaSetTripleProducer; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.prism.delta.PlusMinusZero; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.PolicyViolationException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Evaluated assignment that contains all constructions and authorizations from the assignment * itself and all the applicable inducements from all the roles referenced from the assignment. * * @author Radovan Semancik */ public class EvaluatedAssignmentImpl<F extends FocusType> implements EvaluatedAssignment<F> { private static final Trace LOGGER = TraceManager.getTrace(EvaluatedAssignmentImpl.class); @NotNull private final ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> assignmentIdi; @NotNull private final DeltaSetTriple<Construction<F>> constructionTriple = new DeltaSetTriple<>(); @NotNull private final DeltaSetTriple<PersonaConstruction<F>> personaConstructionTriple = new DeltaSetTriple<>(); @NotNull private final DeltaSetTriple<EvaluatedAssignmentTargetImpl> roles = new DeltaSetTriple<>(); @NotNull private final Collection<PrismReferenceValue> orgRefVals = new ArrayList<>(); @NotNull private final Collection<PrismReferenceValue> membershipRefVals = new ArrayList<>(); @NotNull private final Collection<PrismReferenceValue> delegationRefVals = new ArrayList<>(); @NotNull private final Collection<Authorization> authorizations = new ArrayList<>(); @NotNull private final Collection<Mapping<?,?>> focusMappings = new ArrayList<>(); @NotNull private final Collection<AdminGuiConfigurationType> adminGuiConfigurations = new ArrayList<>(); // rules related to the focal object (typically e.g. "forbid modifications") @NotNull private final Collection<EvaluatedPolicyRule> focusPolicyRules = new ArrayList<>(); // rules related to the target of this assignment (typically e.g. "approve the assignment") @NotNull private final Collection<EvaluatedPolicyRule> thisTargetPolicyRules = new ArrayList<>(); // rules related to other targets provided by this assignment (e.g. induced or obtained by delegation) // usually, these rules do not cause direct action (e.g. in the case of approvals); // however, there are situations in which they are used (e.g. for exclusion rules) @NotNull private final Collection<EvaluatedPolicyRule> otherTargetsPolicyRules = new ArrayList<>(); private PrismObject<?> target; private boolean isValid; private boolean forceRecon; // used also to force recomputation of parentOrgRefs private boolean presentInCurrentObject; private boolean presentInOldObject; private Collection<String> policySituations = new ArrayList<>(); public EvaluatedAssignmentImpl( @NotNull ItemDeltaItem<PrismContainerValue<AssignmentType>, PrismContainerDefinition<AssignmentType>> assignmentIdi) { this.assignmentIdi = assignmentIdi; } @NotNull public ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> getAssignmentIdi() { return assignmentIdi; } /* (non-Javadoc) * @see com.evolveum.midpoint.model.impl.lens.EvaluatedAssignment#getAssignmentType() */ @Override public AssignmentType getAssignmentType() { return assignmentIdi.getItemNew().getValue(0).asContainerable(); } @Override public QName getRelation() { AssignmentType assignmentType = getAssignmentType(); if (assignmentType == null) { return null; } ObjectReferenceType targetRef = assignmentType.getTargetRef(); if (targetRef == null) { return null; } return ObjectTypeUtil.normalizeRelation(targetRef.getRelation()); } @NotNull public DeltaSetTriple<Construction<F>> getConstructionTriple() { return constructionTriple; } /** * Construction is not a part of model-api. To avoid heavy refactoring at present time, there is not a classical * Construction-ConstructionImpl separation, but we use artificial (simplified) EvaluatedConstruction * API class instead. * * @return */ public DeltaSetTriple<EvaluatedConstruction> getEvaluatedConstructions(Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { DeltaSetTriple<EvaluatedConstruction> rv = new DeltaSetTriple<>(); for (PlusMinusZero whichSet : PlusMinusZero.values()) { Collection<Construction<F>> constructionSet = constructionTriple.getSet(whichSet); if (constructionSet != null) { for (Construction<F> construction : constructionSet) { rv.addToSet(whichSet, new EvaluatedConstructionImpl(construction, task, result)); } } } return rv; } public Collection<Construction<F>> getConstructionSet(PlusMinusZero whichSet) { switch (whichSet) { case ZERO: return getConstructionTriple().getZeroSet(); case PLUS: return getConstructionTriple().getPlusSet(); case MINUS: return getConstructionTriple().getMinusSet(); default: throw new IllegalArgumentException("whichSet: " + whichSet); } } public void addConstruction(Construction<F> contruction, PlusMinusZero whichSet) { switch (whichSet) { case ZERO: constructionTriple.addToZeroSet(contruction); break; case PLUS: constructionTriple.addToPlusSet(contruction); break; case MINUS: constructionTriple.addToMinusSet(contruction); break; default: throw new IllegalArgumentException("whichSet: " + whichSet); } } @NotNull public DeltaSetTriple<PersonaConstruction<F>> getPersonaConstructionTriple() { return personaConstructionTriple; } public void addPersonaConstruction(PersonaConstruction<F> personaContruction, PlusMinusZero whichSet) { switch (whichSet) { case ZERO: personaConstructionTriple.addToZeroSet(personaContruction); break; case PLUS: personaConstructionTriple.addToPlusSet(personaContruction); break; case MINUS: personaConstructionTriple.addToMinusSet(personaContruction); break; default: throw new IllegalArgumentException("whichSet: " + whichSet); } } @NotNull @Override public DeltaSetTriple<EvaluatedAssignmentTargetImpl> getRoles() { return roles; } public void addRole(EvaluatedAssignmentTargetImpl role, PlusMinusZero mode) { roles.addToSet(mode, role); } @NotNull public Collection<PrismReferenceValue> getOrgRefVals() { return orgRefVals; } public void addOrgRefVal(PrismReferenceValue org) { orgRefVals.add(org); } @NotNull public Collection<PrismReferenceValue> getMembershipRefVals() { return membershipRefVals; } public void addMembershipRefVal(PrismReferenceValue org) { membershipRefVals.add(org); } @NotNull public Collection<PrismReferenceValue> getDelegationRefVals() { return delegationRefVals; } public void addDelegationRefVal(PrismReferenceValue org) { delegationRefVals.add(org); } @NotNull @Override public Collection<Authorization> getAuthorizations() { return authorizations; } public void addAuthorization(Authorization authorization) { authorizations.add(authorization); } @NotNull public Collection<AdminGuiConfigurationType> getAdminGuiConfigurations() { return adminGuiConfigurations; } public void addAdminGuiConfiguration(AdminGuiConfigurationType adminGuiConfiguration) { adminGuiConfigurations.add(adminGuiConfiguration); } @NotNull public Collection<Mapping<?,?>> getFocusMappings() { return focusMappings; } public void addFocusMapping(Mapping<? extends PrismPropertyValue<?>,? extends PrismPropertyDefinition<?>> focusMapping) { this.focusMappings.add(focusMapping); } /* (non-Javadoc) * @see com.evolveum.midpoint.model.impl.lens.EvaluatedAssignment#getTarget() */ @Override public PrismObject<?> getTarget() { return target; } public void setTarget(PrismObject<?> target) { this.target = target; } /* (non-Javadoc) * @see com.evolveum.midpoint.model.impl.lens.EvaluatedAssignment#isValid() */ @Override public boolean isValid() { return isValid; } public void setValid(boolean isValid) { this.isValid = isValid; } public boolean isForceRecon() { return forceRecon; } public void setForceRecon(boolean forceRecon) { this.forceRecon = forceRecon; } public Collection<ResourceType> getResources(Task task, OperationResult result) throws ObjectNotFoundException, SchemaException { Collection<ResourceType> resources = new ArrayList<ResourceType>(); for (Construction<F> acctConstr: constructionTriple.getAllValues()) { resources.add(acctConstr.getResource(task, result)); } return resources; } // System configuration is used only to provide $configuration script variable (MID-2372) public void evaluateConstructions(ObjectDeltaObject<F> focusOdo, PrismObject<SystemConfigurationType> systemConfiguration, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { for (Construction<F> construction :constructionTriple.getAllValues()) { construction.setFocusOdo(focusOdo); construction.setSystemConfiguration(systemConfiguration); LOGGER.trace("Evaluating construction '{}' in {}", construction, construction.getSource()); construction.evaluate(task, result); } } public void evaluateConstructions(ObjectDeltaObject<F> focusOdo, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { evaluateConstructions(focusOdo, null, task, result); } public void setPresentInCurrentObject(boolean presentInCurrentObject) { this.presentInCurrentObject = presentInCurrentObject; } public void setPresentInOldObject(boolean presentInOldObject) { this.presentInOldObject = presentInOldObject; } @Override public boolean isPresentInCurrentObject() { return presentInCurrentObject; } @Override public boolean isPresentInOldObject() { return presentInOldObject; } @NotNull public Collection<EvaluatedPolicyRule> getFocusPolicyRules() { return focusPolicyRules; } public void addFocusPolicyRule(EvaluatedPolicyRule policyRule) { focusPolicyRules.add(policyRule); } @NotNull public Collection<EvaluatedPolicyRule> getThisTargetPolicyRules() { return thisTargetPolicyRules; } public void addThisTargetPolicyRule(EvaluatedPolicyRule policyRule) { thisTargetPolicyRules.add(policyRule); } @NotNull public Collection<EvaluatedPolicyRule> getOtherTargetsPolicyRules() { return otherTargetsPolicyRules; } public void addOtherTargetPolicyRule(EvaluatedPolicyRule policyRule) { otherTargetsPolicyRules.add(policyRule); } @NotNull public Collection<EvaluatedPolicyRule> getAllTargetsPolicyRules() { return Stream.concat(thisTargetPolicyRules.stream(), otherTargetsPolicyRules.stream()).collect(Collectors.toList()); } public void addLegacyPolicyConstraints(PolicyConstraintsType constraints, AssignmentPath assignmentPath, FocusType directOwner) { if (!constraints.getModification().isEmpty()) { PolicyConstraintsType focusConstraints = constraints.clone(); focusConstraints.getAssignment().clear(); focusConstraints.getMaxAssignees().clear(); focusConstraints.getMinAssignees().clear(); focusConstraints.getExclusion().clear(); focusPolicyRules.add(toEvaluatedPolicyRule(focusConstraints, assignmentPath, directOwner)); } if (!constraints.getMinAssignees().isEmpty() || !constraints.getMaxAssignees().isEmpty() || !constraints.getAssignment().isEmpty() || !constraints.getExclusion().isEmpty()) { PolicyConstraintsType targetConstraints = constraints.clone(); targetConstraints.getModification().clear(); EvaluatedPolicyRule evaluatedPolicyRule = toEvaluatedPolicyRule(targetConstraints, assignmentPath, directOwner); otherTargetsPolicyRules.add(evaluatedPolicyRule); thisTargetPolicyRules.add(evaluatedPolicyRule); } } @NotNull private EvaluatedPolicyRule toEvaluatedPolicyRule(PolicyConstraintsType constraints, AssignmentPath assignmentPath, FocusType directOwner) { PolicyRuleType policyRuleType = new PolicyRuleType(); policyRuleType.setPolicyConstraints(constraints); PolicyActionsType policyActionsType = new PolicyActionsType(); policyActionsType.setEnforcement(new EnforcementPolicyActionType()); policyRuleType.setPolicyActions(policyActionsType); return new EvaluatedPolicyRuleImpl(policyRuleType, assignmentPath); } @Override public Collection<String> getPolicySituations() { return policySituations; } @Override public void triggerConstraint(@Nullable EvaluatedPolicyRule rule, EvaluatedPolicyRuleTrigger trigger) throws PolicyViolationException { boolean hasException = processRuleExceptions(this, rule, trigger); if (trigger instanceof EvaluatedExclusionTrigger) { EvaluatedExclusionTrigger exclTrigger = (EvaluatedExclusionTrigger) trigger; if (exclTrigger.getConflictingAssignment() != null) { hasException = hasException || processRuleExceptions((EvaluatedAssignmentImpl<F>) exclTrigger.getConflictingAssignment(), rule, trigger); } } if (!hasException) { LensUtil.triggerConstraint(rule, trigger, policySituations); } } private boolean processRuleExceptions(EvaluatedAssignmentImpl<F> evaluatedAssignment, EvaluatedPolicyRule rule, EvaluatedPolicyRuleTrigger trigger) { boolean hasException = false; for (PolicyExceptionType policyException: evaluatedAssignment.getAssignmentType().getPolicyException()) { if (policyException.getRuleName().equals(rule.getName())) { LensUtil.processRuleWithException(rule, trigger, policySituations, policyException); hasException = true; // } else { // LOGGER.trace("Skipped exception because it does not match rule name, exception: {}, rule: {}", policyException.getRuleName(), rule.getName()); } } return hasException; } @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.debugDumpLabelLn(sb, "EvaluatedAssignment", indent); DebugUtil.debugDumpWithLabelLn(sb, "assignment old", String.valueOf(assignmentIdi.getItemOld()), indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "assignment delta", String.valueOf(assignmentIdi.getDelta()), indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "assignment new", String.valueOf(assignmentIdi.getItemNew()), indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "target", String.valueOf(target), indent + 1); DebugUtil.debugDumpWithLabel(sb, "isValid", isValid, indent + 1); if (forceRecon) { sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "forceRecon", forceRecon, indent + 1); } if (!constructionTriple.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Constructions", constructionTriple, indent+1); } if (!personaConstructionTriple.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Persona constructions", personaConstructionTriple, indent+1); } if (!roles.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Roles", roles, indent+1); } dumpRefList(indent, sb, "Orgs", orgRefVals); dumpRefList(indent, sb, "Membership", membershipRefVals); dumpRefList(indent, sb, "Delegation", delegationRefVals); if (!authorizations.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpLabel(sb, "Authorizations", indent+1); for (Authorization autz: authorizations) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent+2); sb.append(autz.toString()); } } if (!focusMappings.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpLabel(sb, "Focus Mappings", indent+1); for (PrismValueDeltaSetTripleProducer<?,?> mapping: focusMappings) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent+2); sb.append(mapping.toString()); } } if (target != null) { sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Target", target.toString(), indent+1); } sb.append("\n"); DebugUtil.debugDumpWithLabelLn(sb, "focusPolicyRules " + ruleCountInfo(focusPolicyRules), focusPolicyRules, indent+1); DebugUtil.debugDumpWithLabelLn(sb, "thisTargetPolicyRules " + ruleCountInfo(thisTargetPolicyRules), thisTargetPolicyRules, indent+1); DebugUtil.debugDumpWithLabelLn(sb, "otherTargetsPolicyRules " + ruleCountInfo(otherTargetsPolicyRules), otherTargetsPolicyRules, indent+1); DebugUtil.debugDumpWithLabelLn(sb, "Present in old object", isPresentInOldObject(), indent+1); DebugUtil.debugDumpWithLabel(sb, "Present in current object", isPresentInCurrentObject(), indent+1); return sb.toString(); } private String ruleCountInfo(Collection<EvaluatedPolicyRule> rules) { return "(" + rules.size() + ", triggered " + LensContext.getTriggeredRulesCount(rules) + ")"; } private void dumpRefList(int indent, StringBuilder sb, String label, Collection<PrismReferenceValue> referenceValues) { if (!referenceValues.isEmpty()) { sb.append("\n"); DebugUtil.debugDumpLabel(sb, label, indent+1); for (PrismReferenceValue refVal: referenceValues) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent+2); sb.append(refVal.toString()); } } } @Override public String toString() { return "EvaluatedAssignment(target=" + target + "; constr=" + constructionTriple + "; org="+orgRefVals+"; autz="+authorizations+"; "+focusMappings.size()+" focus mappings; "+ focusPolicyRules .size()+" rules)"; } public String toHumanReadableString() { if (target != null) { return "EvaluatedAssignment(" + target + ")"; } else if (constructionTriple != null && !constructionTriple.isEmpty()) { return "EvaluatedAssignment(" + constructionTriple + ")"; } else if (personaConstructionTriple != null && !personaConstructionTriple.isEmpty()) { return "EvaluatedAssignment(" + personaConstructionTriple + ")"; } else { return toString(); } } public List<EvaluatedAssignmentTargetImpl> getNonNegativeTargets() { List<EvaluatedAssignmentTargetImpl> rv = new ArrayList<>(); rv.addAll(roles.getZeroSet()); rv.addAll(roles.getPlusSet()); return rv; } }