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 1/4] 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 2/4] 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 3/4] 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 4/4] 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.