Skip to content

Commit 09983e2

Browse files
committed
Add ApacheDS Migration Steps
Issue gh-13852
1 parent fc66501 commit 09983e2

File tree

3 files changed

+281
-4
lines changed

3 files changed

+281
-4
lines changed

docs/modules/ROOT/pages/migration-7/ldap.adoc

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,248 @@ Consequently, support for ApacheDS will be discontinued in version 7.0.
99

1010
If you are currently using ApacheDS as an embedded LDAP server, we recommend migrating to https://ldap.com/unboundid-ldap-sdk-for-java/[UnboundId].
1111
You can find instructions in xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap-embedded[this section] that describe how to set up an embedded UnboundId LDAP server.
12+
13+
To migrate, you will need to consider the following:
14+
15+
1. <<ldap-migrate-apacheds-unboundid-dependencies,Dependencies>>
16+
2. <<ldap-migrate-apacheds-unboundid-container,Container Declaration>>
17+
3. <<ldap-migrate-apacheds-unboundid-password-encoding,Password Encoding>>
18+
4. <<ldap-migrate-apacheds-unboundid-password-encoding,Password Hiding>>
19+
20+
[[ldap-migrate-apacheds-unboundid-dependencies]]
21+
=== Switch Your Dependencies
22+
23+
To use UnboundID, you will at least need to remove the ApacheDS dependencies:
24+
25+
[tabs]
26+
======
27+
Maven::
28+
+
29+
[source,maven,role="primary"]
30+
----
31+
<dependency>
32+
<groupId>org.apache.directory.server</groupId>
33+
<artifactId>apacheds-core</artifactId>
34+
<version>1.5.5</version>
35+
<scope>runtime</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.apache.directory.server</groupId>
39+
<artifactId>apacheds-server-jndi</artifactId>
40+
<version>1.5.5</version>
41+
<scope>runtime</scope>
42+
</dependency>
43+
----
44+
45+
Gradle::
46+
+
47+
[source,gradkle,role="secondary"]
48+
----
49+
implementation("org.apache.directory.server:apacheds-server-jndi")
50+
implementation("org.apache.directory.server:apacheds-core")
51+
----
52+
======
53+
54+
and replace them with UnboundID:
55+
56+
[tabs]
57+
======
58+
Maven::
59+
+
60+
[source,maven,role="primary"]
61+
----
62+
<dependency>
63+
<groupId>com.unboundid</groupId>
64+
<artifactId>unboundid-ldapsdk</artifactId>
65+
<version>7.0.3</version>
66+
<scope>runtime</scope>
67+
</dependency>
68+
----
69+
70+
Gradle::
71+
+
72+
[source,gradkle,role="secondary"]
73+
----
74+
implementation("org.apache.directory.server:apacheds-server-jndi")
75+
implementation("org.apache.directory.server:apacheds-core")
76+
----
77+
======
78+
79+
If you are accepting the LDAP server defaults, this is likely all you will need to do.
80+
81+
[[ldap-migrate-apacheds-unboundid-container]]
82+
=== Change Server Declaration
83+
84+
If you are declaring an ApacheDS server, then you will need to change its declaration.
85+
Your configuration may vary somewhat from the following.
86+
Change this:
87+
88+
[tabs]
89+
======
90+
Java::
91+
+
92+
[source,java,role="primary"]
93+
----
94+
@Bean
95+
EmbeddedLdapServerContainer ldapContainer() {
96+
EmbeddedLdapServerContainer container =
97+
new ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
98+
container.setPort(0);
99+
return container;
100+
}
101+
----
102+
103+
Kotlin::
104+
+
105+
[source,kotlin,role="secondary"]
106+
----
107+
@Bean
108+
fun ldapContainer(): EmbeddedLdapServerContainer {
109+
val container =
110+
ApacheDSContainer("dc=springframework,dc=org", "classpath:test-server.ldif")
111+
container.setPort(0)
112+
return container
113+
}
114+
----
115+
116+
Xml::
117+
+
118+
[source,xml,role="secondary"]
119+
----
120+
<ldap-server mode="apacheds"/>
121+
----
122+
======
123+
124+
to this:
125+
126+
[tabs]
127+
======
128+
Java::
129+
+
130+
[source,java,role="primary"]
131+
----
132+
@Bean
133+
EmbeddedLdapServerContainer ldapContainer() {
134+
EmbeddedLdapServerContainer container =
135+
new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif");
136+
container.setPort(0);
137+
return container;
138+
}
139+
----
140+
141+
Kotlin::
142+
+
143+
[source,kotlin,role="secondary"]
144+
----
145+
@Bean
146+
fun ldapContainer(): EmbeddedLdapServerContainer {
147+
val container =
148+
UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif")
149+
container.setPort(0)
150+
return container
151+
}
152+
----
153+
154+
Xml::
155+
+
156+
[source,xml,role="secondary"]
157+
----
158+
<ldap-server mode="unboundid"/>
159+
----
160+
======
161+
162+
163+
[[ldap-migrate-apacheds-unboundid-password-encoding]]
164+
=== Configure Password Encoding
165+
166+
Apache Directory Server supports binding with SHA-hashed passwords, but UnboundID does not.
167+
168+
If you run into trouble with binding users with SHA-hashed passwords, move to Spring Security's `PasswordComparisonAuthenticator` by providing a password encoder to the authentication provider:
169+
170+
[tabs]
171+
======
172+
Java::
173+
+
174+
[source,java,role="primary"]
175+
----
176+
@Bean
177+
AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource contextSource) {
178+
LdapPasswordComparisonAuthenticationManagerFactory factory =
179+
new LdapPasswordComparisonAuthenticationManagerFactory(
180+
contextSource, new LdapShaPasswordEncoder());
181+
// ...
182+
return factory.createAuthenticationManager();
183+
}
184+
----
185+
186+
Kotlin::
187+
+
188+
[source,kotlin,role="secondary"]
189+
----
190+
@Bean
191+
fun ldapAuthenticationManager(val contextSource: BaseLdapPathContextSource): AuthenticationManager {
192+
val factory = LdapPasswordComparisonAuthenticationManagerFactory(
193+
contextSource, LdapShaPasswordEncoder())
194+
// ...
195+
return factory.createAuthenticationManager()
196+
}
197+
----
198+
199+
Xml::
200+
+
201+
[source,xml,role="secondary"]
202+
----
203+
<auhentication-manager>
204+
<ldap-authentication-provider>
205+
<password-compare>
206+
<password-encoder ref='pe' />
207+
</password-compare>
208+
</ldap-authentication-provider>
209+
</auhentication-manager>
210+
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
211+
----
212+
======
213+
214+
[WARN]
215+
====
216+
Hashing passwords with `+{SHA}+` is not recommended.
217+
Please migrate to BCrypt, SCrypt, or Argon2 as soon as possible.
218+
You can use the same approach above to provide the corresponding password encoder.
219+
====
220+
221+
[[ldap-migrate-apacheds-unboundid-password-hiding]]
222+
=== Configure Password Hiding
223+
224+
ApacheDS is configured by Spring Security to hide the `userPassword` attribute from search results unless explicitly queried.
225+
UnboundID does not support this.
226+
227+
You can achieve this behavior with a custom `InMemoryOperationInterceptor` like the following:
228+
229+
[source,java]
230+
----
231+
static class PasswordRemovingOperationInterceptor
232+
extends InMemoryOperationInterceptor {
233+
234+
@Override
235+
public void processSearchEntry(InMemoryInterceptedSearchEntry entry) {
236+
if (!entry.getRequest().getAttributeList().contains("userPassword")) {
237+
if (entry.getSearchEntry().getAttribute("userPassword") != null) {
238+
Entry old = entry.getSearchEntry();
239+
Collection<Attribute> attributes = old.getAttributes().stream()
240+
.filter(attribute ->
241+
!"userPassword".equals(attribute.getName()))
242+
.collect(Collectors.toList());
243+
Entry withoutPassword = new Entry(old.getDN(), attributes);
244+
entry.setSearchEntry(withoutPassword);
245+
}
246+
}
247+
}
248+
}
249+
----
250+
251+
[NOTE]
252+
====
253+
It is better to secure passwords by hashing them and by using queries that identify the specific columns that you need.
254+
====
255+
256+
`UnboundIdContainer` does not currently have a way to register a custom `InMemoryOperationInterceptor`, but you can either copy the contents of `UnboundIdContainer` or use Spring LDAP Test's `EmbeddedLdapServer` builder in order to provide this interceptor and confirm your application's readiness.

docs/modules/ROOT/pages/servlet/appendix/faq.adoc

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ This section addresses common Spring Security architecture questions:
363363
. <<appendix-faq-namespace-to-bean-mapping>>
364364
. <<appendix-faq-role-prefix>>
365365
. <<appendix-faq-what-dependencies>>
366-
. <<appendix-faq-apacheds-deps>>
366+
. <<appendix-faq-unboundid-deps>>
367367
. <<appendix-faq-what-is-userdetailservice>>
368368

369369

@@ -412,9 +412,42 @@ The reference manual also includes <<appendix-namespace,an appendix>> that lists
412412
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.
413413
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.
414414

415+
[[appendix-faq-unboundid-deps]]
416+
=== What dependences are needed to run an embedded UnboundID LDAP server?
417+
418+
You need to add the following dependency to your project:
419+
420+
[tabs]
421+
======
422+
Maven::
423+
+
424+
[source,maven,role="primary"]
425+
----
426+
<dependency>
427+
<groupId>com.unboundid</groupId>
428+
<artifactId>unboundid-ldapsdk</artifactId>
429+
<version>7.0.1</version>
430+
<scope>runtime</scope>
431+
</dependency>
432+
----
433+
434+
Gradle::
435+
+
436+
[source,gradle,role="secondary"]
437+
----
438+
implementation 'com.unboundid:unboundid-ldapsdk:7.0.1'
439+
----
440+
======
441+
415442
[[appendix-faq-apacheds-deps]]
416443
=== What dependencies are needed to run an embedded ApacheDS LDAP server?
417444

445+
[NOTE]
446+
====
447+
Spring Security 7 removes support for Apache DS.
448+
Please use <<appendix-faq-unboundid-deps,UnboundID>> instead.
449+
====
450+
418451
If you use Maven, you need to add the following to your `pom.xml` file dependencies:
419452

420453
[source]

docs/modules/ROOT/pages/servlet/authentication/passwords/ldap.adoc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,8 @@ fun ldapContainer(): UnboundIdContainer {
225225

226226
[NOTE]
227227
====
228-
Spring Security uses ApacheDS 1.x, which is no longer maintained.
229-
Unfortunately, ApacheDS 2.x has only released milestone versions with no stable release.
230-
Once a stable release of ApacheDS 2.x is available, we will consider updating.
228+
Spring Security 7 removes support for Apache DS.
229+
Please use <<servlet-authentication-ldap-unboundid,UnboundID>> instead.
231230
====
232231

233232
If you wish to use https://directory.apache.org/apacheds/[Apache DS], specify the following dependencies:

0 commit comments

Comments
 (0)