From 21bfe52694743a3def98706a9c2e1c784801eb0b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 4 Jan 2017 23:30:17 -0800 Subject: [PATCH 1/6] Add test to check class resources aren't exposed Closes gh-7880 --- .../SampleDevToolsApplicationIntegrationTests.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spring-boot-samples/spring-boot-sample-devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java b/spring-boot-samples/spring-boot-sample-devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java index 978353e4e5..359a86c3bb 100644 --- a/spring-boot-samples/spring-boot-sample-devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java +++ b/spring-boot-samples/spring-boot-sample-devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java @@ -60,4 +60,11 @@ public class SampleDevToolsApplicationIntegrationTests { assertThat(entity.getBody()).contains("public file"); } + @Test + public void testClassResource() throws Exception { + ResponseEntity entity = this.restTemplate + .getForEntity("/application.properties", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + } From 4ea47220e9ba8d949f6e805ac10590408b8e0984 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 4 Jan 2017 19:37:17 -0800 Subject: [PATCH 2/6] Match nested paths for insensitive actuators Update `ManagementWebSecurityAutoConfiguration` to match nested path for insensitive actuators. Prior to this commit, when Spring Security was on the classpath nested paths were considered sensitive (even if the actuator endpoint was not sensitive). i.e. when setting `endpoints.env.sensitive=false` `/env` could be accessed without authentication but `/env/user` could not. Fixes gh-7868 Closes gh-7881 --- ...anagementWebSecurityAutoConfiguration.java | 23 ++++--- spring-boot-samples/pom.xml | 1 + .../pom.xml | 53 ++++++++++++++++ .../SampleHypermediaUiSecureApplication.java | 29 +++++++++ .../src/main/resources/static/index.html | 5 ++ ...pleHypermediaUiSecureApplicationTests.java | 62 +++++++++++++++++++ ...SecureApplicationWithContextPathTests.java | 52 ++++++++++++++++ 7 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml create mode 100644 spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java create mode 100644 spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html create mode 100644 spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java create mode 100644 spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java index 51507d52af..7eac112ff7 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -257,10 +257,11 @@ public class ManagementWebSecurityAutoConfiguration { private void configurePermittedRequests( ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry requests) { + requests.requestMatchers(new LazyEndpointPathRequestMatcher( + this.contextResolver, EndpointPaths.SENSITIVE)).authenticated(); // Permit access to the non-sensitive endpoints requests.requestMatchers(new LazyEndpointPathRequestMatcher( this.contextResolver, EndpointPaths.NON_SENSITIVE)).permitAll(); - requests.anyRequest().authenticated(); } } @@ -276,6 +277,15 @@ public class ManagementWebSecurityAutoConfiguration { return !endpoint.isSensitive(); } + }, + + SENSITIVE { + + @Override + protected boolean isIncluded(MvcEndpoint endpoint) { + return endpoint.isSensitive(); + } + }; public String[] getPaths(EndpointHandlerMapping endpointHandlerMapping) { @@ -289,12 +299,9 @@ public class ManagementWebSecurityAutoConfiguration { String path = endpointHandlerMapping.getPath(endpoint.getPath()); paths.add(path); if (!path.equals("")) { - if (endpoint.isSensitive()) { - // Ensure that nested paths are secured - paths.add(path + "/**"); - // Add Spring MVC-generated additional paths - paths.add(path + ".*"); - } + paths.add(path + "/**"); + // Add Spring MVC-generated additional paths + paths.add(path + ".*"); } paths.add(path + "/"); } diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index e915be811d..489503b52d 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -115,6 +115,7 @@ spring-boot-sample-websocket-undertow spring-boot-sample-webservices spring-boot-sample-xml + spring-boot-sample-hypermedia-ui-secure diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml new file mode 100644 index 0000000000..2e53dc4b89 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml @@ -0,0 +1,53 @@ + + + + spring-boot-samples + org.springframework.boot + 1.5.0.BUILD-SNAPSHOT + + 4.0.0 + spring-boot-sample-hypermedia-ui-secure + Spring Boot Hypermedia UI Secure Sample + Spring Boot Hypermedia UI Secure Sample + + ${basedir}/../.. + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.springframework.boot + spring-boot-actuator-docs + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java new file mode 100644 index 0000000000..476bf446af --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.hypermedia.ui.secure; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleHypermediaUiSecureApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleHypermediaUiSecureApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html new file mode 100644 index 0000000000..66a41f708c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html @@ -0,0 +1,5 @@ + + +

Hello World!

+ + \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java new file mode 100644 index 0000000000..3c043ff8ae --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.hypermedia.ui.secure; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "endpoints.env.sensitive=false" }) +public class SampleHypermediaUiSecureApplicationTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void links() { + String response = this.restTemplate.getForObject("/actuator", String.class); + assertThat(response).contains("\"_links\":"); + } + + @Test + public void testInSecureNestedPath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/env", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + ResponseEntity user = this.restTemplate.getForEntity("/env/user", + String.class); + assertThat(user.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(user.getBody()).contains("{\"user\":"); + } + + @Test + public void testSecurePath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/metrics", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java new file mode 100644 index 0000000000..cd921fe209 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.hypermedia.ui.secure; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "management.context-path=/admin" }) +public class SampleHypermediaUiSecureApplicationWithContextPathTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void links() { + String response = this.restTemplate.getForObject("/admin", String.class); + assertThat(response).contains("\"_links\":"); + } + + @Test + public void testSecurePath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/admin/metrics", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + +} From 6a84c369fd4862b419a18dd8c0da0710901da8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 18 May 2016 22:49:34 +1000 Subject: [PATCH 3/6] Add LDAP auto-configuration support Add auto-configuration support for spring-ldap and spring-data-ldap. See gh-2645 See gh-7733 --- spring-boot-autoconfigure/pom.xml | 10 + .../data/ldap/LdapDataAutoConfiguration.java | 50 +++++ .../LdapRepositoriesAutoConfiguration.java | 44 ++++ .../data/ldap/LdapRepositoriesRegistrar.java | 57 ++++++ .../autoconfigure/data/ldap/package-info.java | 20 ++ .../ldap/LdapAutoConfiguration.java | 65 ++++++ .../autoconfigure/ldap/LdapProperties.java | 127 ++++++++++++ .../EmbeddedLdapAutoConfiguration.java | 192 ++++++++++++++++++ .../ldap/embedded/EmbeddedLdapProperties.java | 112 ++++++++++ .../boot/autoconfigure/ldap/package-info.java | 20 ++ .../main/resources/META-INF/spring.factories | 4 + .../data/alt/ldap/PersonLdapRepository.java | 25 +++ .../ldap/LdapDataAutoConfigurationTests.java | 56 +++++ ...dapRepositoriesAutoConfigurationTests.java | 104 ++++++++++ .../data/ldap/person/Person.java | 36 ++++ .../data/ldap/person/PersonRepository.java | 25 +++ .../ldap/LdapAutoConfigurationTests.java | 104 ++++++++++ .../EmbeddedLdapAutoConfigurationTests.java | 160 +++++++++++++++ .../src/test/resources/schema.ldif | 85 ++++++++ spring-boot-dependencies/pom.xml | 6 + .../appendix-application-properties.adoc | 14 ++ spring-boot-starters/pom.xml | 1 + .../spring-boot-starter-data-ldap/pom.xml | 32 +++ .../main/resources/META-INF/spring.provides | 1 + 24 files changed, 1350 insertions(+) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/package-info.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/package-info.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/PersonRepository.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java create mode 100644 spring-boot-autoconfigure/src/test/resources/schema.ldif create mode 100644 spring-boot-starters/spring-boot-starter-data-ldap/pom.xml create mode 100644 spring-boot-starters/spring-boot-starter-data-ldap/src/main/resources/META-INF/spring.provides diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index eccdc98acf..e55696c6c4 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -220,6 +220,11 @@ sendgrid-java true + + com.unboundid + unboundid-ldapsdk + true + com.zaxxer HikariCP-java6 @@ -368,6 +373,11 @@ spring-data-cassandra true + + org.springframework.data + spring-data-ldap + true + org.springframework.data spring-data-mongodb diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfiguration.java new file mode 100644 index 0000000000..9957ef2603 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap; + +import javax.naming.ldap.LdapContext; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.ldap.repository.LdapRepository; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.LdapOperations; +import org.springframework.ldap.core.LdapTemplate; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's LDAP support. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@Configuration +@ConditionalOnClass({ LdapContext.class, LdapRepository.class }) +@AutoConfigureAfter(LdapAutoConfiguration.class) +public class LdapDataAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(LdapOperations.class) + public LdapTemplate ldapTemplate(ContextSource contextSource) { + return new LdapTemplate(contextSource); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfiguration.java new file mode 100644 index 0000000000..121e719a50 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap; + +import javax.naming.ldap.LdapContext; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.ldap.repository.LdapRepository; +import org.springframework.data.ldap.repository.support.LdapRepositoryFactoryBean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Couchbase + * Repositories. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@Configuration +@ConditionalOnClass({ LdapContext.class, LdapRepository.class }) +@ConditionalOnProperty(prefix = "spring.data.ldap.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnMissingBean(LdapRepositoryFactoryBean.class) +@Import(LdapRepositoriesRegistrar.class) +public class LdapRepositoriesAutoConfiguration { + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java new file mode 100644 index 0000000000..8070beb9d1 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap; + +import java.lang.annotation.Annotation; + +import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.ldap.repository.config.EnableLdapRepositories; +import org.springframework.data.ldap.repository.config.LdapRepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +/** + * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data LDAP + * Repositories. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +class LdapRepositoriesRegistrar + extends AbstractRepositoryConfigurationSourceSupport { + + @Override + protected Class getAnnotation() { + return EnableLdapRepositories.class; + } + + @Override + protected Class getConfiguration() { + return EnableLdapRepositoriesConfiguration.class; + } + + @Override + protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { + return new LdapRepositoryConfigurationExtension(); + } + + @EnableLdapRepositories + private static class EnableLdapRepositoriesConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/package-info.java new file mode 100644 index 0000000000..aac37e601c --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2017 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 + * + * http://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. + */ + +/** + * Auto-configuration for Spring Data LDAP. + */ +package org.springframework.boot.autoconfigure.data.ldap; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java new file mode 100644 index 0000000000..7e5e9447ed --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap; + +import java.util.Collections; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.support.LdapContextSource; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for LDAP. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@Configuration +@ConditionalOnClass(ContextSource.class) +@EnableConfigurationProperties(LdapProperties.class) +public class LdapAutoConfiguration { + + private LdapProperties properties; + + private Environment environment; + + public LdapAutoConfiguration(LdapProperties properties, + Environment environment) { + this.properties = properties; + this.environment = environment; + } + + @Bean + @ConditionalOnMissingBean + public ContextSource contextSource() { + LdapContextSource contextSource = new LdapContextSource(); + contextSource.setUserDn(this.properties.getUsername()); + contextSource.setPassword(this.properties.getPassword()); + contextSource.setBase(this.properties.getBase()); + contextSource.setUrls(this.properties.determineUrls(this.environment)); + contextSource.setBaseEnvironmentProperties(Collections + .unmodifiableMap(this.properties.getBaseEnvironment())); + return contextSource; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java new file mode 100644 index 0000000000..018adc9f30 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java @@ -0,0 +1,127 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.env.Environment; +import org.springframework.ldap.core.LdapTemplate; +/** + * Configuration properties to configure {@link LdapTemplate}. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@ConfigurationProperties(prefix = "spring.ldap") +public class LdapProperties { + + private static final int DEFAULT_PORT = 389; + + /** + * LDAP urls. + */ + private String[] urls = new String[0]; + + /** + * Base suffix from which all operations should originate. + */ + private String base; + + /** + * Login user of the LDAP. + */ + private String username; + + /** + * Login password of the LDAP. + */ + private String password; + + /** + * LDAP custom environment properties. + */ + private Map baseEnvironment = new HashMap(); + + public String[] getUrls() { + return this.urls; + } + + public void setUrls(String[] urls) { + this.urls = urls; + } + + public String getBase() { + return this.base; + } + + public void setBase(String base) { + this.base = base; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Map getBaseEnvironment() { + return this.baseEnvironment; + } + + public void setBaseEnvironment(Map baseEnvironment) { + this.baseEnvironment = baseEnvironment; + } + + public String[] determineUrls(Environment environment) { + if (this.urls.length == 0) { + String protocol = "ldap://"; + String host = "localhost"; + int port = determinePort(environment); + String[] ldapUrls = new String[1]; + ldapUrls[0] = protocol + host + ":" + port; + return ldapUrls; + } + return this.urls; + } + + private int determinePort(Environment environment) { + if (environment != null) { + String localPort = environment.getProperty("local.ldap.port"); + if (localPort != null) { + return Integer.valueOf(localPort); + } + else { + return DEFAULT_PORT; + } + } + throw new IllegalStateException( + "No local ldap port configuration is available"); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java new file mode 100644 index 0000000000..a3c4e55acf --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java @@ -0,0 +1,192 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap.embedded; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.PreDestroy; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.listener.InMemoryListenerConfig; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldif.LDIFReader; +import org.apache.commons.io.IOUtils; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.boot.autoconfigure.ldap.LdapProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.util.StringUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Embedded LDAP. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@Configuration +@EnableConfigurationProperties({ LdapProperties.class, EmbeddedLdapProperties.class }) +@AutoConfigureBefore(LdapAutoConfiguration.class) +@ConditionalOnClass(InMemoryDirectoryServer.class) +public class EmbeddedLdapAutoConfiguration { + + private InMemoryDirectoryServer server; + + private EmbeddedLdapProperties embeddedProperties; + + private LdapProperties properties; + + private ConfigurableApplicationContext applicationContext; + + private Environment environment; + + public EmbeddedLdapAutoConfiguration(EmbeddedLdapProperties embeddedProperties, + LdapProperties properties, + ConfigurableApplicationContext applicationContext, + Environment environment) { + this.embeddedProperties = embeddedProperties; + this.properties = properties; + this.applicationContext = applicationContext; + this.environment = environment; + } + + @Bean + @DependsOn("directoryServer") + @ConditionalOnMissingBean + public ContextSource contextSource() { + LdapContextSource contextSource = new LdapContextSource(); + + EmbeddedLdapProperties.Credential credential = this.embeddedProperties + .getCredential(); + if (StringUtils.hasText(credential.getUsername()) && + StringUtils.hasText(credential.getPassword())) { + contextSource.setUserDn(credential.getUsername()); + contextSource.setPassword(credential.getPassword()); + } + contextSource.setUrls(this.properties.determineUrls(this.environment)); + return contextSource; + } + + @Bean + public InMemoryDirectoryServer directoryServer() throws LDAPException { + InMemoryDirectoryServerConfig config = + new InMemoryDirectoryServerConfig(this.embeddedProperties + .getPartitionSuffix()); + + EmbeddedLdapProperties.Credential credential = this.embeddedProperties + .getCredential(); + if (StringUtils.hasText(credential.getUsername()) && + StringUtils.hasText(credential.getPassword())) { + config.addAdditionalBindCredentials(credential + .getUsername(), credential.getPassword()); + } + + config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", + this.embeddedProperties.getPort())); + + this.server = new InMemoryDirectoryServer(config); + + populateDirectoryServer(); + + this.server.startListening(); + publishPortInfo(this.server.getListenPort()); + return this.server; + } + + private void publishPortInfo(int port) { + setPortProperty(this.applicationContext, port); + } + + private void setPortProperty(ApplicationContext currentContext, + int port) { + if (currentContext instanceof ConfigurableApplicationContext) { + MutablePropertySources sources = ((ConfigurableApplicationContext) + currentContext).getEnvironment().getPropertySources(); + getLdapPorts(sources).put("local.ldap.port", port); + } + if (currentContext.getParent() != null) { + setPortProperty(currentContext.getParent(), port); + } + } + + private Map getLdapPorts(MutablePropertySources sources) { + PropertySource propertySource = sources.get("ldap.ports"); + if (propertySource == null) { + propertySource = new MapPropertySource("ldap.ports", + new HashMap()); + sources.addFirst(propertySource); + } + return (Map) propertySource.getSource(); + } + + private void populateDirectoryServer() throws LDAPException { + String location = this.embeddedProperties.getLdif(); + if (StringUtils.hasText(location)) { + try { + Resource resource = this.applicationContext.getResource( + this.embeddedProperties.getLdif()); + if (resource.exists()) { + File tempFile = File.createTempFile("ldap_test_data", ".ldif"); + try { + InputStream inputStream = resource.getInputStream(); + IOUtils.copy(inputStream, new FileOutputStream(tempFile)); + this.server.importFromLDIF(true, new LDIFReader(tempFile)); + } + catch (LDAPException e) { + e.printStackTrace(); + } + finally { + tempFile.delete(); + } + } + } + catch (IOException ex) { + throw new IllegalStateException( + "Unable to load resource from " + location, ex); + } + } + } + + @PreDestroy + public void close() { + if (this.server != null) { + this.server.shutDown(true); + } + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java new file mode 100644 index 0000000000..0729b03f9c --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap.embedded; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for Embedded LDAP. + * + * @author Eddú Meléndez + * @since 1.5.0 + */ +@ConfigurationProperties(prefix = "spring.ldap.embedded") +public class EmbeddedLdapProperties { + + /** + * Embedded LDAP port. + */ + private int port = 0; + + /** + * Embedded LDAP credentials. + */ + private Credential credential = new Credential(); + + /** + * LDAP partition suffix. + */ + private String partitionSuffix; + + /** + * Schema (LDIF) script resource reference. + */ + private String ldif; + + public int getPort() { + return this.port; + } + + public void setPort(int port) { + this.port = port; + } + + public Credential getCredential() { + return this.credential; + } + + public void setCredential(Credential credential) { + this.credential = credential; + } + + public String getPartitionSuffix() { + return this.partitionSuffix; + } + + public void setPartitionSuffix(String partitionSuffix) { + this.partitionSuffix = partitionSuffix; + } + + public String getLdif() { + return this.ldif; + } + + public void setLdif(String ldif) { + this.ldif = ldif; + } + + static class Credential { + + /** + * Embedded LDAP username. + */ + private String username; + + /** + * Embedded LDAP password. + */ + private String password; + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/package-info.java new file mode 100644 index 0000000000..11f9a8a5c4 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2017 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 + * + * http://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. + */ + +/** + * Auto-configuration for LDAP. + */ +package org.springframework.boot.autoconfigure.ldap; diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index d7d1e0bdf3..47120cc198 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -29,6 +29,8 @@ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfi org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ @@ -63,6 +65,8 @@ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfigu org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ +org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ +org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java new file mode 100644 index 0000000000..cffe65943f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.alt.ldap; + +import javax.naming.Name; + +import org.springframework.boot.autoconfigure.data.ldap.person.Person; +import org.springframework.data.repository.Repository; + +public interface PersonLdapRepository extends Repository { +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java new file mode 100644 index 0000000000..c907ae0a68 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.ldap.core.LdapTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LdapDataAutoConfiguration} + * + * @author Eddú Meléndez + */ +public class LdapDataAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + this.context.close(); + } + + @Test + public void templateExists() { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.ldap.urls:ldap://localhost:389"); + this.context.register(PropertyPlaceholderAutoConfiguration.class, + LdapAutoConfiguration.class, LdapDataAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBeanNamesForType(LdapTemplate.class).length) + .isEqualTo(1); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java new file mode 100644 index 0000000000..27be0268c3 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.alt.ldap.PersonLdapRepository; +import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; +import org.springframework.boot.autoconfigure.data.ldap.person.Person; +import org.springframework.boot.autoconfigure.data.ldap.person.PersonRepository; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.ldap.repository.config.EnableLdapRepositories; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LdapRepositoriesAutoConfiguration} + * + * @author Eddú Meléndez + */ +public class LdapRepositoriesAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testDefaultRepositoryConfiguration() throws Exception { + load(TestConfiguration.class); + + assertThat(this.context.getBean(PersonRepository.class)).isNotNull(); + } + + @Test + public void testNoRepositoryConfiguration() throws Exception { + load(EmptyConfiguration.class); + + assertThat(this.context.getBeanNamesForType(PersonRepository.class).length) + .isEqualTo(0); + } + + @Test + public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { + load(CustomizedConfiguration.class); + assertThat(this.context.getBean(PersonLdapRepository.class)).isNotNull(); + } + + private void load(Class... configurationClasses) { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.ldap.urls:ldap://localhost:389"); + this.context.register(configurationClasses); + this.context.register(LdapAutoConfiguration.class, + LdapDataAutoConfiguration.class, + LdapRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } + + @Configuration + @TestAutoConfigurationPackage(Person.class) + protected static class TestConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(EmptyDataPackage.class) + protected static class EmptyConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(LdapRepositoriesAutoConfigurationTests.class) + @EnableLdapRepositories(basePackageClasses = PersonLdapRepository.class) + protected static class CustomizedConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java new file mode 100644 index 0000000000..52e4b7bcb4 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap.person; + +import javax.naming.Name; + +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.DnAttribute; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.odm.annotations.Id; + +@Entry(objectClasses = {"person", "top"}, base = "ou=someOu") +public class Person { + + @Id + private Name dn; + + @Attribute(name = "cn") + @DnAttribute(value = "cn", index = 1) + private String fullName; + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/PersonRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/PersonRepository.java new file mode 100644 index 0000000000..4f0714ac01 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/PersonRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.data.ldap.person; + +import javax.naming.Name; + +import org.springframework.data.repository.Repository; + +public interface PersonRepository extends Repository { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java new file mode 100644 index 0000000000..2f8653e4d0 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.ldap.core.ContextSource; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LdapAutoConfiguration}. + * + * @author Eddú Meléndez + */ +public class LdapAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @Before + public void setup() { + this.context = new AnnotationConfigApplicationContext(); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testDefaultUrl() { + load(); + assertThat(this.context.getBeanNamesForType(ContextSource.class).length) + .isEqualTo(1); + assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), + "urls")).isEqualTo(new String[]{"ldap://localhost:389"}); + } + + @Test + public void testContextSourceSetOneUrl() { + load("spring.ldap.urls:ldap://localhost:123"); + assertThat(this.context.getBeanNamesForType(ContextSource.class).length) + .isEqualTo(1); + assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), + "urls")).isEqualTo(new String[]{"ldap://localhost:123"}); + } + + @Test + public void testContextSourceSetTwoUrls() { + load("spring.ldap.urls:ldap://localhost:123,ldap://mycompany:123"); + assertThat(this.context.getBeanNamesForType(ContextSource.class).length) + .isEqualTo(1); + assertThat(this.context.getBean(LdapProperties.class).getUrls().length) + .isEqualTo(2); + assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), + "urls")) + .isEqualTo(new String[]{"ldap://localhost:123", "ldap://mycompany:123"}); + } + + @Test + public void testContextSourceWithMoreProperties() { + load("spring.ldap.urls:ldap://localhost:123", + "spring.ldap.username:root", + "spring.ldap.password:root", + "spring.ldap.base:cn=SpringDevelopers", + "spring.ldap.baseEnvironment.java.naming.security" + + ".authentication:DIGEST-MD5"); + assertThat(this.context.getBeanNamesForType(ContextSource.class).length) + .isEqualTo(1); + assertThat(this.context.getBean(LdapProperties.class).getBaseEnvironment()) + .containsEntry("java.naming.security.authentication", "DIGEST-MD5"); + } + + private void load(String... properties) { + EnvironmentTestUtils.addEnvironment(this.context, properties); + this.context + .register(LdapAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java new file mode 100644 index 0000000000..3cb6c00743 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java @@ -0,0 +1,160 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.ldap.embedded; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.sdk.BindResult; +import com.unboundid.ldap.sdk.DN; +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.LdapTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EmbeddedLdapAutoConfiguration} + * + * @author Eddú Meléndez + */ +public class EmbeddedLdapAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @Before + public void setup() { + this.context = new AnnotationConfigApplicationContext(); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testSetDefaultPort() throws LDAPException { + load("spring.ldap.embedded.port:1234", + "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + InMemoryDirectoryServer server = this.context + .getBean(InMemoryDirectoryServer.class); + assertThat(server.getListenPort()).isEqualTo(1234); + } + + @Test + public void testRandomPortWithEnvironment() throws LDAPException { + load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + InMemoryDirectoryServer server = this.context + .getBean(InMemoryDirectoryServer.class); + assertThat(server.getListenPort()).isEqualTo(this.context.getEnvironment() + .getProperty("local.ldap.port", Integer.class)); + } + + @Test + public void testRandomPortWithValueAnnotation() throws LDAPException { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + this.context.register(EmbeddedLdapAutoConfiguration.class, + LdapClientConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + LDAPConnection connection = this.context + .getBean(LDAPConnection.class); + assertThat(connection.getConnectedPort()) + .isEqualTo(this.context.getEnvironment() + .getProperty("local.ldap.port", Integer.class)); + } + + @Test + public void testSetCredentials() throws LDAPException { + load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", + "spring.ldap.embedded.credential.username:uid=root", + "spring.ldap.embedded.credential.password:boot"); + InMemoryDirectoryServer server = this.context + .getBean(InMemoryDirectoryServer.class); + BindResult result = server.bind("uid=root", "boot"); + assertThat(result).isNotNull(); + } + + @Test + public void testSetPartitionSuffix() throws LDAPException { + load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + InMemoryDirectoryServer server = this.context + .getBean(InMemoryDirectoryServer.class); + assertThat(server.getBaseDNs()).containsExactly(new DN("dc=spring,dc=org")); + } + + @Test + public void testSetLdifFile() throws LDAPException { + load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", + "spring.ldap.embedded.ldif:classpath:schema.ldif"); + InMemoryDirectoryServer server = this.context + .getBean(InMemoryDirectoryServer.class); + assertThat(server.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org")) + .isEqualTo(5); + } + @Test + public void testQueryEmbeddedLdap() throws LDAPException { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", + "spring.ldap.embedded.ldif:classpath:schema.ldif"); + this.context.register(EmbeddedLdapAutoConfiguration.class, + LdapAutoConfiguration.class, + LdapDataAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBeanNamesForType(LdapTemplate.class).length) + .isEqualTo(1); + LdapTemplate ldapTemplate = this.context + .getBean(LdapTemplate.class); + assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org").size()) + .isEqualTo(4); + } + + private void load(String... properties) { + EnvironmentTestUtils.addEnvironment(this.context, properties); + this.context.register(EmbeddedLdapAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } + + @Configuration + static class LdapClientConfiguration { + + @Bean + public LDAPConnection ldapConnection(@Value("${local.ldap.port}") int port) + throws LDAPException { + LDAPConnection con = new LDAPConnection(); + con.connect("localhost", port); + return con; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/resources/schema.ldif b/spring-boot-autoconfigure/src/test/resources/schema.ldif new file mode 100644 index 0000000000..df76aace51 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/schema.ldif @@ -0,0 +1,85 @@ +dn: dc=spring,dc=org +objectclass: top +objectclass: domain +objectclass: extensibleObject +dc: spring + +dn: ou=groups,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=spring,dc=org + +dn: cn=ROLE_ADMIN,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org + +dn: c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-123456 + +dn: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person2 +userPassword: password +cn: Some Person2 +sn: Person2 +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + +dn: cn=Some Person3,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person3 +userPassword: password +cn: Some Person3 +sn: Person3 +description: Sweden, Company1, Some Person3 +telephoneNumber: +46 555-123654 + +dn: cn=Some Person4,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: some.person4 +userPassword: password +cn: Some Person +sn: Person +description: Sweden, Company1, Some Person +telephoneNumber: +46 555-456321 diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index ff817aed1a..7f828a13ed 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -185,6 +185,7 @@ 2.1.0.RELEASE 8.5.6 1.4.8.Final + 3.2.0 9f96c74 0.32 1.6.3 @@ -813,6 +814,11 @@ java-statsd-client ${statsd-client.version} + + com.unboundid + unboundid-ldapsdk + ${unboundid-ldapsdk.version} + com.zaxxer HikariCP diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 0c6cf193c3..5eb9b7cfda 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -315,6 +315,20 @@ content into your application; rather pick only the properties that you need. spring.jersey.servlet.load-on-startup=-1 # Load on startup priority of the Jersey servlet. spring.jersey.type=servlet # Jersey integration type. + # EMBEDDED LDAP ({sc-spring-boot-autoconfigure}/ldap/embedded/EmbeddedLdapProperties.{sc-ext}[EmbeddedLdapProperties]) + spring.ldap.embedded.port= # Embedded LDAP port. + spring.ldap.embedded.credential.username= # Embedded LDAP username. + spring.ldap.embedded.credential.password= # Embedded LDAP password. + spring.ldap.embedded.partition-suffix= # LDAP partition suffix. + spring.ldap.embedded.ldif= # Schema (LDIF) script resource reference. + + # SPRING LDAP ({sc-spring-boot-autoconfigure}/ldap/LdapProperties.{sc-ext}[LdapProperties]) + spring.ldap.urls= # LDAP url of the server. + spring.ldap.base= # Base suffix from which all operations should originate. + spring.ldap.username= # Login user of the server. + spring.ldap.password= # Login password of the server. + spring.ldap.base-environment.*= # Ldap specification settings. + # SPRING MOBILE DEVICE VIEWS ({sc-spring-boot-autoconfigure}/mobile/DeviceDelegatingViewResolverAutoConfiguration.{sc-ext}[DeviceDelegatingViewResolverAutoConfiguration]) spring.mobile.devicedelegatingviewresolver.enable-fallback=false # Enable support for fallback resolution. spring.mobile.devicedelegatingviewresolver.enabled=false # Enable device view resolver. diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index 9f10942f9f..de0caf415a 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -33,6 +33,7 @@ spring-boot-starter-data-elasticsearch spring-boot-starter-data-gemfire spring-boot-starter-data-jpa + spring-boot-starter-data-ldap spring-boot-starter-data-mongodb spring-boot-starter-data-neo4j spring-boot-starter-data-redis diff --git a/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml b/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml new file mode 100644 index 0000000000..0f1d8e079d --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + spring-boot-starters + org.springframework.boot + 1.5.0.BUILD-SNAPSHOT + + spring-boot-starter-data-ldap + Spring Boot Data LDAP Starter + Starter for using Spring Data LDAP + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.data + spring-data-ldap + + + + + \ No newline at end of file diff --git a/spring-boot-starters/spring-boot-starter-data-ldap/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-data-ldap/src/main/resources/META-INF/spring.provides new file mode 100644 index 0000000000..510e85a80c --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-ldap/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: spring-ldap-core \ No newline at end of file From 10de30ff112800ddf294779d4bfa743d766c7fb3 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 5 Jan 2017 18:45:15 -0800 Subject: [PATCH 4/6] Polish LDAP contribution See gh-7733 --- .../data/ldap/LdapRepositoriesRegistrar.java | 3 +- .../ldap/LdapAutoConfiguration.java | 23 ++- .../autoconfigure/ldap/LdapProperties.java | 29 ++-- .../EmbeddedLdapAutoConfiguration.java | 149 ++++++++---------- .../ldap/embedded/EmbeddedLdapProperties.java | 14 +- .../data/alt/ldap/PersonLdapRepository.java | 1 + .../ldap/LdapDataAutoConfigurationTests.java | 3 +- ...dapRepositoriesAutoConfigurationTests.java | 8 +- .../data/ldap/person/Person.java | 2 +- .../ldap/LdapAutoConfigurationTests.java | 46 +++--- .../EmbeddedLdapAutoConfigurationTests.java | 34 ++-- spring-boot-dependencies/pom.xml | 5 + .../appendix-application-properties.adoc | 14 +- .../spring-boot-starter-data-ldap/pom.xml | 29 +++- 14 files changed, 171 insertions(+), 189 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java index 8070beb9d1..eb275fa36a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesRegistrar.java @@ -31,8 +31,7 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi * @author Eddú Meléndez * @since 1.5.0 */ -class LdapRepositoriesRegistrar - extends AbstractRepositoryConfigurationSourceSupport { +class LdapRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport { @Override protected Class getAnnotation() { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java index 7e5e9447ed..d8f3acd6e8 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java @@ -39,27 +39,26 @@ import org.springframework.ldap.core.support.LdapContextSource; @EnableConfigurationProperties(LdapProperties.class) public class LdapAutoConfiguration { - private LdapProperties properties; + private final LdapProperties properties; - private Environment environment; + private final Environment environment; - public LdapAutoConfiguration(LdapProperties properties, - Environment environment) { + public LdapAutoConfiguration(LdapProperties properties, Environment environment) { this.properties = properties; this.environment = environment; } @Bean @ConditionalOnMissingBean - public ContextSource contextSource() { - LdapContextSource contextSource = new LdapContextSource(); - contextSource.setUserDn(this.properties.getUsername()); - contextSource.setPassword(this.properties.getPassword()); - contextSource.setBase(this.properties.getBase()); - contextSource.setUrls(this.properties.determineUrls(this.environment)); - contextSource.setBaseEnvironmentProperties(Collections + public ContextSource ldapContextSource() { + LdapContextSource source = new LdapContextSource(); + source.setUserDn(this.properties.getUsername()); + source.setPassword(this.properties.getPassword()); + source.setBase(this.properties.getBase()); + source.setUrls(this.properties.determineUrls(this.environment)); + source.setBaseEnvironmentProperties(Collections .unmodifiableMap(this.properties.getBaseEnvironment())); - return contextSource; + return source; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java index 018adc9f30..35c8ad8b9f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapProperties.java @@ -22,6 +22,9 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.env.Environment; import org.springframework.ldap.core.LdapTemplate; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + /** * Configuration properties to configure {@link LdapTemplate}. * @@ -36,7 +39,7 @@ public class LdapProperties { /** * LDAP urls. */ - private String[] urls = new String[0]; + private String[] urls; /** * Base suffix from which all operations should originate. @@ -99,29 +102,19 @@ public class LdapProperties { } public String[] determineUrls(Environment environment) { - if (this.urls.length == 0) { - String protocol = "ldap://"; - String host = "localhost"; - int port = determinePort(environment); - String[] ldapUrls = new String[1]; - ldapUrls[0] = protocol + host + ":" + port; - return ldapUrls; + if (ObjectUtils.isEmpty(this.urls)) { + return new String[] { "ldap://localhost:" + determinePort(environment) }; } return this.urls; } private int determinePort(Environment environment) { - if (environment != null) { - String localPort = environment.getProperty("local.ldap.port"); - if (localPort != null) { - return Integer.valueOf(localPort); - } - else { - return DEFAULT_PORT; - } + Assert.state(environment != null, "No local LDAP port configured"); + String localPort = environment.getProperty("local.ldap.port"); + if (localPort != null) { + return Integer.valueOf(localPort); } - throw new IllegalStateException( - "No local ldap port configuration is available"); + return DEFAULT_PORT; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java index a3c4e55acf..3d0670be3c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfiguration.java @@ -16,9 +16,6 @@ package org.springframework.boot.autoconfigure.ldap.embedded; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; @@ -30,14 +27,15 @@ import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldif.LDIFReader; -import org.apache.commons.io.IOUtils; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.boot.autoconfigure.ldap.LdapProperties; +import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties.Credential; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; @@ -63,22 +61,24 @@ import org.springframework.util.StringUtils; @EnableConfigurationProperties({ LdapProperties.class, EmbeddedLdapProperties.class }) @AutoConfigureBefore(LdapAutoConfiguration.class) @ConditionalOnClass(InMemoryDirectoryServer.class) +@ConditionalOnProperty(prefix = "spring.ldap.embedded", name = "base-dn") public class EmbeddedLdapAutoConfiguration { + private static final String PROPERTY_SOURCE_NAME = "ldap.ports"; + + private final EmbeddedLdapProperties embeddedProperties; + + private final LdapProperties properties; + + private final ConfigurableApplicationContext applicationContext; + + private final Environment environment; + private InMemoryDirectoryServer server; - private EmbeddedLdapProperties embeddedProperties; - - private LdapProperties properties; - - private ConfigurableApplicationContext applicationContext; - - private Environment environment; - public EmbeddedLdapAutoConfiguration(EmbeddedLdapProperties embeddedProperties, - LdapProperties properties, - ConfigurableApplicationContext applicationContext, - Environment environment) { + LdapProperties properties, ConfigurableApplicationContext applicationContext, + Environment environment) { this.embeddedProperties = embeddedProperties; this.properties = properties; this.applicationContext = applicationContext; @@ -88,100 +88,83 @@ public class EmbeddedLdapAutoConfiguration { @Bean @DependsOn("directoryServer") @ConditionalOnMissingBean - public ContextSource contextSource() { - LdapContextSource contextSource = new LdapContextSource(); - - EmbeddedLdapProperties.Credential credential = this.embeddedProperties - .getCredential(); - if (StringUtils.hasText(credential.getUsername()) && - StringUtils.hasText(credential.getPassword())) { - contextSource.setUserDn(credential.getUsername()); - contextSource.setPassword(credential.getPassword()); + public ContextSource ldapContextSource() { + LdapContextSource source = new LdapContextSource(); + if (hasCredentials(this.embeddedProperties.getCredential())) { + source.setUserDn(this.embeddedProperties.getCredential().getUsername()); + source.setPassword(this.embeddedProperties.getCredential().getPassword()); } - contextSource.setUrls(this.properties.determineUrls(this.environment)); - return contextSource; + source.setUrls(this.properties.determineUrls(this.environment)); + return source; } @Bean public InMemoryDirectoryServer directoryServer() throws LDAPException { - InMemoryDirectoryServerConfig config = - new InMemoryDirectoryServerConfig(this.embeddedProperties - .getPartitionSuffix()); - - EmbeddedLdapProperties.Credential credential = this.embeddedProperties - .getCredential(); - if (StringUtils.hasText(credential.getUsername()) && - StringUtils.hasText(credential.getPassword())) { - config.addAdditionalBindCredentials(credential - .getUsername(), credential.getPassword()); + InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig( + this.embeddedProperties.getBaseDn()); + if (hasCredentials(this.embeddedProperties.getCredential())) { + config.addAdditionalBindCredentials( + this.embeddedProperties.getCredential().getUsername(), + this.embeddedProperties.getCredential().getPassword()); } - - config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", - this.embeddedProperties.getPort())); - + InMemoryListenerConfig listenerConfig = InMemoryListenerConfig + .createLDAPConfig("LDAP", this.embeddedProperties.getPort()); + config.setListenerConfigs(listenerConfig); this.server = new InMemoryDirectoryServer(config); - - populateDirectoryServer(); - + importLdif(); this.server.startListening(); - publishPortInfo(this.server.getListenPort()); + setPortProperty(this.applicationContext, this.server.getListenPort()); return this.server; } - private void publishPortInfo(int port) { - setPortProperty(this.applicationContext, port); + private boolean hasCredentials(Credential credential) { + return StringUtils.hasText(credential.getUsername()) + && StringUtils.hasText(credential.getPassword()); } - private void setPortProperty(ApplicationContext currentContext, - int port) { - if (currentContext instanceof ConfigurableApplicationContext) { - MutablePropertySources sources = ((ConfigurableApplicationContext) - currentContext).getEnvironment().getPropertySources(); + private void importLdif() throws LDAPException { + String location = this.embeddedProperties.getLdif(); + if (StringUtils.hasText(location)) { + try { + Resource resource = this.applicationContext.getResource(location); + if (resource.exists()) { + InputStream inputStream = resource.getInputStream(); + try { + this.server.importFromLDIF(true, new LDIFReader(inputStream)); + } + finally { + inputStream.close(); + } + } + } + catch (Exception ex) { + throw new IllegalStateException("Unable to load LDIF " + location, ex); + } + } + } + + private void setPortProperty(ApplicationContext context, int port) { + if (context instanceof ConfigurableApplicationContext) { + MutablePropertySources sources = ((ConfigurableApplicationContext) context) + .getEnvironment().getPropertySources(); getLdapPorts(sources).put("local.ldap.port", port); } - if (currentContext.getParent() != null) { - setPortProperty(currentContext.getParent(), port); + if (context.getParent() != null) { + setPortProperty(context.getParent(), port); } } + @SuppressWarnings("unchecked") private Map getLdapPorts(MutablePropertySources sources) { - PropertySource propertySource = sources.get("ldap.ports"); + PropertySource propertySource = sources.get(PROPERTY_SOURCE_NAME); if (propertySource == null) { - propertySource = new MapPropertySource("ldap.ports", + propertySource = new MapPropertySource(PROPERTY_SOURCE_NAME, new HashMap()); sources.addFirst(propertySource); } return (Map) propertySource.getSource(); } - private void populateDirectoryServer() throws LDAPException { - String location = this.embeddedProperties.getLdif(); - if (StringUtils.hasText(location)) { - try { - Resource resource = this.applicationContext.getResource( - this.embeddedProperties.getLdif()); - if (resource.exists()) { - File tempFile = File.createTempFile("ldap_test_data", ".ldif"); - try { - InputStream inputStream = resource.getInputStream(); - IOUtils.copy(inputStream, new FileOutputStream(tempFile)); - this.server.importFromLDIF(true, new LDIFReader(tempFile)); - } - catch (LDAPException e) { - e.printStackTrace(); - } - finally { - tempFile.delete(); - } - } - } - catch (IOException ex) { - throw new IllegalStateException( - "Unable to load resource from " + location, ex); - } - } - } - @PreDestroy public void close() { if (this.server != null) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java index 0729b03f9c..1a4ac083ed 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapProperties.java @@ -38,14 +38,14 @@ public class EmbeddedLdapProperties { private Credential credential = new Credential(); /** - * LDAP partition suffix. + * Base DNs. */ - private String partitionSuffix; + private String baseDn; /** * Schema (LDIF) script resource reference. */ - private String ldif; + private String ldif = "classpath:schema.ldif"; public int getPort() { return this.port; @@ -63,12 +63,12 @@ public class EmbeddedLdapProperties { this.credential = credential; } - public String getPartitionSuffix() { - return this.partitionSuffix; + public String getBaseDn() { + return this.baseDn; } - public void setPartitionSuffix(String partitionSuffix) { - this.partitionSuffix = partitionSuffix; + public void setBaseDn(String baseDn) { + this.baseDn = baseDn; } public String getLdif() { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java index cffe65943f..baa9fcbf64 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/ldap/PersonLdapRepository.java @@ -22,4 +22,5 @@ import org.springframework.boot.autoconfigure.data.ldap.person.Person; import org.springframework.data.repository.Repository; public interface PersonLdapRepository extends Repository { + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java index c907ae0a68..1301db97c9 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapDataAutoConfigurationTests.java @@ -49,8 +49,7 @@ public class LdapDataAutoConfigurationTests { this.context.register(PropertyPlaceholderAutoConfiguration.class, LdapAutoConfiguration.class, LdapDataAutoConfiguration.class); this.context.refresh(); - assertThat(this.context.getBeanNamesForType(LdapTemplate.class).length) - .isEqualTo(1); + assertThat(this.context.getBeanNamesForType(LdapTemplate.class)).hasSize(1); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java index 27be0268c3..6ebb70597a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/LdapRepositoriesAutoConfigurationTests.java @@ -52,16 +52,13 @@ public class LdapRepositoriesAutoConfigurationTests { @Test public void testDefaultRepositoryConfiguration() throws Exception { load(TestConfiguration.class); - assertThat(this.context.getBean(PersonRepository.class)).isNotNull(); } @Test public void testNoRepositoryConfiguration() throws Exception { load(EmptyConfiguration.class); - - assertThat(this.context.getBeanNamesForType(PersonRepository.class).length) - .isEqualTo(0); + assertThat(this.context.getBeanNamesForType(PersonRepository.class)).isEmpty(); } @Test @@ -76,8 +73,7 @@ public class LdapRepositoriesAutoConfigurationTests { "spring.ldap.urls:ldap://localhost:389"); this.context.register(configurationClasses); this.context.register(LdapAutoConfiguration.class, - LdapDataAutoConfiguration.class, - LdapRepositoriesAutoConfiguration.class, + LdapDataAutoConfiguration.class, LdapRepositoriesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java index 52e4b7bcb4..02bb4600c3 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/ldap/person/Person.java @@ -23,7 +23,7 @@ import org.springframework.ldap.odm.annotations.DnAttribute; import org.springframework.ldap.odm.annotations.Entry; import org.springframework.ldap.odm.annotations.Id; -@Entry(objectClasses = {"person", "top"}, base = "ou=someOu") +@Entry(objectClasses = { "person", "top" }, base = "ou=someOu") public class Person { @Id diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java index 2f8653e4d0..102e205a6b 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java @@ -52,52 +52,44 @@ public class LdapAutoConfigurationTests { @Test public void testDefaultUrl() { load(); - assertThat(this.context.getBeanNamesForType(ContextSource.class).length) - .isEqualTo(1); - assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), - "urls")).isEqualTo(new String[]{"ldap://localhost:389"}); + ContextSource contextSource = this.context.getBean(ContextSource.class); + String[] urls = (String[]) ReflectionTestUtils.getField(contextSource, "urls"); + assertThat(urls).containsExactly("ldap://localhost:389"); } @Test public void testContextSourceSetOneUrl() { load("spring.ldap.urls:ldap://localhost:123"); - assertThat(this.context.getBeanNamesForType(ContextSource.class).length) - .isEqualTo(1); - assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), - "urls")).isEqualTo(new String[]{"ldap://localhost:123"}); + ContextSource contextSource = this.context.getBean(ContextSource.class); + String[] urls = (String[]) ReflectionTestUtils.getField(contextSource, "urls"); + assertThat(urls).containsExactly("ldap://localhost:123"); } @Test public void testContextSourceSetTwoUrls() { load("spring.ldap.urls:ldap://localhost:123,ldap://mycompany:123"); - assertThat(this.context.getBeanNamesForType(ContextSource.class).length) - .isEqualTo(1); - assertThat(this.context.getBean(LdapProperties.class).getUrls().length) - .isEqualTo(2); - assertThat(ReflectionTestUtils.getField(this.context.getBean(ContextSource.class), - "urls")) - .isEqualTo(new String[]{"ldap://localhost:123", "ldap://mycompany:123"}); + ContextSource contextSource = this.context.getBean(ContextSource.class); + LdapProperties ldapProperties = this.context.getBean(LdapProperties.class); + String[] urls = (String[]) ReflectionTestUtils.getField(contextSource, "urls"); + assertThat(urls).containsExactly("ldap://localhost:123", "ldap://mycompany:123"); + assertThat(ldapProperties.getUrls()).hasSize(2); } @Test public void testContextSourceWithMoreProperties() { - load("spring.ldap.urls:ldap://localhost:123", - "spring.ldap.username:root", - "spring.ldap.password:root", - "spring.ldap.base:cn=SpringDevelopers", - "spring.ldap.baseEnvironment.java.naming.security" + - ".authentication:DIGEST-MD5"); - assertThat(this.context.getBeanNamesForType(ContextSource.class).length) - .isEqualTo(1); - assertThat(this.context.getBean(LdapProperties.class).getBaseEnvironment()) + load("spring.ldap.urls:ldap://localhost:123", "spring.ldap.username:root", + "spring.ldap.password:root", "spring.ldap.base:cn=SpringDevelopers", + "spring.ldap.baseEnvironment.java.naming.security" + + ".authentication:DIGEST-MD5"); + LdapProperties ldapProperties = this.context.getBean(LdapProperties.class); + assertThat(ldapProperties.getBaseEnvironment()) .containsEntry("java.naming.security.authentication", "DIGEST-MD5"); } private void load(String... properties) { EnvironmentTestUtils.addEnvironment(this.context, properties); - this.context - .register(LdapAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); + this.context.register(LdapAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java index 3cb6c00743..c425850826 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/embedded/EmbeddedLdapAutoConfigurationTests.java @@ -61,7 +61,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetDefaultPort() throws LDAPException { load("spring.ldap.embedded.port:1234", - "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + "spring.ldap.embedded.base-dn:dc=spring,dc=org"); InMemoryDirectoryServer server = this.context .getBean(InMemoryDirectoryServer.class); assertThat(server.getListenPort()).isEqualTo(1234); @@ -69,7 +69,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testRandomPortWithEnvironment() throws LDAPException { - load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + load("spring.ldap.embedded.base-dn:dc=spring,dc=org"); InMemoryDirectoryServer server = this.context .getBean(InMemoryDirectoryServer.class); assertThat(server.getListenPort()).isEqualTo(this.context.getEnvironment() @@ -79,21 +79,19 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testRandomPortWithValueAnnotation() throws LDAPException { EnvironmentTestUtils.addEnvironment(this.context, - "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + "spring.ldap.embedded.base-dn:dc=spring,dc=org"); this.context.register(EmbeddedLdapAutoConfiguration.class, LdapClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); - LDAPConnection connection = this.context - .getBean(LDAPConnection.class); - assertThat(connection.getConnectedPort()) - .isEqualTo(this.context.getEnvironment() + LDAPConnection connection = this.context.getBean(LDAPConnection.class); + assertThat(connection.getConnectedPort()).isEqualTo(this.context.getEnvironment() .getProperty("local.ldap.port", Integer.class)); } @Test public void testSetCredentials() throws LDAPException { - load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", + load("spring.ldap.embedded.base-dn:dc=spring,dc=org", "spring.ldap.embedded.credential.username:uid=root", "spring.ldap.embedded.credential.password:boot"); InMemoryDirectoryServer server = this.context @@ -104,7 +102,7 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetPartitionSuffix() throws LDAPException { - load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org"); + load("spring.ldap.embedded.base-dn:dc=spring,dc=org"); InMemoryDirectoryServer server = this.context .getBean(InMemoryDirectoryServer.class); assertThat(server.getBaseDNs()).containsExactly(new DN("dc=spring,dc=org")); @@ -112,35 +110,31 @@ public class EmbeddedLdapAutoConfigurationTests { @Test public void testSetLdifFile() throws LDAPException { - load("spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", - "spring.ldap.embedded.ldif:classpath:schema.ldif"); + load("spring.ldap.embedded.base-dn:dc=spring,dc=org"); InMemoryDirectoryServer server = this.context .getBean(InMemoryDirectoryServer.class); assertThat(server.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org")) .isEqualTo(5); } + @Test public void testQueryEmbeddedLdap() throws LDAPException { EnvironmentTestUtils.addEnvironment(this.context, - "spring.ldap.embedded.partitionSuffix:dc=spring,dc=org", - "spring.ldap.embedded.ldif:classpath:schema.ldif"); + "spring.ldap.embedded.base-dn:dc=spring,dc=org"); this.context.register(EmbeddedLdapAutoConfiguration.class, - LdapAutoConfiguration.class, - LdapDataAutoConfiguration.class, + LdapAutoConfiguration.class, LdapDataAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeanNamesForType(LdapTemplate.class).length) .isEqualTo(1); - LdapTemplate ldapTemplate = this.context - .getBean(LdapTemplate.class); - assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org").size()) - .isEqualTo(4); + LdapTemplate ldapTemplate = this.context.getBean(LdapTemplate.class); + assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org")).hasSize(4); } private void load(String... properties) { EnvironmentTestUtils.addEnvironment(this.context, properties); this.context.register(EmbeddedLdapAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); + PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); } diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 7f828a13ed..73b463b3f9 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -345,6 +345,11 @@ spring-boot-starter-data-jpa 1.5.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-data-ldap + 1.5.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-starter-data-mongodb diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 5eb9b7cfda..82e83fdf38 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -315,13 +315,6 @@ content into your application; rather pick only the properties that you need. spring.jersey.servlet.load-on-startup=-1 # Load on startup priority of the Jersey servlet. spring.jersey.type=servlet # Jersey integration type. - # EMBEDDED LDAP ({sc-spring-boot-autoconfigure}/ldap/embedded/EmbeddedLdapProperties.{sc-ext}[EmbeddedLdapProperties]) - spring.ldap.embedded.port= # Embedded LDAP port. - spring.ldap.embedded.credential.username= # Embedded LDAP username. - spring.ldap.embedded.credential.password= # Embedded LDAP password. - spring.ldap.embedded.partition-suffix= # LDAP partition suffix. - spring.ldap.embedded.ldif= # Schema (LDIF) script resource reference. - # SPRING LDAP ({sc-spring-boot-autoconfigure}/ldap/LdapProperties.{sc-ext}[LdapProperties]) spring.ldap.urls= # LDAP url of the server. spring.ldap.base= # Base suffix from which all operations should originate. @@ -329,6 +322,13 @@ content into your application; rather pick only the properties that you need. spring.ldap.password= # Login password of the server. spring.ldap.base-environment.*= # Ldap specification settings. + # EMBEDDED LDAP ({sc-spring-boot-autoconfigure}/ldap/embedded/EmbeddedLdapProperties.{sc-ext}[EmbeddedLdapProperties]) + spring.ldap.embedded.port= # Embedded LDAP port. + spring.ldap.embedded.credential.username= # Embedded LDAP username. + spring.ldap.embedded.credential.password= # Embedded LDAP password. + spring.ldap.embedded.base-dn= # The base DN + spring.ldap.embedded.ldif= # Schema (LDIF) script resource reference. + # SPRING MOBILE DEVICE VIEWS ({sc-spring-boot-autoconfigure}/mobile/DeviceDelegatingViewResolverAutoConfiguration.{sc-ext}[DeviceDelegatingViewResolverAutoConfiguration]) spring.mobile.devicedelegatingviewresolver.enable-fallback=false # Enable support for fallback resolution. spring.mobile.devicedelegatingviewresolver.enabled=false # Enable device view resolver. diff --git a/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml b/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml index 0f1d8e079d..09ae22b28d 100644 --- a/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml +++ b/spring-boot-starters/spring-boot-starter-data-ldap/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 spring-boot-starters @@ -27,6 +28,26 @@ spring-data-ldap - - - \ No newline at end of file + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + duplicate-dependencies + validate + + check + + + + changelog.txt + + + + + + + + From f96294b63bceeaca671e227d77cb7800d1ed541b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 5 Jan 2017 20:08:02 -0800 Subject: [PATCH 5/6] Add LDAP sample Add an LDAP sample application. See gh-7733 --- spring-boot-samples/pom.xml | 1 + .../spring-boot-sample-data-ldap/pom.xml | 50 +++++++++++++++ .../main/java/sample/data/ldap/Person.java | 39 ++++++++++++ .../sample/data/ldap/PersonRepository.java | 25 ++++++++ .../data/ldap/SampleLdapApplication.java | 59 ++++++++++++++++++ .../src/main/resources/application.properties | 1 + .../src/main/resources/schema.ldif | 62 +++++++++++++++++++ .../data/ldap/SampleLdapApplicationTests.java | 47 ++++++++++++++ 8 files changed, 284 insertions(+) create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/pom.xml create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/Person.java create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/PersonRepository.java create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/SampleLdapApplication.java create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/application.properties create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/schema.ldif create mode 100644 spring-boot-samples/spring-boot-sample-data-ldap/src/test/java/sample/data/ldap/SampleLdapApplicationTests.java diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 489503b52d..d701b33f25 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -38,6 +38,7 @@ spring-boot-sample-data-elasticsearch spring-boot-sample-data-gemfire spring-boot-sample-data-jpa + spring-boot-sample-data-ldap spring-boot-sample-data-mongodb spring-boot-sample-data-neo4j spring-boot-sample-data-redis diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/pom.xml b/spring-boot-samples/spring-boot-sample-data-ldap/pom.xml new file mode 100644 index 0000000000..af2c829d91 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.5.0.BUILD-SNAPSHOT + + spring-boot-sample-data-ldap + Spring Boot Data LDAP Sample + Spring Boot Data LDAP Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + + com.unboundid + unboundid-ldapsdk + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-ldap + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/Person.java b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/Person.java new file mode 100644 index 0000000000..93ef7b3a92 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/Person.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.data.ldap; + +import javax.naming.Name; + +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.odm.annotations.Id; + +@Entry(objectClasses = { "person", "top" }) +public class Person { + + @Id + private Name dn; + + @Attribute(name = "telephoneNumber") + private String phone; + + @Override + public String toString() { + return String.format("Customer[dn=%s, phone='%s']", this.dn, this.phone); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/PersonRepository.java b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/PersonRepository.java new file mode 100644 index 0000000000..526f379d96 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/PersonRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.data.ldap; + +import org.springframework.data.ldap.repository.LdapRepository; + +public interface PersonRepository extends LdapRepository { + + Person findByPhone(String phone); + +} diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/SampleLdapApplication.java b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/SampleLdapApplication.java new file mode 100644 index 0000000000..294f08954f --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/java/sample/data/ldap/SampleLdapApplication.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.data.ldap; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleLdapApplication implements CommandLineRunner { + + private final PersonRepository repository; + + public SampleLdapApplication(PersonRepository repository) { + this.repository = repository; + } + + @Override + public void run(String... args) throws Exception { + + // fetch all people + System.out.println("People found with findAll():"); + System.out.println("-------------------------------"); + for (Person person : this.repository.findAll()) { + System.out.println(person); + } + System.out.println(); + + // fetch an individual person + System.out.println("Person found with findByPhone('+46 555-123456'):"); + System.out.println("--------------------------------"); + System.out.println(this.repository.findByPhone("+46 555-123456")); + // + // System.out.println("Customers found with findByLastName('Smith'):"); + // System.out.println("--------------------------------"); + // for (Customer customer : this.repository.findByLastName("Smith")) { + // System.out.println(customer); + // } + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleLdapApplication.class, args).close(); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/application.properties new file mode 100644 index 0000000000..b574703f0f --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.ldap.embedded.base-dn=dc=spring,dc=org diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/schema.ldif b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/schema.ldif new file mode 100644 index 0000000000..bb97e4a3c5 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/main/resources/schema.ldif @@ -0,0 +1,62 @@ +dn: dc=spring,dc=org +objectclass: top +objectclass: domain +objectclass: extensibleObject +dc: spring + +dn: ou=groups,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: cn=ROLE_USER,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_USER +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person,ou=company1,c=Sweden,dc=spring,dc=org +uniqueMember: cn=Some Person3,ou=company1,c=Sweden,dc=spring,dc=org + +dn: cn=ROLE_ADMIN,ou=groups,dc=spring,dc=org +objectclass: top +objectclass: groupOfUniqueNames +cn: ROLE_ADMIN +uniqueMember: cn=Some Person2,ou=company1,c=Sweden,dc=spring,dc=org + +dn: c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: country +c: Sweden +description: The country of Sweden + +dn: ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: organizationalUnit +ou: company1 +description: First company in Sweden + +dn: cn=Alice Smith,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: alice.smith +userPassword: password +cn: Alice Smith +sn: Alice Smith +description: Sweden, Company1, Alice Smith +telephoneNumber: +46 555-123456 + +dn: cn=Bob Smith,ou=company1,c=Sweden,dc=spring,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: bob.smith +userPassword: password +cn: Bob Smith +sn: Bob Smith +description: Sweden, Company1, Some Person2 +telephoneNumber: +46 555-654321 + diff --git a/spring-boot-samples/spring-boot-sample-data-ldap/src/test/java/sample/data/ldap/SampleLdapApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-ldap/src/test/java/sample/data/ldap/SampleLdapApplicationTests.java new file mode 100644 index 0000000000..4ab05c0670 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-ldap/src/test/java/sample/data/ldap/SampleLdapApplicationTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2017 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 + * + * http://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 sample.data.ldap; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.rule.OutputCapture; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SampleLdapApplication}. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class SampleLdapApplicationTests { + + @ClassRule + public static OutputCapture outputCapture = new OutputCapture(); + + @Test + public void testDefaultSettings() throws Exception { + String output = outputCapture.toString(); + assertThat(output).contains("cn=Alice Smith"); + } + +} From 554f6c8714a88bf9ba5e8cf3a2b7bf2bba9e0e0f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 5 Jan 2017 21:52:49 -0800 Subject: [PATCH 6/6] Document LDAP support Add documentation for the LDAP support. Closes gh-7733 --- .../main/asciidoc/spring-boot-features.adoc | 91 ++++++++++++++++++- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index a1b723f51c..dd92f48364 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3214,11 +3214,12 @@ https://github.com/spring-projects/spring-data-elasticsearch/[Elasticsearch], http://projects.spring.io/spring-data-solr/[Solr], http://projects.spring.io/spring-data-redis/[Redis], http://projects.spring.io/spring-data-gemfire/[Gemfire], +http://projects.spring.io/spring-data-cassandra/[Cassandra], http://projects.spring.io/spring-data-couchbase/[Couchbase] and -http://projects.spring.io/spring-data-cassandra/[Cassandra]. +http://projects.spring.io/spring-data-ldap/[LDAP]. Spring Boot provides auto-configuration for Redis, MongoDB, Neo4j, Elasticsearch, Solr -and Cassandra; you can make use of the other projects, but you will need to configure -them yourself. Refer to the appropriate reference documentation at +Cassandra, Couchbase and LDAP; you can make use of the other projects, but you will need +to configure them yourself. Refer to the appropriate reference documentation at http://projects.spring.io/spring-data[projects.spring.io/spring-data]. @@ -3757,6 +3758,7 @@ TIP: For complete details of Spring Data Cassandra, refer to their http://docs.spring.io/spring-data/cassandra/docs/[reference documentation]. + [[boot-features-couchbase]] === Couchbase http://www.couchbase.com/[Couchbase] is an open-source, distributed multi-model NoSQL @@ -3864,6 +3866,89 @@ implementation. +[[boot-features-ldap]] +=== LDAP +https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol[LDAP] (Lightweight +Directory Access Protocol) is an is an open, vendor-neutral, industry standard application +protocol for accessing and maintaining distributed directory information services over an +IP network. Spring Boot offers auto-configuration for any compliant LDAP server as well +as support for the embedded in-memory LDAP server from +https://www.ldap.com/unboundid-ldap-sdk-for-java[Unbounded]. + +LDAP abstractions are provided by +https://github.com/spring-projects/spring-data-ldap[Spring Data LDAP]. +There is a `spring-boot-starter-data-ldap` '`Starter`' for collecting the dependencies in +a convenient way. + + + +[[boot-features-ldap-connecting]] +==== Connecting to an LDAP server +To connect to an LDAP server make sure you declare a dependency on the +`spring-boot-starter-data-ldap` '`Starter`' or `spring-ldap-core` then declare the +URLs of your server in your application.properties: + +[source,properties,indent=0] +---- + spring.ldap.urls=ldap://myserver:1235 + spring.ldap.username=admin + spring.ldap.password=secret +---- + +If you need to customize connection settings you can use the `spring.ldap.base` and +`spring.ldap.base-environment` properties. + + + +[[boot-features-ldap-spring-data-repositories]] +==== Spring Data LDAP repositories +Spring Data includes repository support for LDAP. For complete details of Spring +Data LDAP, refer to their +http://docs.spring.io/spring-data/ldap/docs/1.0.x/reference/html/[reference documentation]. + +You can also inject an auto-configured `LdapTemplate` instance as you would with any +other Spring Bean. + + +[source,java,indent=0] +---- + @Component + public class MyBean { + + private final LdapTemplate template; + + @Autowired + public MyBean(LdapTemplate template) { + this.template = template; + } + + // ... + + } +---- + + + +[[boot-features-ldap-embedded]] +==== Embedded in-memory LDAP server +For testing purposes Spring Boot supports auto-configuration of an in-memory LDAP server +from https://www.ldap.com/unboundid-ldap-sdk-for-java[Unbounded]. To configure the server +add a dependency to `com.unboundid:unboundid-ldapsdk` and declare a `base-dn` property: + +[source,properties,indent=0] +---- + spring.ldap.embedded.base-dn=dc=spring,dc=io +---- + +By default the server will start on a random port and the trigger the regular LDAP support +(there is not need to specify a `spring.ldap.urls` property). + +If there is a `schema.ldif` file on your classpath it will be used to initialize the +server. You can also use the `spring.ldap.embedded.ldif` property if you want to load +the initialization script from a different resource. + + + [[boot-features-caching]] == Caching The Spring Framework provides support for transparently adding caching to an application.