diff --git a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationIntegrationTests.java index 93453cc99fb..61a6518c38e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -68,11 +68,11 @@ class MailSenderAutoConfigurationIntegrationTests { return message.getSubject(); } catch (MessagingException ex) { - throw new RuntimeException(ex); + throw new RuntimeException("Failed to get message subject", ex); } } - private void assertMessagesContainSubject(Session session, String subject) { + private void assertMessagesContainSubject(Session session, String subject) throws MessagingException { try (Store store = session.getStore("pop3")) { String host = session.getProperty("mail.pop3.host"); int port = Integer.parseInt(session.getProperty("mail.pop3.port")); @@ -86,20 +86,13 @@ class MailSenderAutoConfigurationIntegrationTests { .contains(subject)); } } - catch (MessagingException ex) { - throw new RuntimeException(ex); - } } @Nested class ImplicitTlsTests { - final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class, SslAutoConfiguration.class)); - @Container private static final MailpitContainer mailpit = TestImage.container(MailpitContainer.class) - // force ssl connection .withSmtpRequireTls(true) .withSmtpTlsCert(MountableFile .forClasspathResource("/org/springframework/boot/autoconfigure/mail/ssl/test-server.crt")) @@ -107,6 +100,9 @@ class MailSenderAutoConfigurationIntegrationTests { .forClasspathResource("/org/springframework/boot/autoconfigure/mail/ssl/test-server.key")) .withPop3Auth("user:pass"); + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class, SslAutoConfiguration.class)); + @Test void sendEmailWithSslEnabledAndCert() { this.contextRunner.withPropertyValues("spring.mail.host:" + mailpit.getHost(), @@ -115,14 +111,11 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.ssl.bundle.pem.test-bundle.truststore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-ca.crt", "spring.ssl.bundle.pem.test-bundle.keystore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.crt", "spring.ssl.bundle.pem.test-bundle.keystore.private-key=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.key", - // pop3 "spring.mail.properties.mail.pop3.host:" + mailpit.getHost(), "spring.mail.properties.mail.pop3.port:" + mailpit.getPop3Port()) .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - mailSender.send(createMessage("Hello World!")); - assertMessagesContainSubject(mailSender.getSession(), "Hello World!"); }); } @@ -134,7 +127,6 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.mail.port:" + mailpit.getSmtpPort(), "spring.mail.ssl.enabled:true") .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - assertThatException().isThrownBy(() -> mailSender.send(createMessage("Should fail"))) .withRootCauseInstanceOf(CertPathBuilderException.class); }); @@ -150,7 +142,6 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.ssl.bundle.pem.test-bundle.keystore.private-key=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.key") .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - assertThatException().isThrownBy(() -> mailSender.send(createMessage("Should fail"))) .withRootCauseInstanceOf(SocketTimeoutException.class); }); @@ -161,9 +152,6 @@ class MailSenderAutoConfigurationIntegrationTests { @Nested class StarttlsTests { - final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class, SslAutoConfiguration.class)); - @Container private static final MailpitContainer mailpit = TestImage.container(MailpitContainer.class) .withSmtpRequireStarttls(true) @@ -173,6 +161,9 @@ class MailSenderAutoConfigurationIntegrationTests { .forClasspathResource("/org/springframework/boot/autoconfigure/mail/ssl/test-server.key")) .withPop3Auth("user:pass"); + final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class, SslAutoConfiguration.class)); + @Test void sendEmailWithStarttlsAndCertAndSslDisabled() { this.contextRunner.withPropertyValues("spring.mail.host:" + mailpit.getHost(), @@ -182,14 +173,11 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.ssl.bundle.pem.test-bundle.truststore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-ca.crt", "spring.ssl.bundle.pem.test-bundle.keystore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.crt", "spring.ssl.bundle.pem.test-bundle.keystore.private-key=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.key", - // pop3 "spring.mail.properties.mail.pop3.host:" + mailpit.getHost(), "spring.mail.properties.mail.pop3.port:" + mailpit.getPop3Port()) .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - mailSender.send(createMessage("Sent with STARTTLS")); - assertMessagesContainSubject(mailSender.getSession(), "Sent with STARTTLS"); }); } @@ -203,12 +191,10 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.ssl.bundle.pem.test-bundle.truststore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-ca.crt", "spring.ssl.bundle.pem.test-bundle.keystore.certificate=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.crt", "spring.ssl.bundle.pem.test-bundle.keystore.private-key=classpath:org/springframework/boot/autoconfigure/mail/ssl/test-client.key", - // pop3 "spring.mail.properties.mail.pop3.host:" + mailpit.getHost(), "spring.mail.properties.mail.pop3.port:" + mailpit.getPop3Port()) .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - assertThatException().isThrownBy(() -> mailSender.send(createMessage("Should fail"))) .withRootCauseInstanceOf(SSLException.class); }); @@ -223,7 +209,6 @@ class MailSenderAutoConfigurationIntegrationTests { "spring.mail.properties.mail.smtp.starttls.required:true") .run((context) -> { JavaMailSenderImpl mailSender = context.getBean(JavaMailSenderImpl.class); - assertThatException().isThrownBy(() -> mailSender.send(createMessage("Should fail"))) .withRootCauseInstanceOf(CertPathBuilderException.class); }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderPropertiesConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderPropertiesConfiguration.java index e41b8fd397f..7466dcd9d77 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderPropertiesConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderPropertiesConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -22,6 +22,7 @@ import java.util.Properties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.mail.MailProperties.Ssl; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; @@ -29,6 +30,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.mail.MailSender; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.util.StringUtils; /** * Auto-configure a {@link MailSender} based on properties configuration. @@ -41,21 +43,15 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; @ConditionalOnProperty(prefix = "spring.mail", name = "host") class MailSenderPropertiesConfiguration { - private final ObjectProvider sslBundles; - - MailSenderPropertiesConfiguration(ObjectProvider sslBundles) { - this.sslBundles = sslBundles; - } - @Bean @ConditionalOnMissingBean(JavaMailSender.class) - JavaMailSenderImpl mailSender(MailProperties properties) { + JavaMailSenderImpl mailSender(MailProperties properties, ObjectProvider sslBundles) { JavaMailSenderImpl sender = new JavaMailSenderImpl(); - applyProperties(properties, sender); + applyProperties(properties, sender, sslBundles.getIfAvailable()); return sender; } - private void applyProperties(MailProperties properties, JavaMailSenderImpl sender) { + private void applyProperties(MailProperties properties, JavaMailSenderImpl sender, SslBundles sslBundles) { sender.setHost(properties.getHost()); if (properties.getPort() != null) { sender.setPort(properties.getPort()); @@ -68,14 +64,15 @@ class MailSenderPropertiesConfiguration { } Properties javaMailProperties = asProperties(properties.getProperties()); String protocol = properties.getProtocol(); - if (protocol == null || protocol.isEmpty()) { + if (!StringUtils.hasLength(protocol)) { protocol = "smtp"; } - if (properties.getSsl().isEnabled()) { + Ssl ssl = properties.getSsl(); + if (ssl.isEnabled()) { javaMailProperties.setProperty("mail." + protocol + ".ssl.enable", "true"); } - if (properties.getSsl().getBundle() != null) { - SslBundle sslBundle = this.sslBundles.getObject().getBundle(properties.getSsl().getBundle()); + if (ssl.getBundle() != null) { + SslBundle sslBundle = sslBundles.getBundle(ssl.getBundle()); javaMailProperties.put("mail." + protocol + ".ssl.socketFactory", sslBundle.createSslContext().getSocketFactory()); }