diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java index 205af4c3e0ac..164ff6fbfe6e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -26,13 +26,18 @@ import org.springframework.boot.autoconfigure.ldap.LdapProperties.Template; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.boot.convert.ApplicationConversionService; import org.springframework.context.annotation.Bean; +import org.springframework.core.convert.ConversionService; 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. @@ -72,10 +77,12 @@ public LdapContextSource ldapContextSource(LdapConnectionDetails connectionDetai @Bean @ConditionalOnMissingBean(LdapOperations.class) - public LdapTemplate ldapTemplate(LdapProperties properties, ContextSource contextSource) { + 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); @@ -84,4 +91,18 @@ public LdapTemplate ldapTemplate(LdapProperties properties, ContextSource contex return ldapTemplate; } + @Bean + @ConditionalOnMissingBean + public ObjectDirectoryMapper objectDirectoryMapper() { + DefaultObjectDirectoryMapper objectDirectoryMapper = new DefaultObjectDirectoryMapper(); + objectDirectoryMapper.setConversionService(createConversionService()); + return objectDirectoryMapper; + } + + private static ConversionService createConversionService() { + ApplicationConversionService conversionService = new ApplicationConversionService(); + ConverterUtils.addDefaultConverters(conversionService); + return conversionService; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java index 2d51f84a9b7b..43d4434d9143 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ldap/LdapAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -16,9 +16,13 @@ package org.springframework.boot.autoconfigure.ldap; +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; @@ -27,6 +31,7 @@ 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; @@ -194,6 +199,36 @@ void contextSourceWithCustomNonUniqueDirContextAuthenticationStrategy() { }); } + @Test + void objectDirectoryMapperBeanAutoConfigured() { + this.contextRunner.withPropertyValues("spring.ldap.urls:ldap://localhost:389").run((context) -> { + assertThat(context).hasSingleBean(ObjectDirectoryMapper.class); + assertThat(context).hasSingleBean(LdapTemplate.class); + ObjectDirectoryMapper objectDirectoryMapper = context.getBean(ObjectDirectoryMapper.class); + LdapTemplate ldapTemplate = context.getBean(LdapTemplate.class); + ApplicationConversionService conversionService = assertThat(objectDirectoryMapper) + .extracting("converterManager") + .extracting("conversionService") + .asInstanceOf(InstanceOfAssertFactories.type(ApplicationConversionService.class)) + .actual(); + assertThat(conversionService.canConvert(String.class, Name.class)).isTrue(); + assertThat(conversionService.canConvert(Name.class, String.class)).isTrue(); + assertThat(ldapTemplate).extracting("objectDirectoryMapper").isSameAs(objectDirectoryMapper); + }); + } + + @Test + void customObjectDirectoryMapperBeanCanBeUsed() { + 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); + }); + } + @Configuration(proxyBeanMethods = false) static class ConnectionDetailsConfiguration {