diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailAccountCreationService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailAccountCreationService.java index f96870efaa9..5cac18629c3 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailAccountCreationService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailAccountCreationService.java @@ -1,38 +1,45 @@ package org.cloudfoundry.identity.uaa.account; import com.fasterxml.jackson.core.type.TypeReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.cloudfoundry.identity.uaa.codestore.ExpiringCode; import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.error.UaaException; import org.cloudfoundry.identity.uaa.message.MessageService; import org.cloudfoundry.identity.uaa.message.MessageType; +import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; +import org.cloudfoundry.identity.uaa.provider.NoSuchClientException; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceAlreadyExistsException; import org.cloudfoundry.identity.uaa.scim.util.ScimUtils; import org.cloudfoundry.identity.uaa.scim.validate.PasswordValidator; import org.cloudfoundry.identity.uaa.util.JsonUtils; -import org.cloudfoundry.identity.uaa.provider.NoSuchClientException; -import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.MergedZoneBrandingInformation; +import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; -import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; +import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.client.HttpClientErrorException; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.SpringTemplateEngine; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import static org.cloudfoundry.identity.uaa.codestore.ExpiringCodeType.REGISTRATION; import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.findMatchingRedirectUri; import static org.springframework.http.HttpStatus.BAD_REQUEST; +@Service public class EmailAccountCreationService implements AccountCreationService { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -48,7 +55,7 @@ public class EmailAccountCreationService implements AccountCreationService { private final IdentityZoneManager identityZoneManager; public EmailAccountCreationService( - SpringTemplateEngine templateEngine, + @Qualifier("mailTemplateEngine") SpringTemplateEngine templateEngine, MessageService messageService, ExpiringCodeStore codeStore, ScimUserProvisioning scimUserProvisioning, diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailChangeEmailService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailChangeEmailService.java index 44e84b6e0e2..92606560c19 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailChangeEmailService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/EmailChangeEmailService.java @@ -7,25 +7,32 @@ import org.cloudfoundry.identity.uaa.error.UaaException; import org.cloudfoundry.identity.uaa.message.MessageService; import org.cloudfoundry.identity.uaa.message.MessageType; +import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; +import org.cloudfoundry.identity.uaa.provider.NoSuchClientException; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; -import org.cloudfoundry.identity.uaa.provider.NoSuchClientException; import org.cloudfoundry.identity.uaa.zone.MergedZoneBrandingInformation; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; -import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import java.sql.Timestamp; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import static org.cloudfoundry.identity.uaa.codestore.ExpiringCodeType.EMAIL; import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.findMatchingRedirectUri; +@Service public class EmailChangeEmailService implements ChangeEmailService { static final String CHANGE_EMAIL_REDIRECT_URL = "change_email_redirect_url"; @@ -38,7 +45,8 @@ public class EmailChangeEmailService implements ChangeEmailService { private final MultitenantClientServices clientDetailsService; private final IdentityZoneManager identityZoneManager; - EmailChangeEmailService(final TemplateEngine templateEngine, + EmailChangeEmailService( + @Qualifier("mailTemplateEngine") final TemplateEngine templateEngine, final MessageService messageService, final ScimUserProvisioning scimUserProvisioning, final ExpiringCodeStore codeStore, diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java index 14eceac726a..5361a09ef33 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java @@ -28,6 +28,7 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; @@ -35,6 +36,7 @@ import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +@Service public class UaaChangePasswordService implements ChangePasswordService, ApplicationEventPublisherAware { private final ScimUserProvisioning scimUserProvisioning; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java index fcb60579871..312d3ab0506 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java @@ -27,6 +27,7 @@ import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; import java.sql.Timestamp; @@ -39,6 +40,7 @@ import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import static org.springframework.util.StringUtils.isEmpty; +@Service("resetPasswordService") public class UaaResetPasswordService implements ResetPasswordService, ApplicationEventPublisherAware { public static final int PASSWORD_RESET_LIFETIME = 30 * 60 * 1000; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LoginServerConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LoginServerConfig.java index d681f32fff7..83bea41f9b3 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LoginServerConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LoginServerConfig.java @@ -1,21 +1,138 @@ package org.cloudfoundry.identity.uaa.impl.config; import org.cloudfoundry.identity.uaa.message.EmailService; +import org.cloudfoundry.identity.uaa.message.LocalUaaRestTemplate; import org.cloudfoundry.identity.uaa.message.MessageService; +import org.cloudfoundry.identity.uaa.message.MessageType; import org.cloudfoundry.identity.uaa.message.NotificationsService; +import org.cloudfoundry.identity.uaa.message.util.FakeJavaMailSender; +import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Configuration for sending e-mails or HTTP-based notifications, e.g. on account creation. + *
+ * If a {@code notifications.url} property is defined, it uses HTTP-based notifications. + * Otherwise, if {@code smtp.*} properties are defined, it will send e-mails. If none of + * these properties are configured, it uses a {@link FakeJavaMailSender} mailer. + *
+ * All beans are marked lazy except {@link NotificationConfiguration#notificationMessageService},
+ * as they are all "fallback" options and do not need to be created when {@code notifications.url}
+ * is defined.
+ */
+@Lazy
@Configuration
+@EnableConfigurationProperties(SmtpProperties.class)
public class LoginServerConfig {
+ /**
+ * Fallback bean for when there is no "notifications.url".
+ * TODO: dgarnier annotate with @Fallback in Boot 3.4
+ *
+ * @return -
+ */
@Bean
- public MessageService messageService(EmailService emailService, NotificationsService notificationsService, Environment environment) {
- if (environment.getProperty("notifications.url") != null && !"".equals(environment.getProperty("notifications.url"))) {
- return notificationsService;
- } else {
- return emailService;
+ public MessageService emailMessageService(
+ // dgarnier: use DEFAULT_UAA_URL
+ @Value("${login.url:http://localhost:8080/uaa}") String loginUrl,
+ JavaMailSender mailSender,
+ SmtpProperties smtpProperties,
+ IdentityZoneManager identityZoneManager) {
+ return new EmailService(
+ mailSender,
+ loginUrl,
+ smtpProperties.fromAddress(),
+ identityZoneManager
+ );
+ }
+
+ /**
+ * Fallback for SMTP mail sender, when no real mail sender is used. This is mostly used in tests.
+ * TODO: dgarnier annotate with @Fallback in Boot 3.4
+ *
+ * @return -
+ */
+ @Bean
+ JavaMailSender fakeJavaMailSender() {
+ return new FakeJavaMailSender();
+ }
+
+ @Bean
+ @Primary
+ @ConditionalOnProperty(value = "smtp.host", matchIfMissing = false)
+ JavaMailSender smtpMailSender(SmtpProperties smtpProperties) {
+ var mailSender = new JavaMailSenderImpl();
+ mailSender.setHost(smtpProperties.host());
+ mailSender.setPort(smtpProperties.port());
+ mailSender.setPassword(smtpProperties.password());
+ mailSender.setUsername(smtpProperties.user());
+
+ var javaMailProperties = new Properties();
+ javaMailProperties.put("mail.smtp.auth", smtpProperties.auth());
+ javaMailProperties.put("mail.smtp.starttls.enable", smtpProperties.starttls());
+ javaMailProperties.put("mail.smtp.ssl.protocols", smtpProperties.sslprotocols());
+ mailSender.setJavaMailProperties(javaMailProperties);
+ return mailSender;
+ }
+
+ @Configuration
+ @ConditionalOnProperty(value = "notifications.url", matchIfMissing = false)
+ @EnableConfigurationProperties(NotificationsProperties.class)
+ static class NotificationConfiguration {
+
+ /**
+ * HTTP-based {@link MessageService}. Takes precedence over any email-basedO
+ * configuration.
+ *
+ * @param notificationsTemplate -
+ * @param notificationsProperties -
+ * @return -
+ */
+ @Bean
+ @Primary
+ public MessageService notificationMessageService(
+ LocalUaaRestTemplate notificationsTemplate,
+ NotificationsProperties notificationsProperties
+ ) {
+ return new NotificationsService(
+ notificationsTemplate,
+ notificationsProperties.url(),
+ notifications(),
+ notificationsProperties.sendInDefaultZone()
+ );
+ }
+
+ private static Map