Skip to content

Commit

Permalink
[LDAP-38] Allow setting search scope and anyobject search filters (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sylinsic authored Nov 23, 2023
1 parent 6379a44 commit 3759e52
Show file tree
Hide file tree
Showing 18 changed files with 613 additions and 73 deletions.
159 changes: 125 additions & 34 deletions src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java

Large diffs are not rendered by default.

50 changes: 36 additions & 14 deletions src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
*/
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";

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

private final LdapConnection conn;
Expand Down Expand Up @@ -182,12 +185,16 @@ private LdapInternalSearch getInternalSearch(final Set<String> attrsToGet) {
LdapSearchStrategy strategy;
List<String> dns;
int searchScope;
boolean ignoreUserAnyObjectConfig = false;

String filterEntryDN = filter == null ? null : filter.getEntryDN();
if (filterEntryDN == null) {
strategy = getSearchStrategy();
dns = getBaseDNs();
searchScope = getLdapSearchScope();
if (options.getOptions().containsKey(OP_IGNORE_CUSTOM_ANY_OBJECT_CONFIG)) {
ignoreUserAnyObjectConfig = (boolean) options.getOptions().get(OP_IGNORE_CUSTOM_ANY_OBJECT_CONFIG);
}
searchScope = getLdapSearchScope(ignoreUserAnyObjectConfig);
} else {
// Would be good to check that filterEntryDN is under the configured base contexts.
// However, the adapter is likely to pass entries outside the base contexts,
Expand All @@ -200,7 +207,7 @@ private LdapInternalSearch getInternalSearch(final Set<String> attrsToGet) {
SearchControls controls = LdapInternalSearch.createDefaultSearchControls();
Set<String> ldapAttrsToGet = getLdapAttributesToGet(attrsToGet);

controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[ldapAttrsToGet.size()]));
controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[0]));

controls.setSearchScope(searchScope);

Expand All @@ -210,6 +217,8 @@ private LdapInternalSearch getInternalSearch(final Set<String> attrsToGet) {
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();
return new LdapInternalSearch(conn,
Expand Down Expand Up @@ -262,8 +271,8 @@ private ConnectorObject createConnectorObject(
builder.setUid(conn.getSchemaMapping().createUid(oclass, entry));
builder.setName(conn.getSchemaMapping().createName(oclass, entry));

final List<String> ldapGroups = new ArrayList<String>();
final List<String> posixGroups = new ArrayList<String>();
final List<String> ldapGroups = new ArrayList<>();
final List<String> posixGroups = new ArrayList<>();

for (String attrName : attrsToGet) {
Attribute attribute;
Expand Down Expand Up @@ -375,7 +384,7 @@ private List<String> getBaseDNs() {

if (container != null) {
result = Collections.singletonList(
LdapSearches.findEntryDN(conn, container.getObjectClass(), container.getUid()));
LdapSearches.findEntryDN(conn, container.getObjectClass(), container.getUid(), true));
} else {
result = Arrays.asList(baseDNs);
}
Expand Down Expand Up @@ -448,17 +457,30 @@ private void removeNonReadableAttributes(final Set<String> attributes) {
}
}

private int getLdapSearchScope() {
private int getLdapSearchScope(boolean ignoreUserAnyObjectConfig) {
String scope = options.getScope();

if (OperationOptions.SCOPE_OBJECT.equals(scope)) {
return SearchControls.OBJECT_SCOPE;
} else if (OperationOptions.SCOPE_ONE_LEVEL.equals(scope)) {
return SearchControls.ONELEVEL_SCOPE;
} else if (OperationOptions.SCOPE_SUBTREE.equals(scope) || scope == null) {
return SearchControls.SUBTREE_SCOPE;
} else {
throw new IllegalArgumentException("Invalid search scope " + scope);
if (scope == null) {
if (oclass.is(ObjectClass.ACCOUNT_NAME)) {
scope = conn.getConfiguration().getUserSearchScope();
} else if (oclass.is(ObjectClass.GROUP_NAME)) {
scope = conn.getConfiguration().getGroupSearchScope();
} else if (!ignoreUserAnyObjectConfig) {
scope = conn.getConfiguration().getAnyObjectSearchScope();
} else {
scope = OperationOptions.SCOPE_SUBTREE;
}
}

switch (scope) {
case OperationOptions.SCOPE_OBJECT:
return SearchControls.OBJECT_SCOPE;
case OperationOptions.SCOPE_ONE_LEVEL:
return SearchControls.ONELEVEL_SCOPE;
case OperationOptions.SCOPE_SUBTREE:
return SearchControls.SUBTREE_SCOPE;
default:
throw new IllegalArgumentException("Invalid search scope " + scope);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,29 @@ private LdapSearches() {
* if such an entry does not exists.
*/
public static String getEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid) {
return findEntryDN(conn, oclass, uid, true);
return findEntryDN(conn, oclass, uid, true, false);
}

public static String getEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid,
boolean ignoreCustomAnyObjectConfig) {
return findEntryDN(conn, oclass, uid, true, ignoreCustomAnyObjectConfig);
}

/**
* Returns the DN of the entry identified by the given Uid. May throw <code>UnknownUidException</code>
* if such an entry does not exists, but not necessarily.
*/
public static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid) {
return findEntryDN(conn, oclass, uid, false);
return findEntryDN(conn, oclass, uid, false, false);
}

public static String findEntryDN(
LdapConnection conn,
ObjectClass oclass,
Uid uid,
boolean ignoreCustomAnyObjectConfig) {

return findEntryDN(conn, oclass, uid, false, ignoreCustomAnyObjectConfig);
}

/**
Expand All @@ -87,10 +101,16 @@ public static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid ui
* the method will throw a <code>UnknownUidException</code> if the entry identified
* by the Uid does not exist.
*/
private static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid, boolean check) {
private static String findEntryDN(
LdapConnection conn,
ObjectClass oclass,
Uid uid,
boolean check,
boolean ignoreCustomAnyObjectConfig) {

LOG.ok("Searching for object {0} of class {1}", uid.getUidValue(), oclass.getObjectClassValue());

LdapFilter ldapFilter = null;
LdapFilter ldapFilter;

// If the Uid is actually the entry DN, we do not need to do a search do find the entry DN.
String uidAttr = conn.getSchemaMapping().getLdapUidAttribute(oclass);
Expand All @@ -112,6 +132,7 @@ private static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid u

OperationOptionsBuilder builder = new OperationOptionsBuilder();
builder.setAttributesToGet(conn.getConfiguration().getDnAttribute());
builder.setOption(LdapSearch.OP_IGNORE_CUSTOM_ANY_OBJECT_CONFIG, ignoreCustomAnyObjectConfig);

LdapSearch search = new LdapSearch(conn, oclass, ldapFilter, null, builder.build());
ConnectorObject object = search.getSingleResult();
Expand All @@ -127,7 +148,7 @@ public static List<ConnectorObject> findObjects(
LOG.ok("Searching for object with attribute {0} of class {1} in {2}",
attr, oclass.getObjectClassValue(), baseDN);

final List<ConnectorObject> result = new ArrayList<ConnectorObject>();
final List<ConnectorObject> result = new ArrayList<>();

EqualsFilter filter = (EqualsFilter) FilterBuilder.equalTo(attr);
LdapFilter ldapFilter = new LdapFilterTranslator(conn.getSchemaMapping(), oclass).
Expand Down Expand Up @@ -167,7 +188,7 @@ public static ConnectorObject findObject(
public static LdapEntry getEntry(LdapConnection conn, LdapName entryDN, String... ldapAttrsToGet) {
LOG.ok("Searching for entry {0}", entryDN);

final List<LdapEntry> result = new ArrayList<LdapEntry>();
final List<LdapEntry> result = new ArrayList<>();
if (!LdapUtil.isUnderContexts(entryDN, conn.getConfiguration().
getBaseContextsAsLdapNames())) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ anyObjectClasses.display=Any-object Object Classes
anyObjectClasses.help=The object class or classes that will be used when creating new any-object objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to separate multiple object classes. Some classes may require that you specify all object classes in the class hierarchy.
anyObjectNameAttributes.display=Any-object Name Attributes
anyObjectNameAttributes.help=Attribute or attributes which holds the any-object''s name.
userSearchScope.display=User search scope
userSearchScope.help=The scope to utilise when searching for users. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
groupSearchScope.display=Group search scope
groupSearchScope.help=The scope to utilise when searching for groups. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
anyObjectSearchFilter.display=LDAP filter for retrieving Any Objects
anyObjectSearchFilter.help=An optional LDAP filter to control which any objects are returned from the LDAP resource. If no filter is specified, only any objects that include all specified object classes are returned.
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'

dnAttribute.display=Entry DN attribute name
dnAttribute.help=Entry DN attribute name (default: entryDN)
Expand Down Expand Up @@ -143,6 +151,12 @@ changeLogBlockSize.legalValue=The synchronization block size should be greather
passwordAttributeToSynchronize.notBlank=The password attribute to synchronize cannot be blank
decryptionKey.notBlank=The decryption key cannot be blank
decryptionInitializationVector.notBlank=The decryption initialization vector cannot be blank
userSearchScope.notBlank=The user search scope cannot be blank
userSearchScope.invalidScope=The user search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
groupSearchScope.notBlank=The group search scope cannot be blank
groupSearchScope.invalidScope=The group search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
anyObjectSearchScope.notBlank=The any object search scope cannot be blank
anyObjectSearchScope.invalidScope=The any object search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'

entryNotFound=Entry "{0}" not found
readingPasswordsNotSupported=Returning passwords from a search operation is not supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ anyObjectClasses.notEmpty=The list of any-object object classes cannot be empty
anyObjectClasses.noBlankValues=The list of any-object object classes cannot contain blank values
anyObjectNameAttributes.notEmpty=The list of any-object name attributes cannot be empty
anyObjectNameAttributes.noBlankValues=The list of any-object name attributes cannot contain blank values
userSearchScope.display=User search scope
userSearchScope.help=The scope to utilise when searching for users. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
groupSearchScope.display=Group search scope
groupSearchScope.help=The scope to utilise when searching for groups. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
anyObjectSearchFilter.display=LDAP filter for retrieving Any Objects
anyObjectSearchFilter.help=An optional LDAP filter to control which any objects are returned from the LDAP resource. If no filter is specified, only any objects that include all specified object classes are returned.
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'
groupMemberAttribute.notBlank=Das Gruppenmitgliedattribut darf nicht leer sein
vlvSortAttribute.notBlank=Das VLV-Sortierattribut darf nicht leer sein
uidAttribute.notBlank=Das zur UID zuzuordnende Attribut darf nicht leer sein
Expand All @@ -126,6 +134,12 @@ changeLogBlockSize.legalValue=Die Gr\u00f6\u00dfe des Synchronisationsblocks mus
passwordAttributeToSynchronize.notBlank=Das zu synchronisierende Passwortattribut darf nicht leer sein
decryptionKey.notBlank=Der Entschl\u00fcsselungsschl\u00fcssel darf nicht leer sein
decryptionInitializationVector.notBlank=Der Initialisierungsvektor zur Entschl\u00fcsselung darf nicht leer sein
userSearchScope.notBlank=The user search scope cannot be blank
userSearchScope.invalidScope=The user search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
groupSearchScope.notBlank=The group search scope cannot be blank
groupSearchScope.invalidScope=The group search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
anyObjectSearchScope.notBlank=The any object search scope cannot be blank
anyObjectSearchScope.invalidScope=The any object search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'

entryNotFound=Eintrag "{0}" nicht gefunden
readingPasswordsNotSupported=Die Wiedergabe von Passw\u00f6rtern \u00fcber einen Suchvorgang wird nicht unterst\u00fctzt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ anyObjectClasses.notEmpty=The list of any-object object classes cannot be empty
anyObjectClasses.noBlankValues=The list of any-object object classes cannot contain blank values
anyObjectNameAttributes.notEmpty=The list of any-object name attributes cannot be empty
anyObjectNameAttributes.noBlankValues=The list of any-object name attributes cannot contain blank values
userSearchScope.display=User search scope
userSearchScope.help=The scope to utilise when searching for users. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
groupSearchScope.display=Group search scope
groupSearchScope.help=The scope to utilise when searching for groups. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
anyObjectSearchFilter.display=LDAP filter for retrieving Any Objects
anyObjectSearchFilter.help=An optional LDAP filter to control which any objects are returned from the LDAP resource. If no filter is specified, only any objects that include all specified object classes are returned.
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'
groupMemberAttribute.notBlank=El atributo de miembro de grupo no puede quedar en blanco
vlvSortAttribute.notBlank=El atributo de ordenaci\u00f3n de VLV no puede quedar en blanco
uidAttribute.notBlank=El atributo de asignaci\u00f3n a Uid no puede quedar en blanco
Expand All @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=El tama\u00f1o de bloque de sincronizaci\u00f3n de
passwordAttributeToSynchronize.notBlank=El atributo de contrase\u00f1a para sincronizar no puede quedar en blanco
decryptionKey.notBlank=La clave de descifrado no puede quedar en blanco
decryptionInitializationVector.notBlank=El vector de inicializaci\u00f3n de descifrado no puede quedar en blanco
userSearchScope.notBlank=The user search scope cannot be blank
userSearchScope.invalidScope=The user search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
groupSearchScope.notBlank=The group search scope cannot be blank
groupSearchScope.invalidScope=The group search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
anyObjectSearchScope.notBlank=The any object search scope cannot be blank
anyObjectSearchScope.invalidScope=The any object search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'

entryNotFound=Entrada "{0}" no encontrada
readingPasswordsNotSupported=Una operaci\u00f3n de b\u00fasqueda no puede devolver contrase\u00f1as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ anyObjectClasses.notEmpty=The list of any-object object classes cannot be empty
anyObjectClasses.noBlankValues=The list of any-object object classes cannot contain blank values
anyObjectNameAttributes.notEmpty=The list of any-object name attributes cannot be empty
anyObjectNameAttributes.noBlankValues=The list of any-object name attributes cannot contain blank values
userSearchScope.display=User search scope
userSearchScope.help=The scope to utilise when searching for users. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
groupSearchScope.display=Group search scope
groupSearchScope.help=The scope to utilise when searching for groups. Must be one of 'object', 'onelevel', 'subtree'. Default is 'subtree'
anyObjectSearchFilter.display=LDAP filter for retrieving Any Objects
anyObjectSearchFilter.help=An optional LDAP filter to control which any objects are returned from the LDAP resource. If no filter is specified, only any objects that include all specified object classes are returned.
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'
groupMemberAttribute.notBlank=L\u2019attribut de membre de groupe doit \u00eatre sp\u00e9cifi\u00e9.
vlvSortAttribute.notBlank=L\u2019attribut de tri VLV doit \u00eatre sp\u00e9cifi\u00e9.
uidAttribute.notBlank=L\u2019attribut \u00e0 mapper \u00e0 l\u2019UID doit \u00eatre sp\u00e9cifi\u00e9.
Expand All @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=La taille de bloc de synchronisation doit \u00eatr
passwordAttributeToSynchronize.notBlank=L\u2019attribut de mot de passe \u00e0 synchroniser doit \u00eatre sp\u00e9cifi\u00e9.
decryptionKey.notBlank=La cl\u00e9 de d\u00e9chiffrement doit \u00eatre sp\u00e9cifi\u00e9e.
decryptionInitializationVector.notBlank=Le vecteur d\u2019initialisation de d\u00e9chiffrement doit \u00eatre sp\u00e9cifi\u00e9.
userSearchScope.notBlank=The user search scope cannot be blank
userSearchScope.invalidScope=The user search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
groupSearchScope.notBlank=The group search scope cannot be blank
groupSearchScope.invalidScope=The group search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'
anyObjectSearchScope.notBlank=The any object search scope cannot be blank
anyObjectSearchScope.invalidScope=The any object search scope was invalid, it must be one of 'object', 'onelevel' or 'subtree'

entryNotFound=L\u2019entr\u00e9e \u2019{0}\u2019 est introuvable.
readingPasswordsNotSupported=Le renvoi de mots de passe \u00e0 partir d\u2019une op\u00e9ration de recherche n\u2019est pas pris en charge.
Expand Down
Loading

0 comments on commit 3759e52

Please sign in to comment.