From 5dedeca35b09a0ef374ebedcd0c2456ff909bf8e Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:30:46 +0000 Subject: [PATCH 01/12] Add utility method to get attribute from ConnectorObject --- .../connid/bundles/ldap/commons/LdapUtil.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java index 4184515..493750d 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java @@ -40,6 +40,7 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.AttributeDelta; import org.identityconnectors.framework.common.objects.AttributeDeltaUtil; +import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.Name; public class LdapUtil { @@ -88,6 +89,18 @@ public static String removeBinaryOption(String ldapAttrName) { return ldapAttrName; } + public static String getStringAttrValue(ConnectorObject object, String ldapAttrName) { + org.identityconnectors.framework.common.objects.Attribute attr = object.getAttributeByName(ldapAttrName); + if (attr != null) { + try { + return (String) attr.getValue().get(0); + } catch (IndexOutOfBoundsException e) { + throw new ConnectorException(e); + } + } + return null; + } + /** * Return the value of the {@code ldapAttrName} parameter cast to a String. */ From 46427ca11b626d7919f416043bc0ca2fdc9808f6 Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:31:30 +0000 Subject: [PATCH 02/12] Update both SyncStrategies to use LdapSearch instead of LdapInternalSearch --- .../sync/GenericChangeLogSyncStrategy.java | 134 ++++++++++-------- .../sunds/SunDSChangeLogSyncStrategy.java | 114 ++++++++------- 2 files changed, 140 insertions(+), 108 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index 04de1c8..3581d86 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -32,7 +32,6 @@ import java.util.Map; import java.util.Set; import javax.naming.InvalidNameException; -import javax.naming.directory.SearchControls; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import net.tirasa.connid.bundles.ldap.LdapConnection; @@ -45,7 +44,6 @@ import net.tirasa.connid.bundles.ldap.commons.LdifParser.Separator; import net.tirasa.connid.bundles.ldap.commons.PasswordDecryptor; import net.tirasa.connid.bundles.ldap.search.LdapFilter; -import net.tirasa.connid.bundles.ldap.search.LdapInternalSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearches; import org.identityconnectors.common.CollectionUtil; @@ -59,7 +57,9 @@ import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptions; +import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.OperationalAttributes; +import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; import org.identityconnectors.framework.common.objects.SyncDeltaType; @@ -109,29 +109,34 @@ public GenericChangeLogSyncStrategy(LdapConnection conn) { @Override public SyncToken getLatestSyncToken(ObjectClass oclass) { String changeNumberAttr = getChangeNumberAttribute(); - SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); - controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); - controls.setReturningAttributes(new String[] { - changeNumberAttr }); - - LdapInternalSearch search = new LdapInternalSearch( - conn, - "(objectClass=changelogEntry)", - Collections.singletonList(conn.getConfiguration().getChangeLogContext()), - conn.getConfiguration().newDefaultSearchStrategy(false), - controls); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.setAttributesToGet(changeNumberAttr); + builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); + builder.setPageSize(conn.getConfiguration().getChangeLogBlockSize()); final int[] maxChangeNumber = { 0 }; - search.execute((baseDN, result) -> { - final LdapEntry entry = LdapEntry.create(baseDN, result); - int changeNumber = convertToInt(LdapUtil.getStringAttrValue(entry.getAttributes(), changeNumberAttr), -1); - if (changeNumber > maxChangeNumber[0]) { - maxChangeNumber[0] = changeNumber; + ResultsHandler handler = new ResultsHandler() { + @Override + public boolean handle(final ConnectorObject object) { + int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), -1); + if (changeNumber > maxChangeNumber[0]) { + maxChangeNumber[0] = changeNumber; + } + + return true; } + }; - return true; - }); + LdapSearch search = new LdapSearch(this.conn, + oclass, + LdapFilter.forNativeFilter("(objectClass=changelogEntry)"), + handler, + builder.build(), + conn.getConfiguration().getChangeLogContext()); + + search.execute(); return new SyncToken(maxChangeNumber[0]); } @@ -143,52 +148,65 @@ public void sync( final OperationOptions options, final ObjectClass oclass) { final String changeNumberAttr = getChangeNumberAttribute(); - SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); - controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); - - controls.setReturningAttributes(new String[] { - changeNumberAttr, - "targetDN", - "targetEntryUUID", - "changeType", - "changes", - "newRdn", - "deleteOldRdn", - "newSuperior" }); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); + builder.setAttributesToGet( + changeNumberAttr, + "targetDN", + "targetEntryUUID", + "changeType", + "changes", + "newRdn", + "deleteOldRdn", + "newSuperior" + ); final int[] currentChangeNumber = { getStartChangeNumber(token) }; final boolean[] results = new boolean[1]; - do { - results[0] = false; - - String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); - LdapInternalSearch search = new LdapInternalSearch( - conn, - filter, - Collections.singletonList(conn.getConfiguration().getChangeLogContext()), - conn.getConfiguration().newDefaultSearchStrategy(false), - controls); - - search.execute((baseDN, result) -> { + ResultsHandler resultsHandler = new ResultsHandler() { + @Override + public boolean handle(final ConnectorObject object) { results[0] = true; - final LdapEntry entry = LdapEntry.create(baseDN, result); - - int changeNumber = convertToInt( - LdapUtil.getStringAttrValue(entry.getAttributes(), changeNumberAttr), -1); + + int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), + -1); if (changeNumber > currentChangeNumber[0]) { currentChangeNumber[0] = changeNumber; } - final SyncDelta delta = createSyncDelta(entry, changeNumber, options.getAttributesToGet(), oclass); + SyncDelta delta; + try { + delta = createSyncDelta(object, changeNumber, options.getAttributesToGet(), oclass); + } catch (InvalidNameException e) { + delta = null; + } if (delta != null) { return handler.handle(delta); } return true; - }); + } + }; + + final OperationOptions searchOptions = builder.build(); + + do { + results[0] = false; + + String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); + + LdapSearch search = new LdapSearch(conn, + oclass, + LdapFilter.forNativeFilter(filter), + resultsHandler, + searchOptions, + conn.getConfiguration().getChangeLogContext()); + + search.execute(); // We have already processed the current change. // In the next cycle we want to start with the next change. @@ -200,14 +218,14 @@ public void sync( } private SyncDelta createSyncDelta( - final LdapEntry changeLogEntry, + final ConnectorObject inputObject, final int changeNumber, final String[] attrsToGetOption, ObjectClass oclass) throws InvalidNameException { LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber); - final String targetDN = LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "targetDN"); + final String targetDN = LdapUtil.getStringAttrValue(inputObject, "targetDN"); if (targetDN == null) { LOG.error("Skipping log entry because it does not have a targetDN attribute"); @@ -222,7 +240,7 @@ private SyncDelta createSyncDelta( return null; } - final String changeType = LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "changeType"); + final String changeType = LdapUtil.getStringAttrValue(inputObject, "changeType"); SyncDeltaType deltaType = getSyncDeltaType(changeType); @@ -232,7 +250,7 @@ private SyncDelta createSyncDelta( if (deltaType.equals(SyncDeltaType.DELETE)) { LOG.ok("Creating sync delta for deleted entry {0}", - LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "targetEntryUUID")); + LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); @@ -240,7 +258,7 @@ private SyncDelta createSyncDelta( if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { deletedUid = createUid(uidAttr, targetDN); } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { - deletedUid = new Uid(LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "targetEntryUUID")); + deletedUid = new Uid(LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); } else { // ever fallback to dn without throwing any exception more reliable deletedUid = new Uid(targetDN); @@ -258,7 +276,7 @@ private SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - final String changes = LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "changes"); + final String changes = LdapUtil.getStringAttrValue(inputObject, "changes"); final Map> attrChanges = getAttributeChanges(changeType, changes); @@ -279,14 +297,14 @@ private SyncDelta createSyncDelta( String newTargetDN = targetDN; if ("modrdn".equalsIgnoreCase(changeType)) { - final String newRdn = LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "newRdn"); + final String newRdn = LdapUtil.getStringAttrValue(inputObject, "newRdn"); if (StringUtil.isBlank(newRdn)) { LOG.error("Skipping log entry because it does not have a newRdn attribute"); return null; } - final String newSuperior = LdapUtil.getStringAttrValue(changeLogEntry.getAttributes(), "newSuperior"); + final String newSuperior = LdapUtil.getStringAttrValue(inputObject, "newSuperior"); newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); } diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java index 4bae0c2..d4a0efe 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java @@ -23,14 +23,10 @@ */ package net.tirasa.connid.bundles.ldap.sync.sunds; -import static java.util.Collections.singletonList; import static org.identityconnectors.common.CollectionUtil.isEmpty; import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveMap; import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveSet; -import static org.identityconnectors.common.CollectionUtil.newSet; -import static org.identityconnectors.common.CollectionUtil.nullAsEmpty; import static org.identityconnectors.common.StringUtil.isBlank; -import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.checkedListByFilter; import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.getStringAttrValue; import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.isUnderContexts; import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.nullAsEmpty; @@ -48,11 +44,11 @@ import javax.naming.InvalidNameException; import javax.naming.NamingException; import javax.naming.directory.Attributes; -import javax.naming.directory.SearchControls; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import net.tirasa.connid.bundles.ldap.LdapConnection; import net.tirasa.connid.bundles.ldap.commons.LdapEntry; +import net.tirasa.connid.bundles.ldap.commons.LdapUtil; import net.tirasa.connid.bundles.ldap.commons.LdifParser; import net.tirasa.connid.bundles.ldap.commons.PasswordDecryptor; import net.tirasa.connid.bundles.ldap.commons.LdifParser.ChangeSeparator; @@ -60,10 +56,12 @@ import net.tirasa.connid.bundles.ldap.commons.LdifParser.NameValue; import net.tirasa.connid.bundles.ldap.commons.LdifParser.Separator; import net.tirasa.connid.bundles.ldap.search.LdapFilter; -import net.tirasa.connid.bundles.ldap.search.LdapInternalSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearches; import net.tirasa.connid.bundles.ldap.sync.LdapSyncStrategy; + +import org.identityconnectors.common.CollectionUtil; +import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.exceptions.ConnectorException; @@ -73,7 +71,9 @@ import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptions; +import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.OperationalAttributes; +import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; import org.identityconnectors.framework.common.objects.SyncDeltaType; @@ -133,53 +133,66 @@ public void sync( final SyncResultsHandler handler, final OperationOptions options, final ObjectClass oclass) { - String context = getChangeLogAttributes().getChangeLogContext(); final String changeNumberAttr = getChangeNumberAttribute(); - SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); - controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); - - controls.setReturningAttributes(new String[] { - changeNumberAttr, - "targetDN", - "targetEntryUUID", - "changeType", - "changes", - "newRdn", - "deleteOldRdn", - "newSuperior" }); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); + builder.setAttributesToGet( + changeNumberAttr, + "targetDN", + "targetEntryUUID", + "changeType", + "changes", + "newRdn", + "deleteOldRdn", + "newSuperior" + ); final int[] currentChangeNumber = { getStartChangeNumber(token) }; final boolean[] results = new boolean[1]; - do { - results[0] = false; - - String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); - - LdapInternalSearch search = new LdapInternalSearch( - conn, - filter, - singletonList(context), - conn.getConfiguration().newDefaultSearchStrategy(false), - controls); - search.execute((baseDN, result) -> { + ResultsHandler resultsHandler = new ResultsHandler() { + @Override + public boolean handle(final ConnectorObject object) { results[0] = true; - final LdapEntry entry = LdapEntry.create(baseDN, result); - - int changeNumber = convertToInt(getStringAttrValue(entry.getAttributes(), changeNumberAttr), -1); + + int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), + -1); if (changeNumber > currentChangeNumber[0]) { currentChangeNumber[0] = changeNumber; } - final SyncDelta delta = createSyncDelta(entry, changeNumber, options.getAttributesToGet(), oclass); + SyncDelta delta; + try { + delta = createSyncDelta(object, changeNumber, options.getAttributesToGet(), oclass); + } catch (InvalidNameException e) { + delta = null; + } if (delta != null) { return handler.handle(delta); } return true; - }); + } + }; + + final OperationOptions searchOptions = builder.build(); + + do { + results[0] = false; + + String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); + + LdapSearch search = new LdapSearch(conn, + oclass, + LdapFilter.forNativeFilter(filter), + resultsHandler, + searchOptions, + conn.getConfiguration().getChangeLogContext()); + + search.execute(); // We have already processed the current change. // In the next cycle we want to start with the next change. @@ -191,21 +204,21 @@ public void sync( } private SyncDelta createSyncDelta( - final LdapEntry changeLogEntry, + final ConnectorObject inputObject, final int changeNumber, final String[] attrsToGetOption, ObjectClass oclass) throws InvalidNameException { LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber); - final String targetDN = getStringAttrValue(changeLogEntry.getAttributes(), "targetDN"); + final String targetDN = LdapUtil.getStringAttrValue(inputObject, "targetDN"); if (targetDN == null) { LOG.error("Skipping log entry because it does not have a targetDN attribute"); return null; } - final LdapName targetName = quietCreateLdapName(targetDN); + final LdapName targetName = LdapUtil.quietCreateLdapName(targetDN); if (filterOutByBaseContexts(targetName)) { LOG.ok("Skipping log entry because it does not match any of the " @@ -213,7 +226,7 @@ private SyncDelta createSyncDelta( return null; } - final String changeType = getStringAttrValue(changeLogEntry.getAttributes(), "changeType"); + final String changeType = LdapUtil.getStringAttrValue(inputObject, "changeType"); SyncDeltaType deltaType = getSyncDeltaType(changeType); @@ -222,8 +235,8 @@ private SyncDelta createSyncDelta( syncDeltaBuilder.setDeltaType(deltaType); if (deltaType.equals(SyncDeltaType.DELETE)) { - LOG.ok("Creating sync delta for deleted entry " + getStringAttrValue(changeLogEntry.getAttributes(), - "targetEntryUUID")); + LOG.ok("Creating sync delta for deleted entry {0}", + LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); @@ -231,7 +244,7 @@ private SyncDelta createSyncDelta( if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { deletedUid = createUid(uidAttr, targetDN); } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { - deletedUid = new Uid(getStringAttrValue(changeLogEntry.getAttributes(), "targetEntryUUID")); + deletedUid = new Uid(LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); } else { // ever fallback to dn without throwing any exception more reliable deletedUid = new Uid(targetDN); @@ -249,7 +262,7 @@ private SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - final String changes = getStringAttrValue(changeLogEntry.getAttributes(), "changes"); + final String changes = LdapUtil.getStringAttrValue(inputObject, "changes"); final Map> attrChanges = getAttributeChanges(changeType, changes); @@ -270,14 +283,14 @@ private SyncDelta createSyncDelta( String newTargetDN = targetDN; if ("modrdn".equalsIgnoreCase(changeType)) { - final String newRdn = getStringAttrValue(changeLogEntry.getAttributes(), "newRdn"); + final String newRdn = LdapUtil.getStringAttrValue(inputObject, "newRdn"); - if (isBlank(newRdn)) { + if (StringUtil.isBlank(newRdn)) { LOG.error("Skipping log entry because it does not have a newRdn attribute"); return null; } - final String newSuperior = getStringAttrValue(changeLogEntry.getAttributes(), "newSuperior"); + final String newSuperior = LdapUtil.getStringAttrValue(inputObject, "newSuperior"); newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); } @@ -288,12 +301,12 @@ private SyncDelta createSyncDelta( Set attrsToGet; if (attrsToGetOption != null) { - attrsToGet = newSet(attrsToGetOption); + attrsToGet = CollectionUtil.newSet(attrsToGetOption); // Do not retrieve the password attribute from the entry (usually it is an unusable // hashed value anyway). We will use the one from the change log below. attrsToGet.remove(OperationalAttributes.PASSWORD_NAME); } else { - attrsToGet = newSet(LdapSearch.getAttributesReturnedByDefault(conn, oclass)); + attrsToGet = CollectionUtil.newSet(LdapSearch.getAttributesReturnedByDefault(conn, oclass)); } // If objectClass is not in the list of attributes to get, prepare to remove it later. boolean removeObjectClass = attrsToGet.add("objectClass"); @@ -310,7 +323,8 @@ private SyncDelta createSyncDelta( Attribute oclassAttr = object.getAttributeByName("objectClass"); - final List objectClasses = checkedListByFilter(nullAsEmpty(oclassAttr.getValue()), String.class); + final List objectClasses = LdapUtil.checkedListByFilter( + CollectionUtil.nullAsEmpty(oclassAttr.getValue()), String.class); if (filterOutByObjectClasses(objectClasses)) { LOG.ok("Skipping entry because no object class in the list of " From 5512c4c63ae9758e26fda73a48ae31941160850e Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:12:57 +0000 Subject: [PATCH 03/12] Provide method to override built in filters on LdapSearch --- .../bundles/ldap/search/LdapSearch.java | 35 ++++++++++++++----- .../sync/GenericChangeLogSyncStrategy.java | 14 ++++---- .../sunds/SunDSChangeLogSyncStrategy.java | 11 +++--- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java index 2f59bef..01d24d5 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java @@ -69,6 +69,8 @@ public class LdapSearch { // An Operation Option specific for usage with LDAP public static final String OP_IGNORE_CUSTOM_ANY_OBJECT_CONFIG = "IGNORE_CUSTOM_ANY_OBJECT_CONFIG"; + public static final String OP_IGNORE_BUILT_IN_FILTERS = "IGNORE_BUILT_IN_FILTERS"; + private static final Log LOG = Log.getLog(LdapSearch.class); protected final LdapConnection conn; @@ -208,17 +210,32 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { controls.setSearchScope(searchScope); String optionsFilter = LdapConstants.getSearchFilter(options); - String searchFilter = null; - if (oclass.equals(ObjectClass.ACCOUNT)) { - searchFilter = conn.getConfiguration().getAccountSearchFilter(); - } else if (oclass.equals(ObjectClass.GROUP)) { - searchFilter = conn.getConfiguration().getGroupSearchFilter(); - } else if (!ignoreUserAnyObjectConfig) { - searchFilter = conn.getConfiguration().getAnyObjectSearchFilter(); + String finalFilter = ""; + + boolean ignoreBuiltInFilters; + if (options.getOptions().containsKey(OP_IGNORE_BUILT_IN_FILTERS)) { + ignoreBuiltInFilters = (boolean) options.getOptions().get(OP_IGNORE_BUILT_IN_FILTERS); + } else { + ignoreBuiltInFilters = false; + } + + if (ignoreBuiltInFilters) { + finalFilter = optionsFilter; + } else { + String searchFilter = null; + if (oclass.equals(ObjectClass.ACCOUNT)) { + searchFilter = conn.getConfiguration().getAccountSearchFilter(); + } else if (oclass.equals(ObjectClass.GROUP)) { + searchFilter = conn.getConfiguration().getGroupSearchFilter(); + } else if (!ignoreUserAnyObjectConfig) { + searchFilter = conn.getConfiguration().getAnyObjectSearchFilter(); + } + String nativeFilter = filter == null ? null : filter.getNativeFilter(); + + finalFilter = getSearchFilter(optionsFilter, nativeFilter, searchFilter); } - String nativeFilter = filter == null ? null : filter.getNativeFilter(); return new LdapInternalSearch(conn, - getSearchFilter(optionsFilter, nativeFilter, searchFilter), + finalFilter, dns, strategy, controls); } diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index 3581d86..d7ecfd6 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -35,6 +35,7 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import net.tirasa.connid.bundles.ldap.LdapConnection; +import net.tirasa.connid.bundles.ldap.commons.LdapConstants; import net.tirasa.connid.bundles.ldap.commons.LdapEntry; import net.tirasa.connid.bundles.ldap.commons.LdapUtil; import net.tirasa.connid.bundles.ldap.commons.LdifParser; @@ -114,6 +115,8 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { builder.setAttributesToGet(changeNumberAttr); builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); builder.setPageSize(conn.getConfiguration().getChangeLogBlockSize()); + builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); + builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); final int[] maxChangeNumber = { 0 }; @@ -131,7 +134,7 @@ public boolean handle(final ConnectorObject object) { LdapSearch search = new LdapSearch(this.conn, oclass, - LdapFilter.forNativeFilter("(objectClass=changelogEntry)"), + null, handler, builder.build(), conn.getConfiguration().getChangeLogContext()); @@ -161,6 +164,7 @@ public void sync( "deleteOldRdn", "newSuperior" ); + builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); final int[] currentChangeNumber = { getStartChangeNumber(token) }; @@ -192,18 +196,16 @@ public boolean handle(final ConnectorObject object) { } }; - final OperationOptions searchOptions = builder.build(); - do { results[0] = false; - String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); + builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); LdapSearch search = new LdapSearch(conn, oclass, - LdapFilter.forNativeFilter(filter), + null, resultsHandler, - searchOptions, + builder.build(), conn.getConfiguration().getChangeLogContext()); search.execute(); diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java index d4a0efe..718ae42 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java @@ -47,6 +47,7 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import net.tirasa.connid.bundles.ldap.LdapConnection; +import net.tirasa.connid.bundles.ldap.commons.LdapConstants; import net.tirasa.connid.bundles.ldap.commons.LdapEntry; import net.tirasa.connid.bundles.ldap.commons.LdapUtil; import net.tirasa.connid.bundles.ldap.commons.LdifParser; @@ -147,6 +148,7 @@ public void sync( "deleteOldRdn", "newSuperior" ); + builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); final int[] currentChangeNumber = { getStartChangeNumber(token) }; @@ -178,18 +180,15 @@ public boolean handle(final ConnectorObject object) { } }; - final OperationOptions searchOptions = builder.build(); - do { results[0] = false; - String filter = getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0]); - + builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); LdapSearch search = new LdapSearch(conn, oclass, - LdapFilter.forNativeFilter(filter), + null, resultsHandler, - searchOptions, + builder.build(), conn.getConfiguration().getChangeLogContext()); search.execute(); From 564bf770723d3398485c8a9ee0b5853e6423ea44 Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:12:57 +0000 Subject: [PATCH 04/12] Return null on IndexOutOfBoundsException in getStringAttrValue --- .../java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java index 493750d..ead6a25 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java @@ -95,6 +95,8 @@ public static String getStringAttrValue(ConnectorObject object, String ldapAttrN try { return (String) attr.getValue().get(0); } catch (IndexOutOfBoundsException e) { + return null; + } catch (Exception e) { throw new ConnectorException(e); } } From 4dcecb15c13b2350a7da8445a95149cc3bd8c20b Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:19:30 +0000 Subject: [PATCH 05/12] Add paged synctoken method --- .../ldap/search/LdapSearchStrategy.java | 19 ++++ .../sync/GenericChangeLogSyncStrategy.java | 88 ++++++++++++++++--- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java index 57a34b5..e0eb7d5 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java @@ -29,6 +29,12 @@ import javax.naming.NamingException; import javax.naming.directory.SearchControls; import javax.naming.ldap.LdapContext; +import javax.naming.ldap.PagedResultsControl; +import com.sun.jndi.ldap.ctl.VirtualListViewControl; + +import org.identityconnectors.framework.common.objects.OperationOptions; + +import net.tirasa.connid.bundles.ldap.LdapConnection; public abstract class LdapSearchStrategy { @@ -66,4 +72,17 @@ protected static String searchControlsToString(final SearchControls controls) { builder.append('}'); return builder.toString(); } + + public static Class getSearchStrategy(LdapConnection conn, OperationOptions options) { + Class clazz = DefaultSearchStrategy.class; + if (options.getPageSize() != null) { + if (conn.getConfiguration().isUseVlvControls() && conn.supportsControl(VirtualListViewControl.OID)) { + clazz = VlvIndexSearchStrategy.class; + } else if (conn.supportsControl(PagedResultsControl.OID)) { + clazz = PagedSearchStrategy.class; + } + } + + return clazz; + } } diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index d7ecfd6..5f1c5ca 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -46,6 +46,7 @@ import net.tirasa.connid.bundles.ldap.commons.PasswordDecryptor; import net.tirasa.connid.bundles.ldap.search.LdapFilter; import net.tirasa.connid.bundles.ldap.search.LdapSearch; +import net.tirasa.connid.bundles.ldap.search.LdapSearchStrategy; import net.tirasa.connid.bundles.ldap.search.LdapSearches; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; @@ -61,12 +62,14 @@ import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.OperationalAttributes; import org.identityconnectors.framework.common.objects.ResultsHandler; +import org.identityconnectors.framework.common.objects.SearchResult; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; import org.identityconnectors.framework.common.objects.SyncDeltaType; import org.identityconnectors.framework.common.objects.SyncResultsHandler; import org.identityconnectors.framework.common.objects.SyncToken; import org.identityconnectors.framework.common.objects.Uid; +import org.identityconnectors.framework.spi.SearchResultsHandler; /** * An implementation of the sync operation based on the retro change log @@ -106,24 +109,65 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { public GenericChangeLogSyncStrategy(LdapConnection conn) { this.conn = conn; } + + protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptionsBuilder builder) { + int pageSize = conn.getConfiguration().getChangeLogBlockSize(); + LOG.ok("Getting latest sync token with pages of size {0}", pageSize); - @Override - public SyncToken getLatestSyncToken(ObjectClass oclass) { - String changeNumberAttr = getChangeNumberAttribute(); + builder.setPageSize(pageSize); - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - builder.setAttributesToGet(changeNumberAttr); - builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); - builder.setPageSize(conn.getConfiguration().getChangeLogBlockSize()); - builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); - builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); + final String[] cookies = new String[] { null }; + final int[] maxChangeNumber = { 0 }; + + do { + if (cookies[0] != null) { + LOG.ok("Setting paged results cookie to {0}", cookies[0]); + builder.setPagedResultsCookie(cookies[0]); + } + + cookies[0] = null; + + SearchResultsHandler handler = new SearchResultsHandler() { + @Override + public boolean handle(final ConnectorObject object) { + int changeNumber = convertToInt( + (String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); + if (changeNumber > maxChangeNumber[0]) { + maxChangeNumber[0] = changeNumber; + } + + return true; + } + + @Override + public void handleResult(final SearchResult result) { + if (result.isAllResultsReturned()) { + cookies[0] = result.getPagedResultsCookie(); + } + } + }; + + LdapSearch search = new LdapSearch(this.conn, + oclass, + null, + handler, + builder.build(), + conn.getConfiguration().getChangeLogContext()); + + search.execute(); + } while (cookies[0] != null); + return new SyncToken(maxChangeNumber[0]); + } + + protected SyncToken getLatestSyncTokenDefault(ObjectClass oclass, OperationOptionsBuilder builder) { final int[] maxChangeNumber = { 0 }; + LOG.ok("Getting latest sync token with a regular search"); ResultsHandler handler = new ResultsHandler() { @Override public boolean handle(final ConnectorObject object) { - int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), -1); + int changeNumber = convertToInt((String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); if (changeNumber > maxChangeNumber[0]) { maxChangeNumber[0] = changeNumber; } @@ -144,6 +188,30 @@ public boolean handle(final ConnectorObject object) { return new SyncToken(maxChangeNumber[0]); } + @Override + public SyncToken getLatestSyncToken(ObjectClass oclass) { + String changeNumberAttr = getChangeNumberAttribute(); + + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.setAttributesToGet(changeNumberAttr); + builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); + + builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); + builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); + + Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, + builder.build()); + + switch (searchStrategyClass.getSimpleName()) { + case "PagedSearchStrategy": + return getLatestSyncTokenPaged(oclass, builder); + case "VlvIndexSearchStrategy": + case "DefaultSearchStrategy": + default: + return getLatestSyncTokenDefault(oclass, builder); + } + } + @Override public void sync( final SyncToken token, From 0fbc1b71f3f45af34dfd883500e482e8daf5fb0c Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:26:46 +0000 Subject: [PATCH 06/12] Enable changelog on OpenDJ instance --- src/test/resources/opendj/run.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/resources/opendj/run.sh b/src/test/resources/opendj/run.sh index 5439492..4613a43 100755 --- a/src/test/resources/opendj/run.sh +++ b/src/test/resources/opendj/run.sh @@ -84,6 +84,13 @@ sh ./bin/dsconfig -h localhost -p 4444 -D "cn=Directory Manager" -w password -n --backend-name userRoot --index-name index-uid --set sort-order:uid --set scope:whole-subtree \ --set base-dn:dc=example,dc=com --set filter:"(&(objectClass=inetOrgPerson)(objectClass=organizationalPerson)(objectClass=person)(objectClass=top))" +sh ./bin/dsconfig create-replication-server -h localhost -p 4444 -D "cn=Directory Manager" -w password -n -X \ + --provider-name "Multimaster Synchronization" --set replication-port:8989 --set replication-server-id:2 --type generic + +sh ./bin/dsconfig create-replication-domain -h localhost -p 4444 -D "cn=Directory Manager" -w password -n -X \ + --provider-name "Multimaster Synchronization" --set base-dn:dc=example,dc=com --set replication-server:localhost:8989 \ + --set server-id:3 --type generic --domain-name example_com + sh ./bin/rebuild-index --baseDN "dc=example,dc=com" --bindPassword password -X --index vlv.index-uid # Run upgrade if the server is older From 68fd8185a2f09ffed16a836aa2321c48b23f6e8a Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:35:15 +0000 Subject: [PATCH 07/12] Bring pagesize back to before method determined --- .../bundles/ldap/sync/GenericChangeLogSyncStrategy.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index 5f1c5ca..fb60548 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -111,10 +111,7 @@ public GenericChangeLogSyncStrategy(LdapConnection conn) { } protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptionsBuilder builder) { - int pageSize = conn.getConfiguration().getChangeLogBlockSize(); - LOG.ok("Getting latest sync token with pages of size {0}", pageSize); - - builder.setPageSize(pageSize); + LOG.ok("Getting latest sync token with pages of size {0}", conn.getConfiguration().getChangeLogBlockSize()); final String[] cookies = new String[] { null }; final int[] maxChangeNumber = { 0 }; @@ -195,7 +192,7 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.setAttributesToGet(changeNumberAttr); builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); - + builder.setPageSize(conn.getConfiguration().getChangeLogBlockSize()); builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); @@ -205,8 +202,6 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { switch (searchStrategyClass.getSimpleName()) { case "PagedSearchStrategy": return getLatestSyncTokenPaged(oclass, builder); - case "VlvIndexSearchStrategy": - case "DefaultSearchStrategy": default: return getLatestSyncTokenDefault(oclass, builder); } From e6e9a83cf1810ece97323f438679baed501eecfc Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:43:41 +0000 Subject: [PATCH 08/12] Add option to configure whether to use paging for the changelog --- .../bundles/ldap/LdapConfiguration.java | 39 ++++++++++++------- .../sync/GenericChangeLogSyncStrategy.java | 10 +++-- .../connid/bundles/ldap/Messages.properties | 2 + .../bundles/ldap/Messages_de.properties | 2 + .../bundles/ldap/Messages_es.properties | 2 + .../bundles/ldap/Messages_fr.properties | 2 + .../bundles/ldap/Messages_it.properties | 2 + .../bundles/ldap/Messages_ja.properties | 2 + .../bundles/ldap/Messages_ko.properties | 2 + .../bundles/ldap/Messages_pt.properties | 2 + .../bundles/ldap/Messages_zh.properties | 2 + .../bundles/ldap/Messages_zh_TW.properties | 2 + 12 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java b/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java index 58b8f6f..d00842a 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java @@ -207,6 +207,8 @@ public enum SearchScope { private String changeLogContext = "cn=changelog"; + private boolean changeLogPagingSupport = false; + private boolean filterWithOrInsteadOfAnd; private boolean removeLogEntryObjectClassFromFilter = true; @@ -903,7 +905,18 @@ public void setChangeLogContext(String changeLogContext) { this.changeLogContext = changeLogContext; } - @ConfigurationProperty(order = 39, operations = { SyncOp.class }, + @ConfigurationProperty(order = 40, operations = { SyncOp.class }, + displayMessageKey = "changeLogPagingSupport.display", + helpMessageKey = "changeLogPagingSupport.help") + public boolean getChangeLogPagingSupport() { + return changeLogPagingSupport; + } + + public void setChangeLogPagingSupport(boolean changeLogPagingSupport) { + this.changeLogPagingSupport = changeLogPagingSupport; + } + + @ConfigurationProperty(order = 41, operations = { SyncOp.class }, displayMessageKey = "filterWithOrInsteadOfAnd.display", helpMessageKey = "filterWithOrInsteadOfAnd.help") public boolean isFilterWithOrInsteadOfAnd() { @@ -914,7 +927,7 @@ public void setFilterWithOrInsteadOfAnd(boolean filterWithOrInsteadOfAnd) { this.filterWithOrInsteadOfAnd = filterWithOrInsteadOfAnd; } - @ConfigurationProperty(order = 40, operations = { SyncOp.class }, + @ConfigurationProperty(order = 42, operations = { SyncOp.class }, displayMessageKey = "removeLogEntryObjectClassFromFilter.display", helpMessageKey = "removeLogEntryObjectClassFromFilter.help") public boolean isRemoveLogEntryObjectClassFromFilter() { @@ -925,7 +938,7 @@ public void setRemoveLogEntryObjectClassFromFilter(boolean removeLogEntryObjectC this.removeLogEntryObjectClassFromFilter = removeLogEntryObjectClassFromFilter; } - @ConfigurationProperty(order = 41, operations = { SyncOp.class }, + @ConfigurationProperty(order = 43, operations = { SyncOp.class }, displayMessageKey = "synchronizePasswords.display", helpMessageKey = "synchronizePasswords.help") public boolean isSynchronizePasswords() { @@ -936,7 +949,7 @@ public void setSynchronizePasswords(boolean synchronizePasswords) { this.synchronizePasswords = synchronizePasswords; } - @ConfigurationProperty(order = 42, operations = { SyncOp.class }, + @ConfigurationProperty(order = 44, operations = { SyncOp.class }, displayMessageKey = "passwordAttributeToSynchronize.display", helpMessageKey = "passwordAttributeToSynchronize.help") public String getPasswordAttributeToSynchronize() { @@ -947,7 +960,7 @@ public void setPasswordAttributeToSynchronize(String passwordAttributeToSynchron this.passwordAttributeToSynchronize = passwordAttributeToSynchronize; } - @ConfigurationProperty(order = 43, operations = { SyncOp.class }, confidential = true, + @ConfigurationProperty(order = 45, operations = { SyncOp.class }, confidential = true, displayMessageKey = "passwordDecryptionKey.display", helpMessageKey = "passwordDecryptionKey.help") public GuardedByteArray getPasswordDecryptionKey() { @@ -959,7 +972,7 @@ public void setPasswordDecryptionKey(GuardedByteArray passwordDecryptionKey) { copy() : null; } - @ConfigurationProperty(order = 44, operations = { SyncOp.class }, confidential = true, + @ConfigurationProperty(order = 46, operations = { SyncOp.class }, confidential = true, displayMessageKey = "passwordDecryptionInitializationVector.display", helpMessageKey = "passwordDecryptionInitializationVector.help") public GuardedByteArray getPasswordDecryptionInitializationVector() { @@ -971,7 +984,7 @@ public void setPasswordDecryptionInitializationVector(GuardedByteArray passwordD ? passwordDecryptionInitializationVector.copy() : null; } - @ConfigurationProperty(order = 45, + @ConfigurationProperty(order = 47, displayMessageKey = "statusManagementClass.display", helpMessageKey = "statusManagementClass.help") public String getStatusManagementClass() { @@ -982,7 +995,7 @@ public void setStatusManagementClass(String statusManagementClass) { this.statusManagementClass = statusManagementClass; } - @ConfigurationProperty(order = 46, + @ConfigurationProperty(order = 48, displayMessageKey = "retrievePasswordsWithSearch.display", helpMessageKey = "retrievePasswordsWithSearch.help") public boolean getRetrievePasswordsWithSearch() { @@ -993,7 +1006,7 @@ public void setRetrievePasswordsWithSearch(boolean retrievePasswordsWithSearch) this.retrievePasswordsWithSearch = retrievePasswordsWithSearch; } - @ConfigurationProperty(order = 47, + @ConfigurationProperty(order = 49, displayMessageKey = "dnAttribute.display", helpMessageKey = "dnAttribute.help") public String getDnAttribute() { @@ -1004,7 +1017,7 @@ public void setDnAttribute(String dnAttribute) { this.dnAttribute = dnAttribute; } - @ConfigurationProperty(order = 48, + @ConfigurationProperty(order = 50, displayMessageKey = "groupSearchFilter.display", helpMessageKey = "groupSearchFilter.help") public String getGroupSearchFilter() { @@ -1015,7 +1028,7 @@ public void setGroupSearchFilter(String groupSearchFilter) { this.groupSearchFilter = groupSearchFilter; } - @ConfigurationProperty(order = 49, + @ConfigurationProperty(order = 51, displayMessageKey = "readTimeout.display", helpMessageKey = "readTimeout.help") public long getReadTimeout() { @@ -1026,7 +1039,7 @@ public void setReadTimeout(long readTimeout) { this.readTimeout = readTimeout; } - @ConfigurationProperty(order = 50, + @ConfigurationProperty(order = 52, displayMessageKey = "connectTimeout.display", helpMessageKey = "connectTimeout.help") public long getConnectTimeout() { @@ -1037,7 +1050,7 @@ public void setConnectTimeout(long connectTimeout) { this.connectTimeout = connectTimeout; } - @ConfigurationProperty(order = 51, + @ConfigurationProperty(order = 53, displayMessageKey = "syncStrategy.display", helpMessageKey = "syncStrategy.help") public String getSyncStrategy() { diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index fb60548..8e46507 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -196,12 +196,16 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); - Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, - builder.build()); + Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, builder.build()); switch (searchStrategyClass.getSimpleName()) { case "PagedSearchStrategy": - return getLatestSyncTokenPaged(oclass, builder); + if (conn.getConfiguration().getChangeLogPagingSupport()) { + return getLatestSyncTokenPaged(oclass, builder); + } + else { + return getLatestSyncTokenDefault(oclass, builder); + } default: return getLatestSyncTokenDefault(oclass, builder); } diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties index 4c23cb9..23408a9 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=Change Number Attribute changeNumberAttribute.help=The name of the change number attribute in the change log entry. Default is "changeNumber". changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Filter with Or Instead of And filterWithOrInsteadOfAnd.help=Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead. Default is "false". removeLogEntryObjectClassFromFilter.display=Remove Log Entry Object Class from Filter diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_de.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_de.properties index cd54cf6..0ad98bf 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_de.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_de.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=\u00c4nderungsnummerattribut changeNumberAttribute.help=Das \u00c4nderungsnummerattribut im \u00c4nderungsprotokolleintrag. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Filtern mit Or anstelle von And filterWithOrInsteadOfAnd.help=Normalerweise ist der Filter, der zum Abrufen von \u00c4nderungsprotokolleintr\u00e4gen verwendet wird, ein And-basierter Filter, der ein Intervall von \u00c4nderungseintr\u00e4gen abruft. Wenn diese Eigenschaft gesetzt ist, verwendet der Filter stattdessen eine Or-Bedingung, um die erforderlichen \u00c4nderungsnummern abzurufen. removeLogEntryObjectClassFromFilter.display=\u00c4nderungseintrag-Objektklasse aus dem Filter entfernen diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_es.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_es.properties index 92a0bca..84483b5 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_es.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_es.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=Atributo de cambio de n\u00famero changeNumberAttribute.help=El nombre del atributo de cambio de n\u00famero en la entrada del registro de cambios. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Filtrar con Or en vez de And filterWithOrInsteadOfAnd.help=Para recopilar las entradas del registro de cambios se usa normalmente un filtro basado en And que recupera un intervalo de entradas de cambios. Si se configura esta propiedad, el filtro aplicar\u00e1 Or conjuntamente a los n\u00fameros de cambios necesarios. removeLogEntryObjectClassFromFilter.display=Suprimir del filtro la clase de objeto de entrada de registro diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_fr.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_fr.properties index e463e6e..ec062ff 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_fr.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_fr.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=Attribut de num\u00e9ro\u00b7de modification changeNumberAttribute.help=Nom de l\u2019attribut du num\u00e9ro de modification dans l\u2019entr\u00e9e du journal des modifications. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Filtrer avec Ou au lieu de Et filterWithOrInsteadOfAnd.help=En g\u00e9n\u00e9ral, le filtre utilis\u00e9 pour extraire les entr\u00e9es du journal des modifications est un filtre de type Et r\u00e9cup\u00e9rant un intervalle d\u2019entr\u00e9es de modifications. Si cette propri\u00e9t\u00e9 est d\u00e9finie, le filtre applique \u00e0 la place la r\u00e8gle Ou pour recueillir les num\u00e9ros de modifications requises. removeLogEntryObjectClassFromFilter.display=Supprimer la classe d\u2019objet des entr\u00e9es de journal du filtre diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_it.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_it.properties index 79c256e..4738e7e 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_it.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_it.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=Attributo numero modifiche changeNumberAttribute.help=Il nome dell\u2019attributo del numero modifiche nella voce del log delle modifiche. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Esegui filtro con Or al posto di And filterWithOrInsteadOfAnd.help=In genere, il filtro usato per richiamare le voci del log utilizza l\u2019operatore And quando richiama un intervallo di modifiche. Se si imposta questa propriet\u00e0, il filtro utilizzer\u00e0 l\u2019operatore Or sui numeri delle modifiche richieste. removeLogEntryObjectClassFromFilter.display=Rimuovi classe oggetto changeLogEntry dal filtro diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ja.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ja.properties index 1031d0c..abc954c 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ja.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ja.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=\u5909\u66f4\u756a\u53f7\u5c5e\u6027 changeNumberAttribute.help=\u5909\u66f4\u30ed\u30b0\u30a8\u30f3\u30c8\u30ea\u306e\u5909\u66f4\u756a\u53f7\u5c5e\u6027\u306e\u540d\u524d\u3002 changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=AND \u306e\u4ee3\u308f\u308a\u306b OR \u3067\u30d5\u30a3\u30eb\u30bf\u3092\u9069\u7528 filterWithOrInsteadOfAnd.help=\u901a\u5e38\u3001\u5909\u66f4\u30ed\u30b0\u30a8\u30f3\u30c8\u30ea\u306e\u30d5\u30a7\u30c3\u30c1\u306b\u4f7f\u7528\u3055\u308c\u308b\u30d5\u30a3\u30eb\u30bf\u306f AND \u30d9\u30fc\u30b9\u306e\u30d5\u30a3\u30eb\u30bf\u3067\u3001\u5909\u66f4\u30a8\u30f3\u30c8\u30ea\u306e\u9593\u9694\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\u3053\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u30fc\u3092\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u30d5\u30a3\u30eb\u30bf\u306f\u5fc5\u8981\u306a\u5909\u66f4\u756a\u53f7\u3092 OR \u3067\u53ce\u96c6\u3057\u307e\u3059\u3002 removeLogEntryObjectClassFromFilter.display=\u30d5\u30a3\u30eb\u30bf\u304b\u3089\u30ed\u30b0\u30a8\u30f3\u30c8\u30ea\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u30af\u30e9\u30b9\u3092\u524a\u9664 diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ko.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ko.properties index 4b1b56c..c593ef4 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ko.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_ko.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=\uc22b\uc790 \ubcc0\uacbd \uc18d\uc131 changeNumberAttribute.help=\ubcc0\uacbd \ub85c\uadf8 \ud56d\ubaa9\uc758 \uc22b\uc790 \ubcc0\uacbd \uc18d\uc131 \uc774\ub984\uc785\ub2c8\ub2e4. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=And \ub300\uc2e0 Or\ub85c \ud544\ud130 filterWithOrInsteadOfAnd.help=\uc77c\ubc18\uc801\uc73c\ub85c \ubcc0\uacbd \ub85c\uadf8 \ud56d\ubaa9\uc744 \ubd88\ub7ec\uc62c \ub54c \uc0ac\uc6a9\ub418\ub294 \ud544\ud130\ub294 \ubcc0\uacbd \ud56d\ubaa9\uc758 \uac04\uaca9\uc744 \uac80\uc0c9\ud558\ub294 And \uae30\ubc18 \ud544\ud130\uc785\ub2c8\ub2e4. \uc774 \ub4f1\ub85d \uc815\ubcf4\ub97c \uc124\uc815\ud558\uba74 \ud544\ud130\uac00 \ub300\uc2e0 \ud544\uc218 \ubcc0\uacbd \uc22b\uc790\uac00 \ud568\uaed8 \uc9c0\uc815\ub41c Or\uac00 \ub429\ub2c8\ub2e4. removeLogEntryObjectClassFromFilter.display=\ud544\ud130\uc5d0\uc11c \ub85c\uadf8 \ud56d\ubaa9 \uac1d\uccb4 \ud074\ub798\uc2a4 \uc81c\uac70 diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_pt.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_pt.properties index 4743894..c4c8bd0 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_pt.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_pt.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display= Atributo do n\u00famero de altera\u00e7\u00e3o changeNumberAttribute.help=O nome do atributo do n\u00famero de altera\u00e7\u00e3o na entrada do registro de altera\u00e7\u00e3o. changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=Filtrar com OR em vez de AND filterWithOrInsteadOfAnd.help=Normalmente, o filtro usado para obter entradas de registro de altera\u00e7\u00e3o \u00e9 um filtro baseado em AND que recupera um intervalo de entradas de altera\u00e7\u00e3o. Se essa propriedade estiver definida, o filtro ser\u00e1 aplicado ou, em vez disso, conjuntamente aos n\u00fameros de altera\u00e7\u00f5es necess\u00e1rios. removeLogEntryObjectClassFromFilter.display=Remover classe de objeto de entrada de registro do filtro diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh.properties index 164ed93..8fc2393 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh.properties @@ -76,6 +76,8 @@ changeNumberAttribute.display=\u66f4\u6539\u7f16\u53f7\u5c5e\u6027 changeNumberAttribute.help=\u66f4\u6539\u65e5\u5fd7\u6761\u76ee\u4e2d\u7684\u66f4\u6539\u7f16\u53f7\u5c5e\u6027\u7684\u540d\u79f0\u3002 changeLogContext.display=Change Log Context changeLogContext.help=The DN of the Change Log context. Default is "cn=changelog" +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=\u4f7f\u7528 OR \u800c\u4e0d\u662f AND \u8fdb\u884c\u8fc7\u6ee4 filterWithOrInsteadOfAnd.help=\u901a\u5e38\uff0c\u7528\u4e8e\u83b7\u53d6\u66f4\u6539\u65e5\u5fd7\u6761\u76ee\u7684\u8fc7\u6ee4\u5668\u662f\u57fa\u4e8e AND \u6761\u4ef6\u68c0\u7d22\u4e00\u6bb5\u65f6\u95f4\u95f4\u9694\u5185\u7684\u66f4\u6539\u6761\u76ee\u3002\u5982\u679c\u8bbe\u7f6e\u4e86\u6b64\u5c5e\u6027\uff0c\u5219\u8fc7\u6ee4\u5668\u5c06\u6539\u7528 OR \u6761\u4ef6\u914d\u5408\u6240\u9700\u7684\u66f4\u6539\u6570\u91cf\u8fdb\u884c\u8fc7\u6ee4\u3002 removeLogEntryObjectClassFromFilter.display=\u4ece\u8fc7\u6ee4\u5668\u4e2d\u5220\u9664\u65e5\u5fd7\u6761\u76ee\u5bf9\u8c61\u7c7b diff --git a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh_TW.properties b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh_TW.properties index 52cca91..f6c8a26 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh_TW.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages_zh_TW.properties @@ -92,6 +92,8 @@ anyObjectSearchFilter.help=An optional LDAP filter to control which any objects anyObjectSearchScope.display=Any Object search scope anyObjectSearchScope.help=The scope to utilise when searching for any objects. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree' changeNumberAttribute.help=\u8b8a\u66f4\u8a18\u9304\u9805\u76ee\u4e2d\u4e4b\u8b8a\u66f4\u6578\u5b57\u5c6c\u6027\u7684\u540d\u7a31\u3002 +changeLogPagingSupport.display=Change Log Paging Support +changeLogPagingSupport.help=Whether to use paging when querying the change log. filterWithOrInsteadOfAnd.display=\u4f7f\u7528 OR \u800c\u4e0d\u4f7f\u7528 AND \u7be9\u9078 filterWithOrInsteadOfAnd.help=\u4e00\u822c\u5728\u64f7\u53d6\u8b8a\u66f4\u8a18\u9304\u9805\u76ee\u6642\uff0c\u6703\u4f7f\u7528 AND \u7be9\u9078\u5668\u64f7\u53d6\u4e00\u6bb5\u671f\u9593\u5167\u7684\u8b8a\u66f4\u9805\u76ee\u3002\u82e5\u8a2d\u5b9a\u6b64\u7279\u6027\uff0c\u5247\u6703\u6539\u7528 OR \u7be9\u9078\u5668\u642d\u914d\u6240\u9700\u7684\u8b8a\u66f4\u6578\u5b57\u3002 removeLogEntryObjectClassFromFilter.display=\u5f9e\u7be9\u9078\u5668\u79fb\u9664\u8a18\u9304\u9805\u76ee\u7269\u4ef6\u985e\u5225 From f17f2b76974ea62c34d3f49982ef6da588a4fa8a Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:56:07 +0000 Subject: [PATCH 09/12] Remove targetEntryUUID targetEntryUUID isn't part of the standard so directories such as isode m-vault don't use this field. This prevents delete functionality when using entryUUID as a uid attribute (which does exist on isode m-vault). This change allows using targetDN as the uid to compare against: implementing a new PullCorrelationRule, the __UID__ can be used to compare agains the object DN, enabling the deletion functionality --- .../bundles/ldap/sync/GenericChangeLogSyncStrategy.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index 8e46507..70f2fd5 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -224,7 +224,6 @@ public void sync( builder.setAttributesToGet( changeNumberAttr, "targetDN", - "targetEntryUUID", "changeType", "changes", "newRdn", @@ -318,16 +317,13 @@ private SyncDelta createSyncDelta( syncDeltaBuilder.setDeltaType(deltaType); if (deltaType.equals(SyncDeltaType.DELETE)) { - LOG.ok("Creating sync delta for deleted entry {0}", - LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); + LOG.ok("Creating sync delta for deleted entry {0}", targetDN); String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); Uid deletedUid; if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { deletedUid = createUid(uidAttr, targetDN); - } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { - deletedUid = new Uid(LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); } else { // ever fallback to dn without throwing any exception more reliable deletedUid = new Uid(targetDN); From db0956866cf57aa2b84c5b655742f255e3fafad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 28 Feb 2024 09:09:16 +0100 Subject: [PATCH 10/12] Reflow --- .../connid/bundles/ldap/commons/LdapUtil.java | 39 +++-- .../bundles/ldap/search/LdapSearch.java | 8 +- .../ldap/search/LdapSearchStrategy.java | 10 +- .../sync/GenericChangeLogSyncStrategy.java | 98 ++++++------- .../sunds/SunDSChangeLogSyncStrategy.java | 133 ++++++++---------- .../bundles/ldap/BlindTrustProvider.java | 12 +- 6 files changed, 137 insertions(+), 163 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java index ead6a25..1aa4ab6 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/commons/LdapUtil.java @@ -40,7 +40,6 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.AttributeDelta; import org.identityconnectors.framework.common.objects.AttributeDeltaUtil; -import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.Name; public class LdapUtil { @@ -51,8 +50,7 @@ private LdapUtil() { } /** - * Returns true if the names of the given LDAP attributes are equal. Deals - * with null values as a convenience. + * @return @code true} if the names of the given LDAP attributes are equal. Deals with null values as a convenience. */ public static boolean attrNameEquals(String name1, String name2) { if (name1 == null) { @@ -62,8 +60,7 @@ public static boolean attrNameEquals(String name1, String name2) { } /** - * Returns {@code true} if the attribute has the binary option, - * e.g., {@code userCertificate;binary}. + * @return {@code true} if the attribute has the binary option, e.g., {@code userCertificate;binary}. */ public static boolean hasBinaryOption(String ldapAttrName) { return ldapAttrName.toLowerCase(Locale.US).endsWith(LDAP_BINARY_OPTION); @@ -89,22 +86,8 @@ public static String removeBinaryOption(String ldapAttrName) { return ldapAttrName; } - public static String getStringAttrValue(ConnectorObject object, String ldapAttrName) { - org.identityconnectors.framework.common.objects.Attribute attr = object.getAttributeByName(ldapAttrName); - if (attr != null) { - try { - return (String) attr.getValue().get(0); - } catch (IndexOutOfBoundsException e) { - return null; - } catch (Exception e) { - throw new ConnectorException(e); - } - } - return null; - } - /** - * Return the value of the {@code ldapAttrName} parameter cast to a String. + * @return the value of the {@code ldapAttrName} parameter cast to a String. */ public static String getStringAttrValue(Attributes ldapAttrs, String ldapAttrName) { Attribute attr = ldapAttrs.get(ldapAttrName); @@ -119,11 +102,11 @@ public static String getStringAttrValue(Attributes ldapAttrs, String ldapAttrNam } /** - * Return the case insensitive set of values of the {@code + * @return the case insensitive set of values of the {@code * ldapAttrName} parameter cast to a String. */ public static Set getStringAttrValues(Attributes ldapAttrs, String ldapAttrName) { - Set result = new HashSet(); + Set result = new HashSet<>(); addStringAttrValues(ldapAttrs, ldapAttrName, result); return result; } @@ -145,7 +128,8 @@ public static void addStringAttrValues(Attributes ldapAttrs, String ldapAttrName /** * Escapes the given attribute value to the given {@code StringBuilder}. - * Returns {@code true} iff anything was written to the builder. + * + * @returns {@code true} iff anything was written to the builder. */ public static boolean escapeAttrValue(Object value, StringBuilder toBuilder) { if (value == null) { @@ -446,38 +430,47 @@ public ListItr(ListIterator iter) { this.iter = iter; } + @Override public void add(E o) { iter.add(o); } + @Override public boolean hasNext() { return iter.hasNext(); } + @Override public boolean hasPrevious() { return iter.hasPrevious(); } + @Override public E next() { return cast(iter.next()); } + @Override public int nextIndex() { return iter.nextIndex(); } + @Override public E previous() { return cast(iter.previous()); } + @Override public int previousIndex() { return iter.previousIndex(); } + @Override public void remove() { iter.remove(); } + @Override public void set(E o) { iter.set(o); } diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java index 01d24d5..d7dfcb1 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java @@ -210,7 +210,6 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { controls.setSearchScope(searchScope); String optionsFilter = LdapConstants.getSearchFilter(options); - String finalFilter = ""; boolean ignoreBuiltInFilters; if (options.getOptions().containsKey(OP_IGNORE_BUILT_IN_FILTERS)) { @@ -219,6 +218,7 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { ignoreBuiltInFilters = false; } + String finalFilter; if (ignoreBuiltInFilters) { finalFilter = optionsFilter; } else { @@ -231,12 +231,10 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { searchFilter = conn.getConfiguration().getAnyObjectSearchFilter(); } String nativeFilter = filter == null ? null : filter.getNativeFilter(); - + finalFilter = getSearchFilter(optionsFilter, nativeFilter, searchFilter); } - return new LdapInternalSearch(conn, - finalFilter, - dns, strategy, controls); + return new LdapInternalSearch(conn, finalFilter, dns, strategy, controls); } protected Set getLdapAttributesToGet(final Set attrsToGet) { diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java index e0eb7d5..62570e3 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearchStrategy.java @@ -1,18 +1,18 @@ -/* +/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * + * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. - * + * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. - * + * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. - * + * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index 70f2fd5..d9d659c 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -55,6 +55,8 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; @@ -109,7 +111,7 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { public GenericChangeLogSyncStrategy(LdapConnection conn) { this.conn = conn; } - + protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptionsBuilder builder) { LOG.ok("Getting latest sync token with pages of size {0}", conn.getConfiguration().getChangeLogBlockSize()); @@ -125,6 +127,7 @@ protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptions cookies[0] = null; SearchResultsHandler handler = new SearchResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { int changeNumber = convertToInt( @@ -156,17 +159,21 @@ public void handleResult(final SearchResult result) { return new SyncToken(maxChangeNumber[0]); } - + protected SyncToken getLatestSyncTokenDefault(ObjectClass oclass, OperationOptionsBuilder builder) { final int[] maxChangeNumber = { 0 }; LOG.ok("Getting latest sync token with a regular search"); ResultsHandler handler = new ResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { - int changeNumber = convertToInt((String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); - if (changeNumber > maxChangeNumber[0]) { - maxChangeNumber[0] = changeNumber; + Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); + if (changeNumberAttr != null) { + int changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); + if (changeNumber > maxChangeNumber[0]) { + maxChangeNumber[0] = changeNumber; + } } return true; @@ -179,7 +186,7 @@ public boolean handle(final ConnectorObject object) { handler, builder.build(), conn.getConfiguration().getChangeLogContext()); - + search.execute(); return new SyncToken(maxChangeNumber[0]); @@ -196,15 +203,15 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); - Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, builder.build()); + Class searchStrategyClass = + LdapSearchStrategy.getSearchStrategy(conn, builder.build()); switch (searchStrategyClass.getSimpleName()) { case "PagedSearchStrategy": if (conn.getConfiguration().getChangeLogPagingSupport()) { return getLatestSyncTokenPaged(oclass, builder); - } - else { - return getLatestSyncTokenDefault(oclass, builder); + } else { + return getLatestSyncTokenDefault(oclass, builder); } default: return getLatestSyncTokenDefault(oclass, builder); @@ -217,6 +224,7 @@ public void sync( final SyncResultsHandler handler, final OperationOptions options, final ObjectClass oclass) { + final String changeNumberAttr = getChangeNumberAttribute(); OperationOptionsBuilder builder = new OperationOptionsBuilder(); @@ -237,15 +245,18 @@ public void sync( final boolean[] results = new boolean[1]; ResultsHandler resultsHandler = new ResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { results[0] = true; - - int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), - -1); - if (changeNumber > currentChangeNumber[0]) { - currentChangeNumber[0] = changeNumber; + Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); + int changeNumber = -1; + if (changeNumberAttr != null) { + changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); + if (changeNumber > currentChangeNumber[0]) { + currentChangeNumber[0] = changeNumber; + } } SyncDelta delta; @@ -265,12 +276,14 @@ public boolean handle(final ConnectorObject object) { do { results[0] = false; - builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); + builder.setOption( + LdapConstants.SEARCH_FILTER_NAME, + getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); - LdapSearch search = new LdapSearch(conn, - oclass, - null, - resultsHandler, + LdapSearch search = new LdapSearch(conn, + oclass, + null, + resultsHandler, builder.build(), conn.getConfiguration().getChangeLogContext()); @@ -293,8 +306,9 @@ private SyncDelta createSyncDelta( LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber); - final String targetDN = LdapUtil.getStringAttrValue(inputObject, "targetDN"); + final AttributesAccessor inputAttrs = new AttributesAccessor(inputObject.getAttributes()); + final String targetDN = inputAttrs.findString("targetDN"); if (targetDN == null) { LOG.error("Skipping log entry because it does not have a targetDN attribute"); return null; @@ -308,7 +322,7 @@ private SyncDelta createSyncDelta( return null; } - final String changeType = LdapUtil.getStringAttrValue(inputObject, "changeType"); + final String changeType = inputAttrs.findString("changeType"); SyncDeltaType deltaType = getSyncDeltaType(changeType); @@ -341,19 +355,17 @@ private SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - final String changes = LdapUtil.getStringAttrValue(inputObject, "changes"); + final String changes = inputAttrs.findString("changes"); final Map> attrChanges = getAttributeChanges(changeType, changes); if (filterOutByModifiersNames(attrChanges)) { - LOG.ok("Skipping entry because modifiersName is in the list of " - + "modifiersName's to filter out"); + LOG.ok("Skipping entry because modifiersName is in the list of modifiersName's to filter out"); return null; } if (filterOutByAttributes(attrChanges)) { - LOG.ok("Skipping entry because no changed attributes in the list " - + "of attributes to synchronize"); + LOG.ok("Skipping entry because no changed attributes in the list of attributes to synchronize"); return null; } @@ -362,15 +374,13 @@ private SyncDelta createSyncDelta( String newTargetDN = targetDN; if ("modrdn".equalsIgnoreCase(changeType)) { - final String newRdn = LdapUtil.getStringAttrValue(inputObject, "newRdn"); - + final String newRdn = inputAttrs.findString("newRdn"); if (StringUtil.isBlank(newRdn)) { LOG.error("Skipping log entry because it does not have a newRdn attribute"); return null; } - final String newSuperior = LdapUtil.getStringAttrValue(inputObject, "newSuperior"); - + final String newSuperior = inputAttrs.findString("newSuperior"); newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); } @@ -526,8 +536,7 @@ private boolean filterOutByAttributes( return !containsAny(filter, changedAttrs); } - private boolean filterOutByObjectClasses( - final List objectClasses) { + private boolean filterOutByObjectClasses(final List objectClasses) { Set filter = getObjectClassesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by object class disabled"); @@ -537,22 +546,15 @@ private boolean filterOutByObjectClasses( } private SyncDeltaType getSyncDeltaType(final String changeType) { - if ("delete".equalsIgnoreCase(changeType)) { - return SyncDeltaType.DELETE; - } - return SyncDeltaType.CREATE_OR_UPDATE; + return "delete".equalsIgnoreCase(changeType) ? SyncDeltaType.DELETE : SyncDeltaType.CREATE_OR_UPDATE; } - private String getModifiedEntrySearchFilter(ObjectClass oclass) { - if (oclass.equals(ObjectClass.ACCOUNT)) { - return conn.getConfiguration().getAccountSynchronizationFilter(); - } - return null; + private String getModifiedEntrySearchFilter(final ObjectClass oclass) { + return oclass.equals(ObjectClass.ACCOUNT) ? conn.getConfiguration().getAccountSynchronizationFilter() : null; } private int getStartChangeNumber(final SyncToken lastToken) { - return lastToken != null ? (Integer) lastToken. - getValue() + 1 : 0; + return lastToken != null ? (Integer) lastToken.getValue() + 1 : 0; } private Map> getAttributeChanges(final String changeType, final String ldif) { @@ -633,8 +635,7 @@ private Object decodeAttributeValue(final NameValue nameValue) { } } - private String getChangeLogSearchFilter( - final String changeNumberAttr, final int startChangeNumber) { + private String getChangeLogSearchFilter(final String changeNumberAttr, final int startChangeNumber) { int blockSize = conn.getConfiguration().getChangeLogBlockSize(); boolean filterWithOrInsteadOfAnd = conn.getConfiguration(). isFilterWithOrInsteadOfAnd(); @@ -731,8 +732,7 @@ private PasswordDecryptor getPasswordDecryptor() { return passwordDecryptor; } - private boolean containsAny( - final Set haystack, final Collection needles) { + private boolean containsAny(final Set haystack, final Collection needles) { for (String needle : needles) { if (haystack.contains(needle)) { return true; @@ -751,7 +751,7 @@ private Uid createUid(final String uidAttr, final String targetDN) throws Invali return matchingRnd == null ? null : new Uid(matchingRnd.getValue().toString()); } - public static int convertToInt(String number, final int def) { + private static int convertToInt(String number, final int def) { int result = def; if (number != null && number.length() > 0) { int decimal = number.indexOf('.'); diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java index 718ae42..847e3e6 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java @@ -23,15 +23,6 @@ */ package net.tirasa.connid.bundles.ldap.sync.sunds; -import static org.identityconnectors.common.CollectionUtil.isEmpty; -import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveMap; -import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveSet; -import static org.identityconnectors.common.StringUtil.isBlank; -import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.getStringAttrValue; -import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.isUnderContexts; -import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.nullAsEmpty; -import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.quietCreateLdapName; - import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -60,7 +51,6 @@ import net.tirasa.connid.bundles.ldap.search.LdapSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearches; import net.tirasa.connid.bundles.ldap.sync.LdapSyncStrategy; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; @@ -68,6 +58,8 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; @@ -109,12 +101,12 @@ public class SunDSChangeLogSyncStrategy implements LdapSyncStrategy { private PasswordDecryptor passwordDecryptor; static { - LDIF_MODIFY_OPS = newCaseInsensitiveSet(); + LDIF_MODIFY_OPS = CollectionUtil.newCaseInsensitiveSet(); LDIF_MODIFY_OPS.add("add"); LDIF_MODIFY_OPS.add("delete"); LDIF_MODIFY_OPS.add("replace"); - LDAP_DN_ATTRIBUTES = newCaseInsensitiveSet(); + LDAP_DN_ATTRIBUTES = CollectionUtil.newCaseInsensitiveSet(); LDAP_DN_ATTRIBUTES.add("uid"); LDAP_DN_ATTRIBUTES.add("cn"); } @@ -134,6 +126,7 @@ public void sync( final SyncResultsHandler handler, final OperationOptions options, final ObjectClass oclass) { + final String changeNumberAttr = getChangeNumberAttribute(); OperationOptionsBuilder builder = new OperationOptionsBuilder(); @@ -155,15 +148,18 @@ public void sync( final boolean[] results = new boolean[1]; ResultsHandler resultsHandler = new ResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { results[0] = true; - - int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), - -1); - if (changeNumber > currentChangeNumber[0]) { - currentChangeNumber[0] = changeNumber; + Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); + int changeNumber = -1; + if (changeNumberAttr != null) { + changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); + if (changeNumber > currentChangeNumber[0]) { + currentChangeNumber[0] = changeNumber; + } } SyncDelta delta; @@ -183,11 +179,13 @@ public boolean handle(final ConnectorObject object) { do { results[0] = false; - builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); - LdapSearch search = new LdapSearch(conn, - oclass, - null, - resultsHandler, + builder.setOption( + LdapConstants.SEARCH_FILTER_NAME, + getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); + LdapSearch search = new LdapSearch(conn, + oclass, + null, + resultsHandler, builder.build(), conn.getConfiguration().getChangeLogContext()); @@ -206,12 +204,13 @@ private SyncDelta createSyncDelta( final ConnectorObject inputObject, final int changeNumber, final String[] attrsToGetOption, - ObjectClass oclass) throws InvalidNameException { + final ObjectClass oclass) throws InvalidNameException { LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber); - final String targetDN = LdapUtil.getStringAttrValue(inputObject, "targetDN"); + final AttributesAccessor inputAttrs = new AttributesAccessor(inputObject.getAttributes()); + final String targetDN = inputAttrs.findString("targetDN"); if (targetDN == null) { LOG.error("Skipping log entry because it does not have a targetDN attribute"); return null; @@ -225,7 +224,7 @@ private SyncDelta createSyncDelta( return null; } - final String changeType = LdapUtil.getStringAttrValue(inputObject, "changeType"); + final String changeType = inputAttrs.findString("changeType"); SyncDeltaType deltaType = getSyncDeltaType(changeType); @@ -234,8 +233,7 @@ private SyncDelta createSyncDelta( syncDeltaBuilder.setDeltaType(deltaType); if (deltaType.equals(SyncDeltaType.DELETE)) { - LOG.ok("Creating sync delta for deleted entry {0}", - LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); + LOG.ok("Creating sync delta for deleted entry {0}", inputAttrs.findString("targetEntryUUID")); String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); @@ -243,7 +241,7 @@ private SyncDelta createSyncDelta( if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { deletedUid = createUid(uidAttr, targetDN); } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { - deletedUid = new Uid(LdapUtil.getStringAttrValue(inputObject, "targetEntryUUID")); + deletedUid = new Uid(inputAttrs.findString("targetEntryUUID")); } else { // ever fallback to dn without throwing any exception more reliable deletedUid = new Uid(targetDN); @@ -261,7 +259,7 @@ private SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - final String changes = LdapUtil.getStringAttrValue(inputObject, "changes"); + final String changes = inputAttrs.findString("changes"); final Map> attrChanges = getAttributeChanges(changeType, changes); @@ -282,15 +280,13 @@ private SyncDelta createSyncDelta( String newTargetDN = targetDN; if ("modrdn".equalsIgnoreCase(changeType)) { - final String newRdn = LdapUtil.getStringAttrValue(inputObject, "newRdn"); - + final String newRdn = inputAttrs.findString("newRdn"); if (StringUtil.isBlank(newRdn)) { LOG.error("Skipping log entry because it does not have a newRdn attribute"); return null; } - final String newSuperior = LdapUtil.getStringAttrValue(inputObject, "newSuperior"); - + final String newSuperior = inputAttrs.findString("newSuperior"); newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); } @@ -326,8 +322,7 @@ private SyncDelta createSyncDelta( CollectionUtil.nullAsEmpty(oclassAttr.getValue()), String.class); if (filterOutByObjectClasses(objectClasses)) { - LOG.ok("Skipping entry because no object class in the list of " - + "object classes to synchronize"); + LOG.ok("Skipping entry because no object class in the list of object classes to synchronize"); return null; } @@ -400,7 +395,7 @@ private String getNewTargetDN( newTargetName.remove(targetName.size() - 1); } } else { - newTargetName = quietCreateLdapName(newSuperior); + newTargetName = LdapUtil.quietCreateLdapName(newSuperior); } newTargetName.add(newRdn); return newTargetName.toString(); @@ -410,34 +405,29 @@ private String getNewTargetDN( } private boolean filterOutByBaseContexts(final LdapName targetName) { - List baseContexts = conn.getConfiguration(). - getBaseContextsToSynchronizeAsLdapNames(); + List baseContexts = conn.getConfiguration().getBaseContextsToSynchronizeAsLdapNames(); if (baseContexts.isEmpty()) { baseContexts = conn.getConfiguration().getBaseContextsAsLdapNames(); } - return !isUnderContexts(targetName, baseContexts); + return !LdapUtil.isUnderContexts(targetName, baseContexts); } - private boolean filterOutByModifiersNames( - final Map> changes) { - Set filter = conn.getConfiguration(). - getModifiersNamesToFilterOutAsLdapNames(); + private boolean filterOutByModifiersNames(final Map> changes) { + Set filter = conn.getConfiguration().getModifiersNamesToFilterOutAsLdapNames(); if (filter.isEmpty()) { LOG.ok("Filtering by modifiersName disabled"); return false; } List modifiersNameValues = changes.get("modifiersName"); - if (isEmpty(modifiersNameValues)) { + if (CollectionUtil.isEmpty(modifiersNameValues)) { LOG.ok("Not filtering by modifiersName because not set for this entry"); return false; } - LdapName modifiersName = quietCreateLdapName(modifiersNameValues.get(0). - toString()); + LdapName modifiersName = LdapUtil.quietCreateLdapName(modifiersNameValues.get(0).toString()); return filter.contains(modifiersName); } - private boolean filterOutByAttributes( - final Map> attrChanges) { + private boolean filterOutByAttributes(final Map> attrChanges) { Set filter = getAttributesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by attributes disabled"); @@ -447,8 +437,7 @@ private boolean filterOutByAttributes( return !containsAny(filter, changedAttrs); } - private boolean filterOutByObjectClasses( - final List objectClasses) { + private boolean filterOutByObjectClasses(final List objectClasses) { Set filter = getObjectClassesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by object class disabled"); @@ -472,17 +461,15 @@ private String getModifiedEntrySearchFilter(ObjectClass oclass) { } private int getStartChangeNumber(final SyncToken lastToken) { - Integer lastTokenValue = lastToken != null ? (Integer) lastToken. - getValue() : null; + Integer lastTokenValue = lastToken != null ? (Integer) lastToken.getValue() : null; if (lastTokenValue == null) { return getChangeLogAttributes().getFirstChangeNumber(); } return lastTokenValue + 1; // Since the token value is the last value. } - private Map> getAttributeChanges( - final String changeType, final String ldif) { - final Map> result = newCaseInsensitiveMap(); + private Map> getAttributeChanges(final String changeType, final String ldif) { + final Map> result = CollectionUtil.newCaseInsensitiveMap(); if ("modify".equalsIgnoreCase(changeType)) { LdifParser parser = new LdifParser(ldif); @@ -559,8 +546,7 @@ private Object decodeAttributeValue(final NameValue nameValue) { } } - private String getChangeLogSearchFilter( - final String changeNumberAttr, final int startChangeNumber) { + private String getChangeLogSearchFilter(final String changeNumberAttr, final int startChangeNumber) { int blockSize = conn.getConfiguration().getChangeLogBlockSize(); boolean filterWithOrInsteadOfAnd = conn.getConfiguration(). isFilterWithOrInsteadOfAnd(); @@ -620,21 +606,21 @@ ChangeLogAttributes getChangeLogAttributes() { try { Attributes attrs = conn.getInitialContext().getAttributes("", new String[] { "changeLog", "firstChangeNumber", "lastChangeNumber" }); - String changeLog = getStringAttrValue(attrs, "changeLog"); - String firstChangeNumber = getStringAttrValue(attrs, "firstChangeNumber"); - String lastChangeNumber = getStringAttrValue(attrs, "lastChangeNumber"); + String changeLog = LdapUtil.getStringAttrValue(attrs, "changeLog"); + String firstChangeNumber = LdapUtil.getStringAttrValue(attrs, "firstChangeNumber"); + String lastChangeNumber = LdapUtil.getStringAttrValue(attrs, "lastChangeNumber"); if (changeLog == null || firstChangeNumber == null | lastChangeNumber == null) { - String error = "Unable to locate the replication change log.\n" + throw new ConnectorException("Unable to locate the replication change log.\n" + "From the admin console please verify that the " + "change log is enabled under Configuration: " + "Replication: Supplier Settings and that the Retro " + "Change Log Plugin is enabled under Configuration: " - + "Plug-ins: Retro Change Log Plugin"; - throw new ConnectorException(error); + + "Plug-ins: Retro Change Log Plugin"); } - changeLogAttrs = new ChangeLogAttributes(changeLog, - convertToInt(firstChangeNumber, 0), convertToInt( - lastChangeNumber, 0)); + changeLogAttrs = new ChangeLogAttributes( + changeLog, + convertToInt(firstChangeNumber, 0), + convertToInt(lastChangeNumber, 0)); } catch (NamingException e) { throw new ConnectorException(e); } @@ -644,7 +630,7 @@ ChangeLogAttributes getChangeLogAttributes() { private String getChangeNumberAttribute() { String result = conn.getConfiguration().getChangeNumberAttribute(); - if (isBlank(result)) { + if (StringUtil.isBlank(result)) { result = "changeNumber"; } return result; @@ -652,9 +638,8 @@ private String getChangeNumberAttribute() { private Set getAttributesToSynchronize() { if (attrsToSync == null) { - Set result = newCaseInsensitiveSet(); - result.addAll(Arrays.asList(nullAsEmpty(conn.getConfiguration(). - getAttributesToSynchronize()))); + Set result = CollectionUtil.newCaseInsensitiveSet(); + result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getAttributesToSynchronize()))); if (conn.getConfiguration().isSynchronizePasswords()) { result.add(conn.getConfiguration(). getPasswordAttributeToSynchronize()); @@ -666,9 +651,8 @@ private Set getAttributesToSynchronize() { private Set getObjectClassesToSynchronize() { if (oclassesToSync == null) { - Set result = newCaseInsensitiveSet(); - result.addAll(Arrays.asList(nullAsEmpty(conn.getConfiguration(). - getObjectClassesToSynchronize()))); + Set result = CollectionUtil.newCaseInsensitiveSet(); + result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getObjectClassesToSynchronize()))); oclassesToSync = result; } return oclassesToSync; @@ -686,8 +670,7 @@ private PasswordDecryptor getPasswordDecryptor() { return passwordDecryptor; } - private boolean containsAny( - final Set haystack, final Collection needles) { + private boolean containsAny(final Set haystack, final Collection needles) { for (String needle : needles) { if (haystack.contains(needle)) { return true; diff --git a/src/test/java/net/tirasa/connid/bundles/ldap/BlindTrustProvider.java b/src/test/java/net/tirasa/connid/bundles/ldap/BlindTrustProvider.java index fb3a34d..cfa65fc 100644 --- a/src/test/java/net/tirasa/connid/bundles/ldap/BlindTrustProvider.java +++ b/src/test/java/net/tirasa/connid/bundles/ldap/BlindTrustProvider.java @@ -1,18 +1,18 @@ -/* +/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * + * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. - * + * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. - * + * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. - * + * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields @@ -30,7 +30,6 @@ import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; - import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactorySpi; @@ -58,6 +57,7 @@ public static final void register() { } } + @SuppressWarnings("deprecation") public BlindTrustProvider() { super(ID, 1.0, ID); put("TrustManagerFactory." + ALGORITHM, BlindTrustManagerFactory.class.getName()); From fbfa03e15008ba7a8adb44baf48cc671168d6aac Mon Sep 17 00:00:00 2001 From: Sylinsic <38617929+Sylinsic@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:11:57 +0000 Subject: [PATCH 11/12] SunDSChangeLogSyncStrategy extends GenericChangeLogSyncStrategy Reduce repeated code --- .../sync/GenericChangeLogSyncStrategy.java | 170 ++--- .../sunds/SunDSChangeLogSyncStrategy.java | 628 +----------------- 2 files changed, 110 insertions(+), 688 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index d9d659c..e9fe667 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -55,7 +55,6 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; -import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; @@ -80,22 +79,22 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { // TODO detect that the change log has been trimmed. - private static final Log LOG = Log.getLog(GenericChangeLogSyncStrategy.class); + protected static final Log LOG = Log.getLog(GenericChangeLogSyncStrategy.class); /** * The list of attribute operations supported by the "modify" LDIF change type. */ - private static final Set LDIF_MODIFY_OPS; + protected static final Set LDIF_MODIFY_OPS; - private static final Set LDAP_DN_ATTRIBUTES; + protected static final Set LDAP_DN_ATTRIBUTES; - private final LdapConnection conn; + protected final LdapConnection conn; - private Set oclassesToSync; + protected Set oclassesToSync; - private Set attrsToSync; + protected Set attrsToSync; - private PasswordDecryptor passwordDecryptor; + protected PasswordDecryptor passwordDecryptor; static { LDIF_MODIFY_OPS = CollectionUtil.newCaseInsensitiveSet(); @@ -111,7 +110,7 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { public GenericChangeLogSyncStrategy(LdapConnection conn) { this.conn = conn; } - + protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptionsBuilder builder) { LOG.ok("Getting latest sync token with pages of size {0}", conn.getConfiguration().getChangeLogBlockSize()); @@ -127,7 +126,6 @@ protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptions cookies[0] = null; SearchResultsHandler handler = new SearchResultsHandler() { - @Override public boolean handle(final ConnectorObject object) { int changeNumber = convertToInt( @@ -159,21 +157,17 @@ public void handleResult(final SearchResult result) { return new SyncToken(maxChangeNumber[0]); } - + protected SyncToken getLatestSyncTokenDefault(ObjectClass oclass, OperationOptionsBuilder builder) { final int[] maxChangeNumber = { 0 }; LOG.ok("Getting latest sync token with a regular search"); ResultsHandler handler = new ResultsHandler() { - @Override public boolean handle(final ConnectorObject object) { - Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); - if (changeNumberAttr != null) { - int changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); - if (changeNumber > maxChangeNumber[0]) { - maxChangeNumber[0] = changeNumber; - } + int changeNumber = convertToInt((String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); + if (changeNumber > maxChangeNumber[0]) { + maxChangeNumber[0] = changeNumber; } return true; @@ -186,7 +180,7 @@ public boolean handle(final ConnectorObject object) { handler, builder.build(), conn.getConfiguration().getChangeLogContext()); - + search.execute(); return new SyncToken(maxChangeNumber[0]); @@ -203,15 +197,15 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); - Class searchStrategyClass = - LdapSearchStrategy.getSearchStrategy(conn, builder.build()); + Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, builder.build()); switch (searchStrategyClass.getSimpleName()) { case "PagedSearchStrategy": if (conn.getConfiguration().getChangeLogPagingSupport()) { return getLatestSyncTokenPaged(oclass, builder); - } else { - return getLatestSyncTokenDefault(oclass, builder); + } + else { + return getLatestSyncTokenDefault(oclass, builder); } default: return getLatestSyncTokenDefault(oclass, builder); @@ -224,7 +218,6 @@ public void sync( final SyncResultsHandler handler, final OperationOptions options, final ObjectClass oclass) { - final String changeNumberAttr = getChangeNumberAttribute(); OperationOptionsBuilder builder = new OperationOptionsBuilder(); @@ -245,18 +238,15 @@ public void sync( final boolean[] results = new boolean[1]; ResultsHandler resultsHandler = new ResultsHandler() { - @Override public boolean handle(final ConnectorObject object) { results[0] = true; + + int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), + -1); - Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); - int changeNumber = -1; - if (changeNumberAttr != null) { - changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); - if (changeNumber > currentChangeNumber[0]) { - currentChangeNumber[0] = changeNumber; - } + if (changeNumber > currentChangeNumber[0]) { + currentChangeNumber[0] = changeNumber; } SyncDelta delta; @@ -276,14 +266,12 @@ public boolean handle(final ConnectorObject object) { do { results[0] = false; - builder.setOption( - LdapConstants.SEARCH_FILTER_NAME, - getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); + builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); - LdapSearch search = new LdapSearch(conn, - oclass, - null, - resultsHandler, + LdapSearch search = new LdapSearch(conn, + oclass, + null, + resultsHandler, builder.build(), conn.getConfiguration().getChangeLogContext()); @@ -298,7 +286,7 @@ public boolean handle(final ConnectorObject object) { } while (results[0]); } - private SyncDelta createSyncDelta( + protected SyncDelta createSyncDelta( final ConnectorObject inputObject, final int changeNumber, final String[] attrsToGetOption, @@ -309,6 +297,7 @@ private SyncDelta createSyncDelta( final AttributesAccessor inputAttrs = new AttributesAccessor(inputObject.getAttributes()); final String targetDN = inputAttrs.findString("targetDN"); + if (targetDN == null) { LOG.error("Skipping log entry because it does not have a targetDN attribute"); return null; @@ -331,28 +320,7 @@ private SyncDelta createSyncDelta( syncDeltaBuilder.setDeltaType(deltaType); if (deltaType.equals(SyncDeltaType.DELETE)) { - LOG.ok("Creating sync delta for deleted entry {0}", targetDN); - - String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); - - Uid deletedUid; - if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { - deletedUid = createUid(uidAttr, targetDN); - } else { - // ever fallback to dn without throwing any exception more reliable - deletedUid = new Uid(targetDN); - } - // Build an empty connector object, with minimal information - LDAP-8 - ConnectorObjectBuilder objectBuilder = new ConnectorObjectBuilder(); - objectBuilder.setObjectClass(oclass); - objectBuilder.setUid(deletedUid); - objectBuilder.setName("fake-dn"); - objectBuilder.addAttributes(Collections.emptySet()); - - syncDeltaBuilder.setUid(deletedUid); - syncDeltaBuilder.setObject(objectBuilder.build()); - - return syncDeltaBuilder.build(); + return createDeletionSyncDelta(syncDeltaBuilder, targetDN, oclass, inputAttrs); } final String changes = inputAttrs.findString("changes"); @@ -360,12 +328,14 @@ private SyncDelta createSyncDelta( final Map> attrChanges = getAttributeChanges(changeType, changes); if (filterOutByModifiersNames(attrChanges)) { - LOG.ok("Skipping entry because modifiersName is in the list of modifiersName's to filter out"); + LOG.ok("Skipping entry because modifiersName is in the list of " + + "modifiersName's to filter out"); return null; } if (filterOutByAttributes(attrChanges)) { - LOG.ok("Skipping entry because no changed attributes in the list of attributes to synchronize"); + LOG.ok("Skipping entry because no changed attributes in the list " + + "of attributes to synchronize"); return null; } @@ -375,12 +345,14 @@ private SyncDelta createSyncDelta( if ("modrdn".equalsIgnoreCase(changeType)) { final String newRdn = inputAttrs.findString("newRdn"); + if (StringUtil.isBlank(newRdn)) { LOG.error("Skipping log entry because it does not have a newRdn attribute"); return null; } final String newSuperior = inputAttrs.findString("newSuperior"); + newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); } @@ -477,7 +449,25 @@ private SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - private String getNewTargetDN( + protected SyncDelta createDeletionSyncDelta(SyncDeltaBuilder syncDeltaBuilder, String targetDN, ObjectClass oclass, AttributesAccessor inputAttrs) throws InvalidNameException { + LOG.ok("Creating sync delta for deleted entry {0}", targetDN); + + Uid deletedUid = new Uid(targetDN); + + // Build an empty connector object, with minimal information - LDAP-8 + ConnectorObjectBuilder objectBuilder = new ConnectorObjectBuilder(); + objectBuilder.setObjectClass(oclass); + objectBuilder.setUid(deletedUid); + objectBuilder.setName("fake-dn"); + objectBuilder.addAttributes(Collections.emptySet()); + + syncDeltaBuilder.setUid(deletedUid); + syncDeltaBuilder.setObject(objectBuilder.build()); + + return syncDeltaBuilder.build(); + } + + protected String getNewTargetDN( final LdapName targetName, final String newSuperior, final String newRdn) { @@ -499,7 +489,7 @@ private String getNewTargetDN( } } - private boolean filterOutByBaseContexts(final LdapName targetName) { + protected boolean filterOutByBaseContexts(final LdapName targetName) { List baseContexts = conn.getConfiguration(). getBaseContextsToSynchronizeAsLdapNames(); if (baseContexts.isEmpty()) { @@ -508,7 +498,7 @@ private boolean filterOutByBaseContexts(final LdapName targetName) { return !LdapUtil.isUnderContexts(targetName, baseContexts); } - private boolean filterOutByModifiersNames( + protected boolean filterOutByModifiersNames( final Map> changes) { Set filter = conn.getConfiguration(). getModifiersNamesToFilterOutAsLdapNames(); @@ -525,7 +515,7 @@ private boolean filterOutByModifiersNames( return filter.contains(modifiersName); } - private boolean filterOutByAttributes( + protected boolean filterOutByAttributes( final Map> attrChanges) { Set filter = getAttributesToSynchronize(); if (filter.isEmpty()) { @@ -536,7 +526,8 @@ private boolean filterOutByAttributes( return !containsAny(filter, changedAttrs); } - private boolean filterOutByObjectClasses(final List objectClasses) { + protected boolean filterOutByObjectClasses( + final List objectClasses) { Set filter = getObjectClassesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by object class disabled"); @@ -545,19 +536,26 @@ private boolean filterOutByObjectClasses(final List objectClasses) { return !containsAny(filter, objectClasses); } - private SyncDeltaType getSyncDeltaType(final String changeType) { - return "delete".equalsIgnoreCase(changeType) ? SyncDeltaType.DELETE : SyncDeltaType.CREATE_OR_UPDATE; + protected SyncDeltaType getSyncDeltaType(final String changeType) { + if ("delete".equalsIgnoreCase(changeType)) { + return SyncDeltaType.DELETE; + } + return SyncDeltaType.CREATE_OR_UPDATE; } - private String getModifiedEntrySearchFilter(final ObjectClass oclass) { - return oclass.equals(ObjectClass.ACCOUNT) ? conn.getConfiguration().getAccountSynchronizationFilter() : null; + protected String getModifiedEntrySearchFilter(ObjectClass oclass) { + if (oclass.equals(ObjectClass.ACCOUNT)) { + return conn.getConfiguration().getAccountSynchronizationFilter(); + } + return null; } - private int getStartChangeNumber(final SyncToken lastToken) { - return lastToken != null ? (Integer) lastToken.getValue() + 1 : 0; + protected int getStartChangeNumber(final SyncToken lastToken) { + return lastToken != null ? (Integer) lastToken. + getValue() + 1 : 0; } - private Map> getAttributeChanges(final String changeType, final String ldif) { + protected Map> getAttributeChanges(final String changeType, final String ldif) { final Map> result = CollectionUtil.newCaseInsensitiveMap(); if ("modify".equalsIgnoreCase(changeType)) { @@ -614,7 +612,7 @@ private Map> getAttributeChanges(final String changeType, f return result; } - private Object decodeAttributeValue(final NameValue nameValue) { + protected Object decodeAttributeValue(final NameValue nameValue) { String value = nameValue.getValue(); if (value.startsWith(":")) { // This is a Base64 encoded value... @@ -635,7 +633,8 @@ private Object decodeAttributeValue(final NameValue nameValue) { } } - private String getChangeLogSearchFilter(final String changeNumberAttr, final int startChangeNumber) { + protected String getChangeLogSearchFilter( + final String changeNumberAttr, final int startChangeNumber) { int blockSize = conn.getConfiguration().getChangeLogBlockSize(); boolean filterWithOrInsteadOfAnd = conn.getConfiguration(). isFilterWithOrInsteadOfAnd(); @@ -690,7 +689,7 @@ private String getChangeLogSearchFilter(final String changeNumberAttr, final int return result.toString(); } - private String getChangeNumberAttribute() { + protected String getChangeNumberAttribute() { String result = conn.getConfiguration().getChangeNumberAttribute(); if (StringUtil.isBlank(result)) { result = "changeNumber"; @@ -698,7 +697,7 @@ private String getChangeNumberAttribute() { return result; } - private Set getAttributesToSynchronize() { + protected Set getAttributesToSynchronize() { if (attrsToSync == null) { Set result = CollectionUtil.newCaseInsensitiveSet(); result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getAttributesToSynchronize()))); @@ -711,7 +710,7 @@ private Set getAttributesToSynchronize() { return attrsToSync; } - private Set getObjectClassesToSynchronize() { + protected Set getObjectClassesToSynchronize() { if (oclassesToSync == null) { Set result = CollectionUtil.newCaseInsensitiveSet(); result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getObjectClassesToSynchronize()))); @@ -720,7 +719,7 @@ private Set getObjectClassesToSynchronize() { return oclassesToSync; } - private PasswordDecryptor getPasswordDecryptor() { + protected PasswordDecryptor getPasswordDecryptor() { if (passwordDecryptor == null) { conn.getConfiguration().getPasswordDecryptionKey().access(decryptionKey -> { conn.getConfiguration().getPasswordDecryptionInitializationVector().access(decryptionIV -> { @@ -732,7 +731,8 @@ private PasswordDecryptor getPasswordDecryptor() { return passwordDecryptor; } - private boolean containsAny(final Set haystack, final Collection needles) { + protected boolean containsAny( + final Set haystack, final Collection needles) { for (String needle : needles) { if (haystack.contains(needle)) { return true; @@ -741,7 +741,7 @@ private boolean containsAny(final Set haystack, final Collection return false; } - private Uid createUid(final String uidAttr, final String targetDN) throws InvalidNameException { + protected Uid createUid(final String uidAttr, final String targetDN) throws InvalidNameException { Rdn matchingRnd = null; for (Rdn rdn : new LdapName(targetDN).getRdns()) { if (uidAttr.equalsIgnoreCase(rdn.getType())) { @@ -751,7 +751,7 @@ private Uid createUid(final String uidAttr, final String targetDN) throws Invali return matchingRnd == null ? null : new Uid(matchingRnd.getValue().toString()); } - private static int convertToInt(String number, final int def) { + public static int convertToInt(String number, final int def) { int result = def; if (number != null && number.length() > 0) { int decimal = number.indexOf('.'); diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java index 847e3e6..316ebb6 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java @@ -23,54 +23,22 @@ */ package net.tirasa.connid.bundles.ldap.sync.sunds; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; + import javax.naming.InvalidNameException; import javax.naming.NamingException; import javax.naming.directory.Attributes; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; import net.tirasa.connid.bundles.ldap.LdapConnection; -import net.tirasa.connid.bundles.ldap.commons.LdapConstants; -import net.tirasa.connid.bundles.ldap.commons.LdapEntry; import net.tirasa.connid.bundles.ldap.commons.LdapUtil; -import net.tirasa.connid.bundles.ldap.commons.LdifParser; -import net.tirasa.connid.bundles.ldap.commons.PasswordDecryptor; -import net.tirasa.connid.bundles.ldap.commons.LdifParser.ChangeSeparator; -import net.tirasa.connid.bundles.ldap.commons.LdifParser.Line; -import net.tirasa.connid.bundles.ldap.commons.LdifParser.NameValue; -import net.tirasa.connid.bundles.ldap.commons.LdifParser.Separator; -import net.tirasa.connid.bundles.ldap.search.LdapFilter; -import net.tirasa.connid.bundles.ldap.search.LdapSearch; -import net.tirasa.connid.bundles.ldap.search.LdapSearches; -import net.tirasa.connid.bundles.ldap.sync.LdapSyncStrategy; -import org.identityconnectors.common.CollectionUtil; -import org.identityconnectors.common.StringUtil; +import net.tirasa.connid.bundles.ldap.sync.GenericChangeLogSyncStrategy; import org.identityconnectors.common.logging.Log; -import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; -import org.identityconnectors.framework.common.objects.AttributeBuilder; -import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; -import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; -import org.identityconnectors.framework.common.objects.OperationOptions; -import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; -import org.identityconnectors.framework.common.objects.OperationalAttributes; -import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; -import org.identityconnectors.framework.common.objects.SyncDeltaType; -import org.identityconnectors.framework.common.objects.SyncResultsHandler; import org.identityconnectors.framework.common.objects.SyncToken; import org.identityconnectors.framework.common.objects.Uid; @@ -78,41 +46,15 @@ * An implementation of the sync operation based on the retro change log * plugin of Sun Directory Server. */ -public class SunDSChangeLogSyncStrategy implements LdapSyncStrategy { +public class SunDSChangeLogSyncStrategy extends GenericChangeLogSyncStrategy { // TODO detect that the change log has been trimmed. private static final Log LOG = Log.getLog(SunDSChangeLogSyncStrategy.class); - /** - * The list of attribute operations supported by the "modify" LDIF change type. - */ - private static final Set LDIF_MODIFY_OPS; - - private static final Set LDAP_DN_ATTRIBUTES; - - private final LdapConnection conn; - private ChangeLogAttributes changeLogAttrs; - private Set oclassesToSync; - - private Set attrsToSync; - - private PasswordDecryptor passwordDecryptor; - - static { - LDIF_MODIFY_OPS = CollectionUtil.newCaseInsensitiveSet(); - LDIF_MODIFY_OPS.add("add"); - LDIF_MODIFY_OPS.add("delete"); - LDIF_MODIFY_OPS.add("replace"); - - LDAP_DN_ATTRIBUTES = CollectionUtil.newCaseInsensitiveSet(); - LDAP_DN_ATTRIBUTES.add("uid"); - LDAP_DN_ATTRIBUTES.add("cn"); - } - public SunDSChangeLogSyncStrategy(LdapConnection conn) { - this.conn = conn; + super(conn); } @Override @@ -121,346 +63,36 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { } @Override - public void sync( - final SyncToken token, - final SyncResultsHandler handler, - final OperationOptions options, - final ObjectClass oclass) { - - final String changeNumberAttr = getChangeNumberAttribute(); - - OperationOptionsBuilder builder = new OperationOptionsBuilder(); - builder.setScope(OperationOptions.SCOPE_ONE_LEVEL); - builder.setAttributesToGet( - changeNumberAttr, - "targetDN", - "targetEntryUUID", - "changeType", - "changes", - "newRdn", - "deleteOldRdn", - "newSuperior" - ); - builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); - - final int[] currentChangeNumber = { getStartChangeNumber(token) }; - - final boolean[] results = new boolean[1]; - - ResultsHandler resultsHandler = new ResultsHandler() { - - @Override - public boolean handle(final ConnectorObject object) { - results[0] = true; - - Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); - int changeNumber = -1; - if (changeNumberAttr != null) { - changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); - if (changeNumber > currentChangeNumber[0]) { - currentChangeNumber[0] = changeNumber; - } - } - - SyncDelta delta; - try { - delta = createSyncDelta(object, changeNumber, options.getAttributesToGet(), oclass); - } catch (InvalidNameException e) { - delta = null; - } - - if (delta != null) { - return handler.handle(delta); - } - return true; - } - }; - - do { - results[0] = false; - - builder.setOption( - LdapConstants.SEARCH_FILTER_NAME, - getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); - LdapSearch search = new LdapSearch(conn, - oclass, - null, - resultsHandler, - builder.build(), - conn.getConfiguration().getChangeLogContext()); - - search.execute(); - - // We have already processed the current change. - // In the next cycle we want to start with the next change. - if (results[0]) { - currentChangeNumber[0]++; - } - - } while (results[0]); - } - - private SyncDelta createSyncDelta( - final ConnectorObject inputObject, - final int changeNumber, - final String[] attrsToGetOption, - final ObjectClass oclass) throws InvalidNameException { - - LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber); - - final AttributesAccessor inputAttrs = new AttributesAccessor(inputObject.getAttributes()); - - final String targetDN = inputAttrs.findString("targetDN"); - if (targetDN == null) { - LOG.error("Skipping log entry because it does not have a targetDN attribute"); - return null; - } - - final LdapName targetName = LdapUtil.quietCreateLdapName(targetDN); - - if (filterOutByBaseContexts(targetName)) { - LOG.ok("Skipping log entry because it does not match any of the " - + "base contexts to synchronize"); - return null; - } - - final String changeType = inputAttrs.findString("changeType"); - - SyncDeltaType deltaType = getSyncDeltaType(changeType); - - SyncDeltaBuilder syncDeltaBuilder = new SyncDeltaBuilder(); - syncDeltaBuilder.setToken(new SyncToken(changeNumber)); - syncDeltaBuilder.setDeltaType(deltaType); - - if (deltaType.equals(SyncDeltaType.DELETE)) { - LOG.ok("Creating sync delta for deleted entry {0}", inputAttrs.findString("targetEntryUUID")); - - String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); + protected SyncDelta createDeletionSyncDelta(SyncDeltaBuilder syncDeltaBuilder, String targetDN, ObjectClass oclass, + AttributesAccessor inputAttrs) throws InvalidNameException { + LOG.ok("Creating sync delta for deleted entry {0}", inputAttrs.findString("targetEntryUUID")); - Uid deletedUid; - if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { - deletedUid = createUid(uidAttr, targetDN); - } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { - deletedUid = new Uid(inputAttrs.findString("targetEntryUUID")); - } else { - // ever fallback to dn without throwing any exception more reliable - deletedUid = new Uid(targetDN); - } - // Build an empty connector object, with minimal information - LDAP-8 - ConnectorObjectBuilder objectBuilder = new ConnectorObjectBuilder(); - objectBuilder.setObjectClass(oclass); - objectBuilder.setUid(deletedUid); - objectBuilder.setName("fake-dn"); - objectBuilder.addAttributes(Collections.emptySet()); - - syncDeltaBuilder.setUid(deletedUid); - syncDeltaBuilder.setObject(objectBuilder.build()); - - return syncDeltaBuilder.build(); - } - - final String changes = inputAttrs.findString("changes"); - - final Map> attrChanges = getAttributeChanges(changeType, changes); - - if (filterOutByModifiersNames(attrChanges)) { - LOG.ok("Skipping entry because modifiersName is in the list of " - + "modifiersName's to filter out"); - return null; - } - - if (filterOutByAttributes(attrChanges)) { - LOG.ok("Skipping entry because no changed attributes in the list " - + "of attributes to synchronize"); - return null; - } - - // If the change type was modrdn, we need to compute the DN that the entry - // was modified to. - String newTargetDN = targetDN; + String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); - if ("modrdn".equalsIgnoreCase(changeType)) { - final String newRdn = inputAttrs.findString("newRdn"); - if (StringUtil.isBlank(newRdn)) { - LOG.error("Skipping log entry because it does not have a newRdn attribute"); - return null; - } - - final String newSuperior = inputAttrs.findString("newSuperior"); - newTargetDN = getNewTargetDN(targetName, newSuperior, newRdn); - } - - // Always specify the attributes to get. This will return attributes with - // empty values when the attribute is not present, allowing the client to - // detect that the attribute has been removed. - Set attrsToGet; - - if (attrsToGetOption != null) { - attrsToGet = CollectionUtil.newSet(attrsToGetOption); - // Do not retrieve the password attribute from the entry (usually it is an unusable - // hashed value anyway). We will use the one from the change log below. - attrsToGet.remove(OperationalAttributes.PASSWORD_NAME); + Uid deletedUid; + if (LDAP_DN_ATTRIBUTES.contains(uidAttr)) { + deletedUid = createUid(uidAttr, targetDN); + } else if ("entryUUID".equalsIgnoreCase(uidAttr)) { + deletedUid = new Uid(inputAttrs.findString("targetEntryUUID")); } else { - attrsToGet = CollectionUtil.newSet(LdapSearch.getAttributesReturnedByDefault(conn, oclass)); + // ever fallback to dn without throwing any exception more reliable + deletedUid = new Uid(targetDN); } - // If objectClass is not in the list of attributes to get, prepare to remove it later. - boolean removeObjectClass = attrsToGet.add("objectClass"); - - LdapFilter filter = LdapFilter.forEntryDN(newTargetDN).withNativeFilter(getModifiedEntrySearchFilter(oclass)); - - ConnectorObject object = LdapSearches.findObject(conn, oclass, filter, attrsToGet.toArray(new String[0])); - - if (object == null) { - LOG.ok("Skipping entry because the modified entry is missing, " - + "not of the right object class, or not matching the search filter"); - return null; - } - - Attribute oclassAttr = object.getAttributeByName("objectClass"); - - final List objectClasses = LdapUtil.checkedListByFilter( - CollectionUtil.nullAsEmpty(oclassAttr.getValue()), String.class); + // Build an empty connector object, with minimal information - LDAP-8 + ConnectorObjectBuilder objectBuilder = new ConnectorObjectBuilder(); + objectBuilder.setObjectClass(oclass); + objectBuilder.setUid(deletedUid); + objectBuilder.setName("fake-dn"); + objectBuilder.addAttributes(Collections.emptySet()); - if (filterOutByObjectClasses(objectClasses)) { - LOG.ok("Skipping entry because no object class in the list of object classes to synchronize"); - return null; - } - - Attribute passwordAttr = null; - - if (conn.getConfiguration().isSynchronizePasswords()) { - final List passwordValues = attrChanges.get( - conn.getConfiguration().getPasswordAttributeToSynchronize()); - - if (!passwordValues.isEmpty()) { - byte[] encryptedPwd = (byte[]) passwordValues.get(0); - - final String decryptedPwd = getPasswordDecryptor().decryptPassword(encryptedPwd); - - passwordAttr = AttributeBuilder.buildPassword(new GuardedString(decryptedPwd.toCharArray())); - } - } - - if (removeObjectClass || passwordAttr != null) { - ConnectorObjectBuilder objectBuilder = new ConnectorObjectBuilder(); - objectBuilder.setObjectClass(object.getObjectClass()); - objectBuilder.setUid(object.getUid()); - objectBuilder.setName(object.getName()); - - if (removeObjectClass) { - for (Attribute attr : object.getAttributes()) { - if (attr != oclassAttr) { - objectBuilder.addAttribute(attr); - } - } - } else { - objectBuilder.addAttributes(object.getAttributes()); - } - - if (passwordAttr != null) { - objectBuilder.addAttribute(passwordAttr); - } - - object = objectBuilder.build(); - } - - LOG.ok("Creating sync delta for created or updated entry"); - - if ("modrdn".equalsIgnoreCase(changeType)) { - String uidAttr = conn.getSchema().getLdapUidAttribute(oclass); - - // We can only set the previous Uid if it is the entry DN, - // which is readily available. - if (LdapEntry.isDNAttribute(uidAttr)) { - syncDeltaBuilder.setPreviousUid(conn.getSchema().createUid(oclass, targetDN)); - } - } - - syncDeltaBuilder.setUid(object.getUid()); - syncDeltaBuilder.setObject(object); + syncDeltaBuilder.setUid(deletedUid); + syncDeltaBuilder.setObject(objectBuilder.build()); return syncDeltaBuilder.build(); } - private String getNewTargetDN( - final LdapName targetName, - final String newSuperior, - final String newRdn) { - - try { - LdapName newTargetName; - if (newSuperior == null) { - newTargetName = new LdapName(targetName.getRdns()); - if (newTargetName.size() > 0) { - newTargetName.remove(targetName.size() - 1); - } - } else { - newTargetName = LdapUtil.quietCreateLdapName(newSuperior); - } - newTargetName.add(newRdn); - return newTargetName.toString(); - } catch (InvalidNameException e) { - throw new ConnectorException(e); - } - } - - private boolean filterOutByBaseContexts(final LdapName targetName) { - List baseContexts = conn.getConfiguration().getBaseContextsToSynchronizeAsLdapNames(); - if (baseContexts.isEmpty()) { - baseContexts = conn.getConfiguration().getBaseContextsAsLdapNames(); - } - return !LdapUtil.isUnderContexts(targetName, baseContexts); - } - - private boolean filterOutByModifiersNames(final Map> changes) { - Set filter = conn.getConfiguration().getModifiersNamesToFilterOutAsLdapNames(); - if (filter.isEmpty()) { - LOG.ok("Filtering by modifiersName disabled"); - return false; - } - List modifiersNameValues = changes.get("modifiersName"); - if (CollectionUtil.isEmpty(modifiersNameValues)) { - LOG.ok("Not filtering by modifiersName because not set for this entry"); - return false; - } - LdapName modifiersName = LdapUtil.quietCreateLdapName(modifiersNameValues.get(0).toString()); - return filter.contains(modifiersName); - } - - private boolean filterOutByAttributes(final Map> attrChanges) { - Set filter = getAttributesToSynchronize(); - if (filter.isEmpty()) { - LOG.ok("Filtering by attributes disabled"); - return false; - } - Set changedAttrs = attrChanges.keySet(); - return !containsAny(filter, changedAttrs); - } - - private boolean filterOutByObjectClasses(final List objectClasses) { - Set filter = getObjectClassesToSynchronize(); - if (filter.isEmpty()) { - LOG.ok("Filtering by object class disabled"); - return false; - } - return !containsAny(filter, objectClasses); - } - - private SyncDeltaType getSyncDeltaType(final String changeType) { - if ("delete".equalsIgnoreCase(changeType)) { - return SyncDeltaType.DELETE; - } - return SyncDeltaType.CREATE_OR_UPDATE; - } - - private String getModifiedEntrySearchFilter(ObjectClass oclass) { - if (oclass.equals(ObjectClass.ACCOUNT)) { - return conn.getConfiguration().getAccountSynchronizationFilter(); - } - return null; - } - - private int getStartChangeNumber(final SyncToken lastToken) { + @Override + protected int getStartChangeNumber(final SyncToken lastToken) { Integer lastTokenValue = lastToken != null ? (Integer) lastToken.getValue() : null; if (lastTokenValue == null) { return getChangeLogAttributes().getFirstChangeNumber(); @@ -468,139 +100,6 @@ private int getStartChangeNumber(final SyncToken lastToken) { return lastTokenValue + 1; // Since the token value is the last value. } - private Map> getAttributeChanges(final String changeType, final String ldif) { - final Map> result = CollectionUtil.newCaseInsensitiveMap(); - - if ("modify".equalsIgnoreCase(changeType)) { - LdifParser parser = new LdifParser(ldif); - Iterator lines = parser.iterator(); - while (lines.hasNext()) { - Line line = lines.next(); - // We only expect one change, so ignore any change separators. - if (line instanceof Separator || line instanceof ChangeSeparator) { - continue; - } - NameValue nameValue = (NameValue) line; - String operation = nameValue.getName(); - String attrName = nameValue.getValue(); - if (LDIF_MODIFY_OPS.contains(operation)) { - List values = new ArrayList<>(); - while (lines.hasNext()) { - line = lines.next(); - if (line instanceof Separator) { - break; - } - nameValue = (NameValue) line; - Object value = decodeAttributeValue(nameValue); - if (value != null) { - values.add(value); - } - } - if (!values.isEmpty()) { - result.put(attrName, values); - } - } - } - } else if ("add".equalsIgnoreCase(changeType)) { - LdifParser parser = new LdifParser(ldif); - for (Line line : parser) { - // We only expect one change, so ignore any change separators. - if (line instanceof Separator || line instanceof ChangeSeparator) { - continue; - } - NameValue nameValue = (NameValue) line; - Object value = decodeAttributeValue(nameValue); - if (value != null) { - List values = result.get(nameValue.getName()); - if (values == null) { - values = new ArrayList<>(); - result.put(nameValue.getName(), values); - } - values.add(value); - } - } - } - - // Returning an empty map when changeType is "delete" or "modrdn". - return result; - } - - private Object decodeAttributeValue(final NameValue nameValue) { - String value = nameValue.getValue(); - if (value.startsWith(":")) { - // This is a Base64 encoded value... - String base64 = value.substring(1).trim(); - try { - return Base64.getDecoder().decode(base64); - // TODO the adapter had code here to convert the byte array - // to a string if the attribute was of a string type. Since - // that information is in the schema and we don't have access - // to the resource schema, leaving that functionality out for now. - } catch (Exception e) { - LOG.error("Could not decode attribute {0} with Base64 value {1}", - nameValue.getName(), nameValue.getValue()); - return null; - } - } else { - return value; - } - } - - private String getChangeLogSearchFilter(final String changeNumberAttr, final int startChangeNumber) { - int blockSize = conn.getConfiguration().getChangeLogBlockSize(); - boolean filterWithOrInsteadOfAnd = conn.getConfiguration(). - isFilterWithOrInsteadOfAnd(); - boolean filterByLogEntryOClass = !conn.getConfiguration(). - isRemoveLogEntryObjectClassFromFilter(); - - StringBuilder result = new StringBuilder(); - if (filterWithOrInsteadOfAnd) { - if (filterByLogEntryOClass) { - result.append("(&(objectClass=changeLogEntry)"); - } - result.append("(|("); - result.append(changeNumberAttr); - result.append('='); - result.append(startChangeNumber); - result.append(')'); - - int endChangeNumber = startChangeNumber + blockSize; - for (int i = startChangeNumber + 1; i <= endChangeNumber; i++) { - result.append("("); - result.append(changeNumberAttr); - result.append('='); - result.append(i); - result.append(')'); - } - - result.append(')'); - if (filterByLogEntryOClass) { - result.append(')'); - } - } else { - result.append("(&"); - if (filterByLogEntryOClass) { - result.append("(objectClass=changeLogEntry)"); - } - result.append("("); - result.append(changeNumberAttr); - result.append(">="); - result.append(startChangeNumber); - result.append(')'); - - int endChangeNumber = startChangeNumber + blockSize; - result.append("("); - result.append(changeNumberAttr); - result.append("<="); - result.append(endChangeNumber); - result.append(')'); - - result.append(')'); - } - - return result.toString(); - } - ChangeLogAttributes getChangeLogAttributes() { if (changeLogAttrs == null) { try { @@ -627,81 +126,4 @@ ChangeLogAttributes getChangeLogAttributes() { } return changeLogAttrs; } - - private String getChangeNumberAttribute() { - String result = conn.getConfiguration().getChangeNumberAttribute(); - if (StringUtil.isBlank(result)) { - result = "changeNumber"; - } - return result; - } - - private Set getAttributesToSynchronize() { - if (attrsToSync == null) { - Set result = CollectionUtil.newCaseInsensitiveSet(); - result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getAttributesToSynchronize()))); - if (conn.getConfiguration().isSynchronizePasswords()) { - result.add(conn.getConfiguration(). - getPasswordAttributeToSynchronize()); - } - attrsToSync = result; - } - return attrsToSync; - } - - private Set getObjectClassesToSynchronize() { - if (oclassesToSync == null) { - Set result = CollectionUtil.newCaseInsensitiveSet(); - result.addAll(Arrays.asList(LdapUtil.nullAsEmpty(conn.getConfiguration().getObjectClassesToSynchronize()))); - oclassesToSync = result; - } - return oclassesToSync; - } - - private PasswordDecryptor getPasswordDecryptor() { - if (passwordDecryptor == null) { - conn.getConfiguration().getPasswordDecryptionKey().access(decryptionKey -> { - conn.getConfiguration().getPasswordDecryptionInitializationVector().access(decryptionIV -> { - passwordDecryptor = new PasswordDecryptor(decryptionKey, decryptionIV); - }); - }); - } - assert passwordDecryptor != null; - return passwordDecryptor; - } - - private boolean containsAny(final Set haystack, final Collection needles) { - for (String needle : needles) { - if (haystack.contains(needle)) { - return true; - } - } - return false; - } - - private Uid createUid(final String uidAttr, final String targetDN) throws InvalidNameException { - Rdn matchingRnd = null; - for (Rdn rdn : new LdapName(targetDN).getRdns()) { - if (uidAttr.equalsIgnoreCase(rdn.getType())) { - matchingRnd = rdn; - } - } - return matchingRnd == null ? null : new Uid(matchingRnd.getValue().toString()); - } - - public static int convertToInt(String number, final int def) { - int result = def; - if (number != null && number.length() > 0) { - int decimal = number.indexOf('.'); - if (decimal > 0) { - number = number.substring(0, decimal); - } - try { - result = Integer.parseInt(number); - } catch (NumberFormatException e) { - // Ignore. - } - } - return result; - } } From fd70111bde2ab9673d53f3fe2a3e4f61b232a8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 29 Feb 2024 09:13:46 +0100 Subject: [PATCH 12/12] Reflow (again) --- .../sync/GenericChangeLogSyncStrategy.java | 88 +++++++++++-------- .../sunds/SunDSChangeLogSyncStrategy.java | 13 +-- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java index e9fe667..f99d0f9 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/GenericChangeLogSyncStrategy.java @@ -55,6 +55,7 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; @@ -79,7 +80,7 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { // TODO detect that the change log has been trimmed. - protected static final Log LOG = Log.getLog(GenericChangeLogSyncStrategy.class); + protected static final Log LOG = Log.getLog(LdapSyncStrategy.class); /** * The list of attribute operations supported by the "modify" LDIF change type. @@ -110,7 +111,7 @@ public class GenericChangeLogSyncStrategy implements LdapSyncStrategy { public GenericChangeLogSyncStrategy(LdapConnection conn) { this.conn = conn; } - + protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptionsBuilder builder) { LOG.ok("Getting latest sync token with pages of size {0}", conn.getConfiguration().getChangeLogBlockSize()); @@ -126,12 +127,15 @@ protected SyncToken getLatestSyncTokenPaged(ObjectClass oclass, OperationOptions cookies[0] = null; SearchResultsHandler handler = new SearchResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { - int changeNumber = convertToInt( - (String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); - if (changeNumber > maxChangeNumber[0]) { - maxChangeNumber[0] = changeNumber; + Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); + if (changeNumberAttr != null) { + int changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); + if (changeNumber > maxChangeNumber[0]) { + maxChangeNumber[0] = changeNumber; + } } return true; @@ -157,15 +161,17 @@ public void handleResult(final SearchResult result) { return new SyncToken(maxChangeNumber[0]); } - + protected SyncToken getLatestSyncTokenDefault(ObjectClass oclass, OperationOptionsBuilder builder) { final int[] maxChangeNumber = { 0 }; LOG.ok("Getting latest sync token with a regular search"); ResultsHandler handler = new ResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { - int changeNumber = convertToInt((String) object.getAttributeByName(getChangeNumberAttribute()).getValue().get(0), -1); + int changeNumber = convertToInt((String) object.getAttributeByName(getChangeNumberAttribute()). + getValue().get(0), -1); if (changeNumber > maxChangeNumber[0]) { maxChangeNumber[0] = changeNumber; } @@ -180,7 +186,7 @@ public boolean handle(final ConnectorObject object) { handler, builder.build(), conn.getConfiguration().getChangeLogContext()); - + search.execute(); return new SyncToken(maxChangeNumber[0]); @@ -197,15 +203,15 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { builder.setOption(LdapSearch.OP_IGNORE_BUILT_IN_FILTERS, true); builder.setOption(LdapConstants.SEARCH_FILTER_NAME, "(objectClass=changelogEntry)"); - Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, builder.build()); + Class searchStrategyClass = LdapSearchStrategy.getSearchStrategy(conn, builder. + build()); switch (searchStrategyClass.getSimpleName()) { case "PagedSearchStrategy": if (conn.getConfiguration().getChangeLogPagingSupport()) { return getLatestSyncTokenPaged(oclass, builder); - } - else { - return getLatestSyncTokenDefault(oclass, builder); + } else { + return getLatestSyncTokenDefault(oclass, builder); } default: return getLatestSyncTokenDefault(oclass, builder); @@ -238,15 +244,18 @@ public void sync( final boolean[] results = new boolean[1]; ResultsHandler resultsHandler = new ResultsHandler() { + @Override public boolean handle(final ConnectorObject object) { results[0] = true; - - int changeNumber = convertToInt((String) object.getAttributeByName(changeNumberAttr).getValue().get(0), - -1); - if (changeNumber > currentChangeNumber[0]) { - currentChangeNumber[0] = changeNumber; + Attribute changeNumberAttr = object.getAttributeByName(getChangeNumberAttribute()); + int changeNumber = -1; + if (changeNumberAttr != null) { + changeNumber = convertToInt(AttributeUtil.getStringValue(changeNumberAttr), -1); + if (changeNumber > currentChangeNumber[0]) { + currentChangeNumber[0] = changeNumber; + } } SyncDelta delta; @@ -266,12 +275,14 @@ public boolean handle(final ConnectorObject object) { do { results[0] = false; - builder.setOption(LdapConstants.SEARCH_FILTER_NAME, getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); + builder.setOption( + LdapConstants.SEARCH_FILTER_NAME, + getChangeLogSearchFilter(changeNumberAttr, currentChangeNumber[0])); - LdapSearch search = new LdapSearch(conn, - oclass, - null, - resultsHandler, + LdapSearch search = new LdapSearch(conn, + oclass, + null, + resultsHandler, builder.build(), conn.getConfiguration().getChangeLogContext()); @@ -328,14 +339,12 @@ protected SyncDelta createSyncDelta( final Map> attrChanges = getAttributeChanges(changeType, changes); if (filterOutByModifiersNames(attrChanges)) { - LOG.ok("Skipping entry because modifiersName is in the list of " - + "modifiersName's to filter out"); + LOG.ok("Skipping entry because modifiersName is in the list of modifiersName's to filter out"); return null; } if (filterOutByAttributes(attrChanges)) { - LOG.ok("Skipping entry because no changed attributes in the list " - + "of attributes to synchronize"); + LOG.ok("Skipping entry because no changed attributes in the list of attributes to synchronize"); return null; } @@ -449,7 +458,13 @@ protected SyncDelta createSyncDelta( return syncDeltaBuilder.build(); } - protected SyncDelta createDeletionSyncDelta(SyncDeltaBuilder syncDeltaBuilder, String targetDN, ObjectClass oclass, AttributesAccessor inputAttrs) throws InvalidNameException { + protected SyncDelta createDeletionSyncDelta( + final SyncDeltaBuilder syncDeltaBuilder, + final String targetDN, + final ObjectClass oclass, + final AttributesAccessor inputAttrs) + throws InvalidNameException { + LOG.ok("Creating sync delta for deleted entry {0}", targetDN); Uid deletedUid = new Uid(targetDN); @@ -498,8 +513,7 @@ protected boolean filterOutByBaseContexts(final LdapName targetName) { return !LdapUtil.isUnderContexts(targetName, baseContexts); } - protected boolean filterOutByModifiersNames( - final Map> changes) { + protected boolean filterOutByModifiersNames(final Map> changes) { Set filter = conn.getConfiguration(). getModifiersNamesToFilterOutAsLdapNames(); if (filter.isEmpty()) { @@ -515,8 +529,7 @@ protected boolean filterOutByModifiersNames( return filter.contains(modifiersName); } - protected boolean filterOutByAttributes( - final Map> attrChanges) { + protected boolean filterOutByAttributes(final Map> attrChanges) { Set filter = getAttributesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by attributes disabled"); @@ -526,8 +539,7 @@ protected boolean filterOutByAttributes( return !containsAny(filter, changedAttrs); } - protected boolean filterOutByObjectClasses( - final List objectClasses) { + protected boolean filterOutByObjectClasses(final List objectClasses) { Set filter = getObjectClassesToSynchronize(); if (filter.isEmpty()) { LOG.ok("Filtering by object class disabled"); @@ -551,8 +563,7 @@ protected String getModifiedEntrySearchFilter(ObjectClass oclass) { } protected int getStartChangeNumber(final SyncToken lastToken) { - return lastToken != null ? (Integer) lastToken. - getValue() + 1 : 0; + return lastToken != null ? (Integer) lastToken.getValue() + 1 : 0; } protected Map> getAttributeChanges(final String changeType, final String ldif) { @@ -633,8 +644,7 @@ protected Object decodeAttributeValue(final NameValue nameValue) { } } - protected String getChangeLogSearchFilter( - final String changeNumberAttr, final int startChangeNumber) { + protected String getChangeLogSearchFilter(final String changeNumberAttr, final int startChangeNumber) { int blockSize = conn.getConfiguration().getChangeLogBlockSize(); boolean filterWithOrInsteadOfAnd = conn.getConfiguration(). isFilterWithOrInsteadOfAnd(); @@ -751,7 +761,7 @@ protected Uid createUid(final String uidAttr, final String targetDN) throws Inva return matchingRnd == null ? null : new Uid(matchingRnd.getValue().toString()); } - public static int convertToInt(String number, final int def) { + protected static int convertToInt(String number, final int def) { int result = def; if (number != null && number.length() > 0) { int decimal = number.indexOf('.'); diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java index 316ebb6..c4f7dee 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/sync/sunds/SunDSChangeLogSyncStrategy.java @@ -31,7 +31,6 @@ import net.tirasa.connid.bundles.ldap.LdapConnection; import net.tirasa.connid.bundles.ldap.commons.LdapUtil; import net.tirasa.connid.bundles.ldap.sync.GenericChangeLogSyncStrategy; -import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributesAccessor; @@ -48,9 +47,6 @@ */ public class SunDSChangeLogSyncStrategy extends GenericChangeLogSyncStrategy { - // TODO detect that the change log has been trimmed. - private static final Log LOG = Log.getLog(SunDSChangeLogSyncStrategy.class); - private ChangeLogAttributes changeLogAttrs; public SunDSChangeLogSyncStrategy(LdapConnection conn) { @@ -63,8 +59,13 @@ public SyncToken getLatestSyncToken(ObjectClass oclass) { } @Override - protected SyncDelta createDeletionSyncDelta(SyncDeltaBuilder syncDeltaBuilder, String targetDN, ObjectClass oclass, - AttributesAccessor inputAttrs) throws InvalidNameException { + protected SyncDelta createDeletionSyncDelta( + final SyncDeltaBuilder syncDeltaBuilder, + final String targetDN, + final ObjectClass oclass, + final AttributesAccessor inputAttrs) + throws InvalidNameException { + LOG.ok("Creating sync delta for deleted entry {0}", inputAttrs.findString("targetEntryUUID")); String uidAttr = conn.getSchema().getLdapUidAttribute(oclass);