Skip to content

Commit

Permalink
[LDAP-41] Provide capability for different sync strategies (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sylinsic authored Jan 19, 2024
1 parent dc9266d commit 3230cf5
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 44 deletions.
67 changes: 52 additions & 15 deletions src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package net.tirasa.connid.bundles.ldap;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -38,6 +39,8 @@
import net.tirasa.connid.bundles.ldap.commons.ObjectClassMappingConfig;
import net.tirasa.connid.bundles.ldap.schema.LdapSchemaMapping;
import net.tirasa.connid.bundles.ldap.search.DefaultSearchStrategy;
import net.tirasa.connid.bundles.ldap.sync.LdapSyncStrategy;
import net.tirasa.connid.bundles.ldap.sync.sunds.SunDSChangeLogSyncStrategy;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.EqualsHashCodeBuilder;
import org.identityconnectors.common.StringUtil;
Expand Down Expand Up @@ -213,6 +216,10 @@ public enum SearchScope {

private String dnAttribute = "entryDN";

private String syncStrategy = SunDSChangeLogSyncStrategy.class.getName();

private Class<? extends LdapSyncStrategy> syncStrategyClass = null;

/**
* The SearchScope for user objects
*/
Expand Down Expand Up @@ -348,6 +355,9 @@ public void validate() {
checkNotBlank(passwordDecryptionKey, "decryptionKey.notBlank");
checkNotBlank(passwordDecryptionInitializationVector, "decryptionInitializationVector.notBlank");
}

checkNotBlank(syncStrategy, "syncStrategy.notBlank");
checkLdapSyncStrategy();
}

private void checkNotBlank(String value, String errorMessage) {
Expand All @@ -372,8 +382,22 @@ public void access(byte[] clearBytes) {
}
}

@SuppressWarnings("unchecked")
private void checkLdapSyncStrategy() {
try {
Class<?> clazz = Class.forName(syncStrategy);
if (LdapSyncStrategy.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
syncStrategyClass = (Class<? extends LdapSyncStrategy>) clazz;
} else {
failValidation("syncStrategy.classNotSyncStrategy");
}
} catch (ClassNotFoundException e) {
failValidation("syncStrategy.classNotFound");
}
}

private void checkNotEmpty(Collection<?> collection, String errorMessage) {
if (collection.size() < 1) {
if (collection.isEmpty()) {
failValidation(errorMessage);
}
}
Expand Down Expand Up @@ -523,7 +547,7 @@ public void setPasswordAttribute(String passwordAttribute) {
helpMessageKey = "accountObjectClasses.help")
public String[] getAccountObjectClasses() {
List<String> ldapClasses = accountConfig.getLdapClasses();
return ldapClasses.toArray(new String[ldapClasses.size()]);
return ldapClasses.toArray(new String[0]);
}

public void setAccountObjectClasses(String... accountObjectClasses) {
Expand All @@ -535,7 +559,7 @@ public void setAccountObjectClasses(String... accountObjectClasses) {
helpMessageKey = "accountUserNameAttributes.help")
public String[] getAccountUserNameAttributes() {
List<String> shortNameLdapAttributes = accountConfig.getShortNameLdapAttributes();
return shortNameLdapAttributes.toArray(new String[shortNameLdapAttributes.size()]);
return shortNameLdapAttributes.toArray(new String[0]);
}

public void setAccountUserNameAttributes(String... accountUserNameAttributes) {
Expand Down Expand Up @@ -569,7 +593,7 @@ public void setAccountSearchFilter(String accountSearchFilter) {
helpMessageKey = "groupObjectClasses.help")
public String[] getGroupObjectClasses() {
List<String> ldapClasses = groupConfig.getLdapClasses();
return ldapClasses.toArray(new String[ldapClasses.size()]);
return ldapClasses.toArray(new String[0]);
}

public void setGroupObjectClasses(String... groupObjectClasses) {
Expand All @@ -581,7 +605,7 @@ public void setGroupObjectClasses(String... groupObjectClasses) {
helpMessageKey = "groupNameAttributes.help")
public String[] getGroupNameAttributes() {
List<String> shortNameLdapAttributes = groupConfig.getShortNameLdapAttributes();
return shortNameLdapAttributes.toArray(new String[shortNameLdapAttributes.size()]);
return shortNameLdapAttributes.toArray(new String[0]);
}

public void setGroupNameAttributes(String... groupNameAttributes) {
Expand Down Expand Up @@ -648,7 +672,7 @@ public void setAddPrincipalToNewGroups(boolean addPrincipalToNewGroups) {
helpMessageKey = "anyObjectClasses.help")
public String[] getAnyObjectClasses() {
List<String> ldapClasses = anyObjectConfig.getLdapClasses();
return ldapClasses.toArray(new String[ldapClasses.size()]);
return ldapClasses.toArray(new String[0]);
}

public void setAnyObjectClasses(String... anyObjectClasses) {
Expand All @@ -660,7 +684,7 @@ public void setAnyObjectClasses(String... anyObjectClasses) {
helpMessageKey = "anyObjectNameAttributes.help")
public String[] getAnyObjectNameAttributes() {
List<String> shortNameLdapAttributes = anyObjectConfig.getShortNameLdapAttributes();
return shortNameLdapAttributes.toArray(new String[shortNameLdapAttributes.size()]);
return shortNameLdapAttributes.toArray(new String[0]);
}

public void setAnyObjectNameAttributes(String... anyObjectNameAttributes) {
Expand Down Expand Up @@ -909,8 +933,7 @@ public GuardedByteArray getPasswordDecryptionInitializationVector() {

public void setPasswordDecryptionInitializationVector(GuardedByteArray passwordDecryptionInitializationVector) {
this.passwordDecryptionInitializationVector = passwordDecryptionInitializationVector != null
? passwordDecryptionInitializationVector.
copy() : null;
? passwordDecryptionInitializationVector.copy() : null;
}

@ConfigurationProperty(order = 44,
Expand Down Expand Up @@ -979,10 +1002,25 @@ public void setConnectTimeout(long connectTimeout) {
this.connectTimeout = connectTimeout;
}

@ConfigurationProperty(order = 50,
displayMessageKey = "syncStrategy.display",
helpMessageKey = "syncStrategy.help")
public String getSyncStrategy() {
return syncStrategy;
}

public void setSyncStrategy(String syncStrategy) {
this.syncStrategy = syncStrategy;
}

public Class<? extends LdapSyncStrategy> getSyncStrategyClass() {
return syncStrategyClass;
}

// Getters and setters for configuration properties end here.
public List<LdapName> getBaseContextsAsLdapNames() {
if (baseContextsAsLdapNames == null) {
List<LdapName> result = new ArrayList<LdapName>(baseContexts.length);
List<LdapName> result = new ArrayList<>(baseContexts.length);
try {
for (String baseContext : baseContexts) {
result.add(new LdapName(baseContext));
Expand All @@ -998,7 +1036,7 @@ public List<LdapName> getBaseContextsAsLdapNames() {
public List<LdapName> getBaseContextsToSynchronizeAsLdapNames() {
if (baseContextsToSynchronizeAsLdapNames == null) {
String[] source = LdapUtil.nullAsEmpty(baseContextsToSynchronize);
List<LdapName> result = new ArrayList<LdapName>(source.length);
List<LdapName> result = new ArrayList<>(source.length);
try {
for (String each : source) {
result.add(new LdapName(each));
Expand All @@ -1014,7 +1052,7 @@ public List<LdapName> getBaseContextsToSynchronizeAsLdapNames() {
public Set<LdapName> getModifiersNamesToFilterOutAsLdapNames() {
if (modifiersNamesToFilterOutAsLdapNames == null) {
String[] source = LdapUtil.nullAsEmpty(modifiersNamesToFilterOut);
Set<LdapName> result = new HashSet<LdapName>(source.length);
Set<LdapName> result = new HashSet<>(source.length);
try {
for (String each : source) {
result.add(new LdapName(each));
Expand All @@ -1028,7 +1066,7 @@ public Set<LdapName> getModifiersNamesToFilterOutAsLdapNames() {
}

public Map<ObjectClass, ObjectClassMappingConfig> getObjectClassMappingConfigs() {
Map<ObjectClass, ObjectClassMappingConfig> result = new HashMap<ObjectClass, ObjectClassMappingConfig>();
Map<ObjectClass, ObjectClassMappingConfig> result = new HashMap<>();
result.put(accountConfig.getObjectClass(), accountConfig);
result.put(groupConfig.getObjectClass(), groupConfig);
result.put(anyObjectConfig.getObjectClass(), anyObjectConfig);
Expand Down Expand Up @@ -1102,8 +1140,7 @@ public boolean equals(Object obj) {
if (this == that) {
return true;
}
return this.createHashCodeBuilder().equals(that.
createHashCodeBuilder());
return this.createHashCodeBuilder().equals(that.createHashCodeBuilder());
}
return false;
}
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/net/tirasa/connid/bundles/ldap/LdapConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import net.tirasa.connid.bundles.ldap.search.LdapFilter;
import net.tirasa.connid.bundles.ldap.search.LdapFilterTranslator;
import net.tirasa.connid.bundles.ldap.search.LdapSearch;
import net.tirasa.connid.bundles.ldap.sync.LdapSyncStrategy;
import net.tirasa.connid.bundles.ldap.sync.sunds.SunDSChangeLogSyncStrategy;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeDelta;
Expand Down Expand Up @@ -63,6 +65,8 @@ public class LdapConnector implements
AuthenticateOp, ResolveUsernameOp, CreateOp, UpdateOp, UpdateDeltaOp, UpdateAttributeValuesOp,
DeleteOp, SyncOp {

private static final Log LOG = Log.getLog(LdapConnector.class);

/**
* The configuration for this connector instance.
*/
Expand All @@ -73,6 +77,8 @@ public class LdapConnector implements
*/
private LdapConnection conn;

private LdapSyncStrategy syncStrategy;

@Override
public Configuration getConfiguration() {
return config;
Expand All @@ -82,6 +88,15 @@ public Configuration getConfiguration() {
public void init(Configuration cfg) {
config = (LdapConfiguration) cfg;
conn = new LdapConnection(config);

Class<? extends LdapSyncStrategy> syncStrategyClass = config.getSyncStrategyClass();
try {
syncStrategy = syncStrategyClass.getConstructor(LdapConnection.class).newInstance(conn);
} catch (Exception e) {
LOG.error(e, "Could not instantiate the configured {0} implementation, reverting to {1}",
LdapSyncStrategy.class.getName(), SunDSChangeLogSyncStrategy.class.getName());
syncStrategy = new SunDSChangeLogSyncStrategy(conn);
}
}

@Override
Expand Down Expand Up @@ -192,9 +207,8 @@ public Uid removeAttributeValues(
}

@Override
public SyncToken getLatestSyncToken(
final ObjectClass oclass) {
return new SunDSChangeLogSyncStrategy(conn, oclass).getLatestSyncToken();
public SyncToken getLatestSyncToken(final ObjectClass oclass) {
return syncStrategy.getLatestSyncToken(oclass);
}

@Override
Expand All @@ -203,6 +217,6 @@ public void sync(
final SyncToken token,
final SyncResultsHandler handler,
final OperationOptions options) {
new SunDSChangeLogSyncStrategy(conn, oclass).sync(token, handler, options);
syncStrategy.sync(token, handler, options, oclass);
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,13 +23,14 @@
*/
package net.tirasa.connid.bundles.ldap.sync;

import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;

public interface LdapSyncStrategy {

void sync(SyncToken token, SyncResultsHandler handler, OperationOptions options);
void sync(SyncToken token, SyncResultsHandler handler, OperationOptions options, ObjectClass oclass);

SyncToken getLatestSyncToken();
SyncToken getLatestSyncToken(ObjectClass oclass);
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ public class SunDSChangeLogSyncStrategy implements LdapSyncStrategy {

private final LdapConnection conn;

private final ObjectClass oclass;

private ChangeLogAttributes changeLogAttrs;

private Set<String> oclassesToSync;
Expand All @@ -118,22 +116,21 @@ public class SunDSChangeLogSyncStrategy implements LdapSyncStrategy {
LDAP_DN_ATTRIBUTES.add("cn");
}

public SunDSChangeLogSyncStrategy(LdapConnection conn, ObjectClass oclass) {
public SunDSChangeLogSyncStrategy(LdapConnection conn) {
this.conn = conn;
this.oclass = oclass;
}

@Override
public SyncToken getLatestSyncToken() {
public SyncToken getLatestSyncToken(ObjectClass oclass) {
return new SyncToken(getChangeLogAttributes().getLastChangeNumber());
}

@Override
public void sync(
final SyncToken token,
final SyncResultsHandler handler,
final OperationOptions options) {

final OperationOptions options,
final ObjectClass oclass) {
String context = getChangeLogAttributes().getChangeLogContext();
final String changeNumberAttr = getChangeNumberAttribute();
SearchControls controls = LdapInternalSearch.createDefaultSearchControls();
Expand Down Expand Up @@ -174,7 +171,7 @@ public void sync(
currentChangeNumber[0] = changeNumber;
}

final SyncDelta delta = createSyncDelta(entry, changeNumber, options.getAttributesToGet());
final SyncDelta delta = createSyncDelta(entry, changeNumber, options.getAttributesToGet(), oclass);

if (delta != null) {
return handler.handle(delta);
Expand All @@ -194,7 +191,8 @@ public void sync(
private SyncDelta createSyncDelta(
final LdapEntry changeLogEntry,
final int changeNumber,
final String[] attrsToGetOption) throws InvalidNameException {
final String[] attrsToGetOption,
ObjectClass oclass) throws InvalidNameException {

LOG.ok("Attempting to create sync delta for log entry {0}", changeNumber);

Expand Down Expand Up @@ -298,10 +296,9 @@ private SyncDelta createSyncDelta(
// 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());
LdapFilter filter = LdapFilter.forEntryDN(newTargetDN).withNativeFilter(getModifiedEntrySearchFilter(oclass));

ConnectorObject object = LdapSearches.findObject(conn, oclass, filter,
attrsToGet.toArray(new String[attrsToGet.size()]));
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, "
Expand Down Expand Up @@ -452,7 +449,7 @@ private SyncDeltaType getSyncDeltaType(final String changeType) {
return SyncDeltaType.CREATE_OR_UPDATE;
}

private String getModifiedEntrySearchFilter() {
private String getModifiedEntrySearchFilter(ObjectClass oclass) {
if (oclass.equals(ObjectClass.ACCOUNT)) {
return conn.getConfiguration().getAccountSynchronizationFilter();
}
Expand Down Expand Up @@ -609,10 +606,8 @@ ChangeLogAttributes getChangeLogAttributes() {
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 firstChangeNumber = getStringAttrValue(attrs, "firstChangeNumber");
String lastChangeNumber = getStringAttrValue(attrs, "lastChangeNumber");
if (changeLog == null || firstChangeNumber == null | lastChangeNumber == null) {
String error = "Unable to locate the replication change log.\n"
+ "From the admin console please verify that the "
Expand Down
Loading

0 comments on commit 3230cf5

Please sign in to comment.