import org.sonar.process.logging.LogbackHelper;
import org.sonar.server.component.index.EntityDefinitionIndexer;
import org.sonar.server.config.ConfigurationProvider;
+import org.sonar.server.email.EmailSmtpConfiguration;
import org.sonar.server.es.EsModule;
import org.sonar.server.es.IndexersImpl;
import org.sonar.server.extension.CoreExtensionBootstraper;
// Notifications
QGChangeEmailTemplate.class,
EmailSettings.class,
+ EmailSmtpConfiguration.class,
NotificationService.class,
DefaultNotificationManager.class,
EmailNotificationChannel.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.email;
+
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+
+@ServerSide
+@ComputeEngineSide
+public class EmailSmtpConfiguration {
+ // Common configuration
+ public static final String EMAIL_CONFIG_SMTP_HOST = "email.smtp_host.secured";
+ public static final String EMAIL_CONFIG_SMTP_HOST_DEFAULT = "";
+ public static final String EMAIL_CONFIG_SMTP_PORT = "email.smtp_port.secured";
+ public static final String EMAIL_CONFIG_SMTP_PORT_DEFAULT = "25";
+ public static final String EMAIL_CONFIG_SMTP_SECURE_CONNECTION = "email.smtp_secure_connection.secured";
+ public static final String EMAIL_CONFIG_SMTP_SECURE_CONNECTION_DEFAULT = "";
+ public static final String EMAIL_CONFIG_SMTP_AUTH_METHOD= "email.smtp.auth.method";
+ public static final String EMAIL_CONFIG_SMTP_AUTH_METHOD_DEFAULT = "BASIC";
+ // Email content
+ public static final String EMAIL_CONFIG_FROM = "email.from";
+ public static final String EMAIL_CONFIG_FROM_DEFAULT = "noreply@nowhere";
+ public static final String EMAIL_CONFIG_FROM_NAME = "email.fromName";
+ public static final String EMAIL_CONFIG_FROM_NAME_DEFAULT = "SonarQube";
+ public static final String EMAIL_CONFIG_PREFIX = "email.prefix";
+ public static final String EMAIL_CONFIG_PREFIX_DEFAULT = "[SONARQUBE]";
+ // Basic Auth
+ public static final String EMAIL_CONFIG_SMTP_USERNAME = "email.smtp_username.secured";
+ public static final String EMAIL_CONFIG_SMTP_USERNAME_DEFAULT = "";
+ public static final String EMAIL_CONFIG_SMTP_PASSWORD = "email.smtp_password.secured";
+ public static final String EMAIL_CONFIG_SMTP_PASSWORD_DEFAULT = "";
+ // Modern auth
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_HOST = "email.smtp.oauth.host";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_HOST_DEFAULT = "https://login.microsoftonline.com";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_TENANT = "email.smtp.oauth.tenant";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_CLIENTID = "email.smtp.oauth.clientId";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_CLIENTSECRET = "email.smtp.oauth.clientSecret";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_SCOPE = "email.smtp.oauth.scope";
+ public static final String EMAIL_CONFIG_SMTP_OAUTH_SCOPE_DEFAULT = "client_credentials";
+
+ private final DbClient dbClient;
+
+ public EmailSmtpConfiguration(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ public String getSmtpHost() {
+ return get(EMAIL_CONFIG_SMTP_HOST, EMAIL_CONFIG_SMTP_HOST_DEFAULT);
+ }
+
+ public int getSmtpPort() {
+ return Integer.parseInt(get(EMAIL_CONFIG_SMTP_PORT, EMAIL_CONFIG_SMTP_PORT_DEFAULT));
+ }
+
+ public String getSecureConnection() {
+ return get(EMAIL_CONFIG_SMTP_SECURE_CONNECTION, EMAIL_CONFIG_SMTP_SECURE_CONNECTION_DEFAULT);
+ }
+
+ public String getAuthMethod() {
+ return get(EMAIL_CONFIG_SMTP_AUTH_METHOD, EMAIL_CONFIG_SMTP_AUTH_METHOD_DEFAULT);
+ }
+
+ public String getFrom() {
+ return get(EMAIL_CONFIG_FROM, EMAIL_CONFIG_FROM_DEFAULT);
+ }
+
+ public String getFromName() {
+ return get(EMAIL_CONFIG_FROM_NAME, EMAIL_CONFIG_FROM_NAME_DEFAULT);
+ }
+
+ public String getPrefix() {
+ return get(EMAIL_CONFIG_PREFIX, EMAIL_CONFIG_PREFIX_DEFAULT);
+ }
+
+ public String getSmtpUsername() {
+ return get(EMAIL_CONFIG_SMTP_USERNAME, EMAIL_CONFIG_SMTP_USERNAME_DEFAULT);
+ }
+
+ public String getSmtpPassword() {
+ return get(EMAIL_CONFIG_SMTP_PASSWORD, EMAIL_CONFIG_SMTP_PASSWORD_DEFAULT);
+ }
+
+ public String getOAuthHost() {
+ return get(EMAIL_CONFIG_SMTP_OAUTH_HOST, EMAIL_CONFIG_SMTP_OAUTH_HOST_DEFAULT);
+ }
+
+ public String getOAuthTenant() {
+ return get(EMAIL_CONFIG_SMTP_OAUTH_TENANT, "");
+ }
+
+ public String getOAuthClientId() {
+ return get(EMAIL_CONFIG_SMTP_OAUTH_CLIENTID, "");
+ }
+
+ public String getOAuthClientSecret() {
+ return get(EMAIL_CONFIG_SMTP_OAUTH_CLIENTSECRET, "");
+ }
+
+ public String getOAuthScope() {
+ return get(EMAIL_CONFIG_SMTP_OAUTH_SCOPE, EMAIL_CONFIG_SMTP_OAUTH_SCOPE_DEFAULT);
+ }
+
+ private String get(String key, String defaultValue) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return dbClient.internalPropertiesDao().selectByKey(dbSession, key).orElse(defaultValue);
+ }
+ }
+
+}
import org.apache.commons.mail.SimpleEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.config.EmailSettings;
import org.sonar.api.notifications.Notification;
+import org.sonar.api.platform.Server;
import org.sonar.api.user.User;
import org.sonar.api.utils.SonarException;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
+import org.sonar.server.email.EmailSmtpConfiguration;
import org.sonar.server.issue.notification.EmailMessage;
import org.sonar.server.issue.notification.EmailTemplate;
import org.sonar.server.notification.NotificationChannel;
private static final String SMTP_HOST_NOT_CONFIGURED_DEBUG_MSG = "SMTP host was not configured - email will not be sent";
private static final String MAIL_SENT_FROM = "%sMail sent from: %s";
- private final EmailSettings configuration;
+ private final EmailSmtpConfiguration configuration;
+ private final Server server;
private final EmailTemplate[] templates;
private final DbClient dbClient;
- public EmailNotificationChannel(EmailSettings configuration, EmailTemplate[] templates, DbClient dbClient) {
+ public EmailNotificationChannel(EmailSmtpConfiguration configuration, Server server, EmailTemplate[] templates, DbClient dbClient) {
this.configuration = configuration;
+ this.server = server;
this.templates = templates;
this.dbClient = dbClient;
}
@CheckForNull
private String resolveHost() {
try {
- return new URL(configuration.getServerBaseURL()).getHost();
+ return new URL(server.getPublicRootUrl()).getHost();
} catch (MalformedURLException e) {
// ignore
return null;
}
// Set headers for proper filtering
email.addHeader(LIST_ID_HEADER, "SonarQube <sonar." + host + ">");
- email.addHeader(LIST_ARCHIVE_HEADER, configuration.getServerBaseURL());
+ email.addHeader(LIST_ARCHIVE_HEADER, server.getPublicRootUrl());
}
}
}
private String getServerBaseUrlFooter() {
- return String.format(MAIL_SENT_FROM, "\n\n", configuration.getServerBaseURL());
+ return String.format(MAIL_SENT_FROM, "\n\n", server.getPublicRootUrl());
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.event.Level;
-import org.sonar.api.config.EmailSettings;
import org.sonar.api.notifications.Notification;
+import org.sonar.api.platform.Server;
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.server.email.EmailSmtpConfiguration;
import org.sonar.server.issue.notification.EmailMessage;
import org.sonar.server.issue.notification.EmailTemplate;
import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest;
public LogTester logTester = new LogTester();
private Wiser smtpServer;
- private EmailSettings configuration;
+ private EmailSmtpConfiguration configuration;
+ private Server server;
private EmailNotificationChannel underTest;
@Before
smtpServer = new Wiser(0);
smtpServer.start();
- configuration = mock(EmailSettings.class);
- underTest = new EmailNotificationChannel(configuration, null, null);
+ configuration = mock(EmailSmtpConfiguration.class);
+ server = mock(Server.class);
+
+ underTest = new EmailNotificationChannel(configuration, server, null, null);
}
@After
@Test
public void deliverAll_has_no_effect_if_set_is_empty() {
- EmailSettings emailSettings = mock(EmailSettings.class);
- EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, null, null);
+ EmailSmtpConfiguration emailSettings = mock(EmailSmtpConfiguration.class);
+ EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, server, null, null);
int count = underTest.deliverAll(Collections.emptySet());
@Test
public void deliverAll_has_no_effect_if_smtp_host_is_null() {
- EmailSettings emailSettings = mock(EmailSettings.class);
+ EmailSmtpConfiguration emailSettings = mock(EmailSmtpConfiguration.class);
when(emailSettings.getSmtpHost()).thenReturn(null);
Set<EmailDeliveryRequest> requests = IntStream.range(0, 1 + new Random().nextInt(10))
.mapToObj(i -> new EmailDeliveryRequest("foo" + i + "@moo", mock(Notification.class)))
.collect(toSet());
- EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, null, null);
+ EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, server, null, null);
int count = underTest.deliverAll(requests);
@Test
@UseDataProvider("emptyStrings")
public void deliverAll_ignores_requests_which_recipient_is_empty(String emptyString) {
- EmailSettings emailSettings = mock(EmailSettings.class);
+ EmailSmtpConfiguration emailSettings = mock(EmailSmtpConfiguration.class);
when(emailSettings.getSmtpHost()).thenReturn(null);
Set<EmailDeliveryRequest> requests = IntStream.range(0, 1 + new Random().nextInt(10))
.mapToObj(i -> new EmailDeliveryRequest(emptyString, mock(Notification.class)))
.collect(toSet());
- EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, null, null);
+ EmailNotificationChannel underTest = new EmailNotificationChannel(emailSettings, server, null, null);
int count = underTest.deliverAll(requests);
Set<EmailDeliveryRequest> requests = Stream.of(notification1, notification2, notification3)
.map(t -> new EmailDeliveryRequest(recipientEmail, t))
.collect(toSet());
- EmailNotificationChannel underTest = new EmailNotificationChannel(configuration, new EmailTemplate[] {template1, template3}, null);
+ EmailNotificationChannel underTest = new EmailNotificationChannel(configuration, server, new EmailTemplate[] {template1, template3}, null);
int count = underTest.deliverAll(requests);
when(template11.format(notification1)).thenReturn(emailMessage11);
when(template12.format(notification1)).thenReturn(emailMessage12);
EmailDeliveryRequest request = new EmailDeliveryRequest(recipientEmail, notification1);
- EmailNotificationChannel underTest = new EmailNotificationChannel(configuration, new EmailTemplate[] {template11, template12}, null);
+ EmailNotificationChannel underTest = new EmailNotificationChannel(configuration, server, new EmailTemplate[] {template11, template12}, null);
int count = underTest.deliverAll(Collections.singleton(request));
when(configuration.getFrom()).thenReturn("server@nowhere");
when(configuration.getFromName()).thenReturn("SonarQube from NoWhere");
when(configuration.getPrefix()).thenReturn(SUBJECT_PREFIX);
- when(configuration.getServerBaseURL()).thenReturn("http://nemo.sonarsource.org");
+ when(server.getPublicRootUrl()).thenReturn("http://nemo.sonarsource.org");
}
}
import org.sonar.api.config.EmailSettings;
import org.sonar.core.platform.Module;
+import org.sonar.server.email.EmailSmtpConfiguration;
import org.sonar.server.notification.email.EmailNotificationChannel;
public class NotificationModule extends Module {
protected void configureModule() {
add(
EmailSettings.class,
+ EmailSmtpConfiguration.class,
NotificationService.class,
DefaultNotificationManager.class,
NotificationDaemon.class,
public void verify_count_of_added_components() {
ListContainer container = new ListContainer();
new NotificationModule().configure(container);
- assertThat(container.getAddedObjects()).hasSize(5);
+ assertThat(container.getAddedObjects()).hasSize(6);
}
}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.email;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.ServerExtension;
+
+/**
+ * Ruby uses constants from this class.
+ *
+ * @since 2.10
+ */
+public class EmailConfiguration implements ServerExtension {
+
+ public static final String SMTP_HOST = "email.smtp_host";
+ public static final String SMTP_HOST_DEFAULT = "";
+ public static final String SMTP_PORT = "email.smtp_port";
+ public static final String SMTP_PORT_DEFAULT = "25";
+ public static final String SMTP_USE_TLS = "email.smtp_use_tls";
+ public static final boolean SMTP_USE_TLS_DEFAULT = false;
+ public static final String SMTP_USERNAME = "email.smtp_username";
+ public static final String SMTP_USERNAME_DEFAULT = "";
+ public static final String SMTP_PASSWORD = "email.smtp_password";
+ public static final String SMTP_PASSWORD_DEFAULT = "";
+ public static final String FROM = "email.from";
+ public static final String FROM_DEFAULT = "noreply@nowhere";
+ public static final String PREFIX = "email.prefix";
+ public static final String PREFIX_DEFAULT = "[SONAR]";
+
+ private Configuration configuration;
+
+ public EmailConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ public String getSmtpHost() {
+ return configuration.getString(SMTP_HOST, SMTP_HOST_DEFAULT);
+ }
+
+ public String getSmtpPort() {
+ return configuration.getString(SMTP_PORT, SMTP_PORT_DEFAULT);
+ }
+
+ public boolean isUseTLS() {
+ return configuration.getBoolean(SMTP_USE_TLS, SMTP_USE_TLS_DEFAULT);
+ }
+
+ public String getSmtpUsername() {
+ return configuration.getString(SMTP_USERNAME, SMTP_USERNAME_DEFAULT);
+ }
+
+ public String getSmtpPassword() {
+ return configuration.getString(SMTP_PASSWORD, SMTP_PASSWORD_DEFAULT);
+ }
+
+ public String getFrom() {
+ return configuration.getString(FROM, FROM_DEFAULT);
+ }
+
+ public String getPrefix() {
+ return configuration.getString(PREFIX, PREFIX_DEFAULT);
+ }
+
+ public String getServerBaseURL() {
+ return configuration.getString(CoreProperties.SERVER_BASE_URL, CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
+ }
+
+}