فهرست منبع

Support port=0 for LDAP Servers

Fixes gh-8138
Josh Cummings 5 سال پیش
والد
کامیت
2d8c65db56

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * 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.
@@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.authentication.ldap;
 
 import org.junit.Rule;
 import org.junit.Test;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -61,6 +62,14 @@ public class LdapAuthenticationProviderConfigurerTests {
 				.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS"))));
 	}
 
+	@Test
+	public void authenticationManagerWhenPortZeroThenAuthenticates() throws Exception {
+		this.spring.register(LdapWithRandomPortConfig.class).autowire();
+
+		this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
+				.andExpect(authenticated().withUsername("bob"));
+	}
+
 	@EnableWebSecurity
 	static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
 		// @formatter:off
@@ -98,4 +107,18 @@ public class LdapAuthenticationProviderConfigurerTests {
 		}
 		// @formatter:on
 	}
+
+	@EnableWebSecurity
+	static class LdapWithRandomPortConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			auth
+				.ldapAuthentication()
+					.groupSearchBase("ou=groups")
+					.groupSearchFilter("(member={0})")
+					.userDnPatterns("uid={0},ou=people")
+					.contextSource()
+						.port(0);
+		}
+	}
 }

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * 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.
@@ -16,8 +16,11 @@
 
 package org.springframework.security.config.ldap;
 
+import java.text.MessageFormat;
+
 import org.junit.After;
 import org.junit.Test;
+
 import org.springframework.context.ApplicationContextException;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationProvider;
@@ -29,8 +32,6 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
 
-import java.text.MessageFormat;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class LdapProviderBeanDefinitionParserTests {
@@ -46,7 +47,7 @@ public class LdapProviderBeanDefinitionParserTests {
 
 	@Test
 	public void simpleProviderAuthenticatesCorrectly() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
 				+ "<authentication-manager>"
 				+ "  <ldap-authentication-provider group-search-filter='member={0}' />"
 				+ "</authentication-manager>"
@@ -60,7 +61,7 @@ public class LdapProviderBeanDefinitionParserTests {
 
 	@Test
 	public void multipleProvidersAreSupported() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
 				+ "<authentication-manager>"
 				+ "  <ldap-authentication-provider group-search-filter='member={0}' />"
 				+ "  <ldap-authentication-provider group-search-filter='uniqueMember={0}' />"
@@ -84,7 +85,7 @@ public class LdapProviderBeanDefinitionParserTests {
 
 	@Test
 	public void supportsPasswordComparisonAuthentication() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+		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 />"
@@ -100,7 +101,7 @@ public class LdapProviderBeanDefinitionParserTests {
 
 	@Test
 	public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+		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-attribute='uid'>"
@@ -120,7 +121,7 @@ public class LdapProviderBeanDefinitionParserTests {
 	// SEC-2472
 	@Test
 	public void supportsCryptoPasswordEncoder() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+		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>"
@@ -139,7 +140,7 @@ public class LdapProviderBeanDefinitionParserTests {
 
 	@Test
 	public void inetOrgContextMapperIsSupported() {
-		appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>"
+		appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' port='0'/>"
 				+ "<authentication-manager>"
 				+ "  <ldap-authentication-provider user-details-class='inetOrgPerson' />"
 				+ "</authentication-manager>"

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * 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.
@@ -15,13 +15,12 @@
  */
 package org.springframework.security.config.ldap;
 
-import static org.assertj.core.api.Assertions.assertThat;
-
 import java.io.IOException;
 import java.net.ServerSocket;
 
 import org.junit.After;
 import org.junit.Test;
+
 import org.springframework.ldap.core.LdapTemplate;
 import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.util.InMemoryXmlApplicationContext;
@@ -29,6 +28,8 @@ import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
 import org.springframework.security.ldap.server.ApacheDSContainer;
 import org.springframework.test.util.ReflectionTestUtils;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 /**
  * @author Luke Taylor
  * @author Rob Winch
@@ -47,7 +48,7 @@ public class LdapServerBeanDefinitionParserTests {
 	@Test
 	public void embeddedServerCreationContainsExpectedContextSourceAndData() {
 		appCtx = new InMemoryXmlApplicationContext(
-				"<ldap-server ldif='classpath:test-server.ldif'/>");
+				"<ldap-server ldif='classpath:test-server.ldif' port='0'/>");
 
 		DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
 				.getBean(BeanIds.CONTEXT_SOURCE);
@@ -82,7 +83,7 @@ public class LdapServerBeanDefinitionParserTests {
 	@Test
 	public void loadingSpecificLdifFileIsSuccessful() {
 		appCtx = new InMemoryXmlApplicationContext(
-				"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' />");
+				"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' port='0'/>");
 		DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
 				.getBean(BeanIds.CONTEXT_SOURCE);
 

+ 18 - 24
config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -21,7 +21,6 @@ import java.net.ServerSocket;
 import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationProvider;
-import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.config.annotation.ObjectPostProcessor;
 import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
@@ -29,6 +28,7 @@ import org.springframework.security.config.annotation.web.configurers.ChannelSec
 import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
 import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
 import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
 import org.springframework.security.ldap.authentication.BindAuthenticator;
@@ -478,6 +478,9 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
 		/**
 		 * The port to connect to LDAP to (the default is 33389 or random available port
 		 * if unavailable).
+		 *
+		 * Supplying 0 as the port indicates that a random available port should be selected.
+		 *
 		 * @param port the port to connect to
 		 * @return the {@link ContextSourceBuilder} for further customization
 		 */
@@ -550,36 +553,27 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
 		}
 
 		private int getPort() {
-			if (port == null) {
+			if (port != null && port == 0) {
+				port = getRandomPort();
+			} else if (port == null) {
 				port = getDefaultPort();
 			}
 			return port;
 		}
 
 		private int getDefaultPort() {
-			ServerSocket serverSocket = null;
-			try {
-				try {
-					serverSocket = new ServerSocket(DEFAULT_PORT);
-				}
-				catch (IOException e) {
-					try {
-						serverSocket = new ServerSocket(0);
-					}
-					catch (IOException e2) {
-						return DEFAULT_PORT;
-					}
-				}
+			try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
 				return serverSocket.getLocalPort();
+			} catch (IOException e) {
+				return getRandomPort();
 			}
-			finally {
-				if (serverSocket != null) {
-					try {
-						serverSocket.close();
-					}
-					catch (IOException e) {
-					}
-				}
+		}
+
+		private int getRandomPort() {
+			try (ServerSocket serverSocket = new ServerSocket(0)) {
+				return serverSocket.getLocalPort();
+			} catch (IOException e) {
+				return DEFAULT_PORT;
 			}
 		}
 

+ 18 - 25
config/src/main/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * 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.
@@ -20,6 +20,7 @@ import java.net.ServerSocket;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Element;
 
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -32,7 +33,6 @@ import org.springframework.security.ldap.server.ApacheDSContainer;
 import org.springframework.security.ldap.server.UnboundIdContainer;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
-import org.w3c.dom.Element;
 
 /**
  * @author Luke Taylor
@@ -138,7 +138,12 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
 
 		String port = element.getAttribute(ATT_PORT);
 
-		if (!StringUtils.hasText(port)) {
+		if ("0".equals(port)) {
+			port = getRandomPort();
+			if (logger.isDebugEnabled()) {
+				logger.debug("Using default port of " + port);
+			}
+		} else if (!StringUtils.hasText(port)) {
 			port = getDefaultPort();
 			if (logger.isDebugEnabled()) {
 				logger.debug("Using default port of " + port);
@@ -213,30 +218,18 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
 	}
 
 	private String getDefaultPort() {
-		ServerSocket serverSocket = null;
-		try {
-			try {
-				serverSocket = new ServerSocket(DEFAULT_PORT);
-			}
-			catch (IOException e) {
-				try {
-					serverSocket = new ServerSocket(0);
-				}
-				catch (IOException e2) {
-					return String.valueOf(DEFAULT_PORT);
-				}
-			}
+		try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
 			return String.valueOf(serverSocket.getLocalPort());
-		}
-		finally {
-			if (serverSocket != null) {
-				try {
-					serverSocket.close();
-				}
-				catch (IOException e) {
-				}
-			}
+		} catch (IOException e) {
+			return getRandomPort();
 		}
 	}
 
+	private String getRandomPort() {
+		try (ServerSocket serverSocket = new ServerSocket(0)) {
+			return String.valueOf(serverSocket.getLocalPort());
+		} catch (IOException e) {
+			return String.valueOf(DEFAULT_PORT);
+		}
+	}
 }

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

@@ -4,6 +4,6 @@
 	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"/>
+	<s:ldap-server ldif="classpath:users.ldif" port="0"/>
 
 </beans>

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

@@ -4,6 +4,6 @@
 	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"/>
+	<s:ldap-server mode="apacheds" ldif="classpath:users.ldif" port="0"/>
 
 </beans>

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

@@ -4,6 +4,6 @@
 	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="unboundid" ldif="classpath:users.ldif"/>
+	<s:ldap-server mode="unboundid" ldif="classpath:users.ldif" port="0"/>
 
 </beans>

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

@@ -4,6 +4,6 @@
 	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"/>
+	<s:ldap-server ldif="classpath:users.ldif" port="0"/>
 
 </beans>

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

@@ -4,6 +4,6 @@
 	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"/>
+	<s:ldap-server ldif="classpath:users.ldif" port="0"/>
 
 </beans>