From 12b588cb3add4164f6ab3bbbdb9e940676b510ff Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Fri, 10 Mar 2017 23:08:36 +0100 Subject: [PATCH 1/6] AssignmentEvaluator: Comments/refactorings without change in functionality. --- .../xml/ns/public/common/common-core-3.xsd | 2 +- .../model/api/context/AssignmentPath.java | 9 +- .../api/context/AssignmentPathSegment.java | 21 +- .../model/api/context/EvaluationOrder.java | 4 + .../model/impl/lens/AssignmentEvaluator.java | 45 ++-- .../model/impl/lens/AssignmentPathImpl.java | 18 +- .../impl/lens/AssignmentPathSegmentImpl.java | 163 +++++++++++--- .../model/impl/lens/EvaluationOrderImpl.java | 22 +- .../midpoint/model/impl/lens/LensUtil.java | 2 +- .../impl/lens/TestAssignmentProcessor2.java | 210 ++++++++++++++++++ .../test/AbstractModelIntegrationTest.java | 16 ++ 11 files changed, 440 insertions(+), 72 deletions(-) create mode 100644 model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index c52dc6cef70..5f166ab7ab5 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -8509,7 +8509,7 @@ Selects some assignments from all the assignments in the object. - E.g. may be used to select only some assignments/inducments for a role. + E.g. may be used to select only some assignments/inducements for a role. diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPath.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPath.java index cef295aeb55..9ba8f770d3c 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPath.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPath.java @@ -24,6 +24,11 @@ import java.util.List; /** + * Path from the source object (focus) to the ultimate assignment that is being processed or referenced. + * The path consists of a chain (list) of segments. Each segment corresponds to a single assignment or inducement. + * The source of the first segment is the focus. Source of each following segment (i.e. assignment) is the target + * of previous segment (i.e. assignment). + * * @author semancik * @author mederly */ @@ -31,13 +36,13 @@ public interface AssignmentPath extends DebugDumpable { List getSegments(); - AssignmentPathSegment getFirstAssignmentSegment(); + AssignmentPathSegment first(); boolean isEmpty(); int size(); - EvaluationOrder getEvaluationOrder(); +// EvaluationOrder getEvaluationOrder(); AssignmentPathSegment last(); diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java index 0755b5e4675..47703f5d152 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java @@ -22,27 +22,36 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPathSegmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.jetbrains.annotations.NotNull; /** + * Single assignment in an assignment path. In addition to the AssignmentType, it contains resolved target: + * full object, resolved from targetRef. If targetRef resolves to multiple objects, in the path segment + * one of them is stored: the one that participates in the particular assignment path. + * * @author semancik * @author mederly */ public interface AssignmentPathSegment extends DebugDumpable { - boolean isAssignment(); AssignmentType getAssignment(); - QName getRelation(); - - ObjectType getTarget(); + /** + * True if the segment corresponds to assignment. False if it's an inducement. + */ + boolean isAssignment(); ObjectType getSource(); - EvaluationOrder getEvaluationOrder(); + ObjectType getTarget(); - ObjectType getOrderOneObject(); + QName getRelation(); + /** + * True if the relation is a delegation one. + */ boolean isDelegation(); + @NotNull AssignmentPathSegmentType toAssignmentPathSegmentType(); } diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluationOrder.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluationOrder.java index d275b587dbf..6eedb5a3b9f 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluationOrder.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/EvaluationOrder.java @@ -32,6 +32,10 @@ public interface EvaluationOrder extends DebugDumpable { EvaluationOrder advance(QName relation); + EvaluationOrder decrease(int amount); + + EvaluationOrder decrease(int amount, QName relation); + int getMatchingRelationOrder(QName relation); String shortDump(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java index 2f9375e8c58..274a04c959f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java @@ -257,12 +257,19 @@ private EvaluationOrder getInitialEvaluationOrder( */ private void evaluateFromSegment(AssignmentPathSegmentImpl segment, PlusMinusZero mode, EvaluationContext ctx) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, PolicyViolationException { - LOGGER.trace("Evaluate assignment {} (matching order: {}, mode: {})", ctx.assignmentPath, segment.isMatchingOrder(), mode); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("*** Evaluate from segment: {}", segment); + LOGGER.trace("*** Evaluation order: {}, matching: {}, mode: {}, process membership: {}", + segment.getEvaluationOrder(), segment.isMatchingOrder(), mode, segment.isProcessMembership()); + } assertSourceNotNull(segment.source, ctx.evalAssignment); checkSchema(segment, ctx); ctx.assignmentPath.add(segment); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("*** Path (with current segment already added):\n{}", ctx.assignmentPath.debugDump()); + } boolean evaluateContent = true; AssignmentType assignmentType = getAssignmentType(segment, ctx); @@ -338,7 +345,7 @@ private boolean evaluateSegmentContent(AssignmentPathSegm } if (assignmentType.getTarget() != null || assignmentType.getTargetRef() != null) { List> targets = getTargets(segment, ctx); - LOGGER.trace("Targets in {}: {}", segment.source, targets); + LOGGER.trace("Targets in {}, assignment ID {}: {}", segment.source, assignmentType.getId(), targets); if (isDirectAssignment) { setEvaluatedAssignmentTarget(segment, targets, ctx); @@ -381,8 +388,10 @@ private void checkCycle(AssignmentPathSegmentImpl segment if (target.getOid().equals(segment.source.getOid())) { throw new PolicyViolationException("The "+segment.source+" refers to itself in assignment/inducement"); } - LOGGER.trace("Checking for role cycle, comparing segment order {} with path order {}", segment.getEvaluationOrder(), ctx.assignmentPath.getEvaluationOrder()); - if (ctx.assignmentPath.containsTarget(target.asObjectable()) && segment.getEvaluationOrder().equals(ctx.assignmentPath.getEvaluationOrder())) { + // removed condition "&& segment.getEvaluationOrder().equals(ctx.assignmentPath.getEvaluationOrder())" + // as currently it is always true + // TODO reconsider this + if (ctx.assignmentPath.containsTarget(target.asObjectable())) { LOGGER.debug("Role cycle detected for target {} in {}", ObjectTypeUtil.toShortString(target), ctx.assignmentPath); throw new PolicyViolationException("Attempt to assign "+target+" creates a role cycle"); } @@ -585,7 +594,7 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, segment.setTarget(targetType); segment.setRelation(relation); if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Evaluating TARGET:\n{}", segment.debugDump(1)); + LOGGER.trace("Evaluating segment TARGET:\n{}", segment.debugDump(1)); } checkRelationWithTarget(segment, targetType, relation); @@ -654,7 +663,7 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, return; } - EvaluationOrder evaluationOrder = ctx.assignmentPath.getEvaluationOrder(); + EvaluationOrder evaluationOrder = segment.getEvaluationOrder(); ObjectType orderOneObject; if (evaluationOrder.getSummaryOrder() == 1) { @@ -689,6 +698,11 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, roleInducementIdi.recompute(); String subSourceDescription = targetType+" in "+segment.sourceDescription; AssignmentPathSegmentImpl subAssignmentPathSegment = new AssignmentPathSegmentImpl(targetType, subSourceDescription, roleInducementIdi, false); +// EvaluationOrder newEvaluationOrder = evaluationOrder; +// if (roleInducement.getOrder() != null && roleInducement.getOrder() > 1) { +// newEvaluationOrder = evaluationOrder.decrease(roleInducement.getOrder()-1); // TODO UGLY HACK +// } +// subAssignmentPathSegment.setEvaluationOrder(newEvaluationOrder); subAssignmentPathSegment.setEvaluationOrder(evaluationOrder); subAssignmentPathSegment.setOrderOneObject(orderOneObject); subAssignmentPathSegment.setPathToSourceValid(isValid); @@ -701,9 +715,9 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, // We need to make sure NOT to extract anything other from such inducements. That's why we set e.g. // processMembership attribute to false for these inducements. if (LOGGER.isTraceEnabled()) { - LOGGER.trace("E({}): evaluate inducement({}) {} in {}", - evaluationOrder.shortDump(), FocusTypeUtil.dumpInducementConstraints(roleInducement), - FocusTypeUtil.dumpAssignment(roleInducement), targetType); + LOGGER.trace("orig E({}): evaluate {} inducement({}) {} (new-disabled EO {})", + evaluationOrder.shortDump(), targetType, FocusTypeUtil.dumpInducementConstraints(roleInducement), + FocusTypeUtil.dumpAssignment(roleInducement), evaluationOrder.shortDump()); } assert !ctx.assignmentPath.isEmpty(); evaluateFromSegment(subAssignmentPathSegment, mode, ctx); @@ -721,19 +735,20 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, continue; } } + QName subrelation = getRelation(roleAssignment); + EvaluationOrder newEvaluationOrder = evaluationOrder.advance(subrelation); if (LOGGER.isTraceEnabled()) { - LOGGER.trace("E({}): follow assignment {} in {}", - evaluationOrder.shortDump(), FocusTypeUtil.dumpAssignment(roleAssignment), targetType); + LOGGER.trace("orig EO {}: follow assignment {} {} (new EO {})", + evaluationOrder.shortDump(), targetType, FocusTypeUtil.dumpAssignment(roleAssignment), newEvaluationOrder); } ItemDeltaItem,PrismContainerDefinition> roleAssignmentIdi = new ItemDeltaItem<>(); roleAssignmentIdi.setItemOld(LensUtil.createAssignmentSingleValueContainerClone(roleAssignment)); roleAssignmentIdi.recompute(); String subSourceDescription = targetType+" in "+segment.sourceDescription; AssignmentPathSegmentImpl subAssignmentPathSegment = new AssignmentPathSegmentImpl(targetType, subSourceDescription, roleAssignmentIdi, true); - QName subrelation = getRelation(roleAssignment); - - subAssignmentPathSegment.setEvaluationOrder(evaluationOrder.advance(subrelation)); + subAssignmentPathSegment.setEvaluationOrder(newEvaluationOrder); subAssignmentPathSegment.setOrderOneObject(orderOneObject); + // TODO why??? this should depend on evaluation order if (targetType instanceof AbstractRoleType) { subAssignmentPathSegment.setProcessMembership(false); } else { @@ -760,6 +775,8 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, ctx.evalAssignment.addLegacyPolicyConstraints(policyConstraints, ctx.assignmentPath.clone(), targetType); } } + + LOGGER.trace("Evaluating segment target DONE for {}", segment); } private void checkRelationWithTarget(AssignmentPathSegmentImpl segment, FocusType targetType, QName relation) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathImpl.java index f2793ee6725..2016cc60623 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathImpl.java @@ -59,7 +59,7 @@ public void removeLast(AssignmentPathSegmentImpl segment) { } @Override - public AssignmentPathSegmentImpl getFirstAssignmentSegment() { + public AssignmentPathSegmentImpl first() { return segments.get(0); } @@ -71,14 +71,14 @@ public boolean isEmpty() { @Override public int size() { return segments.size(); } - @Override - public EvaluationOrder getEvaluationOrder() { - if (isEmpty()) { - return EvaluationOrderImpl.ZERO; - } else { - return last().getEvaluationOrder(); - } - } +// @Override +// public EvaluationOrder getEvaluationOrder() { +// if (isEmpty()) { +// return EvaluationOrderImpl.ZERO; +// } else { +// return last().getEvaluationOrder(); +// } +// } @Override public AssignmentPathSegmentImpl last() { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java index 8606906ab1c..5c86eb9ed3c 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java @@ -27,31 +27,102 @@ import com.evolveum.midpoint.prism.xml.XsdTypeMapper; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPathSegmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OrderConstraintsType; +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; /** + * Primary duty of this class is to be a part of assignment path. (This is what is visible through its interface, + * AssignmentPathSegment.) However, it also serves as a place where auxiliary information about assignment evaluation + * is stored. + * * @author semancik * */ public class AssignmentPathSegmentImpl implements AssignmentPathSegment { - final ObjectType source; - final String sourceDescription; + private static final Trace LOGGER = TraceManager.getTrace(AssignmentPathSegmentImpl.class); + + // "assignment path segment" information + + final ObjectType source; // we avoid "getter" notation for some final fields to simplify client code private final ItemDeltaItem,PrismContainerDefinition> assignmentIdi; private final boolean isAssignment; // false means inducement private QName relation; private ObjectType target; - private boolean pathToSourceValid; // is the whole path to source valid? - private boolean validityOverride = false; - private EvaluationOrder evaluationOrder; - private ObjectType varThisObject; + + // assignment evaluation information + + final String sourceDescription; // Human readable text describing the source (for error messages) + private boolean pathToSourceValid; // Is the whole path to *source* valid, i.e. enabled (meaning activation.effectiveStatus)? + private boolean validityOverride = false; // Should we evaluate content of the assignment even if it's not valid i.e. enabled? + // This is set to true on the first assignment in the chain. + + /** + * Constructions, focus mappings, and focus policy rules (or "assignment content") should be collected only + * from assignments that belong directly to the focus; i.e. not from assignments attached to roles, meta roles, + * meta-meta roles, etc. + * + * Content belonging to roles should be collected if it's attached to them via inducements of order 1. + * Content belonging to meta-roles should be collected if it's attached to them via inducements of order 2. + * And so on. + * + * For each segment (i.e. assignment/inducement), we know we can use its content if its "isMatchingOrder" attribute is true. + * + * But how we compute this attribute? + * + * Each segment in the assignment path has an evaluation order. First assignment has an evaluation order of 1, second + * assignment has an order of 2, etc. Order of a segment can be seen as the number of assignments segments in the path + * (including itself). And we collect content from assignment segments of order 1. + * + * But what about inducements? There are two (somewhat related) questions: + * + * 1. How we compute isMatchingOrder for inducement segments? + * 2. How we compute evaluation order for inducement segments? + * + * As for #1: To compute isMatchingOrder, we must take evaluation order of the _previous_ segment, and compare + * it with the inducement order (or, more generally, inducement order constraints). If they match, we say that inducement + * has matching order. + * + * As for #2: For some inducements we can determine resulting order, while for others we can not. Problematic inducements + * are those that do not have strict order, but an interval of orders instead. For the former we can compute the + * resulting order as previous - (N-1), where N is the order of the inducement (unspecified means 1). For the latter + * we consider the resulting order as undefined. + * + * NOTE this is not consistent with the current implementation. + * + * TODO relations + * + * Special consideration must be given when collecting target policy rules, i.e. rules that are attached to + * assignment targets. Such rules are typically attached to roles that are being assigned. So let's consider this: + * + * rule1 (e.g. assignment approval policy rule) + * A + * | + * | + * Pirate + * A + * | + * | + * jack + * + * When evaluating jack->Pirate assignment, rule1 would not be normally taken into account, because its assignment + * (Pirate->rule1) has an order of 2. However, we want to collect it - but not as an item related to focus, but + * as an item related to evaluated assignment's target. Therefore besides isMatchingOrder we maintain isMatchingOrderPlusOne + * that marks all segments (assignments/inducements) that contain policy rules relevant to the evaluated assignment's target. + * + * TODO how exactly do we compute it + */ private Boolean isMatchingOrder = null; + private EvaluationOrder evaluationOrder; + private Boolean isMatchingOrderPlusOne = null; + private boolean processMembership = false; + private ObjectType varThisObject; + AssignmentPathSegmentImpl(ObjectType source, String sourceDescription, ItemDeltaItem, PrismContainerDefinition> assignmentIdi, boolean isAssignment) { @@ -121,7 +192,6 @@ public void setValidityOverride(boolean validityOverride) { this.validityOverride = validityOverride; } - @Override public EvaluationOrder getEvaluationOrder() { return evaluationOrder; } @@ -130,7 +200,6 @@ public void setEvaluationOrder(EvaluationOrder evaluationOrder) { this.evaluationOrder = evaluationOrder; } - @Override public ObjectType getOrderOneObject() { return varThisObject; } @@ -147,6 +216,10 @@ public void setProcessMembership(boolean processMembership) { this.processMembership = processMembership; } + /** + * Whether this assignment/inducement matches the focus level, i.e. if we should collect constructions, + * focus mappings, and focus policy rules from it. + */ public boolean isMatchingOrder() { if (isMatchingOrder == null) { isMatchingOrder = computeMatchingOrder(0); @@ -163,21 +236,27 @@ public boolean isMatchingOrderPlusOne() { private boolean computeMatchingOrder(int offset) { AssignmentType assignmentType = getAssignment(); + boolean rv; if (assignmentType.getOrder() == null && assignmentType.getOrderConstraint().isEmpty()) { // compatibility - return evaluationOrder.getSummaryOrder() - offset == 1; - } - if (assignmentType.getOrder() != null) { - if (evaluationOrder.getSummaryOrder() - offset != assignmentType.getOrder()) { - return false; + rv = evaluationOrder.getSummaryOrder() - offset == 1; + } else { + rv = true; + if (assignmentType.getOrder() != null) { + if (evaluationOrder.getSummaryOrder() - offset != assignmentType.getOrder()) { + rv = false; + } } - } - for (OrderConstraintsType orderConstraint: assignmentType.getOrderConstraint()) { - if (!isMatchingConstraint(orderConstraint, offset)) { - return false; + for (OrderConstraintsType orderConstraint : assignmentType.getOrderConstraint()) { + if (!isMatchingConstraint(orderConstraint, offset)) { + rv = false; + break; + } } } - return true; + LOGGER.trace("computeMatchingOrder => {}, for offset={}; assignment.order={}, assignment.orderConstraint={}, evaluationOrder={} ... assignment = {}", + rv, offset, assignmentType.getOrder(), assignmentType.getOrderConstraint(), evaluationOrder); + return rv; } private boolean isMatchingConstraint(OrderConstraintsType orderConstraint, int offset) { @@ -252,18 +331,22 @@ public String toString() { sb.append(": "); sb.append(source).append(" "); PrismContainer assignment = (PrismContainer) assignmentIdi.getAnyItem(); - if (assignment != null) { - AssignmentType assignmentType = assignment.getValue().asContainerable(); - if (assignmentType.getConstruction() != null) { - sb.append("Constr '").append(assignmentType.getConstruction().getDescription()).append("' "); - } + AssignmentType assignmentType = assignment != null ? assignment.getValue().asContainerable() : null; + if (assignmentType != null && assignmentType.getConstruction() != null) { + sb.append("Constr '").append(assignmentType.getConstruction().getDescription()).append("' "); } - if (target != null) { + ObjectReferenceType targetRef = assignmentType != null ? assignmentType.getTargetRef() : null; + if (target != null || targetRef != null) { sb.append("-["); if (relation != null) { sb.append(relation.getLocalPart()); } - sb.append("]-> ").append(target); + sb.append("]-> "); + if (target != null) { + sb.append(target); + } else { + sb.append(ObjectTypeUtil.toShortString(targetRef, true)); + } } sb.append(")"); return sb.toString(); @@ -279,18 +362,28 @@ public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.debugDumpLabel(sb, "AssignmentPathSegment", indent); sb.append("\n"); - DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrder", isMatchingOrder, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "processMembership", processMembership, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "validityOverride", validityOverride, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "source", source==null?"null":source.toString(), indent + 1); + String assignmentOrInducement = isAssignment ? "assignment" : "inducement"; + if (assignmentIdi != null) { + DebugUtil.debugDumpWithLabelLn(sb, assignmentOrInducement + " old", String.valueOf(assignmentIdi.getItemOld()), indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, assignmentOrInducement + " delta", String.valueOf(assignmentIdi.getDelta()), indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, assignmentOrInducement + " new", String.valueOf(assignmentIdi.getItemNew()), indent + 1); + } else { + DebugUtil.debugDumpWithLabelLn(sb, assignmentOrInducement, "null", indent + 1); + } + DebugUtil.debugDumpWithLabelLn(sb, "target", target==null?"null":target.toString(), indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "evaluationOrder", evaluationOrder, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "assignment", assignmentIdi.toString(), indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrder", isMatchingOrder, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "isMatchingOrderPlusOne", isMatchingOrderPlusOne, indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "relation", relation, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "target", target==null?"null":target.toString(), indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "source", source==null?"null":source.toString(), indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "pathToSourceValid", pathToSourceValid, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "validityOverride", validityOverride, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "processMembership", processMembership, indent + 1); DebugUtil.debugDumpWithLabel(sb, "varThisObject", varThisObject==null?"null":varThisObject.toString(), indent + 1); return sb.toString(); } + @NotNull @Override public AssignmentPathSegmentType toAssignmentPathSegmentType() { AssignmentPathSegmentType rv = new AssignmentPathSegmentType(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluationOrderImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluationOrderImpl.java index ed2e5061bc1..2f42f63f9ae 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluationOrderImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/EvaluationOrderImpl.java @@ -58,27 +58,41 @@ public EvaluationOrder advance() { @Override public EvaluationOrder advance(QName relation) { + return advance(1, relation); + } + + private EvaluationOrder advance(int amount, QName relation) { EvaluationOrderImpl advanced = new EvaluationOrderImpl(); boolean found = false; for (Entry entry: orderMap.entrySet()) { if (QNameUtil.match(entry.getKey(), relation)) { - advanced.orderMap.put(entry.getKey(), entry.getValue() + 1); + advanced.orderMap.put(entry.getKey(), entry.getValue() + amount); found = true; } else { advanced.orderMap.put(entry.getKey(), entry.getValue()); } } if (!found) { - advanced.orderMap.put(relation, 1); + advanced.orderMap.put(relation, amount); } if (DeputyUtils.isDelegationRelation(relation)) { advanced.summaryOrder = this.summaryOrder; } else { - advanced.summaryOrder = this.summaryOrder + 1; + advanced.summaryOrder = this.summaryOrder + amount; } return advanced; } - + + @Override + public EvaluationOrder decrease(int amount) { + return decrease(amount, null); + } + + @Override + public EvaluationOrder decrease(int amount, QName relation) { + return advance(-amount, relation); + } + @Override public int getMatchingRelationOrder(QName relation) { for (Entry entry: orderMap.entrySet()) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java index 16ba45f8d2a..5ba6ba9d348 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java @@ -1062,7 +1062,7 @@ public static AssignmentPathVariables computeAssignmentPathVariables(AssignmentP } } - AssignmentPathSegmentImpl focusAssignmentSegment = assignmentPath.getFirstAssignmentSegment(); + AssignmentPathSegmentImpl focusAssignmentSegment = assignmentPath.first(); ItemDeltaItem,PrismContainerDefinition> focusAssignment = focusAssignmentSegment.getAssignmentIdi().clone(); vars.setFocusAssignment(focusAssignment); diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java new file mode 100644 index 00000000000..1af5122cb56 --- /dev/null +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -0,0 +1,210 @@ +/* + * 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 com.evolveum.midpoint.common.Clock; +import com.evolveum.midpoint.model.impl.lens.projector.AssignmentProcessor; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; + +import static com.evolveum.midpoint.test.IntegrationTestTools.displayObjectTypeCollection; + +/** + * Comprehensive test of assignment evaluator and processor. + * + * MMR1 -----------I------------------------------* + * ^ | + * | I + * | V + * MR1 -----------I-------------*-----> MR3 MR4 + * ^ MR2 --I---* | | | + * | ^ I I I I + * | | V V V V + * R1 --I--> R2 R3 R4 R5 R6 + * ^ + * | + * | + * jack + * + * Straight line means assignment. + * Line marked with "I" means inducement. + * + * Orders of these inducements are given by the levels of participants, so that each induced role belongs to jack, and each + * induced metarole belongs to some role. So, + * - inducement Rx->Ry is of order 1 + * - inducement MRx->MRy is of order 1 + * - inducement MRx->Ry is of order 2 + * - inducement MMRx->MRy is of order 1 + * + * Each role has authorization, construction, focus mapping, focus policy rule and target policy rule. + * + * Each assignment and each role can be selectively enabled/disabled (via activation) and has its condition matched (none/old/new/old+new). + * + * @author mederly + */ +public class TestAssignmentProcessor2 extends AbstractLensTest { + + @Autowired + private AssignmentProcessor assignmentProcessor; + + @Autowired + private Clock clock; + + private RoleType role1, role2, role3, role4, role5, role6; + private RoleType metarole1, metarole2, metarole3, metarole4; + private RoleType metametarole1; + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + role1 = createRole(1, 1); + role2 = createRole(1, 2); + role3 = createRole(1, 3); + role4 = createRole(1, 4); + role5 = createRole(1, 5); + role6 = createRole(1, 6); + metarole1 = createRole(2, 1); + metarole2 = createRole(2, 2); + metarole3 = createRole(2, 3); + metarole4 = createRole(2, 4); + metametarole1 = createRole(3, 1); + assign(role1, metarole1); + assign(role2, metarole2); + assign(metarole1, metametarole1); + induce(role1, role2, 1); + induce(metarole1, metarole3, 1); + induce(metarole1, role4, 2); + induce(metarole2, role3, 2); + induce(metarole3, role5, 2); + induce(metarole4, role6, 2); + induce(metametarole1, metarole4, 2); + + List roles = Arrays + .asList(role1, role2, role3, role4, role5, role6, metarole1, metarole2, metarole3, metarole4, metametarole1); + +// for (ObjectType role : roles) { +// System.out.println(prismContext.xmlSerializer().serialize(role.asPrismObject())); +// } + repoAddObjects(roles, initResult); + recomputeAndRefreshObjects(roles, initTask, initResult); + displayObjectTypeCollection("objects", roles); + + } + + private void induce(RoleType source, RoleType target, int inducementOrder) { + AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); + if (inducementOrder > 1) { + inducement.setOrder(inducementOrder); + } + source.getInducement().add(inducement); + } + + private void assign(RoleType source, RoleType target) { + AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); + source.getAssignment().add(assignment); + } + + private RoleType createRole(int level, int number) { + String name = StringUtils.repeat('M', level-1) + "R" + number; + String oid = getRoleOid(name); + + RoleType role = new RoleType(prismContext); + role.setName(PolyStringType.fromOrig(name)); + role.setOid(oid); + return role; + } + + private String getRoleOid(String name) { + return "99999999-0000-0000-0000-" + StringUtils.repeat('0', 12-name.length()) + name; + } + + @Test + public void test002ModifyUser() throws Exception { + final String TEST_NAME = "test002ModifyUser"; + TestUtil.displayTestTile(this, TEST_NAME); + +// // GIVEN +// Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); +// OperationResult result = task.getResult(); +// +// LensContext context = createUserAccountContext(); +// fillContextWithUser(context, USER_BARBOSSA_OID, result); +// fillContextWithAccount(context, ACCOUNT_HBARBOSSA_DUMMY_OID, result); +// addModificationToContextReplaceUserProperty(context, UserType.F_LOCALITY, new PolyString("Tortuga")); +// context.recompute(); +// +// display("Input context", context); +// +// assertFocusModificationSanity(context); +// +// // WHEN +// assignmentProcessor.processAssignmentsProjections(context, getNow(), task, result); +// +// // THEN +// display("Output context", context); +// display("outbound processor result", result); +// // assertSuccess("Outbound processor failed (result)", result); +// +// assertTrue(context.getFocusContext().getPrimaryDelta().getChangeType() == ChangeType.MODIFY); +// assertNull("Unexpected user changes", context.getFocusContext().getSecondaryDelta()); +// assertFalse("No account changes", context.getProjectionContexts().isEmpty()); +// +// Collection accountContexts = context.getProjectionContexts(); +// assertEquals(1, accountContexts.size()); +// LensProjectionContext accContext = accountContexts.iterator().next(); +// assertNull(accContext.getPrimaryDelta()); +// +// ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); +// assertNull("Account secondary delta sneaked in", accountSecondaryDelta); +// +// assertNoDecision(accContext); +// assertLegal(accContext); +// +// assignmentProcessor.processAssignmentsAccountValues(accContext, result); +// +// PrismValueDeltaSetTriple> accountConstructionDeltaSetTriple = +// accContext.getConstructionDeltaSetTriple(); +// display("accountConstructionDeltaSetTriple", accountConstructionDeltaSetTriple); +// +// PrismAsserts.assertTripleNoMinus(accountConstructionDeltaSetTriple); +// PrismAsserts.assertTripleNoPlus(accountConstructionDeltaSetTriple); +// assertSetSize("zero", accountConstructionDeltaSetTriple.getZeroSet(), 2); +// +// Construction zeroAccountConstruction = getZeroAccountConstruction(accountConstructionDeltaSetTriple, "Brethren account construction"); +// +// assertNoZeroAttributeValues(zeroAccountConstruction, +// getDummyResourceController().getAttributeQName(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOCATION_NAME)); +// assertPlusAttributeValues(zeroAccountConstruction, +// getDummyResourceController().getAttributeQName(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOCATION_NAME), "Tortuga"); +// assertMinusAttributeValues(zeroAccountConstruction, +// getDummyResourceController().getAttributeQName(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOCATION_NAME), "Caribbean"); + + } + +} diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 4c28774c501..cd81e520b35 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -3952,4 +3952,20 @@ protected void resetTriggerTask(String taskOid, File taskFile, OperationResult r taskManager.resumeTasks(Collections.singleton(taskOid), result); } + protected void repoAddObjects(List objects, OperationResult result) + throws EncryptionException, ObjectAlreadyExistsException, SchemaException { + for (ObjectType object : objects) { + repoAddObject(object.asPrismObject(), result); + } + } + + protected void recomputeAndRefreshObjects(List objects, Task task, OperationResult result) + throws CommunicationException, ObjectNotFoundException, ObjectAlreadyExistsException, ConfigurationException, + SchemaException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException { + for (int i = 0; i < objects.size(); i++) { + ObjectType object = objects.get(i); + modelService.recompute(object.getClass(), object.getOid(), null, task, result); + objects.set(i, repositoryService.getObject(object.getClass(), object.getOid(), null, result).asObjectable()); + } + } } From f20d56297e00033019d72fe5f26c3f2b0dac6d0a Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Sat, 11 Mar 2017 00:33:07 +0100 Subject: [PATCH 2/6] AssignmentEvaluator: decreasing eval order for higher-order inducements. TestAssignmentProcessor2 membership refs are computed correctly. --- .../model/impl/lens/AssignmentEvaluator.java | 29 ++++++++++++------- .../impl/lens/AssignmentPathSegmentImpl.java | 19 +++++++----- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java index 274a04c959f..5c74eb79a75 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java @@ -698,15 +698,24 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, roleInducementIdi.recompute(); String subSourceDescription = targetType+" in "+segment.sourceDescription; AssignmentPathSegmentImpl subAssignmentPathSegment = new AssignmentPathSegmentImpl(targetType, subSourceDescription, roleInducementIdi, false); -// EvaluationOrder newEvaluationOrder = evaluationOrder; -// if (roleInducement.getOrder() != null && roleInducement.getOrder() > 1) { -// newEvaluationOrder = evaluationOrder.decrease(roleInducement.getOrder()-1); // TODO UGLY HACK -// } -// subAssignmentPathSegment.setEvaluationOrder(newEvaluationOrder); - subAssignmentPathSegment.setEvaluationOrder(evaluationOrder); + + boolean newIsMatchingOrder = AssignmentPathSegmentImpl.computeMatchingOrder( + subAssignmentPathSegment.getAssignment(), evaluationOrder, 0); + boolean newIsMatchingOrderPlusOne = AssignmentPathSegmentImpl.computeMatchingOrder( + subAssignmentPathSegment.getAssignment(), evaluationOrder, 1); + + EvaluationOrder newEvaluationOrder; + if (roleInducement.getOrder() != null && roleInducement.getOrder() > 1) { + newEvaluationOrder = evaluationOrder.decrease(roleInducement.getOrder()-1); // TODO what about relations? + } else { + newEvaluationOrder = evaluationOrder; + } + // TODO undefined if intervals + subAssignmentPathSegment.setEvaluationOrder(newEvaluationOrder, newIsMatchingOrder, newIsMatchingOrderPlusOne); + subAssignmentPathSegment.setOrderOneObject(orderOneObject); subAssignmentPathSegment.setPathToSourceValid(isValid); - subAssignmentPathSegment.setProcessMembership(subAssignmentPathSegment.isMatchingOrder()); + subAssignmentPathSegment.setProcessMembership(newIsMatchingOrder); // Originally we executed the following only if isMatchingOrder. However, sometimes we have to look even into // inducements with non-matching order: for example because we need to extract target-related policy rules @@ -715,9 +724,9 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, // We need to make sure NOT to extract anything other from such inducements. That's why we set e.g. // processMembership attribute to false for these inducements. if (LOGGER.isTraceEnabled()) { - LOGGER.trace("orig E({}): evaluate {} inducement({}) {} (new-disabled EO {})", + LOGGER.trace("orig EO({}): evaluate {} inducement({}) {}; new EO({})", evaluationOrder.shortDump(), targetType, FocusTypeUtil.dumpInducementConstraints(roleInducement), - FocusTypeUtil.dumpAssignment(roleInducement), evaluationOrder.shortDump()); + FocusTypeUtil.dumpAssignment(roleInducement), newEvaluationOrder.shortDump()); } assert !ctx.assignmentPath.isEmpty(); evaluateFromSegment(subAssignmentPathSegment, mode, ctx); @@ -738,7 +747,7 @@ private void evaluateSegmentTarget(AssignmentPathSegmentImpl segment, QName subrelation = getRelation(roleAssignment); EvaluationOrder newEvaluationOrder = evaluationOrder.advance(subrelation); if (LOGGER.isTraceEnabled()) { - LOGGER.trace("orig EO {}: follow assignment {} {} (new EO {})", + LOGGER.trace("orig EO({}): follow assignment {} {}; new EO({})", evaluationOrder.shortDump(), targetType, FocusTypeUtil.dumpAssignment(roleAssignment), newEvaluationOrder); } ItemDeltaItem,PrismContainerDefinition> roleAssignmentIdi = new ItemDeltaItem<>(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java index 5c86eb9ed3c..d46c0e0a8c9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java @@ -90,8 +90,6 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment { * resulting order as previous - (N-1), where N is the order of the inducement (unspecified means 1). For the latter * we consider the resulting order as undefined. * - * NOTE this is not consistent with the current implementation. - * * TODO relations * * Special consideration must be given when collecting target policy rules, i.e. rules that are attached to @@ -197,7 +195,13 @@ public EvaluationOrder getEvaluationOrder() { } public void setEvaluationOrder(EvaluationOrder evaluationOrder) { + setEvaluationOrder(evaluationOrder, null, null); + } + + public void setEvaluationOrder(EvaluationOrder evaluationOrder, Boolean matchingOrder, Boolean matchingOrderPlusOne) { this.evaluationOrder = evaluationOrder; + this.isMatchingOrder = matchingOrder; + this.isMatchingOrderPlusOne = matchingOrderPlusOne; } public ObjectType getOrderOneObject() { @@ -222,20 +226,19 @@ public void setProcessMembership(boolean processMembership) { */ public boolean isMatchingOrder() { if (isMatchingOrder == null) { - isMatchingOrder = computeMatchingOrder(0); + isMatchingOrder = computeMatchingOrder(getAssignment(), evaluationOrder, 0); } return isMatchingOrder; } public boolean isMatchingOrderPlusOne() { if (isMatchingOrderPlusOne == null) { - isMatchingOrderPlusOne = computeMatchingOrder(1); + isMatchingOrderPlusOne = computeMatchingOrder(getAssignment(), evaluationOrder, 1); } return isMatchingOrderPlusOne; } - private boolean computeMatchingOrder(int offset) { - AssignmentType assignmentType = getAssignment(); + static boolean computeMatchingOrder(AssignmentType assignmentType, EvaluationOrder evaluationOrder, int offset) { boolean rv; if (assignmentType.getOrder() == null && assignmentType.getOrderConstraint().isEmpty()) { // compatibility @@ -248,7 +251,7 @@ private boolean computeMatchingOrder(int offset) { } } for (OrderConstraintsType orderConstraint : assignmentType.getOrderConstraint()) { - if (!isMatchingConstraint(orderConstraint, offset)) { + if (!isMatchingConstraint(orderConstraint, evaluationOrder, offset)) { rv = false; break; } @@ -259,7 +262,7 @@ private boolean computeMatchingOrder(int offset) { return rv; } - private boolean isMatchingConstraint(OrderConstraintsType orderConstraint, int offset) { + private static boolean isMatchingConstraint(OrderConstraintsType orderConstraint, EvaluationOrder evaluationOrder, int offset) { int evaluationOrderInt = evaluationOrder.getMatchingRelationOrder(orderConstraint.getRelation()) - offset; if (orderConstraint.getOrder() != null) { return orderConstraint.getOrder() == evaluationOrderInt; From 789c3188652e97a2949b41170a870da8e43c3406 Mon Sep 17 00:00:00 2001 From: Viliam Repan Date: Sat, 11 Mar 2017 12:58:34 +0100 Subject: [PATCH 3/6] MID-3690 fixed url matching. Fixed redirect to previous page (unauthenticated) after login. --- .../util/DefaultPageParametersEncoder.java | 61 ------------------- .../web/util/ExactMatchMountedMapper.java | 15 ++++- 2 files changed, 14 insertions(+), 62 deletions(-) delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/DefaultPageParametersEncoder.java diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/DefaultPageParametersEncoder.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/DefaultPageParametersEncoder.java deleted file mode 100644 index fb6b9afe2cd..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/DefaultPageParametersEncoder.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2010-2013 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.web.util; - -import org.apache.wicket.request.Url; -import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; -import org.apache.wicket.request.mapper.parameter.PageParameters; - -import java.util.Iterator; - -/** - * @author lazyman - */ -public class DefaultPageParametersEncoder implements IPageParametersEncoder { - - /** - * Encodes URL like this: /mountpoint/paramName1/paramValue1/paramName2/paramValue2 - */ - @Override - public Url encodePageParameters(PageParameters pageParameters) { - Url url = new Url(); - - for (PageParameters.NamedPair pair : pageParameters.getAllNamed()) { - url.getSegments().add(pair.getKey()); - url.getSegments().add(pair.getValue()); - } - - return url; - } - - /** - * Decodes URL like this: /mountpoint/paramName1/paramValue1/paramName2/paramValue2 - */ - @Override - public PageParameters decodePageParameters(Url url) { - PageParameters parameters = new PageParameters(); - - for (Iterator segment = url.getSegments().iterator(); segment.hasNext(); ) { - String key = segment.next(); - String value = segment.next(); - - parameters.add(key, value); - } - - return parameters.isEmpty() ? null : parameters; - } -} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/ExactMatchMountedMapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/ExactMatchMountedMapper.java index 54cc56997c5..a1e5ef756b5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/ExactMatchMountedMapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/ExactMatchMountedMapper.java @@ -16,17 +16,22 @@ package com.evolveum.midpoint.web.util; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import org.apache.commons.lang3.StringUtils; import org.apache.wicket.core.request.mapper.MountedMapper; import org.apache.wicket.request.Url; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; +import org.apache.wicket.request.mapper.parameter.PageParametersEncoder; /** * Created by lazyman on 09/03/2017. */ public class ExactMatchMountedMapper extends MountedMapper { + private static final Trace LOG = TraceManager.getTrace(ExactMatchMountedMapper.class); + public ExactMatchMountedMapper(String mountPath, Class pageClass, IPageParametersEncoder pageParametersEncoder) { @@ -46,7 +51,15 @@ protected boolean urlStartsWithMountedSegments(Url url) { return false; } + if (!(pageParametersEncoder instanceof PageParametersEncoder)) { + LOG.trace("Matching using standard mounted mapper for '{}'", url); + return super.urlStartsWithMountedSegments(url); + } + String mountUrl = StringUtils.join(mountSegments, "/"); - return url.getPath().equals(mountUrl); + boolean matched = url.getPath().equals(mountUrl); + + LOG.trace("Matched: {} for '{}' with mount url '{}'", matched, url, mountUrl); + return matched; } } From 4cdfd17f809bd7da21cd64f3b508091a3d9ddf76 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Sun, 12 Mar 2017 01:04:00 +0100 Subject: [PATCH 4/6] Some more ideas on assignment evaluator + enhanced tests --- .../impl/lens/AssignmentPathSegmentImpl.java | 116 +++++-- .../impl/lens/TestAssignmentProcessor2.java | 292 +++++++++++++++--- .../test/AbstractModelIntegrationTest.java | 34 ++ 3 files changed, 377 insertions(+), 65 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java index d46c0e0a8c9..7e8684022a7 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java @@ -60,37 +60,110 @@ public class AssignmentPathSegmentImpl implements AssignmentPathSegment { // This is set to true on the first assignment in the chain. /** - * Constructions, focus mappings, and focus policy rules (or "assignment content") should be collected only - * from assignments that belong directly to the focus; i.e. not from assignments attached to roles, meta roles, - * meta-meta roles, etc. + * Assignments and inducements can carry constructions, focus mappings, and focus policy rules. + * We can call these "assignment/inducement payload", or "payload" for short. * - * Content belonging to roles should be collected if it's attached to them via inducements of order 1. - * Content belonging to meta-roles should be collected if it's attached to them via inducements of order 2. - * And so on. + * When looking at assignments/inducements in assignment path, payload of some assignments/inducements will be collected + * to focus, while payload from others will be not. How we know what to collect? * - * For each segment (i.e. assignment/inducement), we know we can use its content if its "isMatchingOrder" attribute is true. + * For assignments/inducements belonging directly to the focus, we take payload from all the assignments. Not from inducements. + * For assignments/inducements belonging to roles (assigned to focus), we take payload from all the inducements of order 1. + * For assignments/inducements belonging to meta-roles (assigned to roles), we take payload from all the inducements of order 2. + * And so on. (It is a bit more complicated, as described below when discussing relations. But OK for the moment.) * - * But how we compute this attribute? + * To know whether to collect payload from assignment/inducement, i.e. from assignment path segment, we + * define "isMatchingOrder" attribute - and collect only if value of this attribute is true. * - * Each segment in the assignment path has an evaluation order. First assignment has an evaluation order of 1, second + * How we compute this attribute? + * + * Each assignment path segment has an evaluation order. First assignment has an evaluation order of 1, second * assignment has an order of 2, etc. Order of a segment can be seen as the number of assignments segments in the path - * (including itself). And we collect content from assignment segments of order 1. + * (including itself). And, for "real" assignments, we collect content from assignment segments of order 1. * - * But what about inducements? There are two (somewhat related) questions: + * But what about inducements? There are two - somewhat related - questions: * * 1. How we compute isMatchingOrder for inducement segments? * 2. How we compute evaluation order for inducement segments? * * As for #1: To compute isMatchingOrder, we must take evaluation order of the _previous_ segment, and compare - * it with the inducement order (or, more generally, inducement order constraints). If they match, we say that inducement + * it with the order (or, more generally, order constraints) of the inducement. If they match, we say that inducement * has matching order. * - * As for #2: For some inducements we can determine resulting order, while for others we can not. Problematic inducements - * are those that do not have strict order, but an interval of orders instead. For the former we can compute the - * resulting order as previous - (N-1), where N is the order of the inducement (unspecified means 1). For the latter - * we consider the resulting order as undefined. + * As for #2: It is not usual that inducements have targets with another assignments, i.e. that evaluation continues + * after inducement segments. But it definitely could happen. We can look at it this way: inducement is something like + * a "shortcut" that creates an assignment where no assignment was before. E.g. if we have R1 -A-> MR1 -I-> MR2, + * the "newly created" assignment is R1 -A-> MR2. I.e. as if the " -A-> MR1 -I-> " part was just replaced by " -A-> ". + * If the inducement is of higher order, even more assignments are "cut out". From R1 -A-> MR1 -A-> MMR1 -(I2)-> MR2 + * we have R1 -A-> MR2, i.e. we cut out " -A-> MR1 -A-> MMR1 -(I2)-> " and replaced it by " -A-> ". + * So it looks like that when computing new evaluation order of an inducement, we have to "go back" few steps + * through the assignment path. + * TODO think this through, perhaps based on concrete examples + * It is almost certain that for some inducements we would not be able to determine the resulting order. + * Such problematic inducements are those that do not have strict order, but an interval of orders instead. + * + * Until no better algorithm is devised, we will do an approximation: when "traditional" inducement order is given, + * the we will compute the resulting order as "previous - (N-1)", where N is the order of the inducement + * (unspecified means 1). But beware, we will not manipulate evaluation order parts that are specific to relations. + * So, it is not safe to combine "higher-order inducements with targets" with non-scalar order constraints. + * + * Evaluating relations + * ==================== + * + * With the arrival of various kinds of relations (deputy, manager, approver, owner) things got a bit complicated. + * For instance, the deputy relation cannot be used to determine evaluation order in a usual way, because if + * we have this situation: + * + * Pirate -----I-----> Sailor + * A + * | (member) + * | + * jack + * A + * | (deputy) + * | + * barbossa + * + * we obviously want to have barbossa to obtain all payload from roles Pirate and Sailor: exactly as jack does. + * So, the evaluation order of " barbossa -A-> jack -A-> Pirate " should be 1, not two. So deputy is a very special + * kind of relation, that does _not_ increase the traditional evaluation order. But we really want to record + * the fact that the deputy is on the assignment path; therefore, besides traditional "scalar" evaluation order + * (called "summaryOrder") we maintain evaluation orders for each relation separately. In the above example, + * the evaluation orders would be: + * barbossa--->jack summary: 0, deputy: 1, member: 0 + * jack--->Pirate summary: 1, deputy: 1, member: 1 + * Pirate-I->Sailor summary: 1, deputy: 1, member: 1 (because the inducement has a default order of 1) + * + * When we determine matchingOrder for an inducement (question #1 above), we can ask about summary order, + * as well as about individual components (deputy and member, in this case). + * + * Actually, we have three categories of relations (see MID-3581): + * + * - membership relations: apply membership references, payload, authorizations, gui config + * - delegation relations: similar to membership, bud different handling of order + * - other relations: apply membership references but in limited way; payload, authorizations and gui config + * are - by default - not applied. * - * TODO relations + * Currently, membership relations are: null (i.e. member), manager, and meta. + * Delegation: deputy. + * Other: approver, owner, and custom ones. + * + * As for the "other" relations: they bring membership information, but limited to the target that is directly + * assigned. So if jack is an approver for role Landluber, his roleMembershipRef will contain ref to Landluber + * but not e.g. to role Earthworm that is induced by Landluber. In a similar way, payload from targets assigned + * by "other" relations is not collected. + * + * Both of this can be overridden by using specific orderConstraints on particular inducement. + * (TODO this is just an idea, not implemented yet) + * Set of order constraint is considered to match evaluation order with "other" relations, if for each such "other" + * relation it contains related constraint. So, if one explicitly wants an inducement to be applied when + * "approver" relation is encountered, he may do so. + * + * Note: authorizations and gui config information are not considered to be a payload, because they are not + * part of an assignment/inducement - they are part of a role. In the future we might move them into + * assignments/inducements. + * + * Collecting target policy rules + * ============================== * * Special consideration must be given when collecting target policy rules, i.e. rules that are attached to * assignment targets. Such rules are typically attached to roles that are being assigned. So let's consider this: @@ -328,15 +401,18 @@ public String toString() { if (isMatchingOrder()) { // here is a side effect but most probably it's harmless sb.append("(match)"); } - if (isMatchingOrderPlusOne()) { + if (isMatchingOrderPlusOne()) { // the same here sb.append("(match+1)"); } sb.append(": "); sb.append(source).append(" "); PrismContainer assignment = (PrismContainer) assignmentIdi.getAnyItem(); AssignmentType assignmentType = assignment != null ? assignment.getValue().asContainerable() : null; - if (assignmentType != null && assignmentType.getConstruction() != null) { - sb.append("Constr '").append(assignmentType.getConstruction().getDescription()).append("' "); + if (assignmentType != null) { + sb.append("id:").append(assignmentType.getId()).append(" "); + if (assignmentType.getConstruction() != null) { + sb.append("Constr '").append(assignmentType.getConstruction().getDescription()).append("' "); + } } ObjectReferenceType targetRef = assignmentType != null ? assignmentType.getTargetRef() : null; if (target != null || targetRef != null) { diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java index 1af5122cb56..467a807377f 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -16,23 +16,37 @@ package com.evolveum.midpoint.model.impl.lens; import com.evolveum.midpoint.common.Clock; +import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; import com.evolveum.midpoint.model.impl.lens.projector.AssignmentProcessor; +import com.evolveum.midpoint.prism.delta.PlusMinusZero; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ActivationUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.testng.annotations.Test; -import java.util.Arrays; -import java.util.List; +import javax.xml.bind.JAXBException; +import javax.xml.namespace.QName; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static com.evolveum.midpoint.test.IntegrationTestTools.displayObjectTypeCollection; +import static com.evolveum.midpoint.test.util.TestUtil.assertSuccess; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; /** * Comprehensive test of assignment evaluator and processor. @@ -45,7 +59,7 @@ * ^ MR2 --I---* | | | * | ^ I I I I * | | V V V V - * R1 --I--> R2 R3 R4 R5 R6 + * R1 --I--> R2 O3 R4 R5 R6 * ^ * | * | @@ -69,23 +83,29 @@ */ public class TestAssignmentProcessor2 extends AbstractLensTest { - @Autowired + private static final int CONSTRUCTION_LEVELS = 5; + private static final int FOCUS_MAPPING_LEVELS = 5; + private static final int POLICY_RULES_LEVELS = 5; + + @Autowired private AssignmentProcessor assignmentProcessor; @Autowired private Clock clock; - private RoleType role1, role2, role3, role4, role5, role6; + private RoleType role1, role2, role4, role5, role6; + private OrgType org3; private RoleType metarole1, metarole2, metarole3, metarole4; private RoleType metametarole1; + private static final String R1_OID = getRoleOid("R1"); - @Override + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); role1 = createRole(1, 1); role2 = createRole(1, 2); - role3 = createRole(1, 3); + org3 = createOrg(3); role4 = createRole(1, 4); role5 = createRole(1, 5); role6 = createRole(1, 6); @@ -100,13 +120,13 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti induce(role1, role2, 1); induce(metarole1, metarole3, 1); induce(metarole1, role4, 2); - induce(metarole2, role3, 2); + induce(metarole2, org3, 2); induce(metarole3, role5, 2); induce(metarole4, role6, 2); induce(metametarole1, metarole4, 2); List roles = Arrays - .asList(role1, role2, role3, role4, role5, role6, metarole1, metarole2, metarole3, metarole4, metametarole1); + .asList(role1, role2, org3, role4, role5, role6, metarole1, metarole2, metarole3, metarole4, metametarole1); // for (ObjectType role : roles) { // System.out.println(prismContext.xmlSerializer().serialize(role.asPrismObject())); @@ -117,7 +137,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti } - private void induce(RoleType source, RoleType target, int inducementOrder) { + private void induce(AbstractRoleType source, AbstractRoleType target, int inducementOrder) { AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); if (inducementOrder > 1) { inducement.setOrder(inducementOrder); @@ -131,47 +151,116 @@ private void assign(RoleType source, RoleType target) { } private RoleType createRole(int level, int number) { - String name = StringUtils.repeat('M', level-1) + "R" + number; + return prepareAbstractRole(new RoleType(prismContext), level, number, "R"); + } + + private OrgType createOrg(int number) { + return prepareAbstractRole(new OrgType(prismContext), 1, number, "O"); + } + + private R prepareAbstractRole(R abstractRole, int level, int number, String nameSymbol) { + String name = StringUtils.repeat('M', level-1) + nameSymbol + number; String oid = getRoleOid(name); - RoleType role = new RoleType(prismContext); - role.setName(PolyStringType.fromOrig(name)); - role.setOid(oid); - return role; + abstractRole.setName(PolyStringType.fromOrig(name)); + abstractRole.setOid(oid); + + // constructions + for (int i = 0; i <= CONSTRUCTION_LEVELS; i++) { + ConstructionType c = new ConstructionType(prismContext); + c.setDescription(name + "-" + i); + c.setResourceRef(ObjectTypeUtil.createObjectRef(RESOURCE_DUMMY_OID, ObjectTypes.RESOURCE)); + AssignmentType a = new AssignmentType(prismContext); + a.setDescription("Assignment for " + c.getDescription()); + a.setConstruction(c); + addAssignmentOrInducement(abstractRole, i, a); + } + + // focus mappings + for (int i = 0; i <= FOCUS_MAPPING_LEVELS; i++) { + MappingType mapping = new MappingType(); + mapping.setName(name + "-" + i); + VariableBindingDefinitionType source = new VariableBindingDefinitionType(); + source.setPath(new ItemPath(UserType.F_NAME).asItemPathType()); + mapping.getSource().add(source); + VariableBindingDefinitionType target = new VariableBindingDefinitionType(); + target.setPath(new ItemPath(UserType.F_DESCRIPTION).asItemPathType()); + mapping.setTarget(target); + MappingsType mappings = new MappingsType(prismContext); + mappings.getMapping().add(mapping); + AssignmentType a = new AssignmentType(prismContext); + a.setFocusMappings(mappings); + addAssignmentOrInducement(abstractRole, i, a); + } + + // policy rules + for (int i = 0; i <= POLICY_RULES_LEVELS; i++) { + PolicyRuleType rule = new PolicyRuleType(prismContext); + rule.setName(name + "-" + i); + AssignmentType a = new AssignmentType(prismContext); + a.setPolicyRule(rule); + addAssignmentOrInducement(abstractRole, i, a); + } + + return abstractRole; + } + + private void addAssignmentOrInducement(R abstractRole, int order, AssignmentType assignment) { + if (order == 0) { + abstractRole.getAssignment().add(assignment); + } else { + assignment.setOrder(order); + abstractRole.getInducement().add(assignment); + } } - private String getRoleOid(String name) { + private static String getRoleOid(String name) { return "99999999-0000-0000-0000-" + StringUtils.repeat('0', 12-name.length()) + name; } @Test - public void test002ModifyUser() throws Exception { - final String TEST_NAME = "test002ModifyUser"; + public void test010AssignR1ToJack() throws Exception { + final String TEST_NAME = "test010AssignR1ToJack"; TestUtil.displayTestTile(this, TEST_NAME); -// // GIVEN -// Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); -// OperationResult result = task.getResult(); -// -// LensContext context = createUserAccountContext(); -// fillContextWithUser(context, USER_BARBOSSA_OID, result); -// fillContextWithAccount(context, ACCOUNT_HBARBOSSA_DUMMY_OID, result); -// addModificationToContextReplaceUserProperty(context, UserType.F_LOCALITY, new PolyString("Tortuga")); -// context.recompute(); -// -// display("Input context", context); -// -// assertFocusModificationSanity(context); -// -// // WHEN -// assignmentProcessor.processAssignmentsProjections(context, getNow(), task, result); -// -// // THEN -// display("Output context", context); -// display("outbound processor result", result); -// // assertSuccess("Outbound processor failed (result)", result); -// -// assertTrue(context.getFocusContext().getPrimaryDelta().getChangeType() == ChangeType.MODIFY); + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + LensContext context = createContextForRoleAssignment(USER_JACK_OID, R1_OID, null, null, result); + + // WHEN + assignmentProcessor.processAssignmentsProjections(context, clock.currentTimeXMLGregorianCalendar(), task, result); + + // THEN + display("Output context", context); + display("Evaluated assignment triple", context.getEvaluatedAssignmentTriple()); + + result.computeStatus(); + assertSuccess("Assignment processor failed (result)", result); + + Collection evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0); + EvaluatedAssignmentImpl evaluatedAssignment = evaluatedAssignments.iterator().next(); + assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid()); + + assertPrismRefValues("membershipRef", evaluatedAssignment.getMembershipRefVals(), role1, role2, org3, role4, role5, role6); + assertPrismRefValues("orgRef", evaluatedAssignment.getOrgRefVals(), org3); + assertPrismRefValues("delegationRef", evaluatedAssignment.getDelegationRefVals(), new String[0]); + + // Constructions are named "role-level". We expect e.g. that from R1 we get a construction induced with order=1 (R1-1). + List expectedItems = Arrays.asList("R1-1", "R2-1", "O3-1", "R4-1", "R5-1", "R6-1", "MR1-2", "MR2-2", "MR3-2", "MR4-2", "MMR1-3"); + assertConstructions(evaluatedAssignment, expectedItems,null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, expectedItems); + assertFocusPolicyRules(evaluatedAssignment, expectedItems); + // TODO why R4-0 R5-0 R6-0 ? Sounds not good: when we are adding R1 assignment, we are not interested + // in approval rules residing in induced roles, even if they are induced through a higher levels + // (in the same way as we are not interested in R2-0 and R3-0) + // MR3-1 MR4-1 seems to be OK; these are induced in a quite intuitive way (via MR1) + String expectedThisTargetRules = "R1-0 R4-0 R5-0 R6-0 MR1-1 MR3-1 MR4-1 MMR1-2"; + String expectedTargetRules = expectedThisTargetRules + " R2-0 O3-0 MR2-1"; + assertTargetPolicyRules(evaluatedAssignment, getList(expectedTargetRules), getList(expectedThisTargetRules)); + + // assertTrue(context.getFocusContext().getPrimaryDelta().getChangeType() == ChangeType.MODIFY); // assertNull("Unexpected user changes", context.getFocusContext().getSecondaryDelta()); // assertFalse("No account changes", context.getProjectionContexts().isEmpty()); // @@ -185,7 +274,7 @@ public void test002ModifyUser() throws Exception { // // assertNoDecision(accContext); // assertLegal(accContext); -// + // assignmentProcessor.processAssignmentsAccountValues(accContext, result); // // PrismValueDeltaSetTriple> accountConstructionDeltaSetTriple = @@ -207,4 +296,117 @@ public void test002ModifyUser() throws Exception { } + private List getList(String text) { + return Arrays.asList(StringUtils.split(text)); + } + + private void assertFocusMappings(EvaluatedAssignmentImpl evaluatedAssignment, Collection expectedItems) { + expectedItems = CollectionUtils.emptyIfNull(expectedItems); + assertEquals("Wrong # of focus mappings", expectedItems.size(), evaluatedAssignment.getFocusMappings().size()); + assertEquals("Wrong focus mappings", new HashSet<>(expectedItems), + evaluatedAssignment.getFocusMappings().stream().map(m -> m.getMappingType().getName()).collect(Collectors.toSet())); + // TODO look at the content of the mappings (e.g. zero, plus, minus sets) + } + + private void assertFocusPolicyRules(EvaluatedAssignmentImpl evaluatedAssignment, Collection expectedItems) { + expectedItems = CollectionUtils.emptyIfNull(expectedItems); + assertEquals("Wrong # of focus policy rules", expectedItems.size(), evaluatedAssignment.getFocusPolicyRules().size()); + assertEquals("Wrong focus policy rules", new HashSet<>(expectedItems), + evaluatedAssignment.getFocusPolicyRules().stream().map(r -> r.getName()).collect(Collectors.toSet())); + } + + private void assertTargetPolicyRules(EvaluatedAssignmentImpl evaluatedAssignment, Collection expectedTargetItems, Collection expectedThisTargetItems) { + expectedTargetItems = CollectionUtils.emptyIfNull(expectedTargetItems); + expectedThisTargetItems = CollectionUtils.emptyIfNull(expectedThisTargetItems); + assertEquals("Wrong # of target policy rules", expectedTargetItems.size(), evaluatedAssignment.getTargetPolicyRules().size()); + assertEquals("Wrong # of this target policy rules", expectedThisTargetItems.size(), evaluatedAssignment.getThisTargetPolicyRules().size()); + assertEquals("Wrong target policy rules", new HashSet<>(expectedTargetItems), + evaluatedAssignment.getTargetPolicyRules().stream().map(r -> r.getName()).collect(Collectors.toSet())); + assertEquals("Wrong this target policy rules", new HashSet<>(expectedThisTargetItems), + evaluatedAssignment.getThisTargetPolicyRules().stream().map(r -> r.getName()).collect(Collectors.toSet())); + + // testing (strange) condition on thisTarget vs target policy rules + outer: for (EvaluatedPolicyRule localRule : evaluatedAssignment.getThisTargetPolicyRules()) { + for (EvaluatedPolicyRule rule : evaluatedAssignment.getTargetPolicyRules()) { + if (rule == localRule) { + continue outer; + } + } + fail("This target rule " + localRule + " is not among target rules: " + evaluatedAssignment.getTargetPolicyRules()); + } + } + + private void assertConstructions(EvaluatedAssignmentImpl evaluatedAssignment, + List zeroValid, List zeroInvalid, + List plusValid, List plusInvalid, + List minusValid, List minusInvalid) { + assertConstructions("zero", evaluatedAssignment.getConstructionSet(PlusMinusZero.ZERO), zeroValid, zeroInvalid); + assertConstructions("plus", evaluatedAssignment.getConstructionSet(PlusMinusZero.PLUS), plusValid, plusInvalid); + assertConstructions("minus", evaluatedAssignment.getConstructionSet(PlusMinusZero.MINUS), minusValid, minusInvalid); + } + + private void assertConstructions(String type, Collection> constructions, List valid0, + List invalid0) { + constructions = CollectionUtils.emptyIfNull(constructions); + Collection expectedValid = CollectionUtils.emptyIfNull(valid0); + Collection expectedInvalid = CollectionUtils.emptyIfNull(invalid0); + Collection> realValid = constructions.stream().filter(c -> c.isValid()).collect(Collectors.toList()); + Collection> realInvalid = constructions.stream().filter(c -> !c.isValid()).collect(Collectors.toList()); + assertEquals("Wrong # of valid constructions in " + type + " set", expectedValid.size(), realValid.size()); + assertEquals("Wrong # of invalid constructions in " + type + " set", expectedInvalid.size(), realInvalid.size()); + assertEquals("Wrong valid constructions in " + type + " set", new HashSet<>(expectedValid), + realValid.stream().map(c -> c.getDescription()).collect(Collectors.toSet())); + assertEquals("Wrong invalid constructions in " + type + " set", new HashSet<>(expectedInvalid), + realInvalid.stream().map(c -> c.getDescription()).collect(Collectors.toSet())); + } + + private Collection assertAssignmentTripleSetSize(LensContext context, int zero, int plus, int minus) { + assertEquals("Wrong size of assignment triple zero set", zero, CollectionUtils.size(context.getEvaluatedAssignmentTriple().getZeroSet())); + assertEquals("Wrong size of assignment triple plus set", plus, CollectionUtils.size(context.getEvaluatedAssignmentTriple().getPlusSet())); + assertEquals("Wrong size of assignment triple minus set", minus, CollectionUtils.size(context.getEvaluatedAssignmentTriple().getMinusSet())); + return context.getEvaluatedAssignmentTriple().getAllValues(); + } + + @Test(enabled = false) + public void test020AssignR1ToJackProjectorDisabled() throws Exception { + final String TEST_NAME = "test020AssignR1ToJackProjectorDisabled"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + LensContext context = createContextForRoleAssignment(USER_JACK_OID, R1_OID, null, + a -> a.setActivation(ActivationUtil.createDisabled()), result); + + // WHEN + projector.project(context, "", task, result); + + // THEN + display("Output context", context); + + result.computeStatus(); + assertSuccess("Projector failed (result)", result); + + // MID-3679 + assertEquals("Wrong # of parentOrgRef entries", 0, + context.getFocusContext().getObjectNew().asObjectable().getParentOrgRef().size()); + assertEquals("Wrong # of roleMembershipRef entries", 0, + context.getFocusContext().getObjectNew().asObjectable().getRoleMembershipRef().size()); + } + + @NotNull + private LensContext createContextForRoleAssignment(String userOid, String roleOid, QName relation, + Consumer modificationBlock, OperationResult result) + throws SchemaException, ObjectNotFoundException, JAXBException { + LensContext context = createUserAccountContext(); + fillContextWithUser(context, userOid, result); + addFocusDeltaToContext(context, createAssignmentUserDelta(USER_JACK_OID, roleOid, RoleType.COMPLEX_TYPE, relation, + modificationBlock, true)); + context.recompute(); + display("Input context", context); + assertFocusModificationSanity(context); + return context; + } + } diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index cd81e520b35..7a06bf0a73e 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -1579,6 +1579,40 @@ protected static void assertAssignedOrgs(PrismObject us MidPointAsserts.assertAssignedOrgs(user, orgOids); } + protected void assertObjectRefs(String contextDesc, Collection real, ObjectType... expected) { + assertObjectRefs(contextDesc, real, objectsToOids(expected)); + } + + protected void assertPrismRefValues(String contextDesc, Collection real, ObjectType... expected) { + assertPrismRefValues(contextDesc, real, objectsToOids(expected)); + } + + protected void assertObjectRefs(String contextDesc, Collection real, String... expected) { + List refOids = new ArrayList<>(); + for (ObjectReferenceType ref: real) { + refOids.add(ref.getOid()); + assertNotNull("Missing type in "+ref.getOid()+" in "+contextDesc, ref.getType()); + assertNotNull("Missing name in "+ref.getOid()+" in "+contextDesc, ref.getTargetName()); + } + PrismAsserts.assertSets("Wrong values in "+contextDesc, refOids, expected); + } + + protected void assertPrismRefValues(String contextDesc, Collection real, String... expected) { + List refOids = new ArrayList<>(); + for (PrismReferenceValue ref: real) { + refOids.add(ref.getOid()); + assertNotNull("Missing type in "+ref.getOid()+" in "+contextDesc, ref.getTargetType()); + assertNotNull("Missing name in "+ref.getOid()+" in "+contextDesc, ref.getTargetName()); + } + PrismAsserts.assertSets("Wrong values in "+contextDesc, refOids, expected); + } + + private String[] objectsToOids(ObjectType[] objects) { + return Arrays.stream(objects) + .map(o -> o.getOid()) + .toArray(String[]::new); + } + protected void assertRoleMembershipRef(PrismObject focus, String... roleOids) { List refOids = new ArrayList(); for (ObjectReferenceType ref: focus.asObjectable().getRoleMembershipRef()) { From e795de4928bc18b1bee43cb0167704f1906e33d5 Mon Sep 17 00:00:00 2001 From: honchar Date: Sun, 12 Mar 2017 02:48:49 +0100 Subject: [PATCH 5/6] export data to csv --- .../api/component/MainObjectListPanel.html | 1 - .../api/component/MainObjectListPanel.java | 36 ++++++++++--------- .../gui/api/component/ObjectListPanel.java | 11 +++++- .../web/component/data/BoxedTablePanel.java | 16 +++++++++ .../data/column/ObjectNameColumn.java | 13 +++++-- .../web/page/admin/users/PageUsers.java | 11 +++++- 6 files changed, 67 insertions(+), 21 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html index 576e43d1010..db6b203f509 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html @@ -24,6 +24,5 @@ - diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java index 514d0817a67..3c9463c501f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java @@ -15,15 +15,27 @@ */ package com.evolveum.midpoint.gui.api.component; +import java.io.FileOutputStream; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import com.evolveum.midpoint.web.component.AjaxIconButton; +import com.evolveum.midpoint.web.component.util.Selectable; import org.apache.commons.lang.StringUtils; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.CSVDataExporter; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.ExportToolbar; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IDataExporter; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IExportableColumn; import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.markup.html.panel.Fragment; +import org.apache.wicket.markup.repeater.data.IDataProvider; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -36,6 +48,8 @@ import com.evolveum.midpoint.web.page.admin.configuration.PageImportObject; import com.evolveum.midpoint.web.session.UserProfileStorage.TableId; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.apache.wicket.request.resource.ResourceStreamResource; +import org.apache.wicket.util.resource.IResourceStream; /** * @author katkav @@ -46,7 +60,6 @@ public abstract class MainObjectListPanel extends ObjectLi private static final String ID_REFRESH = "refresh"; private static final String ID_NEW_OBJECT = "newObject"; private static final String ID_IMPORT_OBJECT = "importObject"; - private static final String ID_EXPORT_DATA = "exportData"; private static final String ID_BUTTON_BAR = "buttonBar"; public MainObjectListPanel(String id, Class type, TableId tableId, Collection> options, PageBase parentPage) { @@ -102,9 +115,6 @@ protected boolean isClickable(IModel> rowModel) { protected abstract void newObjectPerformed(AjaxRequestTarget target); - protected void exportData(AjaxRequestTarget target){ - - } @Override protected WebMarkupContainer createTableButtonToolbar(String id) { @@ -161,17 +171,11 @@ public void onClick(AjaxRequestTarget target) { }; add(importObject); - AjaxIconButton exportData = new AjaxIconButton(ID_EXPORT_DATA, new Model<>("fa fa-download"), - mainObjectListPanel.createStringResource("MainObjectListPanel.export")) { - - private static final long serialVersionUID = 1L; - - @Override - public void onClick(AjaxRequestTarget target) { - mainObjectListPanel.exportData(target); - } - }; - add(exportData); } } -} + + @Override + protected boolean getExportToolbarVisibility(){ + return true; + } + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java index e2ca93bc557..6466130e6bc 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java @@ -194,6 +194,11 @@ protected WebMarkupContainer createButtonToolbar(String id) { return bar != null ? bar : super.createButtonToolbar(id); } + + @Override + protected boolean getExportToolbarVisibility(){ + return ObjectListPanel.this.getExportToolbarVisibility(); + } }; table.setOutputMarkupId(true); String storageKey = getStorageKey(); @@ -373,7 +378,7 @@ private PageStorage getPageStorage(String storageKey){ } @SuppressWarnings("unchecked") - private BaseSortableDataProvider> getDataProvider() { + protected BaseSortableDataProvider> getDataProvider() { BoxedTablePanel> table = getTable(); BaseSortableDataProvider> provider = (BaseSortableDataProvider>) table .getDataTable().getDataProvider(); @@ -526,4 +531,8 @@ private String getItemDisplayName(GuiObjectColumnType column){ return parentPage.getPrismContext().getSchemaRegistry() .findObjectDefinitionByCompileTimeClass(type).findItemDefinition(column.getPath().getItemPath()).getDisplayName(); } + + protected boolean getExportToolbarVisibility(){ + return false; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java index 486d3dd598c..be0f804b33e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java @@ -18,12 +18,15 @@ import java.util.List; +import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.CSVDataExporter; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.ExportToolbar; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.navigation.paging.IPageable; @@ -108,6 +111,15 @@ protected Item newRowItem(String id, int index, IModel rowModel) { } }; table.setOutputMarkupId(true); + ExportToolbar exportToolbar = new ExportToolbar(table).addDataExporter(new CSVDataExporter()); + exportToolbar.add(new VisibleEnableBehaviour(){ + @Override + public boolean isVisible(){ + return super.isVisible() && getExportToolbarVisibility(); + } + }); + table.addBottomToolbar(exportToolbar); + tableContainer.add(table); box.add(tableContainer); @@ -324,4 +336,8 @@ private String createCountString(IPageable pageable) { .getString(); } } + + protected boolean getExportToolbarVisibility(){ + return false; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ObjectNameColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ObjectNameColumn.java index b51dd5b0f15..5995e0049bf 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ObjectNameColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/column/ObjectNameColumn.java @@ -19,6 +19,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IExportableColumn; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.AbstractReadOnlyModel; @@ -32,12 +33,14 @@ import com.evolveum.midpoint.web.page.admin.server.dto.OperationResultStatusPresentationProperties; import com.evolveum.midpoint.web.page.error.PageOperationResult; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.apache.wicket.model.Model; /** * @author semancik * */ -public class ObjectNameColumn extends AbstractColumn, String> { +public class ObjectNameColumn extends AbstractColumn, String> + implements IExportableColumn, String>{ private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(ObjectNameColumn.class); @@ -79,7 +82,7 @@ public String getObject() { } - } + } }; if (isClickable(rowModel)) { // beware: rowModel is very probably resolved at this moment; but it seems to cause no problems @@ -114,4 +117,10 @@ public boolean isClickable(IModel> rowModel) { public void onClick(AjaxRequestTarget target, IModel> rowModel) { } + public IModel getDataModel(IModel> rowModel) { + SelectableBean selectableBean = rowModel.getObject(); + O value = selectableBean.getValue(); + return Model.of(value == null ? "" : WebComponentUtil.getName(value)); + } + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUsers.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUsers.java index 15402d751c6..57ff524438d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUsers.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUsers.java @@ -40,6 +40,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.repeater.Item; @@ -253,7 +254,7 @@ private List, String>> initColumns() { SelectableBean.F_VALUE + ".emailAddress"); columns.add(column); - column = new AbstractColumn, String>( + column = new AbstractExportableColumn, String>( createStringResource("pageUsers.accounts")) { @Override @@ -263,6 +264,14 @@ public void populateItem(Item>> cellItem model.getObject().getValue() != null ? model.getObject().getValue().getLinkRef().size() : null)); } + + @Override + public IModel getDataModel(IModel> rowModel) { + return Model.of(rowModel.getObject().getValue() != null ? + Integer.toString(rowModel.getObject().getValue().getLinkRef().size()) : ""); + } + + }; columns.add(column); From a8837dc5de74de4cd63ead7ce44455a1b493d6d4 Mon Sep 17 00:00:00 2001 From: honchar Date: Mon, 13 Mar 2017 01:25:38 +0100 Subject: [PATCH 6/6] some improvements for csv import --- .../api/component/MainObjectListPanel.html | 1 + .../api/component/MainObjectListPanel.java | 52 +++++++++------- .../gui/api/component/ObjectListPanel.java | 8 --- .../web/component/data/BoxedTablePanel.java | 13 ---- .../component/AbstractRoleMemberPanel.java | 62 ++++++++++++------- .../localization/Midpoint.properties | 1 + 6 files changed, 73 insertions(+), 64 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html index db6b203f509..576e43d1010 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.html @@ -24,5 +24,6 @@ + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java index 3c9463c501f..945d5fdb16b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/MainObjectListPanel.java @@ -33,6 +33,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IDataExporter; import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IExportableColumn; import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.data.IDataProvider; @@ -48,6 +49,7 @@ import com.evolveum.midpoint.web.page.admin.configuration.PageImportObject; import com.evolveum.midpoint.web.session.UserProfileStorage.TableId; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.apache.wicket.model.ResourceModel; import org.apache.wicket.request.resource.ResourceStreamResource; import org.apache.wicket.util.resource.IResourceStream; @@ -60,6 +62,7 @@ public abstract class MainObjectListPanel extends ObjectLi private static final String ID_REFRESH = "refresh"; private static final String ID_NEW_OBJECT = "newObject"; private static final String ID_IMPORT_OBJECT = "importObject"; + private static final String ID_EXPORT_DATA = "exportData"; private static final String ID_BUTTON_BAR = "buttonBar"; public MainObjectListPanel(String id, Class type, TableId tableId, Collection> options, PageBase parentPage) { @@ -107,11 +110,11 @@ public boolean isClickable(IModel> rowModel) { } } - protected boolean isClickable(IModel> rowModel) { - return true; - } + protected boolean isClickable(IModel> rowModel) { + return true; + } - protected abstract void objectDetailsPerformed(AjaxRequestTarget target, O object); + protected abstract void objectDetailsPerformed(AjaxRequestTarget target, O object); protected abstract void newObjectPerformed(AjaxRequestTarget target); @@ -123,9 +126,9 @@ protected WebMarkupContainer createTableButtonToolbar(String id) { private static class ButtonBar extends Fragment { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public ButtonBar(String id, String markupId, MainObjectListPanel markupProvider) { + public ButtonBar(String id, String markupId, MainObjectListPanel markupProvider) { super(id, markupId, markupProvider); initLayout(markupProvider); @@ -135,13 +138,13 @@ private void initLayout(final MainObjectListPanel main AjaxIconButton refreshIcon = new AjaxIconButton(ID_REFRESH, new Model<>("fa fa-refresh"), mainObjectListPanel.createStringResource("MainObjectListPanel.refresh")) { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @Override + @Override public void onClick(AjaxRequestTarget target) { - mainObjectListPanel.clearCache(); - mainObjectListPanel.refreshTable((Class) mainObjectListPanel.getType(), target); - + mainObjectListPanel.clearCache(); + mainObjectListPanel.refreshTable((Class) mainObjectListPanel.getType(), target); + target.add((Component) mainObjectListPanel.getTable()); } }; @@ -150,8 +153,8 @@ public void onClick(AjaxRequestTarget target) { AjaxIconButton newObjectIcon = new AjaxIconButton(ID_NEW_OBJECT, new Model<>("fa fa-plus"), mainObjectListPanel.createStringResource("MainObjectListPanel.newObject")) { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = 1L; + @Override public void onClick(AjaxRequestTarget target) { mainObjectListPanel.newObjectPerformed(target); @@ -162,8 +165,8 @@ public void onClick(AjaxRequestTarget target) { AjaxIconButton importObject = new AjaxIconButton(ID_IMPORT_OBJECT, new Model<>("fa fa-upload"), mainObjectListPanel.createStringResource("MainObjectListPanel.import")) { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = 1L; + @Override public void onClick(AjaxRequestTarget target) { ((PageBase) getPage()).navigateToNext(PageImportObject.class); @@ -171,11 +174,18 @@ public void onClick(AjaxRequestTarget target) { }; add(importObject); - } - } + String fileName = mainObjectListPanel.getType().getSimpleName() + + "_" + mainObjectListPanel.createStringResource("MainObjectListPanel.exportFileName").getString(); + CSVDataExporter csvDataExporter = new CSVDataExporter(); + ResourceStreamResource resource = (new ResourceStreamResource() { + protected IResourceStream getResourceStream() { + return new ExportToolbar.DataExportResourceStreamWriter(csvDataExporter, mainObjectListPanel.getTable().getDataTable()); + } + }).setFileName(fileName + "." + csvDataExporter.getFileNameExtension()); + AbstractLink exportDataLink = (new ResourceLink(ID_EXPORT_DATA, resource)).setBody(csvDataExporter.getDataFormatNameModel()); - @Override - protected boolean getExportToolbarVisibility(){ - return true; - } + add(exportDataLink); + + } } +} \ No newline at end of file diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java index 6466130e6bc..9e53573e882 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/ObjectListPanel.java @@ -195,10 +195,6 @@ protected WebMarkupContainer createButtonToolbar(String id) { return bar != null ? bar : super.createButtonToolbar(id); } - @Override - protected boolean getExportToolbarVisibility(){ - return ObjectListPanel.this.getExportToolbarVisibility(); - } }; table.setOutputMarkupId(true); String storageKey = getStorageKey(); @@ -531,8 +527,4 @@ private String getItemDisplayName(GuiObjectColumnType column){ return parentPage.getPrismContext().getSchemaRegistry() .findObjectDefinitionByCompileTimeClass(type).findItemDefinition(column.getPath().getItemPath()).getDisplayName(); } - - protected boolean getExportToolbarVisibility(){ - return false; - } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java index be0f804b33e..a71b3e19502 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/BoxedTablePanel.java @@ -111,15 +111,6 @@ protected Item newRowItem(String id, int index, IModel rowModel) { } }; table.setOutputMarkupId(true); - ExportToolbar exportToolbar = new ExportToolbar(table).addDataExporter(new CSVDataExporter()); - exportToolbar.add(new VisibleEnableBehaviour(){ - @Override - public boolean isVisible(){ - return super.isVisible() && getExportToolbarVisibility(); - } - }); - table.addBottomToolbar(exportToolbar); - tableContainer.add(table); box.add(tableContainer); @@ -336,8 +327,4 @@ private String createCountString(IPageable pageable) { .getString(); } } - - protected boolean getExportToolbarVisibility(){ - return false; - } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/component/AbstractRoleMemberPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/component/AbstractRoleMemberPanel.java index 6352bd5273b..7ab7f5a0253 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/component/AbstractRoleMemberPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/component/AbstractRoleMemberPanel.java @@ -10,6 +10,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; @@ -54,6 +55,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import org.apache.wicket.model.Model; public abstract class AbstractRoleMemberPanel extends BasePanel { @@ -400,7 +402,7 @@ protected void detailsPerformed(AjaxRequestTarget target, ObjectType object) { private List, String>> createMembersColumns() { List, String>> columns = new ArrayList<>(); - IColumn, String> column = new AbstractColumn, String>( + IColumn, String> column = new AbstractExportableColumn, String>( createStringResource("TreeTablePanel.fullName.displayName")) { private static final long serialVersionUID = 1L; @@ -409,22 +411,19 @@ public void populateItem(Item>> cellIt String componentId, IModel> rowModel) { SelectableBean bean = rowModel.getObject(); ObjectType object = bean.getValue(); - if (object instanceof UserType) { - cellItem.add(new Label(componentId, - WebComponentUtil.getOrigStringFromPoly(((UserType) object).getFullName()))); - } else if (object instanceof AbstractRoleType) { - cellItem.add(new Label(componentId, WebComponentUtil - .getOrigStringFromPoly(((AbstractRoleType) object).getDisplayName()))); - } else { - cellItem.add(new Label(componentId, "")); - } + cellItem.add(new Label(componentId, + getMemberObjectDisplayName(object))); + } + @Override + public IModel getDataModel(IModel> rowModel) { + return Model.of(getMemberObjectDisplayName(rowModel.getObject().getValue())); } }; columns.add(column); - column = new AbstractColumn, String>( + column = new AbstractExportableColumn, String>( createStringResource("TreeTablePanel.identifier.description")) { private static final long serialVersionUID = 1L; @@ -433,14 +432,12 @@ public void populateItem(Item>> cellIt String componentId, IModel> rowModel) { SelectableBean bean = rowModel.getObject(); ObjectType object = bean.getValue(); - if (object instanceof UserType) { - cellItem.add(new Label(componentId, ((UserType) object).getEmailAddress())); - } else if (object instanceof AbstractRoleType) { - cellItem.add(new Label(componentId, ((AbstractRoleType) object).getIdentifier())); - } else { - cellItem.add(new Label(componentId, object.getDescription())); - } + cellItem.add(new Label(componentId, getMemberObjectIdentifier(object))); + } + @Override + public IModel getDataModel(IModel> rowModel) { + return Model.of(getMemberObjectIdentifier(rowModel.getObject().getValue())); } }; @@ -471,9 +468,30 @@ protected String getTaskName(String operation, QueryScope scope) { return getTaskName(operation, scope, false); } -// public MainObjectListPanel getMemberTable() { -// return (MainObjectListPanel) get( -// createComponentPath(ID_FORM, ID_CONTAINER_MEMBER, ID_MEMBER_TABLE)); -// } + private String getMemberObjectDisplayName(ObjectType object){ + if (object == null){ + return ""; + } + if (object instanceof UserType) { + return WebComponentUtil.getOrigStringFromPoly(((UserType) object).getFullName()); + } else if (object instanceof AbstractRoleType) { + return WebComponentUtil + .getOrigStringFromPoly(((AbstractRoleType) object).getDisplayName()); + } else { + return ""; + } + } + private String getMemberObjectIdentifier(ObjectType object){ + if (object == null){ + return ""; + } + if (object instanceof UserType) { + return ((UserType) object).getEmailAddress(); + } else if (object instanceof AbstractRoleType) { + return ((AbstractRoleType) object).getIdentifier(); + } else { + return object.getDescription(); + } + } } diff --git a/gui/admin-gui/src/main/resources/localization/Midpoint.properties b/gui/admin-gui/src/main/resources/localization/Midpoint.properties index 2b9eed43678..7ad495a92ee 100644 --- a/gui/admin-gui/src/main/resources/localization/Midpoint.properties +++ b/gui/admin-gui/src/main/resources/localization/Midpoint.properties @@ -3158,6 +3158,7 @@ MainObjectListPanel.refresh=Refresh MainObjectListPanel.newObject=New MainObjectListPanel.import=Import MainObjectListPanel.export=Export +MainObjectListPanel.exportFileName=export TreeTablePanel.menu.createMember=Create member TreeTablePanel.menu.createManager=Create manager TreeTablePanel.menu.addMembers=Assign members