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 fe8933661f0..55ae5fe6a64 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
@@ -1338,10 +1338,24 @@ protected String debugName() {
}
protected String debugIdentifiers() {
- return "oid=" + getOid();
+ return toDebugType()+":" + getOid();
}
+
+ /**
+ * Returns short string identification of object type. It should be in a form
+ * suitable for log messages. There is no requirement for the type name to be unique,
+ * but it rather has to be compact. E.g. short element names are preferred to long
+ * QNames or URIs.
+ * @return
+ */
+ public String toDebugType() {
+ if (objectTypeClass == null) {
+ return "(unknown)";
+ }
+ return objectTypeClass.getSimpleName();
+ }
- @Override
+ @Override
public String debugDump() {
return debugDump(0);
}
diff --git a/infra/schema/.gitignore b/infra/schema/.gitignore
index 5476e3ea27f..182c35e0008 100644
--- a/infra/schema/.gitignore
+++ b/infra/schema/.gitignore
@@ -1,3 +1,4 @@
/target
/test-output
/target
+/target
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd
index 82d4de64227..8d20cf967f2 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-2a.xsd
@@ -4047,6 +4047,7 @@
+
diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java
index 54018697c4b..5983dffa737 100755
--- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java
+++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/ldap/OpenDJController.java
@@ -553,6 +553,13 @@ public static void assertDn(SearchResultEntry response, String expected) throws
}
}
+ public void assertNoEntry(String dn) throws DirectoryException {
+ SearchResultEntry entry = fetchEntry(dn);
+ if (entry != null) {
+ AssertJUnit.fail("Found entry for dn "+dn+" while not expecting it: "+entry);
+ }
+ }
+
public static void assertObjectClass(SearchResultEntry response, String expected) throws DirectoryException {
Collection objectClassValues = getAttributeValues(response, "objectClass");
AssertJUnit.assertTrue("Wrong objectclass for entry "+getDn(response)+", expected "+expected+" but got "+objectClassValues,
@@ -564,6 +571,22 @@ public void assertUniqueMember(SearchResultEntry groupEntry, String accountDn) {
MidPointAsserts.assertContainsCaseIgnore("No member "+accountDn+" in group "+getDn(groupEntry),
members, accountDn);
}
+
+ public void assertUniqueMember(String groupDn, String accountDn) throws DirectoryException {
+ SearchResultEntry groupEntry = fetchEntry(groupDn);
+ assertUniqueMember(groupEntry, accountDn);
+ }
+
+ public void assertNoUniqueMember(String groupDn, String accountDn) throws DirectoryException {
+ SearchResultEntry groupEntry = fetchEntry(groupDn);
+ assertNoUniqueMember(groupEntry, accountDn);
+ }
+
+ public void assertNoUniqueMember(SearchResultEntry groupEntry, String accountDn) {
+ Collection members = getAttributeValues(groupEntry, "uniqueMember");
+ MidPointAsserts.assertNotContainsCaseIgnore("Member "+accountDn+" in group "+getDn(groupEntry),
+ members, accountDn);
+ }
public static void assertAttribute(SearchResultEntry response, String name, String... values) {
List attrs = response.getAttribute(name.toLowerCase());
diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java
index 2e25643de80..2bfe9913bec 100644
--- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java
+++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/MidPointAsserts.java
@@ -171,4 +171,15 @@ public static void assertContainsCaseIgnore(String message, Collection a
}
AssertJUnit.fail(message+", expected "+expectedValue+", got "+actualValues);
}
+
+ public static void assertNotContainsCaseIgnore(String message, Collection actualValues, String expectedValue) {
+ if (actualValues == null) {
+ return;
+ }
+ for (String actualValue: actualValues) {
+ if (StringUtils.equalsIgnoreCase(actualValue, expectedValue)) {
+ AssertJUnit.fail(message+", expected that value "+expectedValue+" will not be present but it is");
+ }
+ }
+ }
}
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java
index 70502d1f273..28f59614ed3 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/lens/ChangeExecutor.java
@@ -228,7 +228,7 @@ public void executeChanges(LensContext syncContext, Ta
+ accCtx.getResourceShadowDiscriminator());
}
if (focusContext != null) {
- updateLinks(focusContext.getObjectNew(), focusContext, accCtx, task,
+ updateLinks(focusContext, accCtx, task,
subResult);
}
@@ -254,7 +254,7 @@ public void executeChanges(LensContext syncContext, Ta
}
if (focusContext != null) {
- updateLinks(focusContext.getObjectNew(), focusContext, accCtx, task, subResult);
+ updateLinks(focusContext, accCtx, task, subResult);
}
executeReconciliationScript(accCtx, syncContext, BeforeAfterType.AFTER, task, subResult);
@@ -323,17 +323,16 @@ private void recordFatalError(OperationResult subResult, OperationResult result,
/**
* Make sure that the account is linked (or unlinked) as needed.
*/
- private void updateLinks(PrismObject prismObject,
- LensFocusContext focusContext, LensProjectionContext projCtx,
+ private void updateLinks(LensFocusContext focusObjectContext, LensProjectionContext projCtx,
Task task, OperationResult result) throws ObjectNotFoundException, SchemaException {
- if (prismObject == null) {
+ if (focusObjectContext == null) {
return;
}
- F objectTypeNew = prismObject.asObjectable();
- if (!(objectTypeNew instanceof FocusType)) {
- return;
- }
- FocusType focusTypeNew = (FocusType) objectTypeNew;
+ Class objectTypeClass = focusObjectContext.getObjectTypeClass();
+ if (!FocusType.class.isAssignableFrom(objectTypeClass)) {
+ return;
+ }
+ LensFocusContext focusContext = (LensFocusContext) focusObjectContext;
if (projCtx.getResourceShadowDiscriminator() != null && projCtx.getResourceShadowDiscriminator().getOrder() > 0) {
// Don't mess with links for higher-order contexts. The link should be dealt with
@@ -357,12 +356,13 @@ private void updateLinks(PrismObject prismObject,
|| projCtx.isDelete()) {
// Link should NOT exist
- PrismReference linkRef = focusTypeNew.asPrismObject().findReference(FocusType.F_LINK_REF);
+ PrismObject objectCurrent = focusContext.getObjectCurrent();
+ PrismReference linkRef = objectCurrent.findReference(FocusType.F_LINK_REF);
if (linkRef != null) {
for (PrismReferenceValue linkRefVal: linkRef.getValues()) {
if (linkRefVal.getOid().equals(projOid)) {
// Linked, need to unlink
- unlinkShadow(focusTypeNew.getOid(), linkRefVal, focusContext, task, result);
+ unlinkShadow(focusContext.getOid(), linkRefVal, focusObjectContext, task, result);
}
}
@@ -372,7 +372,7 @@ private void updateLinks(PrismObject prismObject,
LOGGER.trace("Resource object {} deleted, updating also situation in shadow.", projOid);
// HACK HACK?
try {
- updateSituationInShadow(task, SynchronizationSituationType.DELETED, focusContext, projCtx, result);
+ updateSituationInShadow(task, SynchronizationSituationType.DELETED, focusObjectContext, projCtx, result);
} catch (ObjectNotFoundException e) {
// HACK HACK?
LOGGER.trace("Resource object {} is gone, cannot update situation in shadow (this is probably harmless).", projOid);
@@ -381,30 +381,33 @@ private void updateLinks(PrismObject prismObject,
} else {
// This should NOT be UNLINKED. We just do not know the situation here. Reflect that in the shadow.
LOGGER.trace("Resource object {} unlinked from the user, updating also situation in shadow.", projOid);
- updateSituationInShadow(task, null, focusContext, projCtx, result);
+ updateSituationInShadow(task, null, focusObjectContext, projCtx, result);
}
// Not linked, that's OK
} else {
// Link should exist
- for (ObjectReferenceType linkRef : focusTypeNew.getLinkRef()) {
- if (projOid.equals(linkRef.getOid())) {
- // Already linked, nothing to do, only be sure, the situation is set with the good value
- LOGGER.trace("Updating situation in already linked shadow.");
- updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusContext, projCtx, result);
- return;
- }
- }
+ PrismObject objectCurrent = focusContext.getObjectCurrent();
+ if (objectCurrent != null) {
+ for (ObjectReferenceType linkRef : objectCurrent.asObjectable().getLinkRef()) {
+ if (projOid.equals(linkRef.getOid())) {
+ // Already linked, nothing to do, only be sure, the situation is set with the good value
+ LOGGER.trace("Updating situation in already linked shadow.");
+ updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusObjectContext, projCtx, result);
+ return;
+ }
+ }
+ }
// Not linked, need to link
- linkShadow(focusTypeNew.getOid(), projOid, focusContext, task, result);
+ linkShadow(focusContext.getOid(), projOid, focusObjectContext, task, result);
//be sure, that the situation is set correctly
LOGGER.trace("Updating situation after shadow was linked.");
- updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusContext, projCtx, result);
+ updateSituationInShadow(task, SynchronizationSituationType.LINKED, focusObjectContext, projCtx, result);
}
}
- private void linkShadow(String userOid, String accountOid, LensElementContext focusContext, Task task, OperationResult parentResult) throws ObjectNotFoundException,
+ private void linkShadow(String userOid, String shadowOid, LensElementContext focusContext, Task task, OperationResult parentResult) throws ObjectNotFoundException,
SchemaException {
Class typeClass = focusContext.getObjectTypeClass();
@@ -412,10 +415,10 @@ private void linkShadow(String userOid, String accountOid
return;
}
- LOGGER.trace("Linking shadow " + accountOid + " to focus " + userOid);
+ LOGGER.debug("Linking shadow " + shadowOid + " to focus " + userOid);
OperationResult result = parentResult.createSubresult(OPERATION_LINK_ACCOUNT);
PrismReferenceValue linkRef = new PrismReferenceValue();
- linkRef.setOid(accountOid);
+ linkRef.setOid(shadowOid);
linkRef.setTargetType(ShadowType.COMPLEX_TYPE);
Collection extends ItemDelta> linkRefDeltas = ReferenceDelta.createModificationAddCollection(
FocusType.F_LINK_REF, getUserDefinition(), linkRef);
@@ -438,7 +441,7 @@ private PrismObjectDefinition getUserDefinition() {
return userDefinition;
}
- private void unlinkShadow(String userOid, PrismReferenceValue accountRef, LensElementContext focusContext,
+ private void unlinkShadow(String focusOid, PrismReferenceValue accountRef, LensElementContext focusContext,
Task task, OperationResult parentResult) throws
ObjectNotFoundException, SchemaException {
@@ -447,19 +450,19 @@ private void unlinkShadow(String userOid, PrismReferenceV
return;
}
- LOGGER.trace("Deleting linkRef " + accountRef + " from focus " + userOid);
+ LOGGER.debug("Unlinnking shadow " + accountRef.getOid() + " from focus " + focusOid);
OperationResult result = parentResult.createSubresult(OPERATION_UNLINK_ACCOUNT);
Collection extends ItemDelta> accountRefDeltas = ReferenceDelta.createModificationDeleteCollection(
FocusType.F_LINK_REF, getUserDefinition(), accountRef.clone());
try {
- cacheRepositoryService.modifyObject(typeClass, userOid, accountRefDeltas, result);
+ cacheRepositoryService.modifyObject(typeClass, focusOid, accountRefDeltas, result);
} catch (ObjectAlreadyExistsException ex) {
result.recordFatalError(ex);
throw new SystemException(ex);
} finally {
result.computeStatus();
- ObjectDelta userDelta = ObjectDelta.createModifyDelta(userOid, accountRefDeltas, typeClass, prismContext);
+ ObjectDelta userDelta = ObjectDelta.createModifyDelta(focusOid, accountRefDeltas, typeClass, prismContext);
LensObjectDeltaOperation userDeltaOp = new LensObjectDeltaOperation(userDelta);
userDeltaOp.setExecutionResult(result);
focusContext.addToExecutedDeltas(userDeltaOp);
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java
index dd1576fb269..5a3c2701fc6 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/EntitlementConverter.java
@@ -58,6 +58,8 @@
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.TunnelException;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceObjectAssociationDirectionType;
import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceObjectAssociationType;
import com.evolveum.midpoint.xml.ns._public.common.common_2a.ResourceType;
@@ -76,6 +78,8 @@
@Component
class EntitlementConverter {
+ private static final Trace LOGGER = TraceManager.getTrace(EntitlementConverter.class);
+
@Autowired(required=true)
private PrismContext prismContext;
@@ -307,6 +311,7 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con
Collection entitlementAssociationDefs = objectClassDefinition.getEntitlementAssociations();
if (entitlementAssociationDefs == null || entitlementAssociationDefs.isEmpty()) {
// Nothing to do
+ LOGGER.trace("No associations in deleted shadow");
return;
}
RefinedResourceSchema refinedSchema = RefinedResourceSchema.getRefinedSchema(resourceType);
@@ -314,6 +319,13 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con
for (RefinedAssociationDefinition assocDefType: objectClassDefinition.getEntitlementAssociations()) {
if (assocDefType.getResourceObjectAssociationType().getDirection() != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
// We can ignore these. They will die together with the object. No need to explicitly delete them.
+ LOGGER.trace("Ignoring object-to-subject association in deleted shadow");
+ continue;
+ }
+ if (assocDefType.getResourceObjectAssociationType().isExplicitReferentialIntegrity() != null
+ && !assocDefType.getResourceObjectAssociationType().isExplicitReferentialIntegrity()) {
+ // Referential integrity not required for this one
+ LOGGER.trace("Ignoring association in deleted shadow because it has explicity referential integrity turned off");
continue;
}
QName associationName = assocDefType.getName();
@@ -347,6 +359,7 @@ public void collectEntitlementsAsObjectOperationDelete(ConnectorInstance con
}
// ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES), assocAttrDef, valueAttr.getValue());
+// ObjectFilter filter = InFilter.createIn(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue());
ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue());
ObjectQuery query = ObjectQuery.createObjectQuery(filter);
// new ObjectQuery();
@@ -381,11 +394,13 @@ public boolean handle(PrismObject entitlementShadow) {
}
attributeDelta.addValuesToDelete(valueAttr.getClonedValues());
+ LOGGER.trace("Association in deleted shadow delta: {}", attributeDelta);
return true;
}
};
try {
+ LOGGER.trace("Searching for associations in deleted shadow, query: {}", filter);
connector.search(entitlementOcDef, query, handler, attributesToReturn, parentResult);
} catch (TunnelException e) {
throw (SchemaException)e.getCause();
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java
index 3a88b5bffe9..a36b517e432 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/ucf/query/ValueOperation.java
@@ -30,6 +30,7 @@
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.EqualsFilter;
+import com.evolveum.midpoint.prism.query.InFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.SubstringFilter;
import com.evolveum.midpoint.prism.query.ValueFilter;
@@ -79,6 +80,22 @@ public Filter interpret(ObjectFilter objectFilter, IcfNameMapper icfNameMapp
return FilterBuilder.containsAllValues(attr);
}
}
+
+ } else if (objectFilter instanceof InFilter) {
+ InFilter in = (InFilter) objectFilter;
+
+ Collection