From 434951a0bf4f7165fecda893ee0ea9771784791d Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Thu, 11 Mar 2021 14:30:39 +0100 Subject: SONAR-14586 Move DefaultAdminCredentialsVerifier to sonar-webserver-auth --- .../DefaultAdminCredentialsVerifier.java | 98 ++++++++++++++++ ...efaultAdminCredentialsVerifierNotification.java | 31 ++++++ ...dminCredentialsVerifierNotificationHandler.java | 62 +++++++++++ ...minCredentialsVerifierNotificationTemplate.java | 47 ++++++++ ...CredentialsVerifierNotificationHandlerTest.java | 120 ++++++++++++++++++++ ...redentialsVerifierNotificationTemplateTest.java | 49 ++++++++ .../DefaultAdminCredentialsVerifierTest.java | 124 +++++++++++++++++++++ .../DefaultAdminCredentialsVerifier.java | 98 ---------------- ...efaultAdminCredentialsVerifierNotification.java | 31 ------ ...dminCredentialsVerifierNotificationHandler.java | 62 ----------- ...minCredentialsVerifierNotificationTemplate.java | 47 -------- .../sonar/server/authentication/package-info.java | 23 ---- ...CredentialsVerifierNotificationHandlerTest.java | 120 -------------------- ...redentialsVerifierNotificationTemplateTest.java | 49 -------- .../DefaultAdminCredentialsVerifierTest.java | 124 --------------------- 15 files changed, 531 insertions(+), 554 deletions(-) create mode 100644 server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java create mode 100644 server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java create mode 100644 server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java create mode 100644 server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java create mode 100644 server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java create mode 100644 server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java create mode 100644 server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java delete mode 100644 server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java delete mode 100644 server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java delete mode 100644 server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java delete mode 100644 server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java delete mode 100644 server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/package-info.java delete mode 100644 server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java delete mode 100644 server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java delete mode 100644 server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java (limited to 'server') diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java new file mode 100644 index 00000000000..dd8aeb372c9 --- /dev/null +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java @@ -0,0 +1,98 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import org.picocontainer.Startable; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.user.UserDto; +import org.sonar.server.authentication.event.AuthenticationEvent; +import org.sonar.server.authentication.event.AuthenticationException; +import org.sonar.server.notification.NotificationManager; + +import static org.sonar.server.log.ServerProcessLogging.STARTUP_LOGGER_NAME; +import static org.sonar.server.property.InternalProperties.DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL; + +/** + * Detect usage of an active admin account with default credential in order to ask this account to reset its password during authentication. + */ +public class DefaultAdminCredentialsVerifier implements Startable { + + private static final Logger LOGGER = Loggers.get(STARTUP_LOGGER_NAME); + + private final DbClient dbClient; + private final CredentialsLocalAuthentication localAuthentication; + private final NotificationManager notificationManager; + + public DefaultAdminCredentialsVerifier(DbClient dbClient, CredentialsLocalAuthentication localAuthentication, NotificationManager notificationManager) { + this.dbClient = dbClient; + this.localAuthentication = localAuthentication; + this.notificationManager = notificationManager; + } + + @Override + public void start() { + try (DbSession session = dbClient.openSession(false)) { + UserDto admin = dbClient.userDao().selectActiveUserByLogin(session, "admin"); + if (admin == null || !isDefaultCredentialUser(session, admin)) { + return; + } + addWarningInSonarDotLog(); + dbClient.userDao().update(session, admin.setResetPassword(true)); + sendEmailToAdmins(session); + session.commit(); + } + } + + private static void addWarningInSonarDotLog() { + String highlighter = "####################################################################################################################"; + String msg = "Default Administrator credentials are still being used. Make sure to change the password or deactivate the account."; + + LOGGER.warn(highlighter); + LOGGER.warn(msg); + LOGGER.warn(highlighter); + } + + private boolean isDefaultCredentialUser(DbSession dbSession, UserDto user) { + try { + localAuthentication.authenticate(dbSession, user, "admin", AuthenticationEvent.Method.BASIC); + return true; + } catch (AuthenticationException ex) { + return false; + } + } + + private void sendEmailToAdmins(DbSession session) { + if (dbClient.internalPropertiesDao().selectByKey(session, DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL) + .map(Boolean::parseBoolean) + .orElse(false)) { + return; + } + notificationManager.scheduleForSending(new DefaultAdminCredentialsVerifierNotification()); + dbClient.internalPropertiesDao().save(session, DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, Boolean.TRUE.toString()); + } + + @Override + public void stop() { + // Nothing to do + } +} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java new file mode 100644 index 00000000000..2616dc6290a --- /dev/null +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import org.sonar.api.notifications.Notification; + +public class DefaultAdminCredentialsVerifierNotification extends Notification { + + static final String TYPE = "default-admin-credential-verifier"; + + public DefaultAdminCredentialsVerifierNotification() { + super(TYPE); + } +} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java new file mode 100644 index 00000000000..a7d1d9e186f --- /dev/null +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.server.notification.EmailNotificationHandler; +import org.sonar.server.notification.NotificationDispatcherMetadata; +import org.sonar.server.notification.email.EmailNotificationChannel; +import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest; + +import static java.util.stream.Collectors.toSet; + +public class DefaultAdminCredentialsVerifierNotificationHandler extends EmailNotificationHandler { + + private final DbClient dbClient; + + public DefaultAdminCredentialsVerifierNotificationHandler(DbClient dbClient, EmailNotificationChannel emailNotificationChannel) { + super(emailNotificationChannel); + this.dbClient = dbClient; + } + + @Override + public Optional getMetadata() { + return Optional.empty(); + } + + @Override + public Class getNotificationClass() { + return DefaultAdminCredentialsVerifierNotification.class; + } + + @Override + public Set toEmailDeliveryRequests(Collection notifications) { + try (DbSession session = dbClient.openSession(false)) { + return dbClient.authorizationDao().selectGlobalAdministerEmailSubscribers(session) + .stream() + .flatMap(t -> notifications.stream().map(notification -> new EmailDeliveryRequest(t.getEmail(), notification))) + .collect(toSet()); + } + } +} diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java new file mode 100644 index 00000000000..fb6844b7ab7 --- /dev/null +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import javax.annotation.CheckForNull; +import org.sonar.api.notifications.Notification; +import org.sonar.server.issue.notification.EmailMessage; +import org.sonar.server.issue.notification.EmailTemplate; + +public class DefaultAdminCredentialsVerifierNotificationTemplate implements EmailTemplate { + + static final String SUBJECT = "Default Administrator credentials are still used"; + static final String BODY_FORMAT = "Hello,\n\n" + + "Your SonarQube instance is still using default administrator credentials.\n" + + "Make sure to change the password for the 'admin' account or deactivate this account."; + + @Override + @CheckForNull + public EmailMessage format(Notification notification) { + if (!DefaultAdminCredentialsVerifierNotification.TYPE.equals(notification.getType())) { + return null; + } + + return new EmailMessage() + .setMessageId(DefaultAdminCredentialsVerifierNotification.TYPE) + .setSubject(SUBJECT) + .setPlainTextMessage(BODY_FORMAT); + } + +} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java new file mode 100644 index 00000000000..7c7ebbd1c9f --- /dev/null +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java @@ -0,0 +1,120 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import java.util.Set; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.stubbing.Answer; +import org.sonar.db.DbTester; +import org.sonar.db.user.GroupDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.notification.email.EmailNotificationChannel; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonar.db.permission.GlobalPermission.ADMINISTER; + +public class DefaultAdminCredentialsVerifierNotificationHandlerTest { + + @Rule + public DbTester db = DbTester.create(); + + private EmailNotificationChannel emailNotificationChannel = mock(EmailNotificationChannel.class); + + private DefaultAdminCredentialsVerifierNotificationHandler underTest = new DefaultAdminCredentialsVerifierNotificationHandler(db.getDbClient(), + emailNotificationChannel); + + @Before + public void setUp() { + when(emailNotificationChannel.deliverAll(anySet())) + .then((Answer) invocationOnMock -> ((Set) invocationOnMock.getArguments()[0]).size()); + } + + @Test + public void deliver_to_all_admins_having_emails() { + when(emailNotificationChannel.isActivated()).thenReturn(true); + DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock(DefaultAdminCredentialsVerifierNotification.class); + // Users granted admin permission directly + UserDto admin1 = db.users().insertUser(u -> u.setEmail("admin1")); + UserDto adminWithNoEmail = db.users().insertUser(u -> u.setEmail(null)); + db.users().insertPermissionOnUser(admin1, ADMINISTER); + db.users().insertPermissionOnUser(adminWithNoEmail, ADMINISTER); + // User granted admin permission by group membership + UserDto admin2 = db.users().insertUser(u -> u.setEmail("admin2")); + GroupDto adminGroup = db.users().insertGroup(); + db.users().insertPermissionOnGroup(adminGroup, ADMINISTER); + db.users().insertMember(adminGroup, admin2); + db.users().insertUser(u -> u.setEmail("otherUser")); + + int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); + + // Only 2 admins have there email defined + assertThat(deliver).isEqualTo(2); + verify(emailNotificationChannel).isActivated(); + verify(emailNotificationChannel).deliverAll(anySet()); + verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); + } + + @Test + public void deliver_to_no_one_when_no_admins() { + when(emailNotificationChannel.isActivated()).thenReturn(true); + DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock(DefaultAdminCredentialsVerifierNotification.class); + db.users().insertUser(u -> u.setEmail("otherUser")); + + int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); + + assertThat(deliver).isZero(); + verify(emailNotificationChannel).isActivated(); + verifyNoMoreInteractions(emailNotificationChannel); + verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); + } + + @Test + public void do_nothing_if_emailNotificationChannel_is_disabled() { + when(emailNotificationChannel.isActivated()).thenReturn(false); + DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock( + DefaultAdminCredentialsVerifierNotification.class); + + int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); + + assertThat(deliver).isZero(); + verify(emailNotificationChannel).isActivated(); + verifyNoMoreInteractions(emailNotificationChannel); + verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); + } + + @Test + public void getMetadata_returns_empty() { + assertThat(underTest.getMetadata()).isEmpty(); + } + + @Test + public void getNotificationClass() { + assertThat(underTest.getNotificationClass()).isEqualTo(DefaultAdminCredentialsVerifierNotification.class); + } + +} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java new file mode 100644 index 00000000000..73b16ca5f25 --- /dev/null +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import org.junit.Test; +import org.sonar.api.notifications.Notification; +import org.sonar.server.issue.notification.EmailMessage; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultAdminCredentialsVerifierNotificationTemplateTest { + + private DefaultAdminCredentialsVerifierNotificationTemplate underTest = new DefaultAdminCredentialsVerifierNotificationTemplate(); + + @Test + public void do_not_format_other_notifications() { + assertThat(underTest.format(new Notification("foo"))).isNull(); + } + + @Test + public void format_notification() { + Notification notification = new Notification(DefaultAdminCredentialsVerifierNotification.TYPE); + + EmailMessage emailMessage = underTest.format(notification); + + assertThat(emailMessage.getSubject()).isEqualTo("Default Administrator credentials are still used"); + assertThat(emailMessage.getMessage()).isEqualTo("Hello,\n\n" + + "Your SonarQube instance is still using default administrator credentials.\n" + + "Make sure to change the password for the 'admin' account or deactivate this account."); + } + +} diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java new file mode 100644 index 00000000000..a307fa0475f --- /dev/null +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java @@ -0,0 +1,124 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.authentication; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.notifications.Notification; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.db.DbTester; +import org.sonar.db.user.UserDto; +import org.sonar.server.notification.NotificationManager; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.sonar.server.property.InternalProperties.DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL; + +public class DefaultAdminCredentialsVerifierTest { + + private static final String ADMIN_LOGIN = "admin"; + + @Rule + public DbTester db = DbTester.create(); + @Rule + public LogTester logTester = new LogTester(); + + private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); + private final NotificationManager notificationManager = mock(NotificationManager.class); + + private final DefaultAdminCredentialsVerifier underTest = new DefaultAdminCredentialsVerifier(db.getDbClient(), localAuthentication, notificationManager); + + @After + public void after() { + underTest.stop(); + } + + @Test + public void set_reset_flag_to_true_and_add_log_when_admin_account_with_default_credential_is_detected() { + UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); + changePassword(admin, "admin"); + + underTest.start(); + + assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isTrue(); + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Default Administrator credentials are still being used. Make sure to change the password or deactivate the account."); + assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL).get()).isEqualTo("true"); + verify(notificationManager).scheduleForSending(any(Notification.class)); + } + + @Test + public void do_not_send_email_to_admins_when_already_sent() { + UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); + changePassword(admin, "admin"); + db.getDbClient().internalPropertiesDao().save(db.getSession(), DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, "true"); + db.commit(); + + underTest.start(); + + verifyNoMoreInteractions(notificationManager); + } + + @Test + public void do_nothing_when_admin_is_not_using_default_credential() { + UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); + changePassword(admin, "something_else"); + + underTest.start(); + + assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse(); + assertThat(logTester.logs()).isEmpty(); + verifyNoMoreInteractions(notificationManager); + } + + @Test + public void do_nothing_when_no_admin_account_with_default_credential_detected() { + UserDto otherUser = db.users().insertUser(); + changePassword(otherUser, "admin"); + + underTest.start(); + + assertThat(db.users().selectUserByLogin(otherUser.getLogin()).get().isResetPassword()).isFalse(); + assertThat(logTester.logs()).isEmpty(); + verifyNoMoreInteractions(notificationManager); + } + + @Test + public void do_nothing_when_admin_account_with_default_credential_is_disabled() { + UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN).setActive(false)); + changePassword(admin, "admin"); + + underTest.start(); + + assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse(); + assertThat(logTester.logs()).isEmpty(); + verifyNoMoreInteractions(notificationManager); + } + + private void changePassword(UserDto user, String password) { + localAuthentication.storeHashPassword(user, password); + db.getDbClient().userDao().update(db.getSession(), user); + db.commit(); + } +} diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java deleted file mode 100644 index dd8aeb372c9..00000000000 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifier.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import org.picocontainer.Startable; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.user.UserDto; -import org.sonar.server.authentication.event.AuthenticationEvent; -import org.sonar.server.authentication.event.AuthenticationException; -import org.sonar.server.notification.NotificationManager; - -import static org.sonar.server.log.ServerProcessLogging.STARTUP_LOGGER_NAME; -import static org.sonar.server.property.InternalProperties.DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL; - -/** - * Detect usage of an active admin account with default credential in order to ask this account to reset its password during authentication. - */ -public class DefaultAdminCredentialsVerifier implements Startable { - - private static final Logger LOGGER = Loggers.get(STARTUP_LOGGER_NAME); - - private final DbClient dbClient; - private final CredentialsLocalAuthentication localAuthentication; - private final NotificationManager notificationManager; - - public DefaultAdminCredentialsVerifier(DbClient dbClient, CredentialsLocalAuthentication localAuthentication, NotificationManager notificationManager) { - this.dbClient = dbClient; - this.localAuthentication = localAuthentication; - this.notificationManager = notificationManager; - } - - @Override - public void start() { - try (DbSession session = dbClient.openSession(false)) { - UserDto admin = dbClient.userDao().selectActiveUserByLogin(session, "admin"); - if (admin == null || !isDefaultCredentialUser(session, admin)) { - return; - } - addWarningInSonarDotLog(); - dbClient.userDao().update(session, admin.setResetPassword(true)); - sendEmailToAdmins(session); - session.commit(); - } - } - - private static void addWarningInSonarDotLog() { - String highlighter = "####################################################################################################################"; - String msg = "Default Administrator credentials are still being used. Make sure to change the password or deactivate the account."; - - LOGGER.warn(highlighter); - LOGGER.warn(msg); - LOGGER.warn(highlighter); - } - - private boolean isDefaultCredentialUser(DbSession dbSession, UserDto user) { - try { - localAuthentication.authenticate(dbSession, user, "admin", AuthenticationEvent.Method.BASIC); - return true; - } catch (AuthenticationException ex) { - return false; - } - } - - private void sendEmailToAdmins(DbSession session) { - if (dbClient.internalPropertiesDao().selectByKey(session, DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL) - .map(Boolean::parseBoolean) - .orElse(false)) { - return; - } - notificationManager.scheduleForSending(new DefaultAdminCredentialsVerifierNotification()); - dbClient.internalPropertiesDao().save(session, DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, Boolean.TRUE.toString()); - } - - @Override - public void stop() { - // Nothing to do - } -} diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java deleted file mode 100644 index 2616dc6290a..00000000000 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotification.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import org.sonar.api.notifications.Notification; - -public class DefaultAdminCredentialsVerifierNotification extends Notification { - - static final String TYPE = "default-admin-credential-verifier"; - - public DefaultAdminCredentialsVerifierNotification() { - super(TYPE); - } -} diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java deleted file mode 100644 index a7d1d9e186f..00000000000 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import java.util.Collection; -import java.util.Optional; -import java.util.Set; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.server.notification.EmailNotificationHandler; -import org.sonar.server.notification.NotificationDispatcherMetadata; -import org.sonar.server.notification.email.EmailNotificationChannel; -import org.sonar.server.notification.email.EmailNotificationChannel.EmailDeliveryRequest; - -import static java.util.stream.Collectors.toSet; - -public class DefaultAdminCredentialsVerifierNotificationHandler extends EmailNotificationHandler { - - private final DbClient dbClient; - - public DefaultAdminCredentialsVerifierNotificationHandler(DbClient dbClient, EmailNotificationChannel emailNotificationChannel) { - super(emailNotificationChannel); - this.dbClient = dbClient; - } - - @Override - public Optional getMetadata() { - return Optional.empty(); - } - - @Override - public Class getNotificationClass() { - return DefaultAdminCredentialsVerifierNotification.class; - } - - @Override - public Set toEmailDeliveryRequests(Collection notifications) { - try (DbSession session = dbClient.openSession(false)) { - return dbClient.authorizationDao().selectGlobalAdministerEmailSubscribers(session) - .stream() - .flatMap(t -> notifications.stream().map(notification -> new EmailDeliveryRequest(t.getEmail(), notification))) - .collect(toSet()); - } - } -} diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java deleted file mode 100644 index fb6844b7ab7..00000000000 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplate.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import javax.annotation.CheckForNull; -import org.sonar.api.notifications.Notification; -import org.sonar.server.issue.notification.EmailMessage; -import org.sonar.server.issue.notification.EmailTemplate; - -public class DefaultAdminCredentialsVerifierNotificationTemplate implements EmailTemplate { - - static final String SUBJECT = "Default Administrator credentials are still used"; - static final String BODY_FORMAT = "Hello,\n\n" + - "Your SonarQube instance is still using default administrator credentials.\n" + - "Make sure to change the password for the 'admin' account or deactivate this account."; - - @Override - @CheckForNull - public EmailMessage format(Notification notification) { - if (!DefaultAdminCredentialsVerifierNotification.TYPE.equals(notification.getType())) { - return null; - } - - return new EmailMessage() - .setMessageId(DefaultAdminCredentialsVerifierNotification.TYPE) - .setSubject(SUBJECT) - .setPlainTextMessage(BODY_FORMAT); - } - -} diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/package-info.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/package-info.java deleted file mode 100644 index fabf034df91..00000000000 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/authentication/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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. - */ -@ParametersAreNonnullByDefault -package org.sonar.server.authentication; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java deleted file mode 100644 index 7c7ebbd1c9f..00000000000 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationHandlerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import java.util.Set; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.stubbing.Answer; -import org.sonar.db.DbTester; -import org.sonar.db.user.GroupDto; -import org.sonar.db.user.UserDto; -import org.sonar.server.notification.email.EmailNotificationChannel; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anySet; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.sonar.db.permission.GlobalPermission.ADMINISTER; - -public class DefaultAdminCredentialsVerifierNotificationHandlerTest { - - @Rule - public DbTester db = DbTester.create(); - - private EmailNotificationChannel emailNotificationChannel = mock(EmailNotificationChannel.class); - - private DefaultAdminCredentialsVerifierNotificationHandler underTest = new DefaultAdminCredentialsVerifierNotificationHandler(db.getDbClient(), - emailNotificationChannel); - - @Before - public void setUp() { - when(emailNotificationChannel.deliverAll(anySet())) - .then((Answer) invocationOnMock -> ((Set) invocationOnMock.getArguments()[0]).size()); - } - - @Test - public void deliver_to_all_admins_having_emails() { - when(emailNotificationChannel.isActivated()).thenReturn(true); - DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock(DefaultAdminCredentialsVerifierNotification.class); - // Users granted admin permission directly - UserDto admin1 = db.users().insertUser(u -> u.setEmail("admin1")); - UserDto adminWithNoEmail = db.users().insertUser(u -> u.setEmail(null)); - db.users().insertPermissionOnUser(admin1, ADMINISTER); - db.users().insertPermissionOnUser(adminWithNoEmail, ADMINISTER); - // User granted admin permission by group membership - UserDto admin2 = db.users().insertUser(u -> u.setEmail("admin2")); - GroupDto adminGroup = db.users().insertGroup(); - db.users().insertPermissionOnGroup(adminGroup, ADMINISTER); - db.users().insertMember(adminGroup, admin2); - db.users().insertUser(u -> u.setEmail("otherUser")); - - int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); - - // Only 2 admins have there email defined - assertThat(deliver).isEqualTo(2); - verify(emailNotificationChannel).isActivated(); - verify(emailNotificationChannel).deliverAll(anySet()); - verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); - } - - @Test - public void deliver_to_no_one_when_no_admins() { - when(emailNotificationChannel.isActivated()).thenReturn(true); - DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock(DefaultAdminCredentialsVerifierNotification.class); - db.users().insertUser(u -> u.setEmail("otherUser")); - - int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); - - assertThat(deliver).isZero(); - verify(emailNotificationChannel).isActivated(); - verifyNoMoreInteractions(emailNotificationChannel); - verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); - } - - @Test - public void do_nothing_if_emailNotificationChannel_is_disabled() { - when(emailNotificationChannel.isActivated()).thenReturn(false); - DefaultAdminCredentialsVerifierNotification detectActiveAdminAccountWithDefaultCredentialNotification = mock( - DefaultAdminCredentialsVerifierNotification.class); - - int deliver = underTest.deliver(singletonList(detectActiveAdminAccountWithDefaultCredentialNotification)); - - assertThat(deliver).isZero(); - verify(emailNotificationChannel).isActivated(); - verifyNoMoreInteractions(emailNotificationChannel); - verifyNoMoreInteractions(detectActiveAdminAccountWithDefaultCredentialNotification); - } - - @Test - public void getMetadata_returns_empty() { - assertThat(underTest.getMetadata()).isEmpty(); - } - - @Test - public void getNotificationClass() { - assertThat(underTest.getNotificationClass()).isEqualTo(DefaultAdminCredentialsVerifierNotification.class); - } - -} diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java deleted file mode 100644 index 73b16ca5f25..00000000000 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierNotificationTemplateTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import org.junit.Test; -import org.sonar.api.notifications.Notification; -import org.sonar.server.issue.notification.EmailMessage; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DefaultAdminCredentialsVerifierNotificationTemplateTest { - - private DefaultAdminCredentialsVerifierNotificationTemplate underTest = new DefaultAdminCredentialsVerifierNotificationTemplate(); - - @Test - public void do_not_format_other_notifications() { - assertThat(underTest.format(new Notification("foo"))).isNull(); - } - - @Test - public void format_notification() { - Notification notification = new Notification(DefaultAdminCredentialsVerifierNotification.TYPE); - - EmailMessage emailMessage = underTest.format(notification); - - assertThat(emailMessage.getSubject()).isEqualTo("Default Administrator credentials are still used"); - assertThat(emailMessage.getMessage()).isEqualTo("Hello,\n\n" + - "Your SonarQube instance is still using default administrator credentials.\n" + - "Make sure to change the password for the 'admin' account or deactivate this account."); - } - -} diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java deleted file mode 100644 index a307fa0475f..00000000000 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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.authentication; - -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.notifications.Notification; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.db.DbTester; -import org.sonar.db.user.UserDto; -import org.sonar.server.notification.NotificationManager; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.sonar.server.property.InternalProperties.DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL; - -public class DefaultAdminCredentialsVerifierTest { - - private static final String ADMIN_LOGIN = "admin"; - - @Rule - public DbTester db = DbTester.create(); - @Rule - public LogTester logTester = new LogTester(); - - private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient()); - private final NotificationManager notificationManager = mock(NotificationManager.class); - - private final DefaultAdminCredentialsVerifier underTest = new DefaultAdminCredentialsVerifier(db.getDbClient(), localAuthentication, notificationManager); - - @After - public void after() { - underTest.stop(); - } - - @Test - public void set_reset_flag_to_true_and_add_log_when_admin_account_with_default_credential_is_detected() { - UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); - changePassword(admin, "admin"); - - underTest.start(); - - assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isTrue(); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Default Administrator credentials are still being used. Make sure to change the password or deactivate the account."); - assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL).get()).isEqualTo("true"); - verify(notificationManager).scheduleForSending(any(Notification.class)); - } - - @Test - public void do_not_send_email_to_admins_when_already_sent() { - UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); - changePassword(admin, "admin"); - db.getDbClient().internalPropertiesDao().save(db.getSession(), DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, "true"); - db.commit(); - - underTest.start(); - - verifyNoMoreInteractions(notificationManager); - } - - @Test - public void do_nothing_when_admin_is_not_using_default_credential() { - UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN)); - changePassword(admin, "something_else"); - - underTest.start(); - - assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse(); - assertThat(logTester.logs()).isEmpty(); - verifyNoMoreInteractions(notificationManager); - } - - @Test - public void do_nothing_when_no_admin_account_with_default_credential_detected() { - UserDto otherUser = db.users().insertUser(); - changePassword(otherUser, "admin"); - - underTest.start(); - - assertThat(db.users().selectUserByLogin(otherUser.getLogin()).get().isResetPassword()).isFalse(); - assertThat(logTester.logs()).isEmpty(); - verifyNoMoreInteractions(notificationManager); - } - - @Test - public void do_nothing_when_admin_account_with_default_credential_is_disabled() { - UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN).setActive(false)); - changePassword(admin, "admin"); - - underTest.start(); - - assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse(); - assertThat(logTester.logs()).isEmpty(); - verifyNoMoreInteractions(notificationManager); - } - - private void changePassword(UserDto user, String password) { - localAuthentication.storeHashPassword(user, password); - db.getDbClient().userDao().update(db.getSession(), user); - db.commit(); - } -} -- cgit v1.2.3