Merge pull request #51461 from seanxuu

Closes gh-51461

* gh-51461:
  Polish "Configure required authentication operation types for embedded LDAP"
  Configure required authentication operation types for embedded LDAP
This commit is contained in:
Andy Wilkinson
2026-08-27 14:58:10 +01:00
3 changed files with 49 additions and 0 deletions
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
@@ -30,6 +31,7 @@ import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.OperationType;
import com.unboundid.ldap.sdk.schema.Schema;
import com.unboundid.ldif.LDIFReader;
import org.jspecify.annotations.Nullable;
@@ -81,6 +83,7 @@ import org.springframework.util.StringUtils;
* @author Mathieu Ouellet
* @author Raja Kolli
* @author Moritz Halbritter
* @author Sean Xu
* @since 4.0.0
*/
@AutoConfiguration(before = LdapAutoConfiguration.class)
@@ -110,6 +113,11 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
config.addAdditionalBindCredentials(username, password);
}
Set<OperationType> authenticationRequiredOperationTypes = this.embeddedProperties
.getAuthenticationRequiredOperationTypes();
if (!authenticationRequiredOperationTypes.isEmpty()) {
config.setAuthenticationRequiredOperationTypes(authenticationRequiredOperationTypes);
}
config.setListenerConfigs(createListenerConfig(sslBundles));
setSchema(config);
this.server = new InMemoryDirectoryServer(config);
@@ -17,8 +17,11 @@
package org.springframework.boot.ldap.autoconfigure.embedded;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.unboundid.ldap.sdk.OperationType;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -31,6 +34,7 @@ import org.springframework.util.StringUtils;
*
* @author Eddú Meléndez
* @author Mathieu Ouellet
* @author Sean Xu
* @since 4.0.0
*/
@ConfigurationProperties("spring.ldap.embedded")
@@ -41,6 +45,11 @@ public class EmbeddedLdapProperties {
*/
private int port;
/**
* LDAP operation types that require authentication.
*/
private Set<OperationType> authenticationRequiredOperationTypes = new LinkedHashSet<>();
/**
* Embedded LDAP credentials.
*/
@@ -75,6 +84,14 @@ public class EmbeddedLdapProperties {
this.port = port;
}
public Set<OperationType> getAuthenticationRequiredOperationTypes() {
return this.authenticationRequiredOperationTypes;
}
public void setAuthenticationRequiredOperationTypes(Set<OperationType> authenticationRequiredOperationTypes) {
this.authenticationRequiredOperationTypes = authenticationRequiredOperationTypes;
}
public Credential getCredential() {
return this.credential;
}
@@ -29,6 +29,9 @@ import com.unboundid.ldap.sdk.BindResult;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.schema.Schema;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
@@ -53,6 +56,7 @@ import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link EmbeddedLdapAutoConfiguration}
@@ -498,6 +502,26 @@ class EmbeddedLdapAutoConfigurationTests {
assertThat(properties.getSsl().isEnabled()).isFalse();
}
@Test
@WithSchemaLdifResource
void authenticationRequiredOperationTypesAreApplied() {
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn=dc=spring,dc=org",
"spring.ldap.embedded.credential.username=uid=root", "spring.ldap.embedded.credential.password=boot",
"spring.ldap.embedded.authentication-required-operation-types=search")
.run((context) -> {
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
try (LDAPConnection connection = new LDAPConnection("localhost", server.getListenPort())) {
SearchRequest searchRequest = new SearchRequest("dc=spring,dc=org", SearchScope.SUB,
"(objectClass=*)");
assertThatExceptionOfType(LDAPException.class).isThrownBy(() -> connection.search(searchRequest))
.extracting(LDAPException::getResultCode)
.isEqualTo(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
connection.bind("uid=root", "boot");
assertThat(connection.search(searchRequest).getEntryCount()).isGreaterThan(0);
}
});
}
private String[] sslBundleProperties(String... additionalProperties) {
String location = "classpath:org/springframework/boot/ldap/autoconfigure/embedded/";
List<String> propertyValues = new ArrayList<>();