@ -16,21 +16,32 @@
@@ -16,21 +16,32 @@
package org.springframework.boot.ldap.autoconfigure.embedded ;
import java.io.IOException ;
import java.io.InputStream ;
import java.security.KeyManagementException ;
import java.security.KeyStore ;
import java.security.KeyStoreException ;
import java.security.NoSuchAlgorithmException ;
import java.security.SecureRandom ;
import java.security.UnrecoverableKeyException ;
import java.security.cert.CertificateException ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import javax.net.ssl.KeyManager ;
import javax.net.ssl.KeyManagerFactory ;
import javax.net.ssl.SSLContext ;
import javax.net.ssl.SSLServerSocketFactory ;
import javax.net.ssl.SSLSocketFactory ;
import javax.net.ssl.TrustManager ;
import javax.net.ssl.TrustManagerFactory ;
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.ResultCode ;
import com.unboundid.ldap.sdk.schema.Schema ;
import com.unboundid.ldif.LDIFReader ;
import org.jspecify.annotations.Nullable ;
@ -38,12 +49,12 @@ import org.jspecify.annotations.Nullable;
@@ -38,12 +49,12 @@ import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.RuntimeHints ;
import org.springframework.aot.hint.RuntimeHintsRegistrar ;
import org.springframework.beans.factory.DisposableBean ;
import org.springframework.beans.factory.ObjectProvider ;
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.ConditionalOnBean ;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass ;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition ;
@ -53,6 +64,7 @@ import org.springframework.boot.context.properties.bind.Binder;
@@ -53,6 +64,7 @@ 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.boot.ssl.SslBundle ;
import org.springframework.boot.ssl.SslBundles ;
import org.springframework.context.ApplicationContext ;
import org.springframework.context.ConfigurableApplicationContext ;
@ -67,9 +79,12 @@ import org.springframework.core.env.MapPropertySource;
@@ -67,9 +79,12 @@ 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.io.ResourceLoader ;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver ;
import org.springframework.core.type.AnnotatedTypeMetadata ;
import org.springframework.ldap.core.ContextSource ;
import org.springframework.ldap.core.support.LdapContextSource ;
import org.springframework.util.Assert ;
import org.springframework.util.StringUtils ;
/ * *
@ -91,6 +106,8 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
@@ -91,6 +106,8 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
private final EmbeddedLdapProperties embeddedProperties ;
private final ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver ( ) ;
private @Nullable InMemoryDirectoryServer server ;
EmbeddedLdapAutoConfiguration ( EmbeddedLdapProperties embeddedProperties ) {
@ -98,7 +115,9 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
@@ -98,7 +115,9 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
}
@Bean
InMemoryDirectoryServer directoryServer ( ApplicationContext applicationContext ) throws LDAPException {
InMemoryDirectoryServer directoryServer ( ApplicationContext applicationContext , ObjectProvider < SslBundles > sslBundles )
throws LDAPException , KeyStoreException , IOException ,
NoSuchAlgorithmException , CertificateException , UnrecoverableKeyException , KeyManagementException {
String [ ] baseDn = StringUtils . toStringArray ( this . embeddedProperties . getBaseDn ( ) ) ;
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig ( baseDn ) ;
String username = this . embeddedProperties . getCredential ( ) . getUsername ( ) ;
@ -107,8 +126,13 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
@@ -107,8 +126,13 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
config . addAdditionalBindCredentials ( username , password ) ;
}
setSchema ( config ) ;
if ( this . embeddedProperties . isLdaps ( ) ) {
this . setLdapsListener ( applicationContext , config ) ;
if ( this . embeddedProperties . getSsl ( ) . isEnabled ( ) ) {
EmbeddedLdapProperties . Ssl ssl = this . embeddedProperties . getSsl ( ) ;
SSLContext sslContext = getSslContext ( ssl , sslBundles . getIfAvailable ( ) ) ;
SSLServerSocketFactory serverSocketFactory = sslContext . getServerSocketFactory ( ) ;
SSLSocketFactory clientSocketFactory = sslContext . getSocketFactory ( ) ;
config . setListenerConfigs ( InMemoryListenerConfig . createLDAPSConfig ( "LDAPS" , null ,
this . embeddedProperties . getPort ( ) , serverSocketFactory , clientSocketFactory ) ) ;
}
else {
config
@ -148,22 +172,6 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
@@ -148,22 +172,6 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
}
}
@ConditionalOnBean ( SslBundles . class )
private void setLdapsListener ( ApplicationContext applicationContext , InMemoryDirectoryServerConfig config )
throws LDAPException {
if ( StringUtils . hasText ( this . embeddedProperties . getSslBundleName ( ) ) ) {
SslBundles sslBundles = applicationContext . getBean ( SslBundles . class ) ;
SSLContext sslContext = sslBundles . getBundle ( this . embeddedProperties . getSslBundleName ( ) ) . createSslContext ( ) ;
SSLServerSocketFactory serverSocketFactory = sslContext . getServerSocketFactory ( ) ;
SSLSocketFactory clientSocketFactory = sslContext . getSocketFactory ( ) ;
config . setListenerConfigs ( InMemoryListenerConfig . createLDAPSConfig ( "LDAPS" , null ,
this . embeddedProperties . getPort ( ) , serverSocketFactory , clientSocketFactory ) ) ;
}
else {
throw new LDAPException ( ResultCode . PARAM_ERROR , "SslBundleName property not specified" ) ;
}
}
private void importLdif ( InMemoryDirectoryServer server , ApplicationContext applicationContext ) {
String location = this . embeddedProperties . getLdif ( ) ;
if ( StringUtils . hasText ( location ) ) {
@ -208,6 +216,71 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
@@ -208,6 +216,71 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
}
}
private SSLContext getSslContext ( EmbeddedLdapProperties . Ssl ssl , @Nullable SslBundles sslBundles )
throws KeyStoreException , IOException , NoSuchAlgorithmException , CertificateException ,
UnrecoverableKeyException , KeyManagementException {
if ( sslBundles ! = null & & StringUtils . hasText ( ssl . getBundle ( ) ) ) {
SslBundle sslBundle = sslBundles . getBundle ( ssl . getBundle ( ) ) ;
Assert . notNull ( sslBundle , "SSL bundle name has been set but no SSL bundles found in context" ) ;
return sslBundle . createSslContext ( ) ;
}
else {
Assert . notNull ( ssl . getAlgorithm ( ) , "SSL algorithm must be specified" ) ;
SSLContext sslContext = SSLContext . getInstance ( ssl . getAlgorithm ( ) ) ;
KeyManager [ ] keyManagers = configureKeyManagers ( ssl ) ;
TrustManager [ ] trustManagers = configureTrustManagers ( ssl ) ;
sslContext . init ( keyManagers , trustManagers , new SecureRandom ( ) ) ;
return sslContext ;
}
}
private KeyManager @Nullable [ ] configureKeyManagers ( EmbeddedLdapProperties . Ssl ssl ) throws KeyStoreException ,
IOException , NoSuchAlgorithmException , CertificateException , UnrecoverableKeyException {
String keyStoreName = ssl . getKeyStore ( ) ;
String keyStorePassword = ssl . getKeyStorePassword ( ) ;
String storeType = ssl . getKeyStoreType ( ) ;
char [ ] keyPassphrase = null ;
if ( keyStorePassword ! = null ) {
keyPassphrase = keyStorePassword . toCharArray ( ) ;
}
KeyManager [ ] keyManagers = null ;
if ( StringUtils . hasText ( keyStoreName ) ) {
Resource resource = this . resourceLoader . getResource ( keyStoreName ) ;
KeyStore ks = KeyStore . getInstance ( storeType ) ;
try ( InputStream inputStream = resource . getInputStream ( ) ) {
ks . load ( inputStream , keyPassphrase ) ;
}
KeyManagerFactory kmf = KeyManagerFactory . getInstance ( ssl . getKeyStoreAlgorithm ( ) ) ;
kmf . init ( ks , keyPassphrase ) ;
keyManagers = kmf . getKeyManagers ( ) ;
}
return keyManagers ;
}
private TrustManager @Nullable [ ] configureTrustManagers ( EmbeddedLdapProperties . Ssl ssl )
throws KeyStoreException , IOException , NoSuchAlgorithmException , CertificateException {
String trustStoreName = ssl . getTrustStore ( ) ;
String trustStorePassword = ssl . getTrustStorePassword ( ) ;
String storeType = ssl . getTrustStoreType ( ) ;
char [ ] trustPassphrase = null ;
if ( trustStorePassword ! = null ) {
trustPassphrase = trustStorePassword . toCharArray ( ) ;
}
TrustManager [ ] trustManagers = null ;
if ( StringUtils . hasText ( trustStoreName ) ) {
Resource resource = this . resourceLoader . getResource ( trustStoreName ) ;
KeyStore tks = KeyStore . getInstance ( storeType ) ;
try ( InputStream inputStream = resource . getInputStream ( ) ) {
tks . load ( inputStream , trustPassphrase ) ;
}
TrustManagerFactory tmf = TrustManagerFactory . getInstance ( ssl . getTrustStoreAlgorithm ( ) ) ;
tmf . init ( tks ) ;
trustManagers = tmf . getTrustManagers ( ) ;
}
return trustManagers ;
}
/ * *
* { @link SpringBootCondition } to determine when to apply embedded LDAP
* auto - configuration .