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 e027cfa..73c4d9b 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/LdapConfiguration.java @@ -45,6 +45,7 @@ import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.exceptions.ConfigurationException; import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.spi.AbstractConfiguration; import org.identityconnectors.framework.spi.ConfigurationProperty; import org.identityconnectors.framework.spi.operations.SyncOp; @@ -56,6 +57,13 @@ */ public class LdapConfiguration extends AbstractConfiguration { + public enum SearchScope { + object, + onelevel, + subtree; + + } + // XXX should try to connect to the resource. public static final int DEFAULT_PORT = 389; @@ -112,6 +120,11 @@ public class LdapConfiguration extends AbstractConfiguration { */ private String groupSearchFilter = null; + /** + * A search filter that any anyObject needs to match in order to be returned + */ + private String anyObjectSearchFilter = null; + /** * The LDAP attribute holding the member for non-POSIX static groups. */ @@ -199,6 +212,21 @@ public class LdapConfiguration extends AbstractConfiguration { private String dnAttribute = "entryDN"; + /** + * The SearchScope for user objects + */ + private SearchScope userSearchScope = SearchScope.subtree; + + /** + * The SearchScope for group objects + */ + private SearchScope groupSearchScope = SearchScope.subtree; + + /** + * The SearchScope for anyObject objects + */ + private SearchScope anyObjectSearchScope = SearchScope.subtree; + /** * Whether to retrieve passwords when searching. The default is "false". */ @@ -273,6 +301,14 @@ public void validate() { checkNotEmpty(allConfig.getShortNameLdapAttributes(), "anyObjectNameAttributes.notEmpty"); checkNoBlankValues(allConfig.getShortNameLdapAttributes(), "anyObjectNameAttributes.noBlankValues"); + checkNotBlank(getUserSearchScope(), "userSearchScope.notBlank"); + checkValidScope(getUserSearchScope(), "userSearchScope.invalidScope"); + + checkNotBlank(getGroupSearchScope(), "groupSearchScope.notBlank"); + checkValidScope(getGroupSearchScope(), "groupSearchScope.invalidScope"); + + checkNotBlank(getAnyObjectSearchScope(), "anyObjectSearchScope.notBlank"); + checkValidScope(getAnyObjectSearchScope(), "anyObjectSearchScope.invalidScope"); checkNotBlank(groupMemberAttribute, "groupMemberAttribute.notBlank"); @@ -368,6 +404,17 @@ private void checkNoInvalidLdapNames(String[] array, String errorMessage) { } } + private void checkValidScope(String scope, String errorMessage) { + switch (scope) { + case OperationOptions.SCOPE_OBJECT: + case OperationOptions.SCOPE_ONE_LEVEL: + case OperationOptions.SCOPE_SUBTREE: + break; + default: + failValidation(errorMessage); + } + } + private void failValidation(String key, Object... args) { String message = getConnectorMessages().format(key, null, args); throw new ConfigurationException(message); @@ -490,6 +537,17 @@ public void setAccountUserNameAttributes(String... accountUserNameAttributes) { } @ConfigurationProperty(order = 11, + displayMessageKey = "userSearchScope.display", + helpMessageKey = "userSearchScope.help") + public String getUserSearchScope() { + return userSearchScope.toString(); + } + + public void setUserSearchScope(String userSearchScope) { + this.userSearchScope = SearchScope.valueOf(userSearchScope.toLowerCase()); + } + + @ConfigurationProperty(order = 12, displayMessageKey = "accountSearchFilter.display", helpMessageKey = "accountSearchFilter.help") public String getAccountSearchFilter() { @@ -500,7 +558,7 @@ public void setAccountSearchFilter(String accountSearchFilter) { this.accountSearchFilter = accountSearchFilter; } - @ConfigurationProperty(order = 12, + @ConfigurationProperty(order = 13, displayMessageKey = "groupObjectClasses.display", helpMessageKey = "groupObjectClasses.help") public String[] getGroupObjectClasses() { @@ -512,7 +570,7 @@ public void setGroupObjectClasses(String... groupObjectClasses) { groupConfig.setLdapClasses(Arrays.asList(groupObjectClasses)); } - @ConfigurationProperty(order = 13, + @ConfigurationProperty(order = 14, displayMessageKey = "groupNameAttributes.display", helpMessageKey = "groupNameAttributes.help") public String[] getGroupNameAttributes() { @@ -524,7 +582,18 @@ public void setGroupNameAttributes(String... groupNameAttributes) { groupConfig.setShortNameLdapAttributes(Arrays.asList(groupNameAttributes)); } - @ConfigurationProperty(order = 14, + @ConfigurationProperty(order = 15, + displayMessageKey = "groupSearchScope.display", + helpMessageKey = "groupSearchScope.help") + public String getGroupSearchScope() { + return groupSearchScope.toString(); + } + + public void setGroupSearchScope(String groupSearchScope) { + this.groupSearchScope = SearchScope.valueOf(groupSearchScope.toLowerCase()); + } + + @ConfigurationProperty(order = 16, displayMessageKey = "groupMemberAttribute.display", helpMessageKey = "groupMemberAttribute.help") public String getGroupMemberAttribute() { @@ -535,7 +604,7 @@ public void setGroupMemberAttribute(String groupMemberAttribute) { this.groupMemberAttribute = groupMemberAttribute; } - @ConfigurationProperty(order = 15, + @ConfigurationProperty(order = 17, displayMessageKey = "maintainLdapGroupMembership.display", helpMessageKey = "maintainLdapGroupMembership.help") public boolean isMaintainLdapGroupMembership() { @@ -546,7 +615,7 @@ public void setMaintainLdapGroupMembership(boolean maintainLdapGroupMembership) this.maintainLdapGroupMembership = maintainLdapGroupMembership; } - @ConfigurationProperty(order = 16, + @ConfigurationProperty(order = 18, displayMessageKey = "maintainPosixGroupMembership.display", helpMessageKey = "maintainPosixGroupMembership.help") public boolean isMaintainPosixGroupMembership() { @@ -557,7 +626,7 @@ public void setMaintainPosixGroupMembership(boolean maintainPosixGroupMembership this.maintainPosixGroupMembership = maintainPosixGroupMembership; } - @ConfigurationProperty(order = 17, + @ConfigurationProperty(order = 19, displayMessageKey = "addPrincipalToNewGroups.display", helpMessageKey = "addPrincipalToNewGroups.help") public boolean isAddPrincipalToNewGroups() { @@ -568,7 +637,7 @@ public void setAddPrincipalToNewGroups(boolean addPrincipalToNewGroups) { this.addPrincipalToNewGroups = addPrincipalToNewGroups; } - @ConfigurationProperty(order = 18, + @ConfigurationProperty(order = 20, displayMessageKey = "anyObjectClasses.display", helpMessageKey = "anyObjectClasses.help") public String[] getAnyObjectClasses() { @@ -580,7 +649,7 @@ public void setAnyObjectClasses(String... anyObjectClasses) { allConfig.setLdapClasses(Arrays.asList(anyObjectClasses)); } - @ConfigurationProperty(order = 19, + @ConfigurationProperty(order = 21, displayMessageKey = "anyObjectNameAttributes.display", helpMessageKey = "anyObjectNameAttributes.help") public String[] getAnyObjectNameAttributes() { @@ -592,7 +661,29 @@ public void setAnyObjectNameAttributes(String... anyObjectNameAttributes) { allConfig.setShortNameLdapAttributes(Arrays.asList(anyObjectNameAttributes)); } - @ConfigurationProperty(order = 20, + @ConfigurationProperty(order = 22, + displayMessageKey = "anyObjectSearchFilter.display", + helpMessageKey = "anyObjectSearchFilter.help") + public String getAnyObjectSearchFilter() { + return anyObjectSearchFilter; + } + + public void setAnyObjectSearchFilter(String anyObjectSearchFilter) { + this.anyObjectSearchFilter = anyObjectSearchFilter; + } + + @ConfigurationProperty(order = 23, + displayMessageKey = "anyObjectSearchScope.display", + helpMessageKey = "anyObjectSearchScope.help") + public String getAnyObjectSearchScope() { + return anyObjectSearchScope.toString(); + } + + public void setAnyObjectSearchScope(String anyObjectSearchScope) { + this.anyObjectSearchScope = SearchScope.valueOf(anyObjectSearchScope.toLowerCase()); + } + + @ConfigurationProperty(order = 24, displayMessageKey = "passwordHashAlgorithm.display", helpMessageKey = "passwordHashAlgorithm.help") public String getPasswordHashAlgorithm() { @@ -603,7 +694,7 @@ public void setPasswordHashAlgorithm(String passwordHashAlgorithm) { this.passwordHashAlgorithm = passwordHashAlgorithm; } - @ConfigurationProperty(order = 21, + @ConfigurationProperty(order = 25, displayMessageKey = "respectResourcePasswordPolicyChangeAfterReset.display", helpMessageKey = "respectResourcePasswordPolicyChangeAfterReset.help") public boolean isRespectResourcePasswordPolicyChangeAfterReset() { @@ -614,7 +705,7 @@ public void setRespectResourcePasswordPolicyChangeAfterReset(boolean respectReso this.respectResourcePasswordPolicyChangeAfterReset = respectResourcePasswordPolicyChangeAfterReset; } - @ConfigurationProperty(order = 22, + @ConfigurationProperty(order = 26, displayMessageKey = "useVlvControls.display", helpMessageKey = "useVlvControls.help") public boolean isUseVlvControls() { @@ -625,7 +716,7 @@ public void setUseVlvControls(boolean useVlvControls) { this.useVlvControls = useVlvControls; } - @ConfigurationProperty(order = 23, + @ConfigurationProperty(order = 27, displayMessageKey = "vlvSortAttribute.display", helpMessageKey = "vlvSortAttribute.help") public String getVlvSortAttribute() { @@ -636,7 +727,7 @@ public void setVlvSortAttribute(String vlvSortAttribute) { this.vlvSortAttribute = vlvSortAttribute; } - @ConfigurationProperty(order = 24, + @ConfigurationProperty(order = 28, displayMessageKey = "uidAttribute.display", helpMessageKey = "uidAttribute.help") public String getUidAttribute() { @@ -647,7 +738,7 @@ public void setUidAttribute(final String uidAttribute) { this.uidAttribute = uidAttribute; } - @ConfigurationProperty(order = 25, + @ConfigurationProperty(order = 29, displayMessageKey = "gidAttribute.display", helpMessageKey = "gidAttribute.help") public String getGidAttribute() { @@ -658,7 +749,7 @@ public void setGidAttribute(final String gidAttribute) { this.gidAttribute = gidAttribute; } - @ConfigurationProperty(order = 26, + @ConfigurationProperty(order = 30, displayMessageKey = "readSchema.display", helpMessageKey = "readSchema.help") public boolean isReadSchema() { @@ -670,7 +761,7 @@ public void setReadSchema(boolean readSchema) { } // Sync properties getters and setters. - @ConfigurationProperty(order = 27, operations = { SyncOp.class }, + @ConfigurationProperty(order = 31, operations = { SyncOp.class }, displayMessageKey = "baseContextsToSynchronize.display", helpMessageKey = "baseContextsToSynchronize.help") public String[] getBaseContextsToSynchronize() { @@ -681,7 +772,7 @@ public void setBaseContextsToSynchronize(String... baseContextsToSynchronize) { this.baseContextsToSynchronize = baseContextsToSynchronize.clone(); } - @ConfigurationProperty(order = 28, operations = { SyncOp.class }, + @ConfigurationProperty(order = 32, operations = { SyncOp.class }, displayMessageKey = "objectClassesToSynchronize.display", helpMessageKey = "objectClassesToSynchronize.help") public String[] getObjectClassesToSynchronize() { @@ -692,7 +783,7 @@ public void setObjectClassesToSynchronize(String... objectClassesToSynchronize) this.objectClassesToSynchronize = objectClassesToSynchronize.clone(); } - @ConfigurationProperty(order = 29, operations = { SyncOp.class }, + @ConfigurationProperty(order = 33, operations = { SyncOp.class }, displayMessageKey = "attributesToSynchronize.display", helpMessageKey = "attributesToSynchronize.help") public String[] getAttributesToSynchronize() { @@ -703,7 +794,7 @@ public void setAttributesToSynchronize(String... attributesToSynchronize) { this.attributesToSynchronize = attributesToSynchronize.clone(); } - @ConfigurationProperty(order = 30, operations = { SyncOp.class }, + @ConfigurationProperty(order = 34, operations = { SyncOp.class }, displayMessageKey = "modifiersNamesToFilterOut.display", helpMessageKey = "modifiersNamesToFilterOut.help") public String[] getModifiersNamesToFilterOut() { @@ -714,7 +805,7 @@ public void setModifiersNamesToFilterOut(String... modifiersNamesToFilterOut) { this.modifiersNamesToFilterOut = modifiersNamesToFilterOut.clone(); } - @ConfigurationProperty(order = 31, operations = { SyncOp.class }, + @ConfigurationProperty(order = 35, operations = { SyncOp.class }, displayMessageKey = "accountSynchronizationFilter.display", helpMessageKey = "accountSynchronizationFilter.help") public String getAccountSynchronizationFilter() { @@ -725,7 +816,7 @@ public void setAccountSynchronizationFilter(String accountSynchronizationFilter) this.accountSynchronizationFilter = accountSynchronizationFilter; } - @ConfigurationProperty(order = 32, operations = { SyncOp.class }, + @ConfigurationProperty(order = 36, operations = { SyncOp.class }, displayMessageKey = "changeLogBlockSize.display", helpMessageKey = "changeLogBlockSize.help") public int getChangeLogBlockSize() { @@ -736,7 +827,7 @@ public void setChangeLogBlockSize(int changeLogBlockSize) { this.changeLogBlockSize = changeLogBlockSize; } - @ConfigurationProperty(order = 33, operations = { SyncOp.class }, + @ConfigurationProperty(order = 37, operations = { SyncOp.class }, displayMessageKey = "changeNumberAttribute.display", helpMessageKey = "changeNumberAttribute.help") public String getChangeNumberAttribute() { @@ -747,7 +838,7 @@ public void setChangeNumberAttribute(String changeNumberAttribute) { this.changeNumberAttribute = changeNumberAttribute; } - @ConfigurationProperty(order = 34, operations = { SyncOp.class }, + @ConfigurationProperty(order = 38, operations = { SyncOp.class }, displayMessageKey = "filterWithOrInsteadOfAnd.display", helpMessageKey = "filterWithOrInsteadOfAnd.help") public boolean isFilterWithOrInsteadOfAnd() { @@ -758,7 +849,7 @@ public void setFilterWithOrInsteadOfAnd(boolean filterWithOrInsteadOfAnd) { this.filterWithOrInsteadOfAnd = filterWithOrInsteadOfAnd; } - @ConfigurationProperty(order = 35, operations = { SyncOp.class }, + @ConfigurationProperty(order = 39, operations = { SyncOp.class }, displayMessageKey = "removeLogEntryObjectClassFromFilter.display", helpMessageKey = "removeLogEntryObjectClassFromFilter.help") public boolean isRemoveLogEntryObjectClassFromFilter() { @@ -769,7 +860,7 @@ public void setRemoveLogEntryObjectClassFromFilter(boolean removeLogEntryObjectC this.removeLogEntryObjectClassFromFilter = removeLogEntryObjectClassFromFilter; } - @ConfigurationProperty(order = 36, operations = { SyncOp.class }, + @ConfigurationProperty(order = 40, operations = { SyncOp.class }, displayMessageKey = "synchronizePasswords.display", helpMessageKey = "synchronizePasswords.help") public boolean isSynchronizePasswords() { @@ -780,7 +871,7 @@ public void setSynchronizePasswords(boolean synchronizePasswords) { this.synchronizePasswords = synchronizePasswords; } - @ConfigurationProperty(order = 37, operations = { SyncOp.class }, + @ConfigurationProperty(order = 41, operations = { SyncOp.class }, displayMessageKey = "passwordAttributeToSynchronize.display", helpMessageKey = "passwordAttributeToSynchronize.help") public String getPasswordAttributeToSynchronize() { @@ -791,7 +882,7 @@ public void setPasswordAttributeToSynchronize(String passwordAttributeToSynchron this.passwordAttributeToSynchronize = passwordAttributeToSynchronize; } - @ConfigurationProperty(order = 38, operations = { SyncOp.class }, confidential = true, + @ConfigurationProperty(order = 42, operations = { SyncOp.class }, confidential = true, displayMessageKey = "passwordDecryptionKey.display", helpMessageKey = "passwordDecryptionKey.help") public GuardedByteArray getPasswordDecryptionKey() { @@ -803,7 +894,7 @@ public void setPasswordDecryptionKey(GuardedByteArray passwordDecryptionKey) { copy() : null; } - @ConfigurationProperty(order = 39, operations = { SyncOp.class }, confidential = true, + @ConfigurationProperty(order = 43, operations = { SyncOp.class }, confidential = true, displayMessageKey = "passwordDecryptionInitializationVector.display", helpMessageKey = "passwordDecryptionInitializationVector.help") public GuardedByteArray getPasswordDecryptionInitializationVector() { @@ -816,7 +907,7 @@ public void setPasswordDecryptionInitializationVector(GuardedByteArray passwordD copy() : null; } - @ConfigurationProperty(order = 40, + @ConfigurationProperty(order = 44, displayMessageKey = "statusManagementClass.display", helpMessageKey = "statusManagementClass.help") public String getStatusManagementClass() { @@ -827,7 +918,7 @@ public void setStatusManagementClass(String statusManagementClass) { this.statusManagementClass = statusManagementClass; } - @ConfigurationProperty(order = 41, + @ConfigurationProperty(order = 45, displayMessageKey = "retrievePasswordsWithSearch.display", helpMessageKey = "retrievePasswordsWithSearch.help") public boolean getRetrievePasswordsWithSearch() { @@ -838,7 +929,7 @@ public void setRetrievePasswordsWithSearch(boolean retrievePasswordsWithSearch) this.retrievePasswordsWithSearch = retrievePasswordsWithSearch; } - @ConfigurationProperty(order = 42, + @ConfigurationProperty(order = 46, displayMessageKey = "dnAttribute.display", helpMessageKey = "dnAttribute.help") public String getDnAttribute() { @@ -849,7 +940,7 @@ public void setDnAttribute(String dnAttribute) { this.dnAttribute = dnAttribute; } - @ConfigurationProperty(order = 43, + @ConfigurationProperty(order = 47, displayMessageKey = "groupSearchFilter.display", helpMessageKey = "groupSearchFilter.help") public String getGroupSearchFilter() { @@ -860,7 +951,7 @@ public void setGroupSearchFilter(String groupSearchFilter) { this.groupSearchFilter = groupSearchFilter; } - @ConfigurationProperty(order = 44, + @ConfigurationProperty(order = 48, displayMessageKey = "readTimeout.display", helpMessageKey = "readTimeout.help") public long getReadTimeout() { @@ -871,7 +962,7 @@ public void setReadTimeout(long readTimeout) { this.readTimeout = readTimeout; } - @ConfigurationProperty(order = 45, + @ConfigurationProperty(order = 49, displayMessageKey = "connectTimeout.display", helpMessageKey = "connectTimeout.help") public long getConnectTimeout() { 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 0ff685d..8246c1c 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 @@ -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; @@ -182,12 +185,16 @@ private LdapInternalSearch getInternalSearch(final Set attrsToGet) { LdapSearchStrategy strategy; List 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, @@ -200,7 +207,7 @@ private LdapInternalSearch getInternalSearch(final Set attrsToGet) { SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); Set ldapAttrsToGet = getLdapAttributesToGet(attrsToGet); - controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[ldapAttrsToGet.size()])); + controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[0])); controls.setSearchScope(searchScope); @@ -210,6 +217,8 @@ private LdapInternalSearch getInternalSearch(final Set 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, @@ -262,8 +271,8 @@ private ConnectorObject createConnectorObject( builder.setUid(conn.getSchemaMapping().createUid(oclass, entry)); builder.setName(conn.getSchemaMapping().createName(oclass, entry)); - final List ldapGroups = new ArrayList(); - final List posixGroups = new ArrayList(); + final List ldapGroups = new ArrayList<>(); + final List posixGroups = new ArrayList<>(); for (String attrName : attrsToGet) { Attribute attribute; @@ -375,7 +384,7 @@ private List 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); } @@ -448,17 +457,30 @@ private void removeNonReadableAttributes(final Set 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); } } } diff --git a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearches.java b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearches.java index b27880b..17ebd56 100644 --- a/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearches.java +++ b/src/main/java/net/tirasa/connid/bundles/ldap/search/LdapSearches.java @@ -69,7 +69,12 @@ 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); } /** @@ -77,7 +82,16 @@ public static String getEntryDN(LdapConnection conn, ObjectClass oclass, Uid uid * 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); } /** @@ -87,10 +101,16 @@ public static String findEntryDN(LdapConnection conn, ObjectClass oclass, Uid ui * the method will throw a UnknownUidException 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); @@ -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(); @@ -127,7 +148,7 @@ public static List findObjects( LOG.ok("Searching for object with attribute {0} of class {1} in {2}", attr, oclass.getObjectClassValue(), baseDN); - final List result = new ArrayList(); + final List result = new ArrayList<>(); EqualsFilter filter = (EqualsFilter) FilterBuilder.equalTo(attr); LdapFilter ldapFilter = new LdapFilterTranslator(conn.getSchemaMapping(), oclass). @@ -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 result = new ArrayList(); + final List result = new ArrayList<>(); if (!LdapUtil.isUnderContexts(entryDN, conn.getConfiguration(). getBaseContextsAsLdapNames())) { return null; 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 7ba66b7..6d6d5f0 100644 --- a/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties +++ b/src/main/resources/net/tirasa/connid/bundles/ldap/Messages.properties @@ -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) @@ -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 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 2af2a3f..90e8c3e 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 @@ -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 @@ -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 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 6b64602..743107e 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 @@ -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 @@ -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 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 ec12084..a1ccfba 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 @@ -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. @@ -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. 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 7af84d7..531f548 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 @@ -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\u2019attributo del gruppo non pu\u00f2 essere vuoto vlvSortAttribute.notBlank=L\u2019attributo di ordinamento VLV non pu\u00f2 essere vuoto uidAttribute.notBlank=L\u2019attributo da assegnare all\u2019Uid non pu\u00f2 essere vuoto @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=La dimensione del blocco di sincronizzazione deve passwordAttributeToSynchronize.notBlank=L\u2019attributo password da sincronizzare non pu\u00f2 essere vuoto decryptionKey.notBlank=La chiave di decrittazione non pu\u00f2 essere vuota decryptionInitializationVector.notBlank=Il vettore di inizializzazione di decrittazione non pu\u00f2 essere vuoto +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=Voce "{0}" non trovata readingPasswordsNotSupported=Non \u00e8 possibile restituire le password in un\u2019operazione di ricerca 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 76c78fc..8d69a09 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 @@ -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=\u30b0\u30eb\u30fc\u30d7\u30e1\u30f3\u30d0\u30fc\u5c5e\u6027\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 vlvSortAttribute.notBlank=VLV \u4e26\u3079\u66ff\u3048\u5c5e\u6027\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 uidAttribute.notBlank=UID \u306b\u30de\u30c3\u30d7\u3059\u308b\u5c5e\u6027\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=\u540c\u671f\u30d6\u30ed\u30c3\u30af\u30b5\u30a4\u passwordAttributeToSynchronize.notBlank=\u540c\u671f\u3059\u308b\u30d1\u30b9\u30ef\u30fc\u30c9\u5c5e\u6027\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 decryptionKey.notBlank=\u5fa9\u53f7\u5316\u30ad\u30fc\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 decryptionInitializationVector.notBlank=\u5fa9\u53f7\u5316\u306e\u521d\u671f\u5316\u30d9\u30af\u30c8\u30eb\u306f\u7a7a\u306b\u3067\u304d\u307e\u305b\u3093 +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=\u30a8\u30f3\u30c8\u30ea "{0}" \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 readingPasswordsNotSupported=\u691c\u7d22\u51e6\u7406\u304b\u3089\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u8fd4\u3059\u64cd\u4f5c\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093 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 c81e5b2..58e42ed 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 @@ -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=\uadf8\ub8f9 \uad6c\uc131\uc6d0 \uc18d\uc131\uc740 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. vlvSortAttribute.notBlank=VLV \uc815\ub82c \uc18d\uc131\uc740 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. uidAttribute.notBlank=UID\uc5d0 \ub9e4\ud551\ud560 \uc18d\uc131\uc740 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=\ub3d9\uae30\ud654 \ube14\ub85d \ud06c\uae30\ub294 passwordAttributeToSynchronize.notBlank=\ub3d9\uae30\ud654\ud560 \ube44\ubc00\ubc88\ud638 \uc18d\uc131\uc740 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. decryptionKey.notBlank=\ud574\ub3c5 \ud0a4\ub294 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. decryptionInitializationVector.notBlank=\ud574\ub3c5 \ucd08\uae30\ud654 \ubca1\ud130\ub294 \ube44\uc6cc \ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. +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="{0}" \ud56d\ubaa9\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. readingPasswordsNotSupported=\uac80\uc0c9 \uc791\uc5c5\uc5d0\uc11c \ube44\ubc00\ubc88\ud638\ub294 \ubc18\ud658\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. 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 eaa167e..52e51c8 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 @@ -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=O atributo de membro de grupo n\u00e3o pode ficar em branco vlvSortAttribute.notBlank=O atributo de classifica\u00e7\u00e3o VLV n\u00e3o pode ficar em branco uidAttribute.notBlank=O atributo de mapeamento para Uid n\u00e3o pode ficar em branco @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=O tamanho do bloco de sincroniza\u00e7\u00e3o deve passwordAttributeToSynchronize.notBlank=O atributo de senha para sincroniza\u00e7\u00e3o n\u00e3o pode ficar em branco decryptionKey.notBlank=A chave de descriptografia n\u00e3o pode ficar em branco decryptionInitializationVector.notBlank=O vetor de inicializa\u00e7\u00e3o da descriptografia n\u00e3o pode ficar em branco +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=A entrada "{0}" n\u00e3o foi encontrada readingPasswordsNotSupported=N\u00e3o existe suporte para o retorno de senhas de uma opera\u00e7\u00e3o de pesquisa 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 325762a..d24bb36 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 @@ -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=\u7ec4\u6210\u5458\u5c5e\u6027\u4e0d\u80fd\u4e3a\u7a7a vlvSortAttribute.notBlank=VLV \u6392\u5e8f\u5c5e\u6027\u4e0d\u80fd\u4e3a\u7a7a uidAttribute.notBlank=\u8981\u6620\u5c04\u5230 UID \u7684\u5c5e\u6027\u4e0d\u80fd\u4e3a\u7a7a @@ -123,6 +131,12 @@ changeLogBlockSize.legalValue=\u540c\u6b65\u5757\u5927\u5c0f\u5e94\u5927\u4e8e 0 passwordAttributeToSynchronize.notBlank=\u8981\u540c\u6b65\u7684\u5bc6\u7801\u5c5e\u6027\u4e0d\u80fd\u4e3a\u7a7a decryptionKey.notBlank=\u89e3\u5bc6\u5bc6\u94a5\u4e0d\u80fd\u4e3a\u7a7a decryptionInitializationVector.notBlank=\u89e3\u5bc6\u521d\u59cb\u5316\u5411\u91cf\u4e0d\u80fd\u4e3a\u7a7a +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=\u627e\u4e0d\u5230\u6761\u76ee\u201c{0}\u201d readingPasswordsNotSupported=\u4e0d\u652f\u6301\u4ece\u641c\u7d22\u64cd\u4f5c\u8fd4\u56de\u5bc6\u7801 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 989e3b1..e951738 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 @@ -80,6 +80,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' changeNumberAttribute.help=\u8b8a\u66f4\u8a18\u9304\u9805\u76ee\u4e2d\u4e4b\u8b8a\u66f4\u6578\u5b57\u5c6c\u6027\u7684\u540d\u7a31\u3002 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 @@ -130,6 +138,12 @@ changeLogBlockSize.legalValue=\u540c\u6b65\u5340\u6bb5\u5927\u5c0f\u61c9\u5927\u passwordAttributeToSynchronize.notBlank=\u8981\u540c\u6b65\u7684\u5bc6\u78bc\u5c6c\u6027\u4e0d\u53ef\u70ba\u7a7a\u767d decryptionKey.notBlank=\u89e3\u5bc6\u91d1\u9470\u4e0d\u53ef\u70ba\u7a7a\u767d decryptionInitializationVector.notBlank=\u89e3\u5bc6\u521d\u59cb\u5316\u5411\u91cf\u4e0d\u53ef\u70ba\u7a7a\u767d +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=\u627e\u4e0d\u5230\u9805\u76ee\u300c{0}\u300d readingPasswordsNotSupported=\u4e0d\u652f\u63f4\u5f9e\u641c\u5c0b\u4f5c\u696d\u50b3\u56de\u5bc6\u78bc diff --git a/src/test/java/net/tirasa/connid/bundles/ldap/AdapterCompatibilityTests.java b/src/test/java/net/tirasa/connid/bundles/ldap/AdapterCompatibilityTests.java index 15e697c..aaa47a4 100644 --- a/src/test/java/net/tirasa/connid/bundles/ldap/AdapterCompatibilityTests.java +++ b/src/test/java/net/tirasa/connid/bundles/ldap/AdapterCompatibilityTests.java @@ -134,7 +134,7 @@ public void createGroupOfUniqueNamesWithoutMembers() { ObjectClass oclass = new ObjectClass("groupOfUniqueNames"); Set attributes = new HashSet<>(); - Name name = new Name("cn=Another Group," + ACME_DN); + Name name = new Name("cn=Another Group," + ACME_GROUPS_DN); attributes.add(name); attributes.add(AttributeBuilder.build("cn", "Another Group")); // If "uniqueMember" is sent to the server as an empty attribute, the server complains. diff --git a/src/test/java/net/tirasa/connid/bundles/ldap/LdapConfigurationTests.java b/src/test/java/net/tirasa/connid/bundles/ldap/LdapConfigurationTests.java index 65e50b1..060e6df 100644 --- a/src/test/java/net/tirasa/connid/bundles/ldap/LdapConfigurationTests.java +++ b/src/test/java/net/tirasa/connid/bundles/ldap/LdapConfigurationTests.java @@ -23,6 +23,7 @@ */ package net.tirasa.connid.bundles.ldap; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -34,6 +35,7 @@ import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.security.GuardedByteArray; import org.identityconnectors.framework.common.exceptions.ConfigurationException; +import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.test.common.TestHelpers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -349,6 +351,76 @@ public void defaultValues() { assertEquals(0, config.getConnectTimeout()); assertEquals(CollectionUtil.newList("top"), Arrays.asList(config.getAnyObjectClasses())); assertEquals(CollectionUtil.newList("entryUUID"), Arrays.asList(config.getAnyObjectNameAttributes())); + assertEquals(OperationOptions.SCOPE_SUBTREE, config.getUserSearchScope()); + assertEquals(OperationOptions.SCOPE_SUBTREE, config.getGroupSearchScope()); + assertEquals(OperationOptions.SCOPE_SUBTREE, config.getAnyObjectSearchScope()); + assertNull(config.getAnyObjectSearchFilter()); + } + + @Test + public void userSearchScopeNotNull() { + assertThrows(NullPointerException.class, () -> config.setUserSearchScope((String) null)); + } + + @Test + public void userSearchScopeNotBlank() { + assertThrows(IllegalArgumentException.class, () -> config.setUserSearchScope(" ")); + } + + @Test + public void userSearchScopeInvalid() { + assertThrows(IllegalArgumentException.class, () -> config.setUserSearchScope("abc")); + } + + @Test + public void userSearchScopeValid() { + assertDoesNotThrow(() -> config.setUserSearchScope("object")); + assertDoesNotThrow(() -> config.setUserSearchScope("onelevel")); + assertDoesNotThrow(() -> config.setUserSearchScope("subtree")); + } + + @Test + public void groupSearchScopeNotNull() { + assertThrows(NullPointerException.class, () -> config.setGroupSearchScope((String) null)); + } + + @Test + public void groupSearchScopeNotBlank() { + assertThrows(IllegalArgumentException.class, () -> config.setGroupSearchScope(" ")); + } + + @Test + public void groupSearchScopeInvalid() { + assertThrows(IllegalArgumentException.class, () -> config.setGroupSearchScope("abc")); + } + + @Test + public void groupSearchScopeValid() { + assertDoesNotThrow(() -> config.setGroupSearchScope("object")); + assertDoesNotThrow(() -> config.setGroupSearchScope("onelevel")); + assertDoesNotThrow(() -> config.setGroupSearchScope("subtree")); + } + + @Test + public void anyObjectSearchScopeNotNull() { + assertThrows(NullPointerException.class, () -> config.setAnyObjectSearchScope((String) null)); + } + + @Test + public void anyObjectSearchScopeNotBlank() { + assertThrows(IllegalArgumentException.class, () -> config.setAnyObjectSearchScope(" ")); + } + + @Test + public void anyObjectSearchScopeInvalid() { + assertThrows(IllegalArgumentException.class, () -> config.setAnyObjectSearchScope("abc")); + } + + @Test + public void anyObjectSearchScopeValid() { + assertDoesNotThrow(() -> config.setAnyObjectSearchScope("object")); + assertDoesNotThrow(() -> config.setAnyObjectSearchScope("onelevel")); + assertDoesNotThrow(() -> config.setAnyObjectSearchScope("subtree")); } private static void assertCanValidate(LdapConfiguration config) { diff --git a/src/test/java/net/tirasa/connid/bundles/ldap/LdapConnectorTestBase.java b/src/test/java/net/tirasa/connid/bundles/ldap/LdapConnectorTestBase.java index f7e8281..f922665 100644 --- a/src/test/java/net/tirasa/connid/bundles/ldap/LdapConnectorTestBase.java +++ b/src/test/java/net/tirasa/connid/bundles/ldap/LdapConnectorTestBase.java @@ -81,6 +81,10 @@ public abstract class LdapConnectorTestBase { public static final String ACME_USERS_DN = "ou=Users,o=Acme,dc=example,dc=com"; + public static final String ACME_GROUPS_DN = "ou=Groups,o=Acme,dc=example,dc=com"; + + public static final String ACME_DEVICES_DN = "ou=Devices,o=Acme,dc=example,dc=com"; + public static final String BUGS_BUNNY_DN = "uid=bugs.bunny,ou=Users,o=Acme,dc=example,dc=com"; public static final String RENAME_ONE_TEST_DN = "uid=rename.one,ou=Users,o=Acme,dc=example,dc=com"; @@ -105,25 +109,31 @@ public abstract class LdapConnectorTestBase { public static final String EXPIRED_UID = "expired"; - public static final String BUGS_AND_FRIENDS_DN = "cn=Bugs and Friends,o=Acme,dc=example,dc=com"; + public static final String BUGS_AND_FRIENDS_DN = "cn=Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String EXTERNAL_PEERS_DN = "cn=External Peers,o=Acme,dc=example,dc=com"; + public static final String EXTERNAL_PEERS_DN = "cn=External Peers,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String UNIQUE_BUGS_AND_FRIENDS_DN = "cn=Unique Bugs and Friends,o=Acme,dc=example,dc=com"; + public static final String UNIQUE_BUGS_AND_FRIENDS_DN = + "cn=Unique Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com"; public static final String UNIQUE_BUGS_AND_FRIENDS_CN = "Unique Bugs and Friends"; - public static final String UNIQUE_EXTERNAL_PEERS_DN = "cn=Unique External Peers,o=Acme,dc=example,dc=com"; + public static final String UNIQUE_EXTERNAL_PEERS_DN = "cn=Unique External Peers,ou=Groups,o=Acme,dc=example,dc=com"; + + public static final String UNIQUE_EMPTY_GROUP_DN = "cn=Unique Empty Group,ou=Groups,o=Acme,dc=example,dc=com"; + + public static final String POSIX_BUGS_AND_FRIENDS_DN = + "cn=POSIX Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String UNIQUE_EMPTY_GROUP_DN = "cn=Unique Empty Group,o=Acme,dc=example,dc=com"; + public static final String POSIX_EXTERNAL_PEERS_DN = "cn=POSIX External Peers,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String POSIX_BUGS_AND_FRIENDS_DN = "cn=POSIX Bugs and Friends,o=Acme,dc=example,dc=com"; + public static final String POSIX_EMPTY_GROUP_DN = "cn=POSIX Empty Group,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String POSIX_EXTERNAL_PEERS_DN = "cn=POSIX External Peers,o=Acme,dc=example,dc=com"; + public static final String POSIX_BUGS_BUNNY_GROUP = "cn=POSIX Bugs Bunny Group,ou=Groups,o=Acme,dc=example,dc=com"; - public static final String POSIX_EMPTY_GROUP_DN = "cn=POSIX Empty Group,o=Acme,dc=example,dc=com"; + public static final String CARROT_LAPTOP_DN = "cn=Carrot Laptop,ou=Devices,o=Acme,dc=example,dc=com"; - public static final String POSIX_BUGS_BUNNY_GROUP = "cn=POSIX Bugs Bunny Group,o=Acme,dc=example,dc=com"; + public static final String CARROT_LAPTOP_CN = "Carrot Laptop"; public static final String SMALL_COMPANY_DN = "o=Small Company,dc=example,dc=com"; diff --git a/src/test/java/net/tirasa/connid/bundles/ldap/search/LdapSearchTests.java b/src/test/java/net/tirasa/connid/bundles/ldap/search/LdapSearchTests.java index 41b74b4..476cdfc 100644 --- a/src/test/java/net/tirasa/connid/bundles/ldap/search/LdapSearchTests.java +++ b/src/test/java/net/tirasa/connid/bundles/ldap/search/LdapSearchTests.java @@ -332,6 +332,127 @@ public void scope() { assertFalse(objects.isEmpty()); } + @Test + public void configurableUserScope() { + LdapConfiguration configuration = newConfiguration(); + configuration.setUserSearchScope("object"); + ConnectorFacade facade = newFacade(configuration); + + // Find an organization to pass in OP_CONTAINER. + ObjectClass oclass = new ObjectClass("organization"); + ConnectorObject organization = searchByAttribute(facade, oclass, new Name(ACME_DN)); + + // Prepare options + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + optionsBuilder.setContainer(new QualifiedUid(oclass, organization.getUid())); + optionsBuilder.setPageSize(100); + OperationOptions options = optionsBuilder.build(); + + // We can get bugs bunny with an 'object' search by DN + ConnectorObject bugsBunny = searchByAttribute(facade, ObjectClass.ACCOUNT, new Name(BUGS_BUNNY_DN)); + assertNotNull(bugsBunny); + + // Reconfigure for 'onelevel' search + configuration.setUserSearchScope("onelevel"); + configuration.setAccountSearchFilter("(uid=" + BUGS_BUNNY_UID + ")"); + facade = newFacade(configuration); + + // Bugs Bunny doesn't exist directly under the organisation.. + List objects = TestHelpers.searchToList( + facade, ObjectClass.ACCOUNT, null, options); + assertTrue(objects.isEmpty()); + + // Reconfigure for 'subtree' search + configuration.setUserSearchScope("subtree"); + facade = newFacade(configuration); + + // ... but does in the organisation subtree + objects = TestHelpers.searchToList(facade, ObjectClass.ACCOUNT, null, options); + assertFalse(objects.isEmpty()); + } + + @Test + public void configurableGroupScope() { + LdapConfiguration configuration = newConfiguration(); + configuration.setGroupSearchScope("object"); + ConnectorFacade facade = newFacade(configuration); + + // Find an organization to pass in OP_CONTAINER. + ObjectClass oclass = new ObjectClass("organization"); + ConnectorObject organization = searchByAttribute(facade, oclass, new Name(ACME_DN)); + + // Prepare options + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + optionsBuilder.setContainer(new QualifiedUid(oclass, organization.getUid())); + optionsBuilder.setPageSize(100); + OperationOptions options = optionsBuilder.build(); + + // We can get 'unique bugs and friends' with an 'object' search by DN + ConnectorObject uniqueBugsAndFriends = searchByAttribute( + facade, ObjectClass.GROUP, new Name(UNIQUE_BUGS_AND_FRIENDS_DN)); + assertNotNull(uniqueBugsAndFriends); + + // Reconfigure for 'onelevel' search + configuration.setGroupSearchScope("onelevel"); + configuration.setGroupSearchFilter("(cn=" + UNIQUE_BUGS_AND_FRIENDS_CN + ")"); + facade = newFacade(configuration); + + // Bugs Bunny doesn't exist directly under the organisation.. + List objects = TestHelpers.searchToList( + facade, ObjectClass.GROUP, null, options); + assertTrue(objects.isEmpty()); + + // Reconfigure for 'subtree' search + configuration.setGroupSearchScope("subtree"); + facade = newFacade(configuration); + + // ... but does in the organisation subtree + objects = TestHelpers.searchToList(facade, ObjectClass.GROUP, null, options); + assertFalse(objects.isEmpty()); + } + + @Test + public void configurableAnyObjectScope() { + LdapConfiguration configuration = newConfiguration(); + configuration.setAnyObjectSearchScope("object"); + ConnectorFacade facade = newFacade(configuration); + + // Find an organization to pass in OP_CONTAINER. + ObjectClass oclass = new ObjectClass("organization"); + ConnectorObject organization = searchByAttribute(facade, oclass, new Name(ACME_DN)); + + // Prepare options + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + optionsBuilder.setContainer(new QualifiedUid(oclass, organization.getUid())); + optionsBuilder.setPageSize(100); + OperationOptions options = optionsBuilder.build(); + + // Set up for 'device' search + ObjectClass deviceObjectClass = new ObjectClass("device"); + + // We can get the 'carrot laptop' device with an 'object' search by DN + ConnectorObject carrotLaptop = searchByAttribute(facade, deviceObjectClass, new Name(CARROT_LAPTOP_DN)); + assertNotNull(carrotLaptop); + + // Reconfigure for 'onelevel' search + configuration.setAnyObjectSearchScope("onelevel"); + configuration.setAnyObjectSearchFilter("(cn=" + CARROT_LAPTOP_CN + ")"); + facade = newFacade(configuration); + + // 'carrot laptop'' doesn't exist directly under the organisation.. + List objects = TestHelpers.searchToList( + facade, deviceObjectClass, null, options); + assertTrue(objects.isEmpty()); + + // Reconfigure for 'subtree' search + configuration.setAnyObjectSearchScope("subtree"); + facade = newFacade(configuration); + + // ... but does in the organisation subtree + objects = TestHelpers.searchToList(facade, deviceObjectClass, null, options); + assertFalse(objects.isEmpty()); + } + @Test public void accountSearchFilter() { ConnectorFacade facade = newFacade(); @@ -421,6 +542,53 @@ public void missingParenthesesAddedToGroupSearchFilter() { assertNotNull(searchByAttribute(facade, ObjectClass.GROUP, new Name(UNIQUE_BUGS_AND_FRIENDS_DN))); } + @Test + public void anyObjectSearchFilter() { + ConnectorFacade facade = newFacade(); + // Find an organization to pass in OP_CONTAINER. + ObjectClass oclass = new ObjectClass("organization"); + ConnectorObject organization = searchByAttribute(facade, oclass, new Name(ACME_DN)); + + // First just check that there really are some anyObjects (devices in this case). + ObjectClass deviceObjectClass = new ObjectClass("device"); + + OperationOptionsBuilder optionsBuilder = new OperationOptionsBuilder(); + optionsBuilder.setScope(OperationOptions.SCOPE_SUBTREE); + optionsBuilder.setContainer(new QualifiedUid(oclass, organization.getUid())); + List objects = TestHelpers.searchToList( + facade, deviceObjectClass, null, optionsBuilder.build()); + assertNotNull(getObjectByName(objects, CARROT_LAPTOP_DN)); + + // Test the anyObject search filter + LdapConfiguration config = newConfiguration(); + config.setAnyObjectSearchFilter("(cn=" + CARROT_LAPTOP_CN + ")"); + facade = newFacade(config); + objects = TestHelpers.searchToList(facade, deviceObjectClass, null, optionsBuilder.build()); + assertEquals(1, objects.size()); + assertNotNull(getObjectByName(objects, CARROT_LAPTOP_DN)); + } + + @Test + public void anyObjectSearchFilterOnlyAppliesToAnyObjects() { + LdapConfiguration config = newConfiguration(); + config.setAnyObjectSearchFilter("(cn=foobarbaz)"); + ConnectorFacade facade = newFacade(config); + ConnectorObject bugsBunny = searchByAttribute(facade, ObjectClass.ACCOUNT, new Name(BUGS_BUNNY_DN)); + + // If the (cn=foobarbaz) filter above applied, the search would return nothing. + assertNotNull(bugsBunny); + } + + @Test + public void missingParenthesesAddedToAnyObjectSearchFilter() { + LdapConfiguration config = newConfiguration(); + config.setAnyObjectSearchFilter("cn=" + CARROT_LAPTOP_CN); // No parentheses enclosing the filter. + ConnectorFacade facade = newFacade(config); + + // If parentheses were not added, the search would fail. + assertNotNull(searchByAttribute(facade, new ObjectClass("device"), new Name(CARROT_LAPTOP_DN))); + } + @Test public void multipleBaseDNs() { ConnectorFacade facade = newFacade(); diff --git a/src/test/resources/opendj/data.ldif b/src/test/resources/opendj/data.ldif index 8c86c36..6188b7a 100644 --- a/src/test/resources/opendj/data.ldif +++ b/src/test/resources/opendj/data.ldif @@ -44,6 +44,16 @@ objectClass: top objectClass: organizationalUnit ou: Users +dn: ou=Groups,o=Acme,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: Groups + +dn: ou=Devices,o=Acme,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: Devices + dn: uid=bugs.bunny,ou=Users,o=Acme,dc=example,dc=com objectClass: top objectClass: person @@ -86,7 +96,7 @@ ds-pwp-password-policy-dn: cn=Quickly Expiring Password Policy,cn=Password Polic # Non-unique groups. -dn: cn=Bugs and Friends,o=Acme,dc=example,dc=com +dn: cn=Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: groupOfNames cn: Bugs and Friends @@ -94,7 +104,7 @@ member: uid=bugs.bunny,ou=Users,o=Acme,dc=example,dc=com member: uid=elmer.fudd,ou=Users,o=Acme,dc=example,dc=com member: uid=sylvester,ou=Users,o=Acme,dc=example,dc=com -dn: cn=External Peers,o=Acme,dc=example,dc=com +dn: cn=External Peers,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: groupOfNames cn: External Peers @@ -103,7 +113,7 @@ member: uid=sylvester,ou=Users,o=Acme,dc=example,dc=com # Unique groups. -dn: cn=Unique Bugs and Friends,o=Acme,dc=example,dc=com +dn: cn=Unique Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: groupOfUniqueNames cn: Unique Bugs and Friends @@ -111,21 +121,21 @@ uniqueMember: uid=bugs.bunny,ou=Users,o=Acme,dc=example,dc=com uniqueMember: uid=elmer.fudd,ou=Users,o=Acme,dc=example,dc=com uniqueMember: uid=sylvester,ou=Users,o=Acme,dc=example,dc=com -dn: cn=Unique External Peers,o=Acme,dc=example,dc=com +dn: cn=Unique External Peers,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: groupOfUniqueNames cn: Unique External Peers uniqueMember: uid=single.account,o=Small Company,dc=example,dc=com uniqueMember: uid=sylvester,ou=Users,o=Acme,dc=example,dc=com -dn: cn=Unique Empty Group,o=Acme,dc=example,dc=com +dn: cn=Unique Empty Group,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: groupOfUniqueNames cn: Unique Empty Group # POSIX groups. -dn: cn=POSIX Bugs and Friends,o=Acme,dc=example,dc=com +dn: cn=POSIX Bugs and Friends,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: namedObject objectClass: posixGroup @@ -135,7 +145,7 @@ memberUid: bugs.bunny memberUid: elmer.fudd memberUid: sylvester -dn: cn=POSIX External Peers,o=Acme,dc=example,dc=com +dn: cn=POSIX External Peers,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: namedObject objectClass: posixGroup @@ -145,14 +155,14 @@ memberUid: single.account memberUid: owner memberUid: sylvester -dn: cn=POSIX Empty Group,o=Acme,dc=example,dc=com +dn: cn=POSIX Empty Group,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: namedObject objectClass: posixGroup cn: POSIX Empty Group gidNumber: 3 -dn: cn=POSIX Bugs Bunny Group,o=Acme,dc=example,dc=com +dn: cn=POSIX Bugs Bunny Group,ou=Groups,o=Acme,dc=example,dc=com objectClass: top objectClass: namedObject objectClass: posixGroup @@ -160,6 +170,12 @@ cn: POSIX Bugs Bunny Group gidNumber: 4 memberUid: bbunny +dn: cn=Carrot Laptop,ou=Devices,o=Acme,dc=example,dc=com +objectclass: top +objectClass: device +cn: Carrot Laptop +serialNumber: carrot + # A small company with an account and a person. dn: o=Small Company,dc=example,dc=com