rv = item.createDelta(item.getPath());
+ rv.addValueToAdd((V) CloneUtil.clone(value));
+ return rv;
+ }
+
}
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
index 27101dac28e..80eb9c9614b 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
@@ -26,11 +26,14 @@
import javax.xml.namespace.QName;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
-import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MultiValuedMap;
+import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
@@ -38,6 +41,8 @@
import java.util.*;
import java.util.stream.Collectors;
+import static org.apache.commons.collections4.CollectionUtils.emptyIfNull;
+
/**
* Relative difference (delta) of the object.
*
@@ -56,6 +61,8 @@
*/
public class ObjectDelta implements DebugDumpable, Visitable, PathVisitable, Serializable {
+ private static final Trace LOGGER = TraceManager.getTrace(ObjectDelta.class);
+
private static final long serialVersionUID = -528560467958335366L;
private ChangeType changeType;
@@ -1617,6 +1624,173 @@ public ObjectDelta subtract(@NotNull Collection paths) {
return rv;
}
+ public static class FactorOutResult {
+ public final ObjectDelta remainder;
+ public final List> offsprings = new ArrayList<>();
+
+ public FactorOutResult(ObjectDelta remainder) {
+ this.remainder = remainder;
+ }
+ }
+
+ public FactorOutResult factorOut(Collection paths, boolean cloneDelta) {
+ if (isAdd()) {
+ return factorOutForAddDelta(paths, cloneDelta);
+ } else if (isDelete()) {
+ throw new UnsupportedOperationException("factorOut is not supported for delete deltas");
+ } else {
+ return factorOutForModifyDelta(paths, cloneDelta);
+ }
+ }
+
+ public FactorOutResult factorOutValues(ItemPath path, boolean cloneDelta) throws SchemaException {
+ if (isAdd()) {
+ return factorOutValuesForAddDelta(path, cloneDelta);
+ } else if (isDelete()) {
+ throw new UnsupportedOperationException("factorOut is not supported for delete deltas");
+ } else {
+ return factorOutValuesForModifyDelta(path, cloneDelta);
+ }
+ }
+
+ /**
+ * Works if we are looking e.g. for modification to inducement item,
+ * and delta contains e.g. REPLACE(inducement[1]/validTo, "...").
+ *
+ * Does NOT work the way around: if we are looking for modification to inducement/validTo and
+ * delta contains e.g. ADD(inducement, ...). In such a case we would need to do more complex processing,
+ * involving splitting value-to-be-added into remainder and offspring delta. It's probably doable,
+ * but some conditions would have to be met, e.g. inducement to be added must have an ID.
+ */
+ private FactorOutResult factorOutForModifyDelta(Collection paths, boolean cloneDelta) {
+ ObjectDelta remainder = cloneIfRequested(cloneDelta);
+ FactorOutResult rv = new FactorOutResult<>(remainder);
+ List> modificationsFound = new ArrayList<>();
+ for (Iterator extends ItemDelta, ?>> iterator = remainder.modifications.iterator(); iterator.hasNext(); ) {
+ ItemDelta, ?> modification = iterator.next();
+ if (ItemPath.containsSubpathOrEquivalent(paths, modification.getPath())) {
+ modificationsFound.add(modification);
+ iterator.remove();
+ }
+ }
+ if (!modificationsFound.isEmpty()) {
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ modificationsFound.forEach(offspring::addModification);
+ rv.offsprings.add(offspring);
+ }
+ return rv;
+ }
+
+ private FactorOutResult factorOutForAddDelta(Collection paths, boolean cloneDelta) {
+ List- > itemsFound = new ArrayList<>();
+ for (ItemPath path : paths) {
+ Item, ?> item = objectToAdd.findItem(path);
+ if (item != null && !item.isEmpty()) {
+ itemsFound.add(item);
+ }
+ }
+ if (itemsFound.isEmpty()) {
+ return new FactorOutResult<>(this);
+ }
+ ObjectDelta remainder = cloneIfRequested(cloneDelta);
+ FactorOutResult rv = new FactorOutResult<>(remainder);
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ for (Item, ?> item : itemsFound) {
+ remainder.getObjectToAdd().remove(item);
+ offspring.addModification(ItemDelta.createAddDeltaFor(item));
+ }
+ rv.offsprings.add(offspring);
+ return rv;
+ }
+
+ private ObjectDelta cloneIfRequested(boolean cloneDelta) {
+ return cloneDelta ? clone() : this;
+ }
+
+ /**
+ * Works if we are looking e.g. for modification to inducement item,
+ * and delta contains e.g. REPLACE(inducement[1]/validTo, "...").
+ *
+ * Does NOT work the way around: if we are looking for modification to inducement/validTo and
+ * delta contains e.g. ADD(inducement, ...). In such a case we would need to do more complex processing,
+ * involving splitting value-to-be-added into remainder and offspring delta. It's probably doable,
+ * but some conditions would have to be met, e.g. inducement to be added must have an ID.
+ */
+ private FactorOutResult factorOutValuesForModifyDelta(ItemPath path, boolean cloneDelta) throws SchemaException {
+ ObjectDelta remainder = cloneIfRequested(cloneDelta);
+ FactorOutResult rv = new FactorOutResult<>(remainder);
+
+ MultiValuedMap> modificationsForId = new ArrayListValuedHashMap<>();
+ PrismObjectDefinition objDef = objectTypeClass != null ? prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectTypeClass) : null;
+ ItemDefinition itemDef = objDef != null ? objDef.findItemDefinition(path) : null;
+ Boolean isSingle = itemDef != null ? itemDef.isSingleValue() : null;
+ if (isSingle == null) {
+ LOGGER.warn("Couldn't find definition for {}:{}", objectTypeClass, path);
+ isSingle = false;
+ }
+ for (Iterator extends ItemDelta, ?>> iterator = remainder.modifications.iterator(); iterator.hasNext(); ) {
+ ItemDelta, ?> modification = iterator.next();
+ if (path.equivalent(modification.getPath())) {
+ if (modification.isReplace()) {
+ throw new UnsupportedOperationException("Cannot factor out values for replace item delta. Path = "
+ + path + ", modification = " + modification);
+ }
+ for (PrismValue prismValue : emptyIfNull(modification.getValuesToAdd())) {
+ //noinspection unchecked
+ createNewDelta(rv, modification).addValueToAdd(prismValue.clone());
+ }
+ for (PrismValue prismValue : emptyIfNull(modification.getValuesToDelete())) {
+ //noinspection unchecked
+ createNewDelta(rv, modification).addValueToDelete(prismValue.clone());
+ }
+ iterator.remove();
+ } else if (path.isSubPath(modification.getPath())) {
+ // e.g. factoring inducement, having REPLACE(inducement[x]/activation/validTo, ...) or ADD(inducement[x]/activation)
+ ItemPath remainingPath = modification.getPath().remainder(path);
+ Long id = remainingPath.getFirstIdSegment() != null ? remainingPath.getFirstIdSegment().getId() : null;
+ modificationsForId.put(id, modification);
+ iterator.remove();
+ }
+ }
+ if (Boolean.TRUE.equals(isSingle)) {
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ modificationsForId.values().forEach(mod -> offspring.addModification(mod));
+ rv.offsprings.add(offspring);
+ } else {
+ for (Long id : modificationsForId.keySet()) {
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ modificationsForId.get(id).forEach(mod -> offspring.addModification(mod));
+ rv.offsprings.add(offspring);
+ }
+ }
+ return rv;
+ }
+
+ private ItemDelta createNewDelta(FactorOutResult rv, ItemDelta, ?> modification)
+ throws SchemaException {
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ ItemDelta delta = modification.getDefinition().instantiate().createDelta(modification.getPath());
+ offspring.addModification(delta);
+ rv.offsprings.add(offspring);
+ return delta;
+ }
+
+ private FactorOutResult factorOutValuesForAddDelta(ItemPath path, boolean cloneDelta) {
+ Item, ?> item = objectToAdd.findItem(path);
+ if (item == null || item.isEmpty()) {
+ return new FactorOutResult<>(this);
+ }
+ ObjectDelta remainder = cloneIfRequested(cloneDelta);
+ remainder.getObjectToAdd().remove(item);
+ FactorOutResult rv = new FactorOutResult<>(remainder);
+ for (PrismValue value : item.getValues()) {
+ ObjectDelta offspring = new ObjectDelta<>(objectTypeClass, ChangeType.MODIFY, prismContext);
+ offspring.addModification(ItemDelta.createAddDeltaFor(item, value));
+ rv.offsprings.add(offspring);
+ }
+ return rv;
+ }
+
/**
* Checks if the delta tries to add (or set) a 'value' for the item identified by 'itemPath'. If yes, it removes it.
*
@@ -1648,8 +1822,8 @@ public static boolean subtractFromModifications(Collection extends ItemDelta
if (!fromMinusSet) {
if (dryRun) {
wasPresent = wasPresent
- || CollectionUtils.emptyIfNull(itemDelta.getValuesToAdd()).contains(value)
- || CollectionUtils.emptyIfNull(itemDelta.getValuesToReplace()).contains(value);
+ || emptyIfNull(itemDelta.getValuesToAdd()).contains(value)
+ || emptyIfNull(itemDelta.getValuesToReplace()).contains(value);
} else {
boolean removed1 = itemDelta.removeValueToAdd(value);
boolean removed2 = itemDelta.removeValueToReplace(value);
@@ -1660,7 +1834,7 @@ public static boolean subtractFromModifications(Collection extends ItemDelta
throw new UnsupportedOperationException("Couldn't subtract 'value to be deleted' from REPLACE itemDelta: " + itemDelta);
}
if (dryRun) {
- wasPresent = wasPresent || CollectionUtils.emptyIfNull(itemDelta.getValuesToDelete()).contains(value);
+ wasPresent = wasPresent || emptyIfNull(itemDelta.getValuesToDelete()).contains(value);
} else {
wasPresent = wasPresent || itemDelta.removeValueToDelete(value);
}
@@ -1743,4 +1917,19 @@ public List getDeletedValuesFor(ItemPath itemPath) {
}
}
}
+
+ public void clear() {
+ if (isAdd()) {
+ setObjectToAdd(null);
+ } else if (isModify()) {
+ modifications.clear();
+ } else if (isDelete()) {
+ // hack: convert to empty ADD delta
+ setChangeType(ChangeType.ADD);
+ setObjectToAdd(null);
+ setOid(null);
+ } else {
+ throw new IllegalStateException("Unsupported delta type: " + getChangeType());
+ }
+ }
}
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
index 0522eefa0ca..beaf6038635 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
@@ -703,18 +703,11 @@ public static QName getName(ItemPathSegment segment) {
}
public static IdItemPathSegment getFirstIdSegment(ItemPath itemPath) {
- ItemPathSegment first = itemPath.first();
- if (first instanceof IdItemPathSegment) {
- return (IdItemPathSegment)first;
- }
- return null;
+ return itemPath != null ? itemPath.getFirstIdSegment() : null;
}
public static NameItemPathSegment getFirstNameSegment(ItemPath itemPath) {
- if (itemPath == null) {
- return null;
- }
- return itemPath.getFirstNameSegment();
+ return itemPath != null ? itemPath.getFirstNameSegment() : null;
}
public NameItemPathSegment getFirstNameSegment() {
@@ -728,6 +721,15 @@ public NameItemPathSegment getFirstNameSegment() {
return null;
}
+ public IdItemPathSegment getFirstIdSegment() {
+ ItemPathSegment first = first();
+ if (first instanceof IdItemPathSegment) {
+ return (IdItemPathSegment)first;
+ } else {
+ return null;
+ }
+ }
+
public static QName getFirstName(ItemPath itemPath) {
if (itemPath == null) {
return null;
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/ExistsFilter.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/ExistsFilter.java
index 4efe3baf77c..d843609f615 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/ExistsFilter.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/ExistsFilter.java
@@ -86,8 +86,23 @@ public ExistsFilter cloneEmpty() {
@Override
public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException {
- throw new UnsupportedOperationException();
- }
+ Item itemToFind = value.findItem(fullPath);
+ if (itemToFind == null || itemToFind.getValues().isEmpty()) {
+ return false;
+ }
+ if (!(itemToFind instanceof PrismContainer)) {
+ throw new SchemaException("Couldn't use exists query to search for items other than containers: " + itemToFind);
+ }
+ if (filter == null) {
+ return true;
+ }
+ for (PrismContainerValue> pcv : ((PrismContainer>) itemToFind).getValues()) {
+ if (filter.match(pcv, matchingRuleRegistry)) {
+ return true;
+ }
+ }
+ return false;
+ }
@Override
public void checkConsistence(boolean requireDefinitions) {
diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemPathType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemPathType.java
index 21bee537ea6..a93953c64fc 100644
--- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemPathType.java
+++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemPathType.java
@@ -30,6 +30,8 @@
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
/**
*
@@ -166,4 +168,8 @@ public static ItemPathType asItemPathType(Object value) {
throw new IllegalArgumentException("Value " + value + " is neither ItemPath nor ItemPathType.");
}
}
+
+ public static List toItemPathList(List list) {
+ return list.stream().map(pt -> pt.getItemPath()).collect(Collectors.toList());
+ }
}
diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/query/TestObjectQuery.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/query/TestObjectQuery.java
index c12e4759c8e..492ae17f246 100644
--- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/query/TestObjectQuery.java
+++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/query/TestObjectQuery.java
@@ -19,6 +19,7 @@
import com.evolveum.midpoint.prism.PrismInternalTestUtil;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismPropertyDefinitionImpl;
+import com.evolveum.midpoint.prism.foo.AssignmentType;
import com.evolveum.midpoint.prism.foo.UserType;
import com.evolveum.midpoint.prism.match.MatchingRuleRegistry;
import com.evolveum.midpoint.prism.match.MatchingRuleRegistryFactory;
@@ -38,6 +39,7 @@
import java.io.IOException;
import static com.evolveum.midpoint.prism.PrismInternalTestUtil.DEFAULT_NAMESPACE_PREFIX;
+import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext;
public class TestObjectQuery {
@@ -53,9 +55,9 @@ public void setupDebug() throws SchemaException, SAXException, IOException {
@Test
public void testMatchAndFilter() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
ObjectFilter filter =
- QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_GIVEN_NAME).eq("Jack").matchingCaseIgnore()
.and().item(UserType.F_FULL_NAME).contains("arr")
.buildFilter();
@@ -66,8 +68,8 @@ public void testMatchAndFilter() throws Exception{
@Test
public void testMatchOrFilter() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_GIVEN_NAME).eq("Jack")
.or().item(UserType.F_GIVEN_NAME).eq("Jackie")
.buildFilter();
@@ -77,8 +79,8 @@ public void testMatchOrFilter() throws Exception{
@Test
public void testDontMatchEqualFilter() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_GIVEN_NAME).eq("Jackie")
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -87,9 +89,9 @@ public void testDontMatchEqualFilter() throws Exception{
@Test
public void testMatchEqualMultivalue() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
- PrismPropertyDefinitionImpl def = new PrismPropertyDefinitionImpl(new QName("indexedString"), DOMUtil.XSD_STRING, PrismTestUtil.getPrismContext());
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismPropertyDefinitionImpl def = new PrismPropertyDefinitionImpl(new QName("indexedString"), DOMUtil.XSD_STRING, getPrismContext());
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(new ItemPath(UserType.F_EXTENSION, "indexedString"), def).eq("alpha")
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -98,9 +100,9 @@ public void testMatchEqualMultivalue() throws Exception{
@Test
public void testMatchEqualNonEmptyAgainstEmptyItem() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
// jack has no locality
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_LOCALITY).eq("some")
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -109,9 +111,9 @@ public void testMatchEqualNonEmptyAgainstEmptyItem() throws Exception{
@Test
public void testMatchEqualEmptyAgainstEmptyItem() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
// jack has no locality
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_LOCALITY).isNull()
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -120,9 +122,9 @@ public void testMatchEqualEmptyAgainstEmptyItem() throws Exception{
@Test
public void testMatchEqualEmptyAgainstNonEmptyItem() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
// jack has no locality
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_NAME).isNull()
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -131,11 +133,11 @@ public void testMatchEqualEmptyAgainstNonEmptyItem() throws Exception{
@Test
public void testComplexMatch() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
// System.out.println("user given name" + user.asObjectable().getGivenName());
System.out.println("definition: " +user.findItem(UserType.F_FAMILY_NAME).getDefinition().debugDump());
ObjectFilter filter =
- QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_FAMILY_NAME).eq("Sparrow")
.and().item(UserType.F_FULL_NAME).contains("arr")
.and()
@@ -150,9 +152,9 @@ public void testComplexMatch() throws Exception{
@Test
public void testPolystringMatchEqualFilter() throws Exception{
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
PolyString name = new PolyString("jack", "jack");
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_NAME).eq(name)
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
@@ -161,13 +163,56 @@ public void testPolystringMatchEqualFilter() throws Exception{
@Test // MID-4120
public void testMatchSubstringAgainstEmptyItem() throws Exception {
- PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
// jack has no locality
- ObjectFilter filter = QueryBuilder.queryFor(UserType.class, PrismTestUtil.getPrismContext())
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
.item(UserType.F_LOCALITY).startsWith("C")
.buildFilter();
boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
AssertJUnit.assertFalse("filter matches object, but it should not", match);
}
+ @Test // MID-4173
+ public void testExistsNegative() throws Exception {
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
+ .exists(UserType.F_ASSIGNMENT)
+ .item(AssignmentType.F_DESCRIPTION).eq("Assignment NONE")
+ .buildFilter();
+ boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
+ AssertJUnit.assertFalse("filter matches object, but it should not", match);
+ }
+
+ @Test // MID-4173
+ public void testExistsPositive() throws Exception {
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
+ .exists(UserType.F_ASSIGNMENT)
+ .item(AssignmentType.F_DESCRIPTION).eq("Assignment 2")
+ .buildFilter();
+ boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
+ AssertJUnit.assertTrue("filter does not match object, but it should", match);
+ }
+
+ @Test // MID-4173
+ public void testExistsAnyNegative() throws Exception {
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ user.removeContainer(UserType.F_ASSIGNMENT);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
+ .exists(UserType.F_ASSIGNMENT)
+ .buildFilter();
+ boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
+ AssertJUnit.assertFalse("filter matches object, but it should not", match);
+ }
+
+ @Test // MID-4173
+ public void testExistsAnyPositive() throws Exception {
+ PrismObject user = PrismTestUtil.parseObject(PrismInternalTestUtil.USER_JACK_FILE_XML);
+ ObjectFilter filter = QueryBuilder.queryFor(UserType.class, getPrismContext())
+ .exists(UserType.F_ASSIGNMENT)
+ .buildFilter();
+ boolean match = ObjectQuery.match(user, filter, matchingRuleRegistry);
+ AssertJUnit.assertTrue("filter does not match object, but it should", match);
+ }
+
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/ObjectTreeDeltas.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/ObjectTreeDeltas.java
index 13ae778e813..deb7d09e6a9 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/ObjectTreeDeltas.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/ObjectTreeDeltas.java
@@ -23,10 +23,7 @@
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTreeDeltasType;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.ProjectionObjectDeltaType;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
@@ -38,9 +35,9 @@
*
* @author mederly
*/
-public class ObjectTreeDeltas implements DebugDumpable {
+public class ObjectTreeDeltas implements DebugDumpable {
- private ObjectDelta focusChange;
+ private ObjectDelta focusChange;
private Map> projectionChangeMap = new HashMap<>(); // values are non null here
private PrismContext prismContext;
@@ -48,12 +45,12 @@ public ObjectTreeDeltas(PrismContext prismContext) {
this.prismContext = prismContext;
}
- public ObjectTreeDeltas(ObjectDelta focusChange, PrismContext prismContext) {
+ public ObjectTreeDeltas(ObjectDelta focusChange, PrismContext prismContext) {
this.focusChange = focusChange;
this.prismContext = prismContext;
}
- public ObjectDelta getFocusChange() {
+ public ObjectDelta getFocusChange() {
return focusChange;
}
@@ -65,7 +62,7 @@ public Map> getProjectionCh
return projectionChangeMap;
}
- public void setFocusChange(ObjectDelta focusChange) {
+ public void setFocusChange(ObjectDelta focusChange) {
this.focusChange = focusChange;
}
@@ -105,8 +102,8 @@ public static boolean isEmpty(ObjectTreeDeltasType deltas) {
return true;
}
- public ObjectTreeDeltas clone() {
- ObjectTreeDeltas clone = new ObjectTreeDeltas<>(prismContext);
+ public ObjectTreeDeltas clone() {
+ ObjectTreeDeltas clone = new ObjectTreeDeltas<>(prismContext);
if (focusChange != null) {
clone.setFocusChange(focusChange.clone());
}
@@ -220,7 +217,7 @@ public String debugDump(int indent) {
return sb.toString();
}
- public void merge(ObjectTreeDeltas deltasToMerge) throws SchemaException {
+ public void merge(ObjectTreeDeltas deltasToMerge) throws SchemaException {
if (deltasToMerge == null) {
return;
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SchemaDebugUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SchemaDebugUtil.java
index 39eb96a8cf8..27dbe156f3a 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SchemaDebugUtil.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SchemaDebugUtil.java
@@ -50,7 +50,9 @@
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.PropertyReferenceListType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CachingMetadataType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstExpressionEvaluatorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
@@ -318,6 +320,59 @@ public static String prettyPrint(ResourceAttributeDefinitionType vc) {
sb.append(")");
return sb.toString();
}
+
+ public static String prettyPrint(ExpressionType expressionType) {
+ if (expressionType == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder("ExpressionType(");
+ appendPropertyIfNotNull(sb, "description", expressionType.getDescription());
+ appendPropertyIfNotNull(sb, "extension", expressionType.getExtension());
+ appendPropertyIfNotNull(sb, "trace", expressionType.isTrace());
+ appendPropertyIfNotNull(sb, "variable", expressionType.getVariable());
+ appendPropertyIfNotNull(sb, "returnMultiplicity", expressionType.getReturnMultiplicity());
+ appendPropertyIfNotNull(sb, "allowEmptyValues", expressionType.isAllowEmptyValues());
+ appendPropertyIfNotNull(sb, "queryInterpretationOfNoValue", expressionType.getQueryInterpretationOfNoValue());
+ appendPropertyIfNotNull(sb, "runAsRef", expressionType.getRunAsRef());
+ List> expressionEvaluators = expressionType.getExpressionEvaluator();
+ sb.append("evaluator").append("=");
+ if (expressionEvaluators.isEmpty()) {
+ sb.append("[]");
+ } else {
+ if (expressionEvaluators.size() > 1) {
+ sb.append("[");
+ }
+ for (JAXBElement> expressionEvaluator : expressionEvaluators) {
+ sb.append(expressionEvaluator.getName().getLocalPart());
+ sb.append(":");
+ sb.append(PrettyPrinter.prettyPrint(expressionEvaluator.getValue()));
+ if (expressionEvaluators.size() > 1) {
+ sb.append(", ");
+ }
+ }
+ if (expressionEvaluators.size() > 1) {
+ sb.append("]");
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ public static String prettyPrint(ConstExpressionEvaluatorType expressionType) {
+ if (expressionType == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder("ConstExpressionEvaluatorType(");
+ sb.append(expressionType.getValue());
+ sb.append(")");
+ return sb.toString();
+ }
+
+ private static void appendPropertyIfNotNull(StringBuilder sb, String propName, Object value) {
+ if (value != null) {
+ sb.append(propName).append("=").append(value).append(",");
+ }
+ }
public static String prettyPrint(CachingMetadataType cachingMetadata) {
if (cachingMetadata == null) {
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 e6f6d19fef8..ec183cbcc60 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
@@ -9507,6 +9507,20 @@
+
+
+
+ If set to false (defaul) the specification will apply only to active relations
+ (e.g active delegations). If set to true then the specificaiton will also be applied
+ to inactive relations (e.g. expired delegations).
+ Only partially implemented. Works only for some cases (delegator).
+ EXPERIMENTAL. Use at your own risk.
+
+
+ 3.6.1
+
+
+
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd
index e90e0ff0231..35920ab76b8 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd
@@ -602,6 +602,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1140,7 +1152,19 @@
-
+
+
+
+ TODO
+ EXPERIMENTAL
+
+
+ 3.7
+ true
+
+
+
+
@@ -1319,6 +1343,100 @@
+
+
+
+ Specification of a process that is to be started.
+ EXPERIMENTAL
+
+
+
+ 3.7
+ true
+
+
+
+
+
+
+
+ Reference to existing process specification. NOT IMPLEMENTED YET
+
+
+
+
+
+
+
+ Name for this process specification. NOT IMPLEMENTED YET
+
+
+
+
+
+
+ In what order this process specification is to be considered.
+
+
+
+
+
+
+ TODO
+
+
+
+
+
+
+ What approval actions to include during approval schema creation for this process. NOT IMPLEMENTED YET
+
+
+
+
+
+
+ What approval actions to exclude during approval schema creation for this process. NOT IMPLEMENTED YET
+
+
+
+
+
+
+
+
+
+
+
+ TODO
+ EXPERIMENTAL
+
+
+
+ 3.7
+ true
+
+
+
+
+
+
+
+ Include all modifications of these items as a delta that is to be approved.
+
+
+
+
+
+
+ Create a delta for each value of this item that is to be added, deleted or modified.
+
+
+
+
+
+
+
diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestSchemaDelta.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestSchemaDelta.java
index bb506565e5a..5c0e79f5b8a 100644
--- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestSchemaDelta.java
+++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestSchemaDelta.java
@@ -22,6 +22,7 @@
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
+import com.evolveum.midpoint.util.DebugUtil;
import org.testng.annotations.Test;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
@@ -40,6 +41,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext;
+import static java.util.Arrays.asList;
+import static java.util.Collections.singleton;
import static org.testng.AssertJUnit.*;
/**
@@ -386,4 +389,145 @@ public void testSubtractAssignmentFromModifyDelta() throws Exception {
assertTrue("Remaining delta is not a MODIFY delta", delta.isModify());
assertEquals("Wrong # of remaining modifications", 0, delta.getModifications().size());
}
+
+ // subtract of single-valued PCV from multivalued one
+ @Test
+ public void testFactorAddDeltaForItem() throws Exception {
+ final String TEST_NAME = "testFactorAddDeltaForItem";
+ displayTestTile(TEST_NAME);
+
+ // GIVEN
+ PrismObject user = PrismTestUtil.parseObject(USER_BILL_FILE);
+ ObjectDelta addDelta = ObjectDelta.createAddDelta(user);
+
+ // WHEN
+ ObjectDelta.FactorOutResult out = addDelta.factorOut(singleton(new ItemPath(UserType.F_ASSIGNMENT)), true);
+
+ // THEN
+ System.out.println("Delta before factorOut:\n" + addDelta.debugDump() + "\n");
+ System.out.println("Delta after factorOut:\n" + out.remainder.debugDump() + "\n");
+ System.out.println("Offspring deltas:\n" + DebugUtil.debugDump(out.offsprings) + "\n");
+
+ assertTrue("Remaining delta is not an ADD delta", out.remainder.isAdd());
+ assertEquals("Wrong # of remaining assignments", 0, out.remainder.getObjectToAdd().asObjectable().getAssignment().size());
+ assertEquals("Wrong # of offspring deltas", 1, out.offsprings.size());
+ assertEquals("Wrong # of modifications in offspring", 1, out.offsprings.get(0).getModifications().size());
+ assertEquals("Wrong # of assignments to add", 3, out.offsprings.get(0).getModifications().iterator().next().getValuesToAdd().size());
+ }
+
+ // subtract of single-valued PCV from multivalued one
+ @Test
+ public void testFactorAddDeltaForItems() throws Exception {
+ final String TEST_NAME = "testFactorAddDeltaForItems";
+ displayTestTile(TEST_NAME);
+
+ // GIVEN
+ PrismObject user = PrismTestUtil.parseObject(USER_BILL_FILE);
+ ObjectDelta addDelta = ObjectDelta.createAddDelta(user);
+
+ // WHEN
+ ObjectDelta.FactorOutResult out = addDelta.factorOut(asList(new ItemPath(UserType.F_GIVEN_NAME), new ItemPath(UserType.F_FAMILY_NAME)), true);
+
+ // THEN
+ System.out.println("Delta before factorOut:\n" + addDelta.debugDump() + "\n");
+ System.out.println("Delta after factorOut:\n" + out.remainder.debugDump() + "\n");
+ System.out.println("Offspring deltas:\n" + DebugUtil.debugDump(out.offsprings) + "\n");
+
+ assertTrue("Remaining delta is not an ADD delta", out.remainder.isAdd());
+ assertEquals("Wrong # of remaining assignments", 3, out.remainder.getObjectToAdd().asObjectable().getAssignment().size());
+ assertEquals("Wrong # of offspring deltas", 1, out.offsprings.size());
+ assertEquals("Wrong # of modifications in offspring", 2, out.offsprings.get(0).getModifications().size());
+ }
+
+ @Test
+ public void testFactorAddDeltaForItemValues() throws Exception {
+ final String TEST_NAME = "testFactorAddDeltaForItemValues";
+ displayTestTile(TEST_NAME);
+
+ // GIVEN
+ PrismObject user = PrismTestUtil.parseObject(USER_BILL_FILE);
+ ObjectDelta addDelta = ObjectDelta.createAddDelta(user);
+
+ // WHEN
+ ObjectDelta.FactorOutResult out = addDelta.factorOutValues(new ItemPath(UserType.F_ASSIGNMENT), true);
+
+ // THEN
+ System.out.println("Delta before factorOut:\n" + addDelta.debugDump() + "\n");
+ System.out.println("Delta after factorOut:\n" + out.remainder.debugDump() + "\n");
+ System.out.println("Offspring deltas:\n" + DebugUtil.debugDump(out.offsprings) + "\n");
+
+ assertTrue("Remaining delta is not an ADD delta", out.remainder.isAdd());
+ assertEquals("Wrong # of remaining assignments", 0, out.remainder.getObjectToAdd().asObjectable().getAssignment().size());
+ assertEquals("Wrong # of offspring deltas", 3, out.offsprings.size());
+ for (ObjectDelta offspring : out.offsprings) {
+ assertEquals("Wrong # of modifications in offspring", 1, offspring.getModifications().size());
+ assertEquals("Wrong # of assignments to add", 1, offspring.getModifications().iterator().next().getValuesToAdd().size());
+ }
+ }
+
+ @Test
+ public void testFactorModifyDeltaForItem() throws Exception {
+ final String TEST_NAME = "testFactorModifyDeltaForItem";
+ displayTestTile(TEST_NAME);
+
+ // GIVEN
+ ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, getPrismContext())
+ .item(UserType.F_ASSIGNMENT)
+ .add(ObjectTypeUtil.createAssignmentTo("oid-r", ObjectTypes.ROLE, getPrismContext()))
+ .delete(new AssignmentType().id(101L), new AssignmentType().id(102L))
+ .item(UserType.F_ASSIGNMENT, 100L, AssignmentType.F_LIFECYCLE_STATE).replace("draft")
+ .item(UserType.F_GIVEN_NAME).replace("bill")
+ .asObjectDeltaCast("oid1");
+
+ // WHEN
+ ObjectDelta.FactorOutResult out = delta.factorOut(singleton(new ItemPath(UserType.F_ASSIGNMENT)), true);
+
+ // THEN
+ System.out.println("Delta before operation:\n" + delta.debugDump() + "\n");
+ System.out.println("Delta after factorOut:\n" + out.remainder.debugDump() + "\n");
+ System.out.println("Offspring deltas:\n" + DebugUtil.debugDump(out.offsprings) + "\n");
+
+ assertTrue("Remaining delta is not a MODIFY delta", out.remainder.isModify());
+ assertEquals("Wrong # of remaining modifications", 1, out.remainder.getModifications().size());
+ assertEquals("Wrong # of offspring deltas", 1, out.offsprings.size());
+ assertEquals("Wrong # of modifications in offspring 0", 2, out.offsprings.get(0).getModifications().size());
+ }
+
+ @Test
+ public void testFactorModifyDeltaForItemValues() throws Exception {
+ final String TEST_NAME = "testFactorModifyDeltaForItemValues";
+ displayTestTile(TEST_NAME);
+
+ // GIVEN
+ ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, getPrismContext())
+ .item(UserType.F_ASSIGNMENT)
+ .add(ObjectTypeUtil.createAssignmentTo("oid-r", ObjectTypes.ROLE, getPrismContext()))
+ .delete(new AssignmentType().id(101L), new AssignmentType().id(102L))
+ .item(UserType.F_ASSIGNMENT, 100L, AssignmentType.F_LIFECYCLE_STATE).replace("draft")
+ .item(UserType.F_ASSIGNMENT, 100L, AssignmentType.F_DESCRIPTION).replace("descr")
+ .item(UserType.F_ASSIGNMENT, 77L, AssignmentType.F_LIFECYCLE_STATE).replace("active")
+ .item(UserType.F_GIVEN_NAME).replace("bill")
+ .asObjectDeltaCast("oid1");
+
+ // WHEN
+ ObjectDelta.FactorOutResult out = delta.factorOutValues(new ItemPath(UserType.F_ASSIGNMENT), true);
+
+ // THEN
+ System.out.println("Delta before operation:\n" + delta.debugDump() + "\n");
+ System.out.println("Delta after factorOut:\n" + out.remainder.debugDump() + "\n");
+ System.out.println("Offspring deltas:\n" + DebugUtil.debugDump(out.offsprings) + "\n");
+
+ assertTrue("Remaining delta is not a MODIFY delta", out.remainder.isModify());
+ assertEquals("Wrong # of remaining modifications", 1, out.remainder.getModifications().size());
+ assertEquals("Wrong # of offspring deltas", 5, out.offsprings.size());
+ assertEquals("Wrong # of modifications in offspring 0", 1, out.offsprings.get(0).getModifications().size());
+ assertEquals("Wrong # of assignments to add in offspring 0", 1, out.offsprings.get(0).getModifications().iterator().next().getValuesToAdd().size());
+ assertEquals("Wrong # of modifications in offspring 1", 1, out.offsprings.get(1).getModifications().size());
+ assertEquals("Wrong # of assignments to delete in offspring 1", 1, out.offsprings.get(1).getModifications().iterator().next().getValuesToDelete().size());
+ assertEquals("Wrong # of modifications in offspring 2", 1, out.offsprings.get(2).getModifications().size());
+ assertEquals("Wrong # of assignments to delete in offspring 2", 1, out.offsprings.get(2).getModifications().iterator().next().getValuesToDelete().size());
+ // fragile - can be swapped if hashcodes change
+ assertEquals("Wrong # of modifications in offspring 3", 2, out.offsprings.get(3).getModifications().size());
+ assertEquals("Wrong # of modifications in offspring 4", 1, out.offsprings.get(4).getModifications().size());
+ }
}
diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java
index 1eec6d4f366..fc82041efa8 100644
--- a/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java
+++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java
@@ -191,7 +191,7 @@ public static String debugDump(Object object, int indent) {
} else {
StringBuilder sb = new StringBuilder();
indentDebugDump(sb, indent + 1);
- sb.append(object.toString());
+ sb.append(PrettyPrinter.prettyPrint(object));
return sb.toString();
}
}
diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java
index 5700889f2d7..a2821e59f90 100644
--- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java
+++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java
@@ -27,8 +27,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.function.Predicate;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
@@ -71,6 +73,8 @@
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.util.TestUtil;
+import com.evolveum.midpoint.util.FailableProcessor;
+import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
@@ -220,6 +224,9 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg
protected static final File ROLE_DELEGATOR_FILE = new File(TEST_DIR, "role-delegator.xml");
protected static final String ROLE_DELEGATOR_OID = "00000000-0000-0000-0000-00000000d001";
+
+ protected static final File ROLE_DELEGATOR_PLUS_FILE = new File(TEST_DIR, "role-delegator-plus.xml");
+ protected static final String ROLE_DELEGATOR_PLUS_OID = "00000000-0000-0000-0000-00000000d101";
protected static final File ROLE_ORG_READ_ORGS_MINISTRY_OF_RUM_FILE = new File(TEST_DIR, "role-org-read-orgs-ministry-of-rum.xml");
protected static final String ROLE_ORG_READ_ORGS_MINISTRY_OF_RUM_OID = "00000000-0000-0000-0000-00000000aa0d";
@@ -349,7 +356,7 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg
protected static final XMLGregorianCalendar JACK_VALID_FROM_LONG_AGO = XmlTypeConverter.createXMLGregorianCalendar(10000L);
protected static final int NUMBER_OF_ALL_USERS = 11;
- protected static final int NUMBER_OF_IMPORTED_ROLES = 63;
+ protected static final int NUMBER_OF_IMPORTED_ROLES = 64;
protected static final int NUMBER_OF_ALL_ORGS = 11;
protected String userRumRogersOid;
@@ -393,6 +400,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti
repoAddObjectFromFile(ROLE_ASSIGN_REQUESTABLE_ROLES_FILE, initResult);
repoAddObjectFromFile(ROLE_ASSIGN_ORGRELATION_FILE, initResult);
repoAddObjectFromFile(ROLE_DELEGATOR_FILE, initResult);
+ repoAddObjectFromFile(ROLE_DELEGATOR_PLUS_FILE, initResult);
repoAddObjectFromFile(ROLE_ORG_READ_ORGS_MINISTRY_OF_RUM_FILE, initResult);
repoAddObjectFromFile(ROLE_FILTER_OBJECT_USER_LOCATION_SHADOWS_FILE, initResult);
repoAddObjectFromFile(ROLE_FILTER_OBJECT_USER_TYPE_SHADOWS_FILE, initResult);
@@ -615,12 +623,12 @@ protected void cleanupDelete(Class type, String oid, T
}
}
- protected void assertVisibleUsers(int expectedNumAllUsers) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertVisibleUsers(int expectedNumAllUsers) throws Exception {
assertSearch(UserType.class, null, expectedNumAllUsers);
}
- protected void assertReadDeny() throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadDeny() throws Exception {
assertReadDeny(0);
assertReadDenyRaw();
}
@@ -637,7 +645,7 @@ protected void assertReadCertCases(int expectedNumber) throws ObjectNotFoundExce
assertContainerSearch(AccessCertificationCaseType.class, null, expectedNumber);
}
- protected void assertReadDeny(int expectedNumAllUsers) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadDeny(int expectedNumAllUsers) throws Exception {
assertGetDeny(UserType.class, USER_JACK_OID);
assertGetDeny(UserType.class, USER_JACK_OID, SelectorOptions.createCollection(GetOperationOptions.createRaw()));
assertGetDeny(UserType.class, USER_GUYBRUSH_OID);
@@ -650,7 +658,7 @@ protected void assertReadDeny(int expectedNumAllUsers) throws ObjectNotFoundExce
assertSearch(UserType.class, createNameQuery(USER_GUYBRUSH_USERNAME), SelectorOptions.createCollection(GetOperationOptions.createRaw()), 0);
}
- protected void assertReadDenyRaw() throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
+ protected void assertReadDenyRaw() throws Exception {
assertGetDeny(UserType.class, USER_JACK_OID, SelectorOptions.createCollection(GetOperationOptions.createRaw()));
assertGetDeny(UserType.class, USER_GUYBRUSH_OID, SelectorOptions.createCollection(GetOperationOptions.createRaw()));
@@ -659,11 +667,11 @@ protected void assertReadDenyRaw() throws ObjectNotFoundException, SchemaExcepti
assertSearchDeny(UserType.class, createNameQuery(USER_GUYBRUSH_USERNAME), SelectorOptions.createCollection(GetOperationOptions.createRaw()));
}
- protected void assertReadAllow() throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadAllow() throws Exception {
assertReadAllow(NUMBER_OF_ALL_USERS);
}
- protected void assertReadAllow(int expectedNumAllUsers) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadAllow(int expectedNumAllUsers) throws Exception {
assertGetAllow(UserType.class, USER_JACK_OID);
assertGetAllow(UserType.class, USER_GUYBRUSH_OID);
@@ -672,11 +680,11 @@ protected void assertReadAllow(int expectedNumAllUsers) throws ObjectNotFoundExc
assertSearch(UserType.class, createNameQuery(USER_GUYBRUSH_USERNAME), 1);
}
- protected void assertReadAllowRaw() throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadAllowRaw() throws Exception {
assertReadAllowRaw(NUMBER_OF_ALL_USERS);
}
- protected void assertReadAllowRaw(int expectedNumAllUsers) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertReadAllowRaw(int expectedNumAllUsers) throws Exception {
assertGetAllow(UserType.class, USER_JACK_OID, SelectorOptions.createCollection(GetOperationOptions.createRaw()));
assertGetAllow(UserType.class, USER_GUYBRUSH_OID, SelectorOptions.createCollection(GetOperationOptions.createRaw()));
@@ -773,11 +781,11 @@ protected PrismObject assertGetAllow(Class type, St
return object;
}
- protected void assertSearch(Class type, ObjectQuery query, int expectedResults) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertSearch(Class type, ObjectQuery query, int expectedResults) throws Exception {
assertSearch(type, query, null, expectedResults);
}
- protected void assertSearchRaw(Class type, ObjectQuery query, int expectedResults) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
+ protected void assertSearchRaw(Class type, ObjectQuery query, int expectedResults) throws Exception {
assertSearch(type, query, SelectorOptions.createCollection(GetOperationOptions.createRaw()), expectedResults);
}
@@ -786,7 +794,7 @@ protected void assertContainerSearch(Class type, Ob
}
protected void assertSearchDeny(Class type, ObjectQuery query,
- Collection> options) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
+ Collection> options) throws Exception {
try {
assertSearch(type, query, options, 0);
} catch (SecurityViolationException e) {
@@ -795,21 +803,70 @@ protected void assertSearchDeny(Class type, ObjectQuer
}
}
+
+ protected void assertSearch(Class type, ObjectQuery query,
+ Collection> options, int expectedResults) throws Exception {
+ assertSearch(type, query, options,
+ new SearchAssertion() {
+
+ @Override
+ public void assertObjects(String message, List