Skip to content

Commit 2d8c65d

Browse files
committed
Support port=0 for LDAP Servers
Fixes gh-8138
1 parent 4d99ee2 commit 2d8c65d

File tree

10 files changed

+81
-69
lines changed

10 files changed

+81
-69
lines changed

config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderConfigurerTests.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -18,6 +18,7 @@
1818

1919
import org.junit.Rule;
2020
import org.junit.Test;
21+
2122
import org.springframework.beans.factory.annotation.Autowired;
2223
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
2324
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -61,6 +62,14 @@ public void authenticationManagerSupportMultipleLdapContextWithCustomRolePrefix(
6162
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS"))));
6263
}
6364

65+
@Test
66+
public void authenticationManagerWhenPortZeroThenAuthenticates() throws Exception {
67+
this.spring.register(LdapWithRandomPortConfig.class).autowire();
68+
69+
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
70+
.andExpect(authenticated().withUsername("bob"));
71+
}
72+
6473
@EnableWebSecurity
6574
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
6675
// @formatter:off
@@ -98,4 +107,18 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
98107
}
99108
// @formatter:on
100109
}
110+
111+
@EnableWebSecurity
112+
static class LdapWithRandomPortConfig extends WebSecurityConfigurerAdapter {
113+
@Override
114+
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
115+
auth
116+
.ldapAuthentication()
117+
.groupSearchBase("ou=groups")
118+
.groupSearchFilter("(member={0})")
119+
.userDnPatterns("uid={0},ou=people")
120+
.contextSource()
121+
.port(0);
122+
}
123+
}
101124
}

config/src/integration-test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -16,8 +16,11 @@
1616

1717
package org.springframework.security.config.ldap;
1818

19+
import java.text.MessageFormat;
20+
1921
import org.junit.After;
2022
import org.junit.Test;
23+
2124
import org.springframework.context.ApplicationContextException;
2225
import org.springframework.security.authentication.AuthenticationManager;
2326
import org.springframework.security.authentication.AuthenticationProvider;
@@ -29,8 +32,6 @@
2932
import org.springframework.security.core.userdetails.UserDetails;
3033
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
3134

32-
import java.text.MessageFormat;
33-
3435
import static org.assertj.core.api.Assertions.assertThat;
3536

3637
public class LdapProviderBeanDefinitionParserTests {
@@ -46,7 +47,7 @@ public void closeAppContext() {
4647

4748
@Test
4849
public void simpleProviderAuthenticatesCorrectly() {
49-
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
50+
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
5051
+ "<authentication-manager>"
5152
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
5253
+ "</authentication-manager>"
@@ -60,7 +61,7 @@ public void simpleProviderAuthenticatesCorrectly() {
6061

6162
@Test
6263
public void multipleProvidersAreSupported() {
63-
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
64+
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
6465
+ "<authentication-manager>"
6566
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
6667
+ " <ldap-authentication-provider group-search-filter='uniqueMember={0}' />"
@@ -84,7 +85,7 @@ public void missingServerEltCausesConfigException() {
8485

8586
@Test
8687
public void supportsPasswordComparisonAuthentication() {
87-
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
88+
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
8889
+ "<authentication-manager>"
8990
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
9091
+ " <password-compare />"
@@ -100,7 +101,7 @@ public void supportsPasswordComparisonAuthentication() {
100101

101102
@Test
102103
public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
103-
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
104+
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
104105
+ "<authentication-manager>"
105106
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
106107
+ " <password-compare password-attribute='uid'>"
@@ -120,7 +121,7 @@ public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
120121
// SEC-2472
121122
@Test
122123
public void supportsCryptoPasswordEncoder() {
123-
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
124+
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
124125
+ "<authentication-manager>"
125126
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
126127
+ " <password-compare>"
@@ -139,7 +140,7 @@ public void supportsCryptoPasswordEncoder() {
139140

140141
@Test
141142
public void inetOrgContextMapperIsSupported() {
142-
appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>"
143+
appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' port='0'/>"
143144
+ "<authentication-manager>"
144145
+ " <ldap-authentication-provider user-details-class='inetOrgPerson' />"
145146
+ "</authentication-manager>"

config/src/integration-test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java

+6-5
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-2020 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.
@@ -15,20 +15,21 @@
1515
*/
1616
package org.springframework.security.config.ldap;
1717

18-
import static org.assertj.core.api.Assertions.assertThat;
19-
2018
import java.io.IOException;
2119
import java.net.ServerSocket;
2220

2321
import org.junit.After;
2422
import org.junit.Test;
23+
2524
import org.springframework.ldap.core.LdapTemplate;
2625
import org.springframework.security.config.BeanIds;
2726
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
2827
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
2928
import org.springframework.security.ldap.server.ApacheDSContainer;
3029
import org.springframework.test.util.ReflectionTestUtils;
3130

31+
import static org.assertj.core.api.Assertions.assertThat;
32+
3233
/**
3334
* @author Luke Taylor
3435
* @author Rob Winch
@@ -47,7 +48,7 @@ public void closeAppContext() {
4748
@Test
4849
public void embeddedServerCreationContainsExpectedContextSourceAndData() {
4950
appCtx = new InMemoryXmlApplicationContext(
50-
"<ldap-server ldif='classpath:test-server.ldif'/>");
51+
"<ldap-server ldif='classpath:test-server.ldif' port='0'/>");
5152

5253
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
5354
.getBean(BeanIds.CONTEXT_SOURCE);
@@ -82,7 +83,7 @@ public void useOfUrlAttributeCreatesCorrectContextSource() throws Exception {
8283
@Test
8384
public void loadingSpecificLdifFileIsSuccessful() {
8485
appCtx = new InMemoryXmlApplicationContext(
85-
"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' />");
86+
"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' port='0'/>");
8687
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
8788
.getBean(BeanIds.CONTEXT_SOURCE);
8889

config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java

+18-24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -21,14 +21,14 @@
2121
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
2222
import org.springframework.security.authentication.AuthenticationManager;
2323
import org.springframework.security.authentication.AuthenticationProvider;
24-
import org.springframework.security.crypto.password.PasswordEncoder;
2524
import org.springframework.security.config.annotation.ObjectPostProcessor;
2625
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
2726
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
2827
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
2928
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
3029
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
3130
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
31+
import org.springframework.security.crypto.password.PasswordEncoder;
3232
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
3333
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
3434
import org.springframework.security.ldap.authentication.BindAuthenticator;
@@ -478,6 +478,9 @@ public ContextSourceBuilder managerPassword(String managerPassword) {
478478
/**
479479
* The port to connect to LDAP to (the default is 33389 or random available port
480480
* if unavailable).
481+
*
482+
* Supplying 0 as the port indicates that a random available port should be selected.
483+
*
481484
* @param port the port to connect to
482485
* @return the {@link ContextSourceBuilder} for further customization
483486
*/
@@ -550,36 +553,27 @@ else if (ClassUtils.isPresent("com.unboundid.ldap.listener.InMemoryDirectoryServ
550553
}
551554

552555
private int getPort() {
553-
if (port == null) {
556+
if (port != null && port == 0) {
557+
port = getRandomPort();
558+
} else if (port == null) {
554559
port = getDefaultPort();
555560
}
556561
return port;
557562
}
558563

559564
private int getDefaultPort() {
560-
ServerSocket serverSocket = null;
561-
try {
562-
try {
563-
serverSocket = new ServerSocket(DEFAULT_PORT);
564-
}
565-
catch (IOException e) {
566-
try {
567-
serverSocket = new ServerSocket(0);
568-
}
569-
catch (IOException e2) {
570-
return DEFAULT_PORT;
571-
}
572-
}
565+
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
573566
return serverSocket.getLocalPort();
567+
} catch (IOException e) {
568+
return getRandomPort();
574569
}
575-
finally {
576-
if (serverSocket != null) {
577-
try {
578-
serverSocket.close();
579-
}
580-
catch (IOException e) {
581-
}
582-
}
570+
}
571+
572+
private int getRandomPort() {
573+
try (ServerSocket serverSocket = new ServerSocket(0)) {
574+
return serverSocket.getLocalPort();
575+
} catch (IOException e) {
576+
return DEFAULT_PORT;
583577
}
584578
}
585579

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

+18-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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+
import org.w3c.dom.Element;
2324

2425
import org.springframework.beans.factory.config.BeanDefinition;
2526
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -32,7 +33,6 @@
3233
import org.springframework.security.ldap.server.UnboundIdContainer;
3334
import org.springframework.util.ClassUtils;
3435
import org.springframework.util.StringUtils;
35-
import org.w3c.dom.Element;
3636

3737
/**
3838
* @author Luke Taylor
@@ -138,7 +138,12 @@ private RootBeanDefinition createEmbeddedServer(Element element,
138138

139139
String port = element.getAttribute(ATT_PORT);
140140

141-
if (!StringUtils.hasText(port)) {
141+
if ("0".equals(port)) {
142+
port = getRandomPort();
143+
if (logger.isDebugEnabled()) {
144+
logger.debug("Using default port of " + port);
145+
}
146+
} else if (!StringUtils.hasText(port)) {
142147
port = getDefaultPort();
143148
if (logger.isDebugEnabled()) {
144149
logger.debug("Using default port of " + port);
@@ -213,30 +218,18 @@ private boolean isUnboundidEnabled(String mode) {
213218
}
214219

215220
private String getDefaultPort() {
216-
ServerSocket serverSocket = null;
217-
try {
218-
try {
219-
serverSocket = new ServerSocket(DEFAULT_PORT);
220-
}
221-
catch (IOException e) {
222-
try {
223-
serverSocket = new ServerSocket(0);
224-
}
225-
catch (IOException e2) {
226-
return String.valueOf(DEFAULT_PORT);
227-
}
228-
}
221+
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
229222
return String.valueOf(serverSocket.getLocalPort());
230-
}
231-
finally {
232-
if (serverSocket != null) {
233-
try {
234-
serverSocket.close();
235-
}
236-
catch (IOException e) {
237-
}
238-
}
223+
} catch (IOException e) {
224+
return getRandomPort();
239225
}
240226
}
241227

228+
private String getRandomPort() {
229+
try (ServerSocket serverSocket = new ServerSocket(0)) {
230+
return String.valueOf(serverSocket.getLocalPort());
231+
} catch (IOException e) {
232+
return String.valueOf(DEFAULT_PORT);
233+
}
234+
}
242235
}

itest/ldap/embedded-ldap-apacheds-default/src/integration-test/resources/applicationContext-security.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
55
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
66

7-
<s:ldap-server ldif="classpath:users.ldif"/>
7+
<s:ldap-server ldif="classpath:users.ldif" port="0"/>
88

99
</beans>

itest/ldap/embedded-ldap-mode-apacheds/src/integration-test/resources/applicationContext-security.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
55
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
66

7-
<s:ldap-server mode="apacheds" ldif="classpath:users.ldif"/>
7+
<s:ldap-server mode="apacheds" ldif="classpath:users.ldif" port="0"/>
88

99
</beans>

itest/ldap/embedded-ldap-mode-unboundid/src/integration-test/resources/applicationContext-security.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
55
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
66

7-
<s:ldap-server mode="unboundid" ldif="classpath:users.ldif"/>
7+
<s:ldap-server mode="unboundid" ldif="classpath:users.ldif" port="0"/>
88

99
</beans>

itest/ldap/embedded-ldap-none/src/integration-test/resources/applicationContext-security.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
55
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
66

7-
<s:ldap-server ldif="classpath:users.ldif"/>
7+
<s:ldap-server ldif="classpath:users.ldif" port="0"/>
88

99
</beans>

itest/ldap/embedded-ldap-unboundid-default/src/integration-test/resources/applicationContext-security.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
55
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
66

7-
<s:ldap-server ldif="classpath:users.ldif"/>
7+
<s:ldap-server ldif="classpath:users.ldif" port="0"/>
88

99
</beans>

0 commit comments

Comments
 (0)