1616
1717package org .springframework .boot .ldap .autoconfigure .embedded ;
1818
19+ import java .io .IOException ;
1920import java .io .InputStream ;
21+ import java .security .KeyManagementException ;
22+ import java .security .KeyStore ;
23+ import java .security .KeyStoreException ;
24+ import java .security .NoSuchAlgorithmException ;
25+ import java .security .SecureRandom ;
26+ import java .security .UnrecoverableKeyException ;
27+ import java .security .cert .CertificateException ;
2028import java .util .Collections ;
2129import java .util .HashMap ;
2230import java .util .List ;
2331import java .util .Map ;
2432
33+ import javax .net .ssl .KeyManager ;
34+ import javax .net .ssl .KeyManagerFactory ;
2535import javax .net .ssl .SSLContext ;
2636import javax .net .ssl .SSLServerSocketFactory ;
2737import javax .net .ssl .SSLSocketFactory ;
38+ import javax .net .ssl .TrustManager ;
39+ import javax .net .ssl .TrustManagerFactory ;
2840
2941import com .unboundid .ldap .listener .InMemoryDirectoryServer ;
3042import com .unboundid .ldap .listener .InMemoryDirectoryServerConfig ;
3143import com .unboundid .ldap .listener .InMemoryListenerConfig ;
3244import com .unboundid .ldap .sdk .LDAPException ;
33- import com .unboundid .ldap .sdk .ResultCode ;
3445import com .unboundid .ldap .sdk .schema .Schema ;
3546import com .unboundid .ldif .LDIFReader ;
3647import org .jspecify .annotations .Nullable ;
3748
3849import org .springframework .aot .hint .RuntimeHints ;
3950import org .springframework .aot .hint .RuntimeHintsRegistrar ;
4051import org .springframework .beans .factory .DisposableBean ;
52+ import org .springframework .beans .factory .ObjectProvider ;
4153import org .springframework .boot .autoconfigure .AutoConfiguration ;
4254import org .springframework .boot .autoconfigure .EnableAutoConfiguration ;
4355import org .springframework .boot .autoconfigure .condition .ConditionMessage ;
4456import org .springframework .boot .autoconfigure .condition .ConditionMessage .Builder ;
4557import org .springframework .boot .autoconfigure .condition .ConditionOutcome ;
46- import org .springframework .boot .autoconfigure .condition .ConditionalOnBean ;
4758import org .springframework .boot .autoconfigure .condition .ConditionalOnClass ;
4859import org .springframework .boot .autoconfigure .condition .ConditionalOnMissingBean ;
4960import org .springframework .boot .autoconfigure .condition .SpringBootCondition ;
5364import org .springframework .boot .ldap .autoconfigure .LdapAutoConfiguration ;
5465import org .springframework .boot .ldap .autoconfigure .LdapProperties ;
5566import org .springframework .boot .ldap .autoconfigure .embedded .EmbeddedLdapAutoConfiguration .EmbeddedLdapAutoConfigurationRuntimeHints ;
67+ import org .springframework .boot .ssl .SslBundle ;
5668import org .springframework .boot .ssl .SslBundles ;
5769import org .springframework .context .ApplicationContext ;
5870import org .springframework .context .ConfigurableApplicationContext ;
6779import org .springframework .core .env .MutablePropertySources ;
6880import org .springframework .core .env .PropertySource ;
6981import org .springframework .core .io .Resource ;
82+ import org .springframework .core .io .ResourceLoader ;
83+ import org .springframework .core .io .support .PathMatchingResourcePatternResolver ;
7084import org .springframework .core .type .AnnotatedTypeMetadata ;
7185import org .springframework .ldap .core .ContextSource ;
7286import org .springframework .ldap .core .support .LdapContextSource ;
87+ import org .springframework .util .Assert ;
7388import org .springframework .util .StringUtils ;
7489
7590/**
@@ -91,14 +106,18 @@ public final class EmbeddedLdapAutoConfiguration implements DisposableBean {
91106
92107 private final EmbeddedLdapProperties embeddedProperties ;
93108
109+ private final ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver ();
110+
94111 private @ Nullable InMemoryDirectoryServer server ;
95112
96113 EmbeddedLdapAutoConfiguration (EmbeddedLdapProperties embeddedProperties ) {
97114 this .embeddedProperties = embeddedProperties ;
98115 }
99116
100117 @ Bean
101- InMemoryDirectoryServer directoryServer (ApplicationContext applicationContext ) throws LDAPException {
118+ InMemoryDirectoryServer directoryServer (ApplicationContext applicationContext , ObjectProvider <SslBundles > sslBundles )
119+ throws LDAPException , KeyStoreException , IOException ,
120+ NoSuchAlgorithmException , CertificateException , UnrecoverableKeyException , KeyManagementException {
102121 String [] baseDn = StringUtils .toStringArray (this .embeddedProperties .getBaseDn ());
103122 InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig (baseDn );
104123 String username = this .embeddedProperties .getCredential ().getUsername ();
@@ -107,8 +126,13 @@ InMemoryDirectoryServer directoryServer(ApplicationContext applicationContext) t
107126 config .addAdditionalBindCredentials (username , password );
108127 }
109128 setSchema (config );
110- if (this .embeddedProperties .isLdaps ()) {
111- this .setLdapsListener (applicationContext , config );
129+ if (this .embeddedProperties .getSsl ().isEnabled ()) {
130+ EmbeddedLdapProperties .Ssl ssl = this .embeddedProperties .getSsl ();
131+ SSLContext sslContext = getSslContext (ssl , sslBundles .getIfAvailable ());
132+ SSLServerSocketFactory serverSocketFactory = sslContext .getServerSocketFactory ();
133+ SSLSocketFactory clientSocketFactory = sslContext .getSocketFactory ();
134+ config .setListenerConfigs (InMemoryListenerConfig .createLDAPSConfig ("LDAPS" , null ,
135+ this .embeddedProperties .getPort (), serverSocketFactory , clientSocketFactory ));
112136 }
113137 else {
114138 config
@@ -148,22 +172,6 @@ private void setSchema(InMemoryDirectoryServerConfig config, Resource resource)
148172 }
149173 }
150174
151- @ ConditionalOnBean (SslBundles .class )
152- private void setLdapsListener (ApplicationContext applicationContext , InMemoryDirectoryServerConfig config )
153- throws LDAPException {
154- if (StringUtils .hasText (this .embeddedProperties .getSslBundleName ())) {
155- SslBundles sslBundles = applicationContext .getBean (SslBundles .class );
156- SSLContext sslContext = sslBundles .getBundle (this .embeddedProperties .getSslBundleName ()).createSslContext ();
157- SSLServerSocketFactory serverSocketFactory = sslContext .getServerSocketFactory ();
158- SSLSocketFactory clientSocketFactory = sslContext .getSocketFactory ();
159- config .setListenerConfigs (InMemoryListenerConfig .createLDAPSConfig ("LDAPS" , null ,
160- this .embeddedProperties .getPort (), serverSocketFactory , clientSocketFactory ));
161- }
162- else {
163- throw new LDAPException (ResultCode .PARAM_ERROR , "SslBundleName property not specified" );
164- }
165- }
166-
167175 private void importLdif (InMemoryDirectoryServer server , ApplicationContext applicationContext ) {
168176 String location = this .embeddedProperties .getLdif ();
169177 if (StringUtils .hasText (location )) {
@@ -208,6 +216,71 @@ public void destroy() throws Exception {
208216 }
209217 }
210218
219+ private SSLContext getSslContext (EmbeddedLdapProperties .Ssl ssl , @ Nullable SslBundles sslBundles )
220+ throws KeyStoreException , IOException , NoSuchAlgorithmException , CertificateException ,
221+ UnrecoverableKeyException , KeyManagementException {
222+ if (sslBundles != null && StringUtils .hasText (ssl .getBundle ())) {
223+ SslBundle sslBundle = sslBundles .getBundle (ssl .getBundle ());
224+ Assert .notNull (sslBundle , "SSL bundle name has been set but no SSL bundles found in context" );
225+ return sslBundle .createSslContext ();
226+
227+ }
228+ else {
229+ Assert .notNull (ssl .getAlgorithm (), "SSL algorithm must be specified" );
230+ SSLContext sslContext = SSLContext .getInstance (ssl .getAlgorithm ());
231+ KeyManager [] keyManagers = configureKeyManagers (ssl );
232+ TrustManager [] trustManagers = configureTrustManagers (ssl );
233+ sslContext .init (keyManagers , trustManagers , new SecureRandom ());
234+ return sslContext ;
235+ }
236+ }
237+
238+ private KeyManager @ Nullable [] configureKeyManagers (EmbeddedLdapProperties .Ssl ssl ) throws KeyStoreException ,
239+ IOException , NoSuchAlgorithmException , CertificateException , UnrecoverableKeyException {
240+ String keyStoreName = ssl .getKeyStore ();
241+ String keyStorePassword = ssl .getKeyStorePassword ();
242+ String storeType = ssl .getKeyStoreType ();
243+ char [] keyPassphrase = null ;
244+ if (keyStorePassword != null ) {
245+ keyPassphrase = keyStorePassword .toCharArray ();
246+ }
247+ KeyManager [] keyManagers = null ;
248+ if (StringUtils .hasText (keyStoreName )) {
249+ Resource resource = this .resourceLoader .getResource (keyStoreName );
250+ KeyStore ks = KeyStore .getInstance (storeType );
251+ try (InputStream inputStream = resource .getInputStream ()) {
252+ ks .load (inputStream , keyPassphrase );
253+ }
254+ KeyManagerFactory kmf = KeyManagerFactory .getInstance (ssl .getKeyStoreAlgorithm ());
255+ kmf .init (ks , keyPassphrase );
256+ keyManagers = kmf .getKeyManagers ();
257+ }
258+ return keyManagers ;
259+ }
260+
261+ private TrustManager @ Nullable [] configureTrustManagers (EmbeddedLdapProperties .Ssl ssl )
262+ throws KeyStoreException , IOException , NoSuchAlgorithmException , CertificateException {
263+ String trustStoreName = ssl .getTrustStore ();
264+ String trustStorePassword = ssl .getTrustStorePassword ();
265+ String storeType = ssl .getTrustStoreType ();
266+ char [] trustPassphrase = null ;
267+ if (trustStorePassword != null ) {
268+ trustPassphrase = trustStorePassword .toCharArray ();
269+ }
270+ TrustManager [] trustManagers = null ;
271+ if (StringUtils .hasText (trustStoreName )) {
272+ Resource resource = this .resourceLoader .getResource (trustStoreName );
273+ KeyStore tks = KeyStore .getInstance (storeType );
274+ try (InputStream inputStream = resource .getInputStream ()) {
275+ tks .load (inputStream , trustPassphrase );
276+ }
277+ TrustManagerFactory tmf = TrustManagerFactory .getInstance (ssl .getTrustStoreAlgorithm ());
278+ tmf .init (tks );
279+ trustManagers = tmf .getTrustManagers ();
280+ }
281+ return trustManagers ;
282+ }
283+
211284 /**
212285 * {@link SpringBootCondition} to determine when to apply embedded LDAP
213286 * auto-configuration.
0 commit comments