Create spring-boot-ldap module
This commit is contained in:
committed by
Phillip Webb
parent
fab4500e4a
commit
26183f1edf
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
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.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.boot.ldap.autoconfigure.LdapProperties.Template;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.ldap.convert.ConverterUtils;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
|
||||
import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for LDAP.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Vedran Pavic
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(ContextSource.class)
|
||||
@EnableConfigurationProperties(LdapProperties.class)
|
||||
public class LdapAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LdapConnectionDetails.class)
|
||||
PropertiesLdapConnectionDetails propertiesLdapConnectionDetails(LdapProperties properties,
|
||||
Environment environment) {
|
||||
return new PropertiesLdapConnectionDetails(properties, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public LdapContextSource ldapContextSource(LdapConnectionDetails connectionDetails, LdapProperties properties,
|
||||
ObjectProvider<DirContextAuthenticationStrategy> dirContextAuthenticationStrategy) {
|
||||
LdapContextSource source = new LdapContextSource();
|
||||
dirContextAuthenticationStrategy.ifUnique(source::setAuthenticationStrategy);
|
||||
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
propertyMapper.from(connectionDetails.getUsername()).to(source::setUserDn);
|
||||
propertyMapper.from(connectionDetails.getPassword()).to(source::setPassword);
|
||||
propertyMapper.from(properties.getAnonymousReadOnly()).to(source::setAnonymousReadOnly);
|
||||
propertyMapper.from(properties.getReferral())
|
||||
.as(((referral) -> referral.name().toLowerCase(Locale.ROOT)))
|
||||
.to(source::setReferral);
|
||||
propertyMapper.from(connectionDetails.getBase()).to(source::setBase);
|
||||
propertyMapper.from(connectionDetails.getUrls()).to(source::setUrls);
|
||||
propertyMapper.from(properties.getBaseEnvironment())
|
||||
.to((baseEnvironment) -> source.setBaseEnvironmentProperties(Collections.unmodifiableMap(baseEnvironment)));
|
||||
return source;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ObjectDirectoryMapper objectDirectoryMapper() {
|
||||
ApplicationConversionService conversionService = new ApplicationConversionService();
|
||||
ConverterUtils.addDefaultConverters(conversionService);
|
||||
DefaultObjectDirectoryMapper objectDirectoryMapper = new DefaultObjectDirectoryMapper();
|
||||
objectDirectoryMapper.setConversionService(conversionService);
|
||||
return objectDirectoryMapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LdapOperations.class)
|
||||
public LdapTemplate ldapTemplate(LdapProperties properties, ContextSource contextSource,
|
||||
ObjectDirectoryMapper objectDirectoryMapper) {
|
||||
Template template = properties.getTemplate();
|
||||
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
|
||||
ldapTemplate.setObjectDirectoryMapper(objectDirectoryMapper);
|
||||
propertyMapper.from(template.isIgnorePartialResultException())
|
||||
.to(ldapTemplate::setIgnorePartialResultException);
|
||||
propertyMapper.from(template.isIgnoreNameNotFoundException()).to(ldapTemplate::setIgnoreNameNotFoundException);
|
||||
propertyMapper.from(template.isIgnoreSizeLimitExceededException())
|
||||
.to(ldapTemplate::setIgnoreSizeLimitExceededException);
|
||||
return ldapTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an LDAP service.
|
||||
*
|
||||
* @author Philipp Kessler
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface LdapConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* LDAP URLs of the server.
|
||||
* @return the LDAP URLs to use
|
||||
*/
|
||||
String[] getUrls();
|
||||
|
||||
/**
|
||||
* Base suffix from which all operations should originate.
|
||||
* @return base suffix
|
||||
*/
|
||||
default String getBase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login username of the server.
|
||||
* @return login username
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login password of the server.
|
||||
* @return login password
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
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.ReferralException;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for LDAP.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.ldap")
|
||||
public class LdapProperties {
|
||||
|
||||
private static final int DEFAULT_PORT = 389;
|
||||
|
||||
/**
|
||||
* LDAP URLs of the server.
|
||||
*/
|
||||
private String[] urls;
|
||||
|
||||
/**
|
||||
* Base suffix from which all operations should originate.
|
||||
*/
|
||||
private String base;
|
||||
|
||||
/**
|
||||
* Login username of the server.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Login password of the server.
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Whether read-only operations should use an anonymous environment. Disabled by
|
||||
* default unless a username is set.
|
||||
*/
|
||||
private Boolean anonymousReadOnly;
|
||||
|
||||
/**
|
||||
* Specify how referrals encountered by the service provider are to be processed. If
|
||||
* not specified, the default is determined by the provider.
|
||||
*/
|
||||
private Referral referral;
|
||||
|
||||
/**
|
||||
* LDAP specification settings.
|
||||
*/
|
||||
private final Map<String, String> baseEnvironment = new HashMap<>();
|
||||
|
||||
private final Template template = new Template();
|
||||
|
||||
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 Boolean getAnonymousReadOnly() {
|
||||
return this.anonymousReadOnly;
|
||||
}
|
||||
|
||||
public void setAnonymousReadOnly(Boolean anonymousReadOnly) {
|
||||
this.anonymousReadOnly = anonymousReadOnly;
|
||||
}
|
||||
|
||||
public Referral getReferral() {
|
||||
return this.referral;
|
||||
}
|
||||
|
||||
public void setReferral(Referral referral) {
|
||||
this.referral = referral;
|
||||
}
|
||||
|
||||
public Map<String, String> getBaseEnvironment() {
|
||||
return this.baseEnvironment;
|
||||
}
|
||||
|
||||
public Template getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
public String[] determineUrls(Environment environment) {
|
||||
if (ObjectUtils.isEmpty(this.urls)) {
|
||||
return new String[] { "ldap://localhost:" + determinePort(environment) };
|
||||
}
|
||||
return this.urls;
|
||||
}
|
||||
|
||||
private int determinePort(Environment environment) {
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
String localPort = environment.getProperty("local.ldap.port");
|
||||
if (localPort != null) {
|
||||
return Integer.parseInt(localPort);
|
||||
}
|
||||
return DEFAULT_PORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link LdapTemplate settings}.
|
||||
*/
|
||||
public static class Template {
|
||||
|
||||
/**
|
||||
* Whether PartialResultException should be ignored in searches through the
|
||||
* LdapTemplate.
|
||||
*/
|
||||
private boolean ignorePartialResultException = false;
|
||||
|
||||
/**
|
||||
* Whether NameNotFoundException should be ignored in searches through the
|
||||
* LdapTemplate.
|
||||
*/
|
||||
private boolean ignoreNameNotFoundException = false;
|
||||
|
||||
/**
|
||||
* Whether SizeLimitExceededException should be ignored in searches through the
|
||||
* LdapTemplate.
|
||||
*/
|
||||
private boolean ignoreSizeLimitExceededException = true;
|
||||
|
||||
public boolean isIgnorePartialResultException() {
|
||||
return this.ignorePartialResultException;
|
||||
}
|
||||
|
||||
public void setIgnorePartialResultException(boolean ignorePartialResultException) {
|
||||
this.ignorePartialResultException = ignorePartialResultException;
|
||||
}
|
||||
|
||||
public boolean isIgnoreNameNotFoundException() {
|
||||
return this.ignoreNameNotFoundException;
|
||||
}
|
||||
|
||||
public void setIgnoreNameNotFoundException(boolean ignoreNameNotFoundException) {
|
||||
this.ignoreNameNotFoundException = ignoreNameNotFoundException;
|
||||
}
|
||||
|
||||
public boolean isIgnoreSizeLimitExceededException() {
|
||||
return this.ignoreSizeLimitExceededException;
|
||||
}
|
||||
|
||||
public void setIgnoreSizeLimitExceededException(Boolean ignoreSizeLimitExceededException) {
|
||||
this.ignoreSizeLimitExceededException = ignoreSizeLimitExceededException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the methods to handle referrals.
|
||||
*
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public enum Referral {
|
||||
|
||||
/**
|
||||
* Follow referrals automatically.
|
||||
*/
|
||||
FOLLOW,
|
||||
|
||||
/**
|
||||
* Ignore referrals.
|
||||
*/
|
||||
IGNORE,
|
||||
|
||||
/**
|
||||
* Throw {@link ReferralException} when a referral is encountered.
|
||||
*/
|
||||
THROW
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Adapts {@link LdapProperties} to {@link LdapConnectionDetails}.
|
||||
*
|
||||
* @author Philipp Kessler
|
||||
*/
|
||||
class PropertiesLdapConnectionDetails implements LdapConnectionDetails {
|
||||
|
||||
private final LdapProperties properties;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
PropertiesLdapConnectionDetails(LdapProperties properties, Environment environment) {
|
||||
this.properties = properties;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUrls() {
|
||||
return this.properties.determineUrls(this.environment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBase() {
|
||||
return this.properties.getBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure.embedded;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.ldap.sdk.schema.Schema;
|
||||
import com.unboundid.ldif.LDIFReader;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.ldap.autoconfigure.LdapAutoConfiguration;
|
||||
import org.springframework.boot.ldap.autoconfigure.LdapProperties;
|
||||
import org.springframework.boot.ldap.autoconfigure.embedded.EmbeddedLdapAutoConfiguration.EmbeddedLdapAutoConfigurationRuntimeHints;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
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.core.type.AnnotatedTypeMetadata;
|
||||
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
|
||||
* @author Mathieu Ouellet
|
||||
* @author Raja Kolli
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(before = LdapAutoConfiguration.class)
|
||||
@EnableConfigurationProperties({ LdapProperties.class, EmbeddedLdapProperties.class })
|
||||
@ConditionalOnClass(InMemoryDirectoryServer.class)
|
||||
@Conditional(EmbeddedLdapAutoConfiguration.EmbeddedLdapCondition.class)
|
||||
@ImportRuntimeHints(EmbeddedLdapAutoConfigurationRuntimeHints.class)
|
||||
public class EmbeddedLdapAutoConfiguration implements DisposableBean {
|
||||
|
||||
private static final String PROPERTY_SOURCE_NAME = "ldap.ports";
|
||||
|
||||
private final EmbeddedLdapProperties embeddedProperties;
|
||||
|
||||
private InMemoryDirectoryServer server;
|
||||
|
||||
public EmbeddedLdapAutoConfiguration(EmbeddedLdapProperties embeddedProperties) {
|
||||
this.embeddedProperties = embeddedProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InMemoryDirectoryServer directoryServer(ApplicationContext applicationContext) throws LDAPException {
|
||||
String[] baseDn = StringUtils.toStringArray(this.embeddedProperties.getBaseDn());
|
||||
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(baseDn);
|
||||
if (this.embeddedProperties.getCredential().isAvailable()) {
|
||||
config.addAdditionalBindCredentials(this.embeddedProperties.getCredential().getUsername(),
|
||||
this.embeddedProperties.getCredential().getPassword());
|
||||
}
|
||||
setSchema(config);
|
||||
InMemoryListenerConfig listenerConfig = InMemoryListenerConfig.createLDAPConfig("LDAP",
|
||||
this.embeddedProperties.getPort());
|
||||
config.setListenerConfigs(listenerConfig);
|
||||
this.server = new InMemoryDirectoryServer(config);
|
||||
importLdif(applicationContext);
|
||||
this.server.startListening();
|
||||
setPortProperty(applicationContext, this.server.getListenPort());
|
||||
return this.server;
|
||||
}
|
||||
|
||||
private void setSchema(InMemoryDirectoryServerConfig config) {
|
||||
if (!this.embeddedProperties.getValidation().isEnabled()) {
|
||||
config.setSchema(null);
|
||||
return;
|
||||
}
|
||||
Resource schema = this.embeddedProperties.getValidation().getSchema();
|
||||
if (schema != null) {
|
||||
setSchema(config, schema);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSchema(InMemoryDirectoryServerConfig config, Resource resource) {
|
||||
try {
|
||||
Schema defaultSchema = Schema.getDefaultStandardSchema();
|
||||
Schema schema = Schema.getSchema(resource.getInputStream());
|
||||
config.setSchema(Schema.mergeSchemas(defaultSchema, schema));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to load schema " + resource.getDescription(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void importLdif(ApplicationContext applicationContext) {
|
||||
String location = this.embeddedProperties.getLdif();
|
||||
if (StringUtils.hasText(location)) {
|
||||
try {
|
||||
Resource resource = applicationContext.getResource(location);
|
||||
if (resource.exists()) {
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
this.server.importFromLDIF(true, new LDIFReader(inputStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to load LDIF " + location, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setPortProperty(ApplicationContext context, int port) {
|
||||
if (context instanceof ConfigurableApplicationContext configurableContext) {
|
||||
MutablePropertySources sources = configurableContext.getEnvironment().getPropertySources();
|
||||
getLdapPorts(sources).put("local.ldap.port", port);
|
||||
}
|
||||
if (context.getParent() != null) {
|
||||
setPortProperty(context.getParent(), port);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> getLdapPorts(MutablePropertySources sources) {
|
||||
PropertySource<?> propertySource = sources.get(PROPERTY_SOURCE_NAME);
|
||||
if (propertySource == null) {
|
||||
propertySource = new MapPropertySource(PROPERTY_SOURCE_NAME, new HashMap<>());
|
||||
sources.addFirst(propertySource);
|
||||
}
|
||||
return (Map<String, Object>) propertySource.getSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if (this.server != null) {
|
||||
this.server.shutDown(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SpringBootCondition} to determine when to apply embedded LDAP
|
||||
* auto-configuration.
|
||||
*/
|
||||
static class EmbeddedLdapCondition extends SpringBootCondition {
|
||||
|
||||
private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Builder message = ConditionMessage.forCondition("Embedded LDAP");
|
||||
Environment environment = context.getEnvironment();
|
||||
if (environment != null && !Binder.get(environment)
|
||||
.bind("spring.ldap.embedded.base-dn", STRING_LIST)
|
||||
.orElseGet(Collections::emptyList)
|
||||
.isEmpty()) {
|
||||
return ConditionOutcome.match(message.because("Found base-dn property"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.because("No base-dn property found"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ContextSource.class)
|
||||
static class EmbeddedLdapContextConfiguration {
|
||||
|
||||
@Bean
|
||||
@DependsOn("directoryServer")
|
||||
@ConditionalOnMissingBean
|
||||
LdapContextSource ldapContextSource(Environment environment, LdapProperties properties,
|
||||
EmbeddedLdapProperties embeddedProperties) {
|
||||
LdapContextSource source = new LdapContextSource();
|
||||
source.setBase(properties.getBase());
|
||||
if (embeddedProperties.getCredential().isAvailable()) {
|
||||
source.setUserDn(embeddedProperties.getCredential().getUsername());
|
||||
source.setPassword(embeddedProperties.getCredential().getPassword());
|
||||
}
|
||||
source.setUrls(properties.determineUrls(environment));
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class EmbeddedLdapAutoConfigurationRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.resources()
|
||||
.registerPatternIfPresent(classLoader, "schema.ldif", (hint) -> hint.includes("schema.ldif"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure.embedded;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.Delimiter;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for Embedded LDAP.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Mathieu Ouellet
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.ldap.embedded")
|
||||
public class EmbeddedLdapProperties {
|
||||
|
||||
/**
|
||||
* Embedded LDAP port.
|
||||
*/
|
||||
private int port = 0;
|
||||
|
||||
/**
|
||||
* Embedded LDAP credentials.
|
||||
*/
|
||||
private Credential credential = new Credential();
|
||||
|
||||
/**
|
||||
* List of base DNs.
|
||||
*/
|
||||
@Delimiter(Delimiter.NONE)
|
||||
private List<String> baseDn = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Schema (LDIF) script resource reference.
|
||||
*/
|
||||
private String ldif = "classpath:schema.ldif";
|
||||
|
||||
/**
|
||||
* Schema validation.
|
||||
*/
|
||||
private final Validation validation = new Validation();
|
||||
|
||||
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 List<String> getBaseDn() {
|
||||
return this.baseDn;
|
||||
}
|
||||
|
||||
public void setBaseDn(List<String> baseDn) {
|
||||
this.baseDn = baseDn;
|
||||
}
|
||||
|
||||
public String getLdif() {
|
||||
return this.ldif;
|
||||
}
|
||||
|
||||
public void setLdif(String ldif) {
|
||||
this.ldif = ldif;
|
||||
}
|
||||
|
||||
public Validation getValidation() {
|
||||
return this.validation;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
boolean isAvailable() {
|
||||
return StringUtils.hasText(this.username) && StringUtils.hasText(this.password);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Validation {
|
||||
|
||||
/**
|
||||
* Whether to enable LDAP schema validation.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Path to the custom schema.
|
||||
*/
|
||||
private Resource schema;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Resource getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public void setSchema(Resource schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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 embedded LDAP.
|
||||
*/
|
||||
package org.springframework.boot.ldap.autoconfigure.embedded;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.ldap.autoconfigure.LdapAutoConfiguration
|
||||
org.springframework.boot.ldap.autoconfigure.embedded.EmbeddedLdapAutoConfiguration
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
|
||||
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
|
||||
import org.springframework.ldap.pool2.factory.PoolConfig;
|
||||
import org.springframework.ldap.pool2.factory.PooledContextSource;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link LdapAutoConfiguration}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
class LdapAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(LdapAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void contextSourceWithDefaultUrl() {
|
||||
this.contextRunner.run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUrls()).containsExactly("ldap://localhost:389");
|
||||
assertThat(contextSource.isAnonymousReadOnly()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithSingleUrl() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:123").run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUrls()).containsExactly("ldap://localhost:123");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithSeveralUrls() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:123,ldap://mycompany:123")
|
||||
.run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
LdapProperties ldapProperties = context.getBean(LdapProperties.class);
|
||||
assertThat(contextSource.getUrls()).containsExactly("ldap://localhost:123", "ldap://mycompany:123");
|
||||
assertThat(ldapProperties.getUrls()).hasSize(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithUserDoesNotEnableAnonymousReadOnly() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.username:root").run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUserDn()).isEqualTo("root");
|
||||
assertThat(contextSource.isAnonymousReadOnly()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithReferral() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.referral:ignore").run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource).hasFieldOrPropertyWithValue("referral", "ignore");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithExtraCustomization() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.urls:ldap://localhost:123", "spring.ldap.username:root",
|
||||
"spring.ldap.password:secret", "spring.ldap.anonymous-read-only:true",
|
||||
"spring.ldap.base:cn=SpringDevelopers",
|
||||
"spring.ldap.baseEnvironment.java.naming.security.authentication:DIGEST-MD5")
|
||||
.run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUserDn()).isEqualTo("root");
|
||||
assertThat(contextSource.getPassword()).isEqualTo("secret");
|
||||
assertThat(contextSource.isAnonymousReadOnly()).isTrue();
|
||||
assertThat(contextSource.getBaseLdapPathAsString()).isEqualTo("cn=SpringDevelopers");
|
||||
LdapProperties ldapProperties = context.getBean(LdapProperties.class);
|
||||
assertThat(ldapProperties.getBaseEnvironment()).containsEntry("java.naming.security.authentication",
|
||||
"DIGEST-MD5");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithNoCustomization() {
|
||||
this.contextRunner.run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUserDn()).isEmpty();
|
||||
assertThat(contextSource.getPassword()).isEmpty();
|
||||
assertThat(contextSource.isAnonymousReadOnly()).isTrue();
|
||||
assertThat(contextSource.getBaseLdapPathAsString()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void definesPropertiesBasedConnectionDetailsByDefault() {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesLdapConnectionDetails.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesCustomConnectionDetailsWhenDefined() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(LdapContextSource.class)
|
||||
.hasSingleBean(LdapConnectionDetails.class)
|
||||
.doesNotHaveBean(PropertiesLdapConnectionDetails.class);
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUrls()).isEqualTo(new String[] { "ldaps://ldap.example.com" });
|
||||
assertThat(contextSource.getBaseLdapName()).isEqualTo(LdapUtils.newLdapName("dc=base"));
|
||||
assertThat(contextSource.getUserDn()).isEqualTo("ldap-user");
|
||||
assertThat(contextSource.getPassword()).isEqualTo("ldap-password");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void objectDirectoryMapperExists() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:389").run((context) -> {
|
||||
assertThat(context).hasSingleBean(ObjectDirectoryMapper.class);
|
||||
ObjectDirectoryMapper objectDirectoryMapper = context.getBean(ObjectDirectoryMapper.class);
|
||||
assertThat(objectDirectoryMapper).extracting("converterManager")
|
||||
.extracting("conversionService", InstanceOfAssertFactories.type(ApplicationConversionService.class))
|
||||
.satisfies((conversionService) -> {
|
||||
assertThat(conversionService.canConvert(String.class, Name.class)).isTrue();
|
||||
assertThat(conversionService.canConvert(Name.class, String.class)).isTrue();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void templateExists() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:389").run((context) -> {
|
||||
assertThat(context).hasSingleBean(LdapTemplate.class);
|
||||
LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignorePartialResultException", false);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreNameNotFoundException", false);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreSizeLimitExceededException", true);
|
||||
assertThat(ldapTemplate).extracting("objectDirectoryMapper")
|
||||
.isSameAs(context.getBean(ObjectDirectoryMapper.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void templateCanBeConfiguredWithCustomObjectDirectoryMapper() {
|
||||
ObjectDirectoryMapper objectDirectoryMapper = mock(ObjectDirectoryMapper.class);
|
||||
this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:389")
|
||||
.withBean(ObjectDirectoryMapper.class, () -> objectDirectoryMapper)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(LdapTemplate.class);
|
||||
LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class);
|
||||
assertThat(ldapTemplate).extracting("objectDirectoryMapper").isSameAs(objectDirectoryMapper);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void templateConfigurationCanBeCustomized() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.urls:ldap://localhost:389",
|
||||
"spring.ldap.template.ignorePartialResultException=true",
|
||||
"spring.ldap.template.ignoreNameNotFoundException=true",
|
||||
"spring.ldap.template.ignoreSizeLimitExceededException=false")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(LdapTemplate.class);
|
||||
LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignorePartialResultException", true);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreNameNotFoundException", true);
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreSizeLimitExceededException", false);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithUserProvidedPooledContextSource() {
|
||||
this.contextRunner.withUserConfiguration(PooledContextSourceConfig.class).run((context) -> {
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource.getUrls()).containsExactly("ldap://localhost:389");
|
||||
assertThat(contextSource.isAnonymousReadOnly()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithCustomUniqueDirContextAuthenticationStrategy() {
|
||||
this.contextRunner.withUserConfiguration(CustomDirContextAuthenticationStrategy.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(DirContextAuthenticationStrategy.class);
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource).extracting("authenticationStrategy")
|
||||
.isSameAs(context.getBean("customDirContextAuthenticationStrategy"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextSourceWithCustomNonUniqueDirContextAuthenticationStrategy() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomDirContextAuthenticationStrategy.class,
|
||||
AnotherCustomDirContextAuthenticationStrategy.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasBean("customDirContextAuthenticationStrategy")
|
||||
.hasBean("anotherCustomDirContextAuthenticationStrategy");
|
||||
LdapContextSource contextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(contextSource).extracting("authenticationStrategy")
|
||||
.isNotSameAs(context.getBean("customDirContextAuthenticationStrategy"))
|
||||
.isNotSameAs(context.getBean("anotherCustomDirContextAuthenticationStrategy"))
|
||||
.isInstanceOf(SimpleDirContextAuthenticationStrategy.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
LdapConnectionDetails ldapConnectionDetails() {
|
||||
return new LdapConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String[] getUrls() {
|
||||
return new String[] { "ldaps://ldap.example.com" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBase() {
|
||||
return "dc=base";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "ldap-user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "ldap-password";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class PooledContextSourceConfig {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
PooledContextSource pooledContextSource(LdapContextSource ldapContextSource) {
|
||||
PooledContextSource pooledContextSource = new PooledContextSource(new PoolConfig());
|
||||
pooledContextSource.setContextSource(ldapContextSource);
|
||||
return pooledContextSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomDirContextAuthenticationStrategy {
|
||||
|
||||
@Bean
|
||||
DirContextAuthenticationStrategy customDirContextAuthenticationStrategy() {
|
||||
return mock(DirContextAuthenticationStrategy.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AnotherCustomDirContextAuthenticationStrategy {
|
||||
|
||||
@Bean
|
||||
DirContextAuthenticationStrategy anotherCustomDirContextAuthenticationStrategy() {
|
||||
return mock(DirContextAuthenticationStrategy.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.ldap.autoconfigure.LdapProperties.Template;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link LdapProperties}
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
class LdapPropertiesTests {
|
||||
|
||||
@Test
|
||||
void ldapTemplatePropertiesUseConsistentLdapTemplateDefaultValues() {
|
||||
Template templateProperties = new LdapProperties().getTemplate();
|
||||
LdapTemplate ldapTemplate = new LdapTemplate();
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignorePartialResultException",
|
||||
templateProperties.isIgnorePartialResultException());
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreNameNotFoundException",
|
||||
templateProperties.isIgnoreNameNotFoundException());
|
||||
assertThat(ldapTemplate).hasFieldOrPropertyWithValue("ignoreSizeLimitExceededException",
|
||||
templateProperties.isIgnoreSizeLimitExceededException());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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
|
||||
*
|
||||
* https://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.ldap.autoconfigure.embedded;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
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.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.ldap.autoconfigure.LdapAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithResource;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link EmbeddedLdapAutoConfiguration}
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class EmbeddedLdapAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(EmbeddedLdapAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void testSetDefaultPort() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.embedded.port:1234", "spring.ldap.embedded.base-dn:dc=spring,dc=org")
|
||||
.run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.getListenPort()).isEqualTo(1234);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRandomPortWithEnvironment() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org").run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.getListenPort())
|
||||
.isEqualTo(context.getEnvironment().getProperty("local.ldap.port", Integer.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRandomPortWithValueAnnotation() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("spring.ldap.embedded.base-dn:dc=spring,dc=org").applyTo(context);
|
||||
context.register(EmbeddedLdapAutoConfiguration.class, LdapClientConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
context.refresh();
|
||||
LDAPConnection connection = context.getBean(LDAPConnection.class);
|
||||
assertThat(connection.getConnectedPort())
|
||||
.isEqualTo(context.getEnvironment().getProperty("local.ldap.port", Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetCredentials() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org",
|
||||
"spring.ldap.embedded.credential.username:uid=root", "spring.ldap.embedded.credential.password:boot")
|
||||
.run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
BindResult result = server.bind("uid=root", "boot");
|
||||
assertThat(result).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetPartitionSuffix() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org").run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.getBaseDNs()).containsExactly(new DN("dc=spring,dc=org"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithSchemaLdifResource
|
||||
void testSetLdifFile() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org").run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org")).isEqualTo(5);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithSchemaLdifResource
|
||||
void testQueryEmbeddedLdap() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
|
||||
.withConfiguration(AutoConfigurations.of(LdapAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(LdapTemplate.class);
|
||||
LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class);
|
||||
assertThat(ldapTemplate.list("ou=company1,c=Sweden,dc=spring,dc=org")).hasSize(4);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableSchemaValidation() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.embedded.validation.enabled:false",
|
||||
"spring.ldap.embedded.base-dn:dc=spring,dc=org")
|
||||
.run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.getSchema()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithResource(name = "custom-schema.ldif", content = """
|
||||
dn: cn=schema
|
||||
attributeTypes: ( 1.3.6.1.4.1.32473.1.1.1
|
||||
NAME 'exampleAttributeName'
|
||||
DESC 'An example attribute type definition'
|
||||
EQUALITY caseIgnoreMatch
|
||||
ORDERING caseIgnoreOrderingMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE
|
||||
X-ORIGIN 'Managing Schema Document' )
|
||||
objectClasses: ( 1.3.6.1.4.1.32473.1.2.2
|
||||
NAME 'exampleAuxiliaryClass'
|
||||
DESC 'An example auxiliary object class definition'
|
||||
SUP top
|
||||
AUXILIARY
|
||||
MAY exampleAttributeName
|
||||
X-ORIGIN 'Managing Schema Document' )
|
||||
""")
|
||||
@WithResource(name = "custom-schema-sample.ldif", content = """
|
||||
dn: dc=spring,dc=org
|
||||
objectclass: top
|
||||
objectclass: domain
|
||||
objectclass: extensibleObject
|
||||
objectClass: exampleAuxiliaryClass
|
||||
dc: spring
|
||||
exampleAttributeName: exampleAttributeName
|
||||
""")
|
||||
void testCustomSchemaValidation() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.embedded.validation.schema:classpath:custom-schema.ldif",
|
||||
"spring.ldap.embedded.ldif:classpath:custom-schema-sample.ldif",
|
||||
"spring.ldap.embedded.base-dn:dc=spring,dc=org")
|
||||
.run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
|
||||
assertThat(server.getSchema().getObjectClass("exampleAuxiliaryClass")).isNotNull();
|
||||
assertThat(server.getSchema().getAttributeType("exampleAttributeName")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithResource(name = "schema-multi-basedn.ldif", content = """
|
||||
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
|
||||
|
||||
dn: dc=vmware,dc=com
|
||||
objectclass: top
|
||||
objectclass: domain
|
||||
objectclass: extensibleObject
|
||||
dc: vmware
|
||||
|
||||
dn: ou=groups,dc=vmware,dc=com
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: c=Sweden,dc=vmware,dc=com
|
||||
objectclass: top
|
||||
objectclass: country
|
||||
c: Sweden
|
||||
description:The country of Sweden
|
||||
|
||||
dn: cn=Some Random Person,c=Sweden,dc=vmware,dc=com
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: some.random.person
|
||||
userPassword: password
|
||||
cn: Some Random Person
|
||||
sn: Person
|
||||
description: Sweden, VMware, Some Random Person
|
||||
telephoneNumber: +46 555-123456
|
||||
""")
|
||||
void testMultiBaseDn() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.ldif:classpath:schema-multi-basedn.ldif",
|
||||
"spring.ldap.embedded.base-dn[0]:dc=spring,dc=org", "spring.ldap.embedded.base-dn[1]:dc=vmware,dc=com")
|
||||
.run((context) -> {
|
||||
InMemoryDirectoryServer server = context.getBean(InMemoryDirectoryServer.class);
|
||||
assertThat(server.countEntriesBelow("ou=company1,c=Sweden,dc=spring,dc=org")).isEqualTo(5);
|
||||
assertThat(server.countEntriesBelow("c=Sweden,dc=vmware,dc=com")).isEqualTo(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void ldapContextSourceWithCredentialsIsCreated() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org",
|
||||
"spring.ldap.embedded.credential.username:uid=root", "spring.ldap.embedded.credential.password:boot")
|
||||
.run((context) -> {
|
||||
LdapContextSource ldapContextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(ldapContextSource.getUrls()).isNotEmpty();
|
||||
assertThat(ldapContextSource.getUserDn()).isEqualTo("uid=root");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void ldapContextSourceWithoutCredentialsIsCreated() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org").run((context) -> {
|
||||
LdapContextSource ldapContextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(ldapContextSource.getUrls()).isNotEmpty();
|
||||
assertThat(ldapContextSource.getUserDn()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void ldapContextWithoutSpringLdapIsNotCreated() {
|
||||
this.contextRunner.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org")
|
||||
.withClassLoader(new FilteredClassLoader(ContextSource.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).hasNotFailed();
|
||||
assertThat(context).doesNotHaveBean(LdapContextSource.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void ldapContextIsCreatedWithBase() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.ldap.embedded.base-dn:dc=spring,dc=org", "spring.ldap.base:dc=spring,dc=org")
|
||||
.run((context) -> {
|
||||
LdapContextSource ldapContextSource = context.getBean(LdapContextSource.class);
|
||||
assertThat(ldapContextSource.getBaseLdapPathAsString()).isEqualTo("dc=spring,dc=org");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class LdapClientConfiguration {
|
||||
|
||||
@Bean
|
||||
LDAPConnection ldapConnection(@Value("${local.ldap.port}") int port) throws LDAPException {
|
||||
LDAPConnection con = new LDAPConnection();
|
||||
con.connect("localhost", port);
|
||||
return con;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@WithResource(name = "schema.ldif", content = """
|
||||
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
|
||||
""")
|
||||
@interface WithSchemaLdifResource {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user