Remove ApacheDS Support

Closes gh-13852
This commit is contained in:
Josh Cummings 2025-06-18 18:29:16 -06:00
parent 42e24aa53c
commit 6ddb964c61
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
49 changed files with 138 additions and 1406 deletions

View File

@ -78,12 +78,6 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
testImplementation "org.apache.directory.server:apacheds-core"
testImplementation "org.apache.directory.server:apacheds-core-entry"
testImplementation "org.apache.directory.server:apacheds-protocol-shared"
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
testImplementation "org.apache.directory.server:apacheds-server-jndi"
testImplementation 'org.apache.directory.shared:shared-ldap'
testImplementation "com.unboundid:unboundid-ldapsdk"
testImplementation 'jakarta.persistence:jakarta.persistence-api'
testImplementation "org.hibernate.orm:hibernate-core"

View File

@ -44,7 +44,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
@ -326,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
UnboundIdContainer ldapServer() throws Exception {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
"classpath:/test-server.ldif");
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
unboundIdContainer.setPort(getPort());
return unboundIdContainer;
}
}

View File

@ -43,7 +43,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
@ -226,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container;
private UnboundIdContainer container;
@Bean
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}

View File

@ -31,7 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container;
private UnboundIdContainer container;
@Bean
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}

View File

@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3);
}
@ -127,6 +127,27 @@ public class LdapProviderBeanDefinitionParserTests {
assertThat(auth).isNotNull();
}
@Test
public void supportsShaPasswordEncoder() {
this.appCtx = new InMemoryXmlApplicationContext("""
<ldap-server ldif='classpath:test-server.ldif' port='0'/>
<authentication-manager>
<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>
<password-compare>
<password-encoder ref='pe' />
</password-compare>
</ldap-authentication-provider>
</authentication-manager>
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
""");
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@Test
public void inetOrgContextMapperIsSupported() {
this.appCtx = new InMemoryXmlApplicationContext(

View File

@ -26,7 +26,7 @@ import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
@Test
public void defaultLdifFileIsSuccessful() {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
}
private int getDefaultPort() throws IOException {

View File

@ -7,7 +7,6 @@
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<logger name="org.apache.directory" level="ERROR"/>
<logger name="JdbmTable" level="INFO"/>
<logger name="JdbmIndex" level="INFO"/>
<logger name="org.apache.mina" level="WARN"/>

View File

@ -54,8 +54,6 @@ public abstract class BeanIds {
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";

View File

@ -37,7 +37,6 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
@ -60,12 +59,8 @@ import org.springframework.util.ClassUtils;
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final boolean apacheDsPresent;
private static final boolean unboundIdPresent;
private String groupRoleAttribute = "cn";
@ -100,7 +95,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
static {
ClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
unboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);
}
@ -467,8 +461,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
*/
public final class ContextSourceBuilder {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final int DEFAULT_PORT = 33389;
@ -584,14 +576,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return contextSource;
}
private void startEmbeddedLdapServer() throws Exception {
if (apacheDsPresent) {
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(this.root, this.ldif);
apacheDsContainer.setPort(getPort());
postProcess(apacheDsContainer);
this.port = apacheDsContainer.getLocalPort();
}
else if (unboundIdPresent) {
private void startEmbeddedLdapServer() {
if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
unboundIdContainer.setPort(getPort());
postProcess(unboundIdContainer);

View File

@ -32,7 +32,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -47,7 +46,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
/**
* Defines the Url of the ldap server to use. If not specified, an embedded apache DS
* Defines the Url of the ldap server to use. If not specified, an embedded UnboundID
* instance will be created
*/
private static final String ATT_URL = "url";
@ -78,22 +77,15 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
private static final int DEFAULT_PORT = 33389;
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final String APACHEDS_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.ApacheDSContainer";
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
private static final boolean unboundIdPresent;
private static final boolean apacheDsPresent;
static {
ClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();
unboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);
apacheDsPresent = ClassUtils.isPresent(APACHEDS_CLASSNAME, classLoader);
}
@Override
@ -128,10 +120,9 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
/**
* Will be called if no url attribute is supplied.
*
* Registers beans to create an embedded apache directory server.
* Registers beans to create an embedded UnboundID Server.
* @return the BeanDefinition for the ContextSource for the embedded server.
*
* @see ApacheDSContainer
* @see UnboundIdContainer
*/
private RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {
@ -162,8 +153,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
ldapContainer.getPropertyValues().addPropertyValue("port", getPort(element));
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)
|| parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
if (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
parserContext.getReaderContext()
.error("Only one embedded server bean is allowed per application context", element);
}
@ -175,9 +165,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private RootBeanDefinition getRootBeanDefinition(String mode) {
if (isApacheDsEnabled(mode)) {
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
}
if (isUnboundidEnabled(mode)) {
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
}
@ -185,19 +172,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private String resolveBeanId(String mode) {
if (isApacheDsEnabled(mode)) {
return BeanIds.EMBEDDED_APACHE_DS;
}
if (isUnboundidEnabled(mode)) {
return BeanIds.EMBEDDED_UNBOUNDID;
}
return null;
}
private boolean isApacheDsEnabled(String mode) {
return "apacheds".equals(mode) || apacheDsPresent;
}
private boolean isUnboundidEnabled(String mode) {
return "unboundid".equals(mode) || unboundIdPresent;
}
@ -233,10 +213,6 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private int getPort() {
if (apacheDsPresent) {
ApacheDSContainer apacheDSContainer = this.applicationContext.getBean(ApacheDSContainer.class);
return apacheDSContainer.getLocalPort();
}
if (unboundIdPresent) {
UnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);
return unboundIdContainer.getPort();

View File

@ -84,8 +84,8 @@ ldap-server.attlist &=
## Optional root suffix for the embedded LDAP server. Default is "dc=springframework,dc=org"
attribute root { xsd:string }?
ldap-server.attlist &=
## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.
attribute mode { "apacheds" | "unboundid" }?
## Explicitly specifies which embedded ldap server should use. The only supported value is 'unboundid'. By default, it will depends if the library is available in the classpath.
attribute mode { "unboundid" }?
ldap-server-ref-attribute =
## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.

View File

@ -224,13 +224,12 @@
</xs:attribute>
<xs:attribute name="mode">
<xs:annotation>
<xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and
<xs:documentation>Explicitly specifies which embedded ldap server should use. The only supported value is
'unboundid'. By default, it will depends if the library is available in the classpath.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="apacheds"/>
<xs:enumeration value="unboundid"/>
</xs:restriction>
</xs:simpleType>

View File

@ -21,7 +21,6 @@ import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.apache.commons.lang.StringUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -39,6 +38,7 @@ import org.springframework.security.oauth2.server.resource.web.reactive.function
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
@ -197,7 +197,7 @@ public class SecurityReactorContextConfigurationResourceServerTests {
public MockResponse dispatch(RecordedRequest request) {
MockResponse response = new MockResponse().setResponseCode(200);
String header = request.getHeader("Authorization");
if (StringUtils.isBlank(header)) {
if (!StringUtils.hasText(header)) {
return response;
}
return response.setBody(header);

View File

@ -30,12 +30,12 @@ import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang.StringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.http.SecurityFiltersAssertions;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -86,7 +86,7 @@ public class XsdDocumentedTests {
.flatMap(XmlNode::children)
.flatMap(XmlNode::children)
.map((node) -> node.attribute("value"))
.filter(StringUtils::isNotEmpty)
.filter(StringUtils::hasText)
.collect(Collectors.toList());
// @formatter:on
SecurityFiltersAssertions.assertEquals(nodes);
@ -129,7 +129,7 @@ public class XsdDocumentedTests {
.flatMap(XmlNode::children)
.flatMap(XmlNode::children)
.map((node) -> node.attribute("value"))
.filter(StringUtils::isNotEmpty)
.filter(StringUtils::hasText)
.collect(Collectors.toList());
// @formatter:on
assertThat(nodes).isEqualTo(expected);

View File

@ -7,8 +7,6 @@
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<logger name="org.apache.directory" level="ERROR"/>
<root level="${root.level:-WARN}">
<appender-ref ref="STDOUT" />
</root>

View File

@ -49,12 +49,6 @@ dependencies {
api libs.ldapsdk
api libs.net.sourceforge.htmlunit
api libs.org.htmlunit.htmlunit
api libs.org.apache.directory.server.apacheds.entry
api libs.org.apache.directory.server.apacheds.core
api libs.org.apache.directory.server.apacheds.protocol.ldap
api libs.org.apache.directory.server.apacheds.protocol.shared
api libs.org.apache.directory.server.apacheds.server.jndi
api libs.org.apache.directory.shared.shared.ldap
api libs.org.apache.httpcomponents.httpclient
api libs.org.aspectj.aspectjrt
api libs.org.aspectj.aspectjweaver

View File

@ -166,13 +166,9 @@ The top-level package is `org.springframework.security.ldap`.
|
| Data exception classes are required.
| apache-ds
| 1.5.5
| Required if you are using an embedded LDAP server (optional). If you use `apache-ds`, the `apacheds-core`, `apacheds-core-entry`, `apacheds-protocol-shared`, `apacheds-protocol-ldap` and `apacheds-server-jndi` modules are required.
| shared-ldap
| 0.9.15
| Required if you are using an embedded LDAP server (optional).
| com.unboundid:unboundid-ldapsdk
|
| Required if using an embedded LDAP server
| ldapsdk
| 4.1

View File

@ -363,7 +363,7 @@ This section addresses common Spring Security architecture questions:
. <<appendix-faq-namespace-to-bean-mapping>>
. <<appendix-faq-role-prefix>>
. <<appendix-faq-what-dependencies>>
. <<appendix-faq-apacheds-deps>>
. <<appendix-faq-unboundid-deps>>
. <<appendix-faq-what-is-userdetailservice>>
@ -412,30 +412,39 @@ The reference manual also includes <<appendix-namespace,an appendix>> that lists
If you build your project with Maven, adding the appropriate Spring Security modules as dependencies to your `pom.xml` file automatically pulls in the core jars that the framework requires.
Any that are marked as "`optional`" in the Spring Security `pom.xml` files have to be added to your own `pom.xml` file if you need them.
[[appendix-faq-unboundid-deps]]
=== What dependences are needed to run an embedded UnboundID LDAP server?
You need to add the following dependency to your project:
[tabs]
======
Maven::
+
[source,maven,role="primary"]
----
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>7.0.1</version>
<scope>runtime</scope>
</dependency>
----
Gradle::
+
[source,gradle,role="secondary"]
----
implementation 'com.unboundid:unboundid-ldapsdk:7.0.1'
----
======
[[appendix-faq-apacheds-deps]]
=== What dependencies are needed to run an embedded ApacheDS LDAP server?
If you use Maven, you need to add the following to your `pom.xml` file dependencies:
[source]
----
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
----
The other required jars should be pulled in transitively.
Spring Security 7 removes support for Apache DS.
Please use <<appendix-faq-unboundid-deps,UnboundID>> instead.
[[appendix-faq-what-is-userdetailservice]]
=== What is a UserDetailsService and do I need one?

View File

@ -24,7 +24,7 @@ This is actually the bean `id` of the `ContextSource` instance, if you want to u
[[nsa-ldap-server-mode]]
* **mode**
Explicitly specifies which embedded ldap server should use. Values are `apacheds` and `unboundid`. By default, it will depends if the library is available in the classpath.
Explicitly specifies which embedded ldap server should use. The only supported value is `unboundid`. By default, it will depends if the library is available in the classpath.
[[nsa-ldap-server-id]]
* **id**

View File

@ -223,82 +223,8 @@ fun ldapContainer(): UnboundIdContainer {
[[servlet-authentication-ldap-apacheds]]
=== Embedded ApacheDS Server
[NOTE]
====
Spring Security uses ApacheDS 1.x, which is no longer maintained.
Unfortunately, ApacheDS 2.x has only released milestone versions with no stable release.
Once a stable release of ApacheDS 2.x is available, we will consider updating.
====
If you wish to use https://directory.apache.org/apacheds/[Apache DS], specify the following dependencies:
.ApacheDS Dependencies
[tabs]
======
Maven::
+
[source,xml,role="primary",subs="+attributes"]
----
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>{apacheds-core-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>{apacheds-core-version}</version>
<scope>runtime</scope>
</dependency>
----
Gradle::
+
[source,groovy,role="secondary",subs="+attributes"]
----
depenendencies {
runtimeOnly "org.apache.directory.server:apacheds-core:{apacheds-core-version}"
runtimeOnly "org.apache.directory.server:apacheds-server-jndi:{apacheds-core-version}"
}
----
======
You can then configure the Embedded LDAP Server:
.Embedded LDAP Server Configuration
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
ApacheDSContainer ldapContainer() {
return new ApacheDSContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}
----
XML::
+
[source,xml,role="secondary"]
----
<b:bean class="org.springframework.security.ldap.server.ApacheDSContainer"
c:defaultPartitionSuffix="dc=springframework,dc=org"
c:ldif="classpath:users.ldif"/>
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun ldapContainer(): ApacheDSContainer {
return ApacheDSContainer("dc=springframework,dc=org", "classpath:users.ldif")
}
----
======
Spring Security 7 removes support for Apache DS.
Please use <<servlet-authentication-ldap-unboundid,UnboundID>> instead.
[[servlet-authentication-ldap-contextsource]]
== LDAP ContextSource

View File

@ -15,7 +15,7 @@ For example, adding the following element from the `security` namespace to an ap
<security:ldap-server />
----
This is much simpler than wiring up the equivalent Apache Directory Server beans.
This is much simpler than wiring up the equivalent UnboundID Server beans.
The most common alternative configuration requirements are supported by attributes on the `ldap-server` element, and the user is isolated from worrying about which beans they need to create and what the bean property names are.
You can find out more about the use of the `ldap-server` element in the chapter on xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP Authentication].
A good XML editor while editing the application context file should provide information on the attributes and elements that are available.

View File

@ -45,7 +45,6 @@ dependencies {
testImplementation libs.webauthn4j.core
testImplementation 'org.jetbrains.kotlin:kotlin-reflect'
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation 'org.apache.directory.server:apacheds-core'
testImplementation 'org.springframework:spring-core'
testImplementation 'org.springframework:spring-test'

View File

@ -4,7 +4,6 @@ io-rsocket = "1.1.5"
io-spring-javaformat = "0.0.46"
io-spring-nohttp = "0.0.11"
jakarta-websocket = "2.2.0"
org-apache-directory-server = "1.5.5"
org-apache-maven-resolver = "1.9.23"
org-aspectj = "1.9.24"
org-bouncycastle = "1.80"
@ -50,12 +49,6 @@ jakarta-xml-bind-jakarta-xml-bind-api = "jakarta.xml.bind:jakarta.xml.bind-api:4
ldapsdk = "ldapsdk:ldapsdk:4.1"
net-sourceforge-htmlunit = "net.sourceforge.htmlunit:htmlunit:2.70.0"
org-htmlunit-htmlunit = "org.htmlunit:htmlunit:4.11.1"
org-apache-directory-server-apacheds-core = { module = "org.apache.directory.server:apacheds-core", version.ref = "org-apache-directory-server" }
org-apache-directory-server-apacheds-entry = { module = "org.apache.directory.server:apacheds-core-entry", version.ref = "org-apache-directory-server" }
org-apache-directory-server-apacheds-protocol-ldap = { module = "org.apache.directory.server:apacheds-protocol-ldap", version.ref = "org-apache-directory-server" }
org-apache-directory-server-apacheds-protocol-shared = { module = "org.apache.directory.server:apacheds-protocol-shared", version.ref = "org-apache-directory-server" }
org-apache-directory-server-apacheds-server-jndi = { module = "org.apache.directory.server:apacheds-server-jndi", version.ref = "org-apache-directory-server" }
org-apache-directory-shared-shared-ldap = "org.apache.directory.shared:shared-ldap:0.9.15"
org-apache-httpcomponents-httpclient = "org.apache.httpcomponents:httpclient:4.5.14"
org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.9.10"
org-apache-maven-resolver-maven-resolver-connector-basic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "org-apache-maven-resolver" }

View File

@ -1,30 +0,0 @@
apply plugin: 'io.spring.convention.spring-test'
dependencies {
implementation platform(project(":spring-security-dependencies"))
implementation project(':spring-security-core')
implementation 'org.springframework:spring-beans'
implementation 'org.springframework:spring-context'
implementation 'org.springframework:spring-core'
implementation 'org.springframework:spring-tx'
implementation project(':spring-security-config')
implementation project(':spring-security-ldap')
runtimeOnly "org.apache.directory.server:apacheds-core"
runtimeOnly "org.apache.directory.server:apacheds-core-entry"
runtimeOnly "org.apache.directory.server:apacheds-protocol-shared"
runtimeOnly "org.apache.directory.server:apacheds-protocol-ldap"
runtimeOnly "org.apache.directory.server:apacheds-server-jndi"
runtimeOnly 'org.apache.directory.shared:shared-ldap'
testImplementation project(path : ':spring-security-ldap', configuration : 'tests')
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
testImplementation "org.junit.jupiter:junit-jupiter-engine"
testImplementation "org.mockito:mockito-core"
testImplementation "org.mockito:mockito-junit-jupiter"
testImplementation "org.springframework:spring-test"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.server.ApacheDSContainer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class LdapServerBeanDefinitionParserTests {
private ClassPathXmlApplicationContext context;
@BeforeEach
public void setup() {
this.context = new ClassPathXmlApplicationContext("applicationContext-security.xml");
}
@AfterEach
public void closeAppContext() {
if (this.context != null) {
this.context.close();
this.context = null;
}
}
@Test
public void apacheDirectoryServerIsStartedByDefault() {
String[] beanNames = this.context.getBeanNamesForType(ApacheDSContainer.class);
assertThat(beanNames).hasSize(1);
assertThat(beanNames[0]).isEqualTo(BeanIds.EMBEDDED_APACHE_DS);
}
}

View File

@ -1,9 +0,0 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:s="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
<s:ldap-server ldif="classpath:users.ldif" port="0"/>
</beans>

View File

@ -1,60 +0,0 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=rod,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: rod
userPassword: koala
dn: uid=dianne,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: dianne
userPassword: emu
dn: uid=scott,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Scott
sn: Wombat
uid: scott
userPassword: wombat
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
member: uid=rod,ou=people,dc=springframework,dc=org
member: uid=dianne,ou=people,dc=springframework,dc=org
member: uid=scott,ou=people,dc=springframework,dc=org
dn: cn=teller,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: teller
member: uid=rod,ou=people,dc=springframework,dc=org
member: uid=dianne,ou=people,dc=springframework,dc=org
dn: cn=supervisor,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: supervisor
member: uid=rod,ou=people,dc=springframework,dc=org

View File

@ -1,30 +0,0 @@
apply plugin: 'io.spring.convention.spring-test'
dependencies {
implementation platform(project(":spring-security-dependencies"))
implementation project(':spring-security-core')
implementation 'org.springframework:spring-beans'
implementation 'org.springframework:spring-context'
implementation 'org.springframework:spring-core'
implementation 'org.springframework:spring-tx'
implementation project(':spring-security-config')
implementation project(':spring-security-ldap')
runtimeOnly "org.apache.directory.server:apacheds-core"
runtimeOnly "org.apache.directory.server:apacheds-core-entry"
runtimeOnly "org.apache.directory.server:apacheds-protocol-shared"
runtimeOnly "org.apache.directory.server:apacheds-protocol-ldap"
runtimeOnly "org.apache.directory.server:apacheds-server-jndi"
runtimeOnly 'org.apache.directory.shared:shared-ldap'
testImplementation project(path : ':spring-security-ldap', configuration : 'tests')
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
testImplementation "org.junit.jupiter:junit-jupiter-engine"
testImplementation "org.mockito:mockito-core"
testImplementation "org.mockito:mockito-junit-jupiter"
testImplementation "org.springframework:spring-test"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.config.BeanIds;
import org.springframework.security.ldap.server.ApacheDSContainer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class LdapServerBeanDefinitionParserTests {
private ClassPathXmlApplicationContext context;
@BeforeEach
public void setup() {
this.context = new ClassPathXmlApplicationContext("applicationContext-security.xml");
}
@AfterEach
public void closeAppContext() {
if (this.context != null) {
this.context.close();
this.context = null;
}
}
@Test
public void apacheDirectoryServerIsStartedByDefault() {
String[] beanNames = this.context.getBeanNamesForType(ApacheDSContainer.class);
assertThat(beanNames).hasSize(1);
assertThat(beanNames[0]).isEqualTo(BeanIds.EMBEDDED_APACHE_DS);
}
}

View File

@ -1,9 +0,0 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:s="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
<s:ldap-server mode="apacheds" ldif="classpath:users.ldif" port="0"/>
</beans>

View File

@ -1,60 +0,0 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=rod,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: rod
userPassword: koala
dn: uid=dianne,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: dianne
userPassword: emu
dn: uid=scott,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Scott
sn: Wombat
uid: scott
userPassword: wombat
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
member: uid=rod,ou=people,dc=springframework,dc=org
member: uid=dianne,ou=people,dc=springframework,dc=org
member: uid=scott,ou=people,dc=springframework,dc=org
dn: cn=teller,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: teller
member: uid=rod,ou=people,dc=springframework,dc=org
member: uid=dianne,ou=people,dc=springframework,dc=org
dn: cn=supervisor,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: supervisor
member: uid=rod,ou=people,dc=springframework,dc=org

View File

@ -15,12 +15,6 @@ dependencies {
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'ldapsdk:ldapsdk'
optional "com.unboundid:unboundid-ldapsdk"
optional "org.apache.directory.server:apacheds-core"
optional "org.apache.directory.server:apacheds-core-entry"
optional "org.apache.directory.server:apacheds-protocol-shared"
optional "org.apache.directory.server:apacheds-protocol-ldap"
optional "org.apache.directory.server:apacheds-server-jndi"
optional 'org.apache.directory.shared:shared-ldap'
api ('org.springframework.ldap:spring-ldap-core') {
exclude(group: 'commons-logging', module: 'commons-logging')
exclude(group: 'org.springframework', module: 'spring-beans')

View File

@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class DefaultSpringSecurityContextSourceTests {
@Autowired

View File

@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class SpringSecurityLdapTemplateITests {
@Autowired
@ -116,10 +116,10 @@ public class SpringSecurityLdapTemplateITests {
assertThat(values).hasSize(1);
Map<String, List<String>> record = values.iterator().next();
assertAttributeValue(record, "uid", "bob");
assertAttributeValue(record, "objectclass", "top", "person", "organizationalPerson", "inetOrgPerson");
assertAttributeValue(record, "objectClass", "top", "person", "organizationalPerson", "inetOrgPerson");
assertAttributeValue(record, "cn", "Bob Hamilton");
assertAttributeValue(record, "sn", "Hamilton");
assertThat(record.containsKey("userPassword")).isFalse();
assertThat(record.containsKey("userPassword")).isTrue();
}
@Test
@ -129,10 +129,10 @@ public class SpringSecurityLdapTemplateITests {
assertThat(values).hasSize(1);
Map<String, List<String>> record = values.iterator().next();
assertAttributeValue(record, "uid", "bob");
assertAttributeValue(record, "objectclass", "top", "person", "organizationalPerson", "inetOrgPerson");
assertAttributeValue(record, "objectClass", "top", "person", "organizationalPerson", "inetOrgPerson");
assertAttributeValue(record, "cn", "Bob Hamilton");
assertAttributeValue(record, "sn", "Hamilton");
assertThat(record.containsKey("userPassword")).isFalse();
assertThat(record.containsKey("userPassword")).isTrue();
}
@Test
@ -145,7 +145,7 @@ public class SpringSecurityLdapTemplateITests {
assertAttributeValue(record, "cn", "Bob Hamilton");
assertAttributeValue(record, "sn", "Hamilton");
assertThat(record.containsKey("userPassword")).isFalse();
assertThat(record.containsKey("objectclass")).isFalse();
assertThat(record.containsKey("objectClass")).isFalse();
}
protected void assertAttributeValue(Map<String, List<String>> record, String attributeName, String... values) {

View File

@ -20,31 +20,31 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
/**
* @author Eddú Meléndez
*/
@Configuration
public class ApacheDsContainerConfig implements DisposableBean {
public class UnboundIdContainerConfig implements DisposableBean {
private ApacheDSContainer container;
private UnboundIdContainer container;
@Bean
ApacheDSContainer ldapContainer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
UnboundIdContainer ldapContainer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
ContextSource contextSource(ApacheDSContainer ldapContainer) throws Exception {
ContextSource contextSource(UnboundIdContainer ldapContainer) {
return new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=springframework,dc=org");
"ldap://127.0.0.1:" + ldapContainer.getPort() + "/dc=springframework,dc=org");
}
@Override
public void destroy() throws Exception {
public void destroy() {
this.container.stop();
}

View File

@ -33,8 +33,8 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -53,7 +53,7 @@ import static org.mockito.Mockito.spy;
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class BindAuthenticatorTests {
@Autowired

View File

@ -30,8 +30,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class PasswordComparisonAuthenticatorTests {
@Autowired

View File

@ -25,8 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -40,7 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class FilterBasedLdapUserSearchTests {
@Autowired

View File

@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -40,7 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Steve Riesenberg
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = FilterBasedLdapUserSearchWithSpacesTests.ApacheDsContainerWithSpacesConfig.class)
@ContextConfiguration(classes = FilterBasedLdapUserSearchWithSpacesTests.UnboundIdContainerWithSpacesConfig.class)
public class FilterBasedLdapUserSearchWithSpacesTests {
@Autowired
@ -61,22 +61,22 @@ public class FilterBasedLdapUserSearchWithSpacesTests {
}
@Configuration
static class ApacheDsContainerWithSpacesConfig implements DisposableBean {
static class UnboundIdContainerWithSpacesConfig implements DisposableBean {
private ApacheDSContainer container;
private UnboundIdContainer container;
@Bean
ApacheDSContainer ldapContainer() throws Exception {
this.container = new ApacheDSContainer("dc=spring framework,dc=org",
UnboundIdContainer ldapContainer() {
this.container = new UnboundIdContainer("dc=spring framework,dc=org",
"classpath:test-server-with-spaces.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
ContextSource contextSource(ApacheDSContainer ldapContainer) {
ContextSource contextSource(UnboundIdContainer ldapContainer) {
return new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=spring%20framework,dc=org");
"ldap://127.0.0.1:" + ldapContainer.getPort() + "/dc=spring%20framework,dc=org");
}
@Override

View File

@ -1,221 +0,0 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.server;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.fail;
/**
* Useful for debugging the container by itself.
*
* @author Luke Taylor
* @author Rob Winch
* @author Gunnar Hillert
* @author Evgeniy Cheban
* @since 3.0
*/
public class ApacheDSContainerTests {
@TempDir
public File temporaryFolder;
// SEC-2162
@Test
public void failsToStartThrowsException() throws Exception {
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:missing.ldif");
List<Integer> ports = getDefaultPorts(1);
server1.setPort(ports.get(0));
server2.setPort(ports.get(0));
try {
server1.afterPropertiesSet();
try {
server2.afterPropertiesSet();
fail("Expected Exception");
}
catch (RuntimeException success) {
}
}
finally {
try {
server1.destroy();
}
catch (Throwable ex) {
}
try {
server2.destroy();
}
catch (Throwable ex) {
}
}
}
// SEC-2161
@Test
public void multipleInstancesSimultanciously() throws Exception {
ApacheDSContainer server1 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
ApacheDSContainer server2 = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
List<Integer> ports = getDefaultPorts(2);
server1.setPort(ports.get(0));
server2.setPort(ports.get(1));
try {
server1.afterPropertiesSet();
server2.afterPropertiesSet();
}
finally {
try {
server1.destroy();
}
catch (Throwable ex) {
}
try {
server2.destroy();
}
catch (Throwable ex) {
}
}
}
@Test
public void startWithLdapOverSslWithoutCertificate() throws Exception {
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
List<Integer> ports = getDefaultPorts(1);
server.setPort(ports.get(0));
server.setLdapOverSslEnabled(true);
assertThatIllegalArgumentException().isThrownBy(server::afterPropertiesSet)
.withMessage("When LdapOverSsl is enabled, the keyStoreFile property must be set.");
}
@Test
@DisabledOnOs(OS.WINDOWS)
public void startWithLdapOverSslWithWrongPassword() throws Exception {
final ClassPathResource keyStoreResource = new ClassPathResource(
"/org/springframework/security/ldap/server/spring.keystore");
final File temporaryKeyStoreFile = new File(this.temporaryFolder, "spring.keystore");
FileCopyUtils.copy(keyStoreResource.getInputStream(), new FileOutputStream(temporaryKeyStoreFile));
assertThat(temporaryKeyStoreFile).isFile();
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
List<Integer> ports = getDefaultPorts(1);
server.setPort(ports.get(0));
server.setLdapOverSslEnabled(true);
server.setKeyStoreFile(temporaryKeyStoreFile);
server.setCertificatePassord("incorrect-password");
assertThatExceptionOfType(RuntimeException.class).isThrownBy(server::afterPropertiesSet)
.withMessage("Server startup failed")
.withRootCauseInstanceOf(UnrecoverableKeyException.class);
}
/**
* This test starts an LDAP server using LDAPs (LDAP over SSL). A self-signed
* certificate is being used, which was previously generated with:
*
* <pre>
* {@code
* keytool -genkey -alias spring -keyalg RSA -keystore spring.keystore -validity 3650 -storetype JKS \
* -dname "CN=localhost, OU=Spring, O=Pivotal, L=Kailua-Kona, ST=HI, C=US" -keypass spring -storepass spring
* }
* </pre>
* @throws Exception
*/
@Test
@DisabledOnOs(OS.WINDOWS)
public void startWithLdapOverSsl() throws Exception {
final ClassPathResource keyStoreResource = new ClassPathResource(
"/org/springframework/security/ldap/server/spring.keystore");
final File temporaryKeyStoreFile = new File(this.temporaryFolder, "spring.keystore");
FileCopyUtils.copy(keyStoreResource.getInputStream(), new FileOutputStream(temporaryKeyStoreFile));
assertThat(temporaryKeyStoreFile).isFile();
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
List<Integer> ports = getDefaultPorts(1);
server.setPort(ports.get(0));
server.setLdapOverSslEnabled(true);
server.setKeyStoreFile(temporaryKeyStoreFile);
server.setCertificatePassord("spring");
try {
server.afterPropertiesSet();
}
finally {
try {
server.destroy();
}
catch (Throwable ex) {
}
}
}
private List<Integer> getDefaultPorts(int count) throws IOException {
List<ServerSocket> connections = new ArrayList<>();
List<Integer> availablePorts = new ArrayList<>(count);
try {
for (int i = 0; i < count; i++) {
ServerSocket socket = new ServerSocket(0);
connections.add(socket);
availablePorts.add(socket.getLocalPort());
}
return availablePorts;
}
finally {
for (ServerSocket conn : connections) {
conn.close();
}
}
}
@Test
public void afterPropertiesSetWhenPortIsZeroThenRandomPortIsSelected() throws Exception {
ApacheDSContainer server = new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
server.setPort(0);
try {
server.afterPropertiesSet();
assertThat(server.getPort()).isEqualTo(0);
assertThat(server.getLocalPort()).isNotEqualTo(0);
}
finally {
server.destroy();
}
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.server;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests reproducing problems with loading structures from ldif on embedded ApacheDS
* server.
*
* @author Marcin Zajączkowski
*/
public class ApacheDSEmbeddedLdifTests {
private static final String LDAP_ROOT = "ou=ssattributes,dc=springframework,dc=org";
private static final int LDAP_PORT = 52389;
private ApacheDSContainer server;
private SpringSecurityLdapTemplate ldapTemplate;
@BeforeEach
public void setUp() throws Exception {
// TODO: InMemoryXmlApplicationContext would be useful here, but it is not visible
this.server = new ApacheDSContainer(LDAP_ROOT, "classpath:test-server-custom-attribute-types.ldif");
this.server.setPort(LDAP_PORT);
this.server.afterPropertiesSet();
this.ldapTemplate = new SpringSecurityLdapTemplate(createLdapContextSource());
}
private LdapContextSource createLdapContextSource() {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl("ldap://localhost:" + LDAP_PORT);
ldapContextSource.setBase(LDAP_ROOT);
ldapContextSource.afterPropertiesSet();
return ldapContextSource;
}
@AfterEach
public void tearDown() throws Exception {
if (this.server != null) {
this.server.destroy();
}
}
@Disabled // Not fixed yet
@Test // SEC-2387
public void customAttributeTypesShouldBeProperlyCreatedWhenLoadedFromLdif() {
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1", "uid", "objectWithCustomAttribute1"))
.isTrue();
assertThat(this.ldapTemplate.compare("uid=objectWithCustomAttribute1", "customAttribute", "I am custom"))
.isTrue();
}
}

View File

@ -31,7 +31,7 @@ import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.ApacheDsContainerWithUndefinedGroupRoleAttributeConfig.class)
classes = DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.UnboundIdContainerWithUndefinedGroupRoleAttributeConfig.class)
public class DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests {
@Autowired
@ -77,22 +77,22 @@ public class DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests {
}
@Configuration
static class ApacheDsContainerWithUndefinedGroupRoleAttributeConfig implements DisposableBean {
static class UnboundIdContainerWithUndefinedGroupRoleAttributeConfig implements DisposableBean {
private ApacheDSContainer container;
private UnboundIdContainer container;
@Bean
ApacheDSContainer ldapContainer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org",
UnboundIdContainer ldapContainer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org",
"classpath:test-server-with-undefined-group-role-attributes.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
ContextSource contextSource(ApacheDSContainer ldapContainer) {
ContextSource contextSource(UnboundIdContainer ldapContainer) {
return new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=springframework,dc=org");
"ldap://127.0.0.1:" + ldapContainer.getPort() + "/dc=springframework,dc=org");
}
@Override

View File

@ -31,8 +31,8 @@ import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
@SuppressWarnings({ "deprecation" })
public class DefaultLdapAuthoritiesPopulatorTests {

View File

@ -34,9 +34,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -52,7 +52,7 @@ import static org.mockito.Mockito.verify;
* @author Roman Zabaluev
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class LdapUserDetailsManagerTests {
@Autowired

View File

@ -28,7 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.ldap.ApacheDsContainerConfig;
import org.springframework.security.ldap.UnboundIdContainerConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Eddú Meléndez
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ApacheDsContainerConfig.class)
@ContextConfiguration(classes = UnboundIdContainerConfig.class)
public class NestedLdapAuthoritiesPopulatorTests {
@Autowired

View File

@ -1,365 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.server;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.authn.AuthenticationInterceptor;
import org.apache.directory.server.core.entry.ServerEntry;
import org.apache.directory.server.core.exception.ExceptionInterceptor;
import org.apache.directory.server.core.interceptor.Interceptor;
import org.apache.directory.server.core.normalization.NormalizationInterceptor;
import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
import org.apache.directory.server.core.referral.ReferralInterceptor;
import org.apache.directory.server.core.subtree.SubentryInterceptor;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Lifecycle;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Assert;
/**
* Provides lifecycle services for the embedded apacheDS server defined by the supplied
* configuration. Used by {@code LdapServerBeanDefinitionParser}. An instance will be
* stored in the application context for each embedded server instance. It will start the
* server when the context is initialized and shut it down when it is closed. It is
* intended for temporary embedded use and will not retain changes across start/stop
* boundaries. The working directory is deleted on shutdown.
*
* <p>
* If used repeatedly in a single JVM process with the same configuration (for example,
* when repeatedly loading an application context during testing), it's important that the
* application context is closed to allow the bean to be disposed of and the server
* shutdown prior to attempting to start it again.
* <p>
* This class is intended for testing and internal security namespace use, only, and is
* not considered part of the framework's public API.
*
* @author Luke Taylor
* @author Rob Winch
* @author Gunnar Hillert
* @author Evgeniy Cheban
* @deprecated For removal in 7.0. Use {@link UnboundIdContainer} instead because ApacheDS
* 1.x is no longer supported with no GA version to replace it.
*/
@Deprecated(since = "5.2", forRemoval = true)
public class ApacheDSContainer
implements EmbeddedLdapServerContainer, InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
private final Log logger = LogFactory.getLog(getClass());
final DefaultDirectoryService service;
LdapServer server;
private TcpTransport transport;
private ApplicationContext ctxt;
private File workingDir;
private boolean running;
private final String ldifResources;
private final JdbmPartition partition;
private final String root;
private int port = 53389;
private int localPort;
private boolean ldapOverSslEnabled;
private File keyStoreFile;
private String certificatePassord;
public ApacheDSContainer(String root, String ldifs) throws Exception {
this.ldifResources = ldifs;
this.service = new DefaultDirectoryService();
List<Interceptor> list = new ArrayList<>();
list.add(new NormalizationInterceptor());
list.add(new AuthenticationInterceptor());
list.add(new ReferralInterceptor());
list.add(new ExceptionInterceptor());
list.add(new OperationalAttributeInterceptor());
list.add(new SubentryInterceptor());
this.service.setInterceptors(list);
this.partition = new JdbmPartition();
this.partition.setId("rootPartition");
this.partition.setSuffix(root);
this.root = root;
this.service.addPartition(this.partition);
this.service.setExitVmOnShutdown(false);
this.service.setShutdownHookEnabled(false);
this.service.getChangeLog().setEnabled(false);
this.service.setDenormalizeOpAttrsEnabled(true);
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.workingDir == null) {
String apacheWorkDir = System.getProperty("apacheDSWorkDir");
if (apacheWorkDir == null) {
apacheWorkDir = createTempDirectory("apacheds-spring-security-");
}
setWorkingDirectory(new File(apacheWorkDir));
}
Assert.isTrue(!this.ldapOverSslEnabled || this.keyStoreFile != null,
"When LdapOverSsl is enabled, the keyStoreFile property must be set.");
this.server = new LdapServer();
this.server.setDirectoryService(this.service);
// AbstractLdapIntegrationTests assume IPv4, so we specify the same here
this.transport = new TcpTransport(this.port);
if (this.ldapOverSslEnabled) {
this.transport.setEnableSSL(true);
this.server.setKeystoreFile(this.keyStoreFile.getAbsolutePath());
this.server.setCertificatePassword(this.certificatePassord);
}
this.server.setTransports(this.transport);
start();
}
@Override
public void destroy() {
stop();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctxt = applicationContext;
}
public void setWorkingDirectory(File workingDir) {
Assert.notNull(workingDir, "workingDir cannot be null");
this.logger.info("Setting working directory for LDAP_PROVIDER: " + workingDir.getAbsolutePath());
Assert.isTrue(!workingDir.exists(),
"The specified working directory '" + workingDir.getAbsolutePath()
+ "' already exists. Another directory service instance may be using it or it may be from a "
+ " previous unclean shutdown. Please confirm and delete it or configure a different "
+ "working directory");
this.workingDir = workingDir;
this.service.setWorkingDirectory(workingDir);
}
@Override
public void setPort(int port) {
this.port = port;
}
@Override
public int getPort() {
return this.port;
}
/**
* Returns the port that is resolved by {@link TcpTransport}.
* @return the port that is resolved by {@link TcpTransport}
*/
public int getLocalPort() {
return this.localPort;
}
/**
* If set to {@code true} will enable LDAP over SSL (LDAPs). If set to {@code true}
* {@link ApacheDSContainer#setCertificatePassord(String)} must be set as well.
* @param ldapOverSslEnabled If not set, will default to false
*/
public void setLdapOverSslEnabled(boolean ldapOverSslEnabled) {
this.ldapOverSslEnabled = ldapOverSslEnabled;
}
/**
* The keyStore must not be null and must be a valid file. Will set the keyStore file
* on the underlying {@link LdapServer}.
* @param keyStoreFile Mandatory if LDAPs is enabled
*/
public void setKeyStoreFile(File keyStoreFile) {
Assert.notNull(keyStoreFile, "The keyStoreFile must not be null.");
Assert.isTrue(keyStoreFile.isFile(), "The keyStoreFile must be a file.");
this.keyStoreFile = keyStoreFile;
}
/**
* Will set the certificate password on the underlying {@link LdapServer}.
* @param certificatePassord May be null
*/
public void setCertificatePassord(String certificatePassord) {
this.certificatePassord = certificatePassord;
}
public DefaultDirectoryService getService() {
return this.service;
}
@Override
public void start() {
if (isRunning()) {
return;
}
Assert.state(!this.service.isStarted(), "DirectoryService is already running.");
this.logger.info("Starting directory server...");
try {
this.service.startup();
this.server.start();
}
catch (Exception ex) {
throw new RuntimeException("Server startup failed", ex);
}
try {
this.service.getAdminSession().lookup(this.partition.getSuffixDn());
}
catch (LdapNameNotFoundException ex) {
handleLdapNameNotFoundException();
}
catch (Exception ex) {
this.logger.error("Lookup failed", ex);
}
SocketAcceptor socketAcceptor = this.server.getSocketAcceptor(this.transport);
InetSocketAddress localAddress = socketAcceptor.getLocalAddress();
this.localPort = localAddress.getPort();
this.running = true;
try {
importLdifs();
}
catch (Exception ex) {
throw new RuntimeException("Failed to import LDIF file(s)", ex);
}
}
private void handleLdapNameNotFoundException() {
try {
LdapDN dn = new LdapDN(this.root);
Assert.isTrue(this.root.startsWith("dc="), "root must start with dc=");
String dc = this.root.substring(3, this.root.indexOf(','));
ServerEntry entry = this.service.newEntry(dn);
entry.add("objectClass", "top", "domain", "extensibleObject");
entry.add("dc", dc);
this.service.getAdminSession().add(entry);
}
catch (Exception ex) {
this.logger.error("Failed to create dc entry", ex);
}
}
@Override
public void stop() {
if (!isRunning()) {
return;
}
this.logger.info("Shutting down directory server ...");
try {
this.server.stop();
this.service.shutdown();
}
catch (Exception ex) {
this.logger.error("Shutdown failed", ex);
return;
}
this.running = false;
if (this.workingDir.exists()) {
this.logger.info("Deleting working directory " + this.workingDir.getAbsolutePath());
deleteDir(this.workingDir);
}
}
private void importLdifs() throws Exception {
// Import any ldif files
Resource[] ldifs = (this.ctxt != null) ? this.ctxt.getResources(this.ldifResources)
: new PathMatchingResourcePatternResolver().getResources(this.ldifResources);
// Note that we can't just import using the ServerContext returned
// from starting Apache DS, apparently because of the long-running issue
// DIRSERVER-169.
// We need a standard context.
// DirContext dirContext = contextSource.getReadWriteContext();
if (ldifs == null || ldifs.length == 0) {
return;
}
Assert.isTrue(ldifs.length == 1, () -> "More than one LDIF resource found with the supplied pattern:"
+ this.ldifResources + " Got " + Arrays.toString(ldifs));
String ldifFile = getLdifFile(ldifs);
this.logger.info("Loading LDIF file: " + ldifFile);
LdifFileLoader loader = new LdifFileLoader(this.service.getAdminSession(), new File(ldifFile), null,
getClass().getClassLoader());
loader.execute();
}
private String getLdifFile(Resource[] ldifs) throws IOException {
try {
return ldifs[0].getFile().getAbsolutePath();
}
catch (IOException ex) {
return ldifs[0].getURI().toString();
}
}
private String createTempDirectory(String prefix) throws IOException {
String parentTempDir = System.getProperty("java.io.tmpdir");
String fileNamePrefix = prefix + System.nanoTime();
String fileName = fileNamePrefix;
for (int i = 0; i < 1000; i++) {
File tempDir = new File(parentTempDir, fileName);
if (!tempDir.exists()) {
return tempDir.getAbsolutePath();
}
fileName = fileNamePrefix + "~" + i;
}
throw new IOException(
"Failed to create a temporary directory for file at " + new File(parentTempDir, fileNamePrefix));
}
private boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (String child : children) {
boolean success = deleteDir(new File(dir, child));
if (!success) {
return false;
}
}
}
return dir.delete();
}
@Override
public boolean isRunning() {
return this.running;
}
}

View File

@ -15,7 +15,6 @@
*/
/**
* Embedded Apache Directory Server implementation, as used by the configuration
* namespace.
* Embedded UnboundID Server implementation, as used by the configuration namespace.
*/
package org.springframework.security.ldap.server;

View File

@ -1,173 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.directory.server.core.avltree;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Comparator;
import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to serialize the Array data.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
@SuppressWarnings("unchecked")
public class ArrayMarshaller<E> implements Marshaller<ArrayTree<E>> {
/** static logger */
private static final Logger LOG = LoggerFactory.getLogger(ArrayMarshaller.class);
/** used for serialized form of an empty AvlTree */
private static final byte[] EMPTY_TREE = new byte[1];
/** marshaller to be used for marshalling the keys */
private Marshaller<E> keyMarshaller;
/** key Comparator for the AvlTree */
private Comparator<E> comparator;
/**
* Creates a new instance of AvlTreeMarshaller with a custom key Marshaller.
* @param comparator Comparator to be used for key comparision
* @param keyMarshaller marshaller for keys
*/
public ArrayMarshaller(Comparator<E> comparator, Marshaller<E> keyMarshaller) {
this.comparator = comparator;
this.keyMarshaller = keyMarshaller;
}
/**
* Creates a new instance of AvlTreeMarshaller with the default key Marshaller which
* uses Java Serialization.
* @param comparator Comparator to be used for key comparision
*/
public ArrayMarshaller(Comparator<E> comparator) {
this.comparator = comparator;
this.keyMarshaller = DefaultMarshaller.INSTANCE;
}
/**
* Marshals the given tree to bytes
* @param tree the tree to be marshalled
*/
public byte[] serialize(ArrayTree<E> tree) {
if ((tree == null) || tree.isEmpty()) {
return EMPTY_TREE;
}
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(byteStream);
byte[] data = null;
try {
out.writeByte(0); // represents the start of an Array byte stream
out.writeInt(tree.size());
for (int position = 0; position < tree.size(); position++) {
E value = tree.get(position);
byte[] bytes = this.keyMarshaller.serialize(value);
// Write the key length
out.writeInt(bytes.length);
// Write the key if its length is not null
if (bytes.length != 0) {
out.write(bytes);
}
}
out.flush();
data = byteStream.toByteArray();
// Try to deserialize, just to see
try {
deserialize(data);
}
catch (NullPointerException npe) {
System.out.println("Bad serialization, tree : [" + StringTools.dumpBytes(data) + "]");
throw npe;
}
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
return data;
}
/**
* Creates an Array from given bytes of data.
* @param data byte array to be converted into an array
*/
public ArrayTree<E> deserialize(byte[] data) throws IOException {
try {
if ((data == null) || (data.length == 0)) {
throw new IOException("Null or empty data array is invalid.");
}
if ((data.length == 1) && (data[0] == 0)) {
E[] array = (E[]) new Object[] {};
ArrayTree<E> tree = new ArrayTree<E>(this.comparator, array);
return tree;
}
ByteArrayInputStream bin = new ByteArrayInputStream(data);
DataInputStream din = new DataInputStream(bin);
byte startByte = din.readByte();
if (startByte != 0) {
throw new IOException("wrong array serialized data format");
}
int size = din.readInt();
E[] nodes = (E[]) new Object[size];
for (int i = 0; i < size; i++) {
// Read the object's size
int dataSize = din.readInt();
if (dataSize != 0) {
byte[] bytes = new byte[dataSize];
din.read(bytes);
E key = this.keyMarshaller.deserialize(bytes);
nodes[i] = key;
}
}
ArrayTree<E> arrayTree = new ArrayTree<E>(this.comparator, nodes);
return arrayTree;
}
catch (NullPointerException npe) {
System.out.println("Bad tree : [" + StringTools.dumpBytes(data) + "]");
throw npe;
}
}
}

View File

@ -29,7 +29,6 @@ import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.apache.directory.shared.ldap.util.EmptyEnumeration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@ -175,7 +174,7 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
@Test
public void noUserSearchCausesUsernameNotFound() throws Exception {
given(this.ctx.search(any(Name.class), any(String.class), any(Object[].class), any(SearchControls.class)))
.willReturn(new EmptyEnumeration<>());
.willReturn(new MockNamingEnumeration(null));
this.provider.contextFactory = createContextFactoryReturning(this.ctx);
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));
}