Skip to content

Commit abc9028

Browse files
authored
Add unbounid support in xml
Add unbounid support in xml Fixes gh-6011
2 parents f396e01 + 2e63def commit abc9028

File tree

30 files changed

+770
-18
lines changed

30 files changed

+770
-18
lines changed

config/src/main/java/org/springframework/security/config/BeanIds.java

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public abstract class BeanIds {
5353
+ "methodSecurityMetadataSourceAdvisor";
5454
public static final String EMBEDDED_APACHE_DS = PREFIX
5555
+ "apacheDirectoryServerContainer";
56+
public static final String EMBEDDED_UNBOUNDID = PREFIX
57+
+ "unboundidServerContainer";
5658
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
5759

5860
public static final String DEBUG_FILTER = PREFIX + "debugFilter";

config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public BeanDefinition parse(Element element, ParserContext pc) {
8686
if (!namespaceMatchesVersion(element)) {
8787
pc.getReaderContext()
8888
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
89-
+ "with Spring Security 4.2. Please update your schema declarations to the 4.2 schema.",
89+
+ "with Spring Security 5.2. Please update your schema declarations to the 5.2 schema.",
9090
element);
9191
}
9292
String name = pc.getDelegate().getLocalName(element);
@@ -221,7 +221,7 @@ private boolean namespaceMatchesVersion(Element element) {
221221
private boolean matchesVersionInternal(Element element) {
222222
String schemaLocation = element.getAttributeNS(
223223
"http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
224-
return schemaLocation.matches("(?m).*spring-security-4\\.2.*.xsd.*")
224+
return schemaLocation.matches("(?m).*spring-security-5\\.2.*.xsd.*")
225225
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
226226
|| !schemaLocation.matches("(?m).*spring-security.*");
227227
}

config/src/main/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParser.java

+53-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020

2121
import org.apache.commons.logging.Log;
2222
import org.apache.commons.logging.LogFactory;
23+
2324
import org.springframework.beans.factory.config.BeanDefinition;
2425
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2526
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -28,11 +29,14 @@
2829
import org.springframework.beans.factory.xml.ParserContext;
2930
import org.springframework.security.config.BeanIds;
3031
import org.springframework.security.ldap.server.ApacheDSContainer;
32+
import org.springframework.security.ldap.server.UnboundIdContainer;
33+
import org.springframework.util.ClassUtils;
3134
import org.springframework.util.StringUtils;
3235
import org.w3c.dom.Element;
3336

3437
/**
3538
* @author Luke Taylor
39+
* @author Eddú Meléndez
3640
*/
3741
public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
3842
private static final String CONTEXT_SOURCE_CLASS = "org.springframework.security.ldap.DefaultSpringSecurityContextSource";
@@ -65,6 +69,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
6569
private static final int DEFAULT_PORT = 33389;
6670
public static final String OPT_DEFAULT_PORT = String.valueOf(DEFAULT_PORT);
6771

72+
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
73+
private static final String UNBOUNID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
74+
75+
private static final String APACHEDS_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.ApacheDSContainer";
76+
private static final String UNBOUNDID_CONTAINER_CLASSNAME = "org.springframework.security.ldap.server.UnboundIdContainer";
77+
6878
public BeanDefinition parse(Element elt, ParserContext parserContext) {
6979
String url = elt.getAttribute(ATT_URL);
7080

@@ -114,6 +124,7 @@ public BeanDefinition parse(Element elt, ParserContext parserContext) {
114124
* @return the BeanDefinition for the ContextSource for the embedded server.
115125
*
116126
* @see ApacheDSContainer
127+
* @see UnboundIdContainer
117128
*/
118129
private RootBeanDefinition createEmbeddedServer(Element element,
119130
ParserContext parserContext) {
@@ -142,34 +153,65 @@ private RootBeanDefinition createEmbeddedServer(Element element,
142153
contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
143154
contextSource.addPropertyValue("password", "secret");
144155

145-
RootBeanDefinition apacheContainer = new RootBeanDefinition(
146-
"org.springframework.security.ldap.server.ApacheDSContainer", null, null);
147-
apacheContainer.setSource(source);
148-
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(suffix);
156+
String mode = element.getAttribute("mode");
157+
RootBeanDefinition ldapContainer = getRootBeanDefinition(mode);
158+
ldapContainer.setSource(source);
159+
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(suffix);
149160

150161
String ldifs = element.getAttribute(ATT_LDIF_FILE);
151162
if (!StringUtils.hasText(ldifs)) {
152163
ldifs = OPT_DEFAULT_LDIF_FILE;
153164
}
154165

155-
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
156-
apacheContainer.getPropertyValues().addPropertyValue("port", port);
166+
ldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);
167+
ldapContainer.getPropertyValues().addPropertyValue("port", port);
157168

158169
logger.info("Embedded LDAP server bean definition created for URL: " + url);
159170

160171
if (parserContext.getRegistry()
161-
.containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS)) {
172+
.containsBeanDefinition(BeanIds.EMBEDDED_APACHE_DS) ||
173+
parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {
162174
parserContext.getReaderContext().error(
163175
"Only one embedded server bean is allowed per application context",
164176
element);
165177
}
166178

167-
parserContext.getRegistry().registerBeanDefinition(BeanIds.EMBEDDED_APACHE_DS,
168-
apacheContainer);
179+
String beanId = resolveBeanId(mode);
180+
if (beanId != null) {
181+
parserContext.getRegistry().registerBeanDefinition(beanId, ldapContainer);
182+
}
169183

170184
return (RootBeanDefinition) contextSource.getBeanDefinition();
171185
}
172186

187+
private RootBeanDefinition getRootBeanDefinition(String mode) {
188+
if (isApacheDsEnabled(mode)) {
189+
return new RootBeanDefinition(APACHEDS_CONTAINER_CLASSNAME, null, null);
190+
}
191+
else if (isUnboundidEnabled(mode)) {
192+
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
193+
}
194+
throw new IllegalStateException("Embedded LDAP server is not provided");
195+
}
196+
197+
private String resolveBeanId(String mode) {
198+
if (isApacheDsEnabled(mode)) {
199+
return BeanIds.EMBEDDED_APACHE_DS;
200+
}
201+
else if (isUnboundidEnabled(mode)) {
202+
return BeanIds.EMBEDDED_UNBOUNDID;
203+
}
204+
return null;
205+
}
206+
207+
private boolean isApacheDsEnabled(String mode) {
208+
return "apacheds".equals(mode) || ClassUtils.isPresent(APACHEDS_CLASSNAME, getClass().getClassLoader());
209+
}
210+
211+
private boolean isUnboundidEnabled(String mode) {
212+
return "unboundid".equals(mode) || ClassUtils.isPresent(UNBOUNID_CLASSNAME, getClass().getClassLoader());
213+
}
214+
173215
private String getDefaultPort() {
174216
ServerSocket serverSocket = null;
175217
try {
@@ -196,4 +238,5 @@ private String getDefaultPort() {
196238
}
197239
}
198240
}
241+
199242
}

config/src/main/resources/META-INF/spring.schemas

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.2.xsd
2-
http\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd
1+
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.2.xsd
2+
https\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd
33
http\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd
44
http\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd
55
http\://www.springframework.org/schema/security/spring-security-4.2.xsd=org/springframework/security/config/spring-security-4.2.xsd

config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc

+3
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ ldap-server.attlist &=
8383
ldap-server.attlist &=
8484
## Optional root suffix for the embedded LDAP server. Default is "dc=springframework,dc=org"
8585
attribute root { xsd:string }?
86+
ldap-server.attlist &=
87+
## 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.
88+
attribute mode { "apacheds" | "unboundid" }?
8689

8790
ldap-server-ref-attribute =
8891
## 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.

config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd

+13
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,19 @@
222222
</xs:documentation>
223223
</xs:annotation>
224224
</xs:attribute>
225+
<xs:attribute name="mode">
226+
<xs:annotation>
227+
<xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and
228+
'unboundid'. By default, it will depends if the library is available in the classpath.
229+
</xs:documentation>
230+
</xs:annotation>
231+
<xs:simpleType>
232+
<xs:restriction base="xs:token">
233+
<xs:enumeration value="apacheds"/>
234+
<xs:enumeration value="unboundid"/>
235+
</xs:restriction>
236+
</xs:simpleType>
237+
</xs:attribute>
225238
</xs:attributeGroup>
226239
<xs:attributeGroup name="ldap-server-ref-attribute">
227240
<xs:attribute name="server-ref" use="required" type="xs:token">

config/src/test/java/org/springframework/security/config/util/InMemoryXmlApplicationContext.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2013 the original author or authors.
2+
* Copyright 2009-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323

2424
/**
2525
* @author Luke Taylor
26+
* @author Eddú Meléndez
2627
*/
2728
public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {
2829
static final String BEANS_OPENING = "<b:beans xmlns='http://www.springframework.org/schema/security'\n"
@@ -37,10 +38,10 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
3738
+ "http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd\n"
3839
+ "http://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\n"
3940
+ "http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd\n"
40-
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-";
41+
+ "http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-";
4142
static final String BEANS_CLOSE = "</b:beans>\n";
4243

43-
static final String SPRING_SECURITY_VERSION = "4.2";
44+
static final String SPRING_SECURITY_VERSION = "5.2";
4445

4546
Resource inMemoryXml;
4647

docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/ldap.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ This can be configured to point at an external LDAP server, using the `url` attr
4747
<ldap-server url="ldap://springframework.org:389/dc=springframework,dc=org" />
4848
----
4949

50+
NOTE: `spring-security` provides integration with `apacheds` and `unboundid` as a embedded ldap servers. You can choose between them using the attribute `mode` in `ldap-server`.
51+
5052
==== Using an Embedded Test Server
5153
The `<ldap-server>` element can also be used to create an embedded server, which can be very useful for testing and demonstrations.
5254
In this case you use it without the `url` attribute:

docs/manual/src/docs/asciidoc/_includes/servlet/appendix/namespace.adoc

+3
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,9 @@ This is actually the bean `id` of the `ContextSource` instance, if you want to u
23602360
[[nsa-ldap-server-attributes]]
23612361
===== <ldap-server> Attributes
23622362

2363+
[[nsa-ldap-server-mode]]
2364+
* **mode**
2365+
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.
23632366

23642367
[[nsa-ldap-server-id]]
23652368
* **id**

docs/manual/src/docs/asciidoc/index.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
= Spring Security Reference
2-
Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant
2+
Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant; Eddú Meléndez
33
:include-dir: _includes
44
:security-api-url: https://docs.spring.io/spring-security/site/docs/current/api/
55
:source-indent: 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apply plugin: 'io.spring.convention.spring-test'
2+
3+
dependencies {
4+
compile project(':spring-security-core')
5+
compile 'org.springframework:spring-beans'
6+
compile 'org.springframework:spring-context'
7+
compile 'org.springframework:spring-core'
8+
compile 'org.springframework:spring-tx'
9+
compile project(':spring-security-config')
10+
compile project(':spring-security-ldap')
11+
12+
runtime apachedsDependencies
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security;
18+
19+
import org.junit.After;
20+
import org.junit.Before;
21+
import org.junit.Test;
22+
23+
import org.springframework.context.support.ClassPathXmlApplicationContext;
24+
import org.springframework.security.config.BeanIds;
25+
import org.springframework.security.ldap.server.ApacheDSContainer;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* @author Eddú Meléndez
31+
*/
32+
public class LdapServerBeanDefinitionParserTests {
33+
34+
private ClassPathXmlApplicationContext context;
35+
36+
@Before
37+
public void setup() {
38+
this.context = new ClassPathXmlApplicationContext("applicationContext-security.xml");
39+
}
40+
41+
@After
42+
public void closeAppContext() {
43+
if (this.context != null) {
44+
this.context.close();
45+
this.context = null;
46+
}
47+
}
48+
49+
@Test
50+
public void apacheDirectoryServerIsStartedByDefault() {
51+
String[] beanNames = this.context.getBeanNamesForType(ApacheDSContainer.class);
52+
assertThat(beanNames).hasSize(1);
53+
assertThat(beanNames[0]).isEqualTo(BeanIds.EMBEDDED_APACHE_DS);
54+
}
55+
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<beans xmlns="http://www.springframework.org/schema/beans"
2+
xmlns:s="http://www.springframework.org/schema/security"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
5+
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
6+
7+
<s:ldap-server ldif="classpath:users.ldif"/>
8+
9+
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
dn: ou=groups,dc=springframework,dc=org
2+
objectclass: top
3+
objectclass: organizationalUnit
4+
ou: groups
5+
6+
dn: ou=people,dc=springframework,dc=org
7+
objectclass: top
8+
objectclass: organizationalUnit
9+
ou: people
10+
11+
dn: uid=rod,ou=people,dc=springframework,dc=org
12+
objectclass: top
13+
objectclass: person
14+
objectclass: organizationalPerson
15+
objectclass: inetOrgPerson
16+
cn: Rod Johnson
17+
sn: Johnson
18+
uid: rod
19+
userPassword: koala
20+
21+
dn: uid=dianne,ou=people,dc=springframework,dc=org
22+
objectclass: top
23+
objectclass: person
24+
objectclass: organizationalPerson
25+
objectclass: inetOrgPerson
26+
cn: Dianne Emu
27+
sn: Emu
28+
uid: dianne
29+
userPassword: emu
30+
31+
dn: uid=scott,ou=people,dc=springframework,dc=org
32+
objectclass: top
33+
objectclass: person
34+
objectclass: organizationalPerson
35+
objectclass: inetOrgPerson
36+
cn: Scott
37+
sn: Wombat
38+
uid: scott
39+
userPassword: wombat
40+
41+
dn: cn=user,ou=groups,dc=springframework,dc=org
42+
objectclass: top
43+
objectclass: groupOfNames
44+
cn: user
45+
member: uid=rod,ou=people,dc=springframework,dc=org
46+
member: uid=dianne,ou=people,dc=springframework,dc=org
47+
member: uid=scott,ou=people,dc=springframework,dc=org
48+
49+
dn: cn=teller,ou=groups,dc=springframework,dc=org
50+
objectclass: top
51+
objectclass: groupOfNames
52+
cn: teller
53+
member: uid=rod,ou=people,dc=springframework,dc=org
54+
member: uid=dianne,ou=people,dc=springframework,dc=org
55+
56+
dn: cn=supervisor,ou=groups,dc=springframework,dc=org
57+
objectclass: top
58+
objectclass: groupOfNames
59+
cn: supervisor
60+
member: uid=rod,ou=people,dc=springframework,dc=org

0 commit comments

Comments
 (0)