From: Jacek Date: Wed, 25 Nov 2020 15:19:57 +0000 (+0100) Subject: SONAR-14175 Add 'reset_password' column to 'users' table X-Git-Tag: 8.6.0.39681~43 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=89d9f0efa05022bb7577ff0d2014accc580da0a6;p=sonarqube.git SONAR-14175 Add 'reset_password' column to 'users' table - add and populate 'reset_password' column in users table - add boolean 'resetPassword' to UserDto --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java index c81e64516bd..0f7a0b59f1c 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java @@ -54,6 +54,7 @@ public class UserDto { private boolean local = true; private boolean root = false; private boolean onboarded = false; + private boolean resetPassword = false; /** * Date of the last time the user has accessed to the server. @@ -261,6 +262,15 @@ public class UserDto { return this; } + public boolean isResetPassword() { + return resetPassword; + } + + public UserDto setResetPassword(boolean resetPassword) { + this.resetPassword = resetPassword; + return this; + } + @CheckForNull public Long getLastConnectionDate() { return lastConnectionDate; diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml index a1b5e6eca7b..8909e23aafa 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml @@ -19,6 +19,7 @@ u.user_local as "local", u.is_root as "root", u.onboarded as "onboarded", + u.reset_password as "resetPassword", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", u.last_connection_date as "lastConnectionDate", @@ -216,6 +217,7 @@ hash_method, is_root, onboarded, + reset_password, homepage_type, homepage_parameter, created_at, @@ -236,6 +238,7 @@ #{user.hashMethod,jdbcType=VARCHAR}, #{user.root,jdbcType=BOOLEAN}, #{user.onboarded,jdbcType=BOOLEAN}, + #{user.resetPassword,jdbcType=BOOLEAN}, #{user.homepageType,jdbcType=VARCHAR}, #{user.homepageParameter,jdbcType=VARCHAR}, #{user.createdAt,jdbcType=BIGINT}, @@ -255,6 +258,7 @@ external_identity_provider = #{user.externalIdentityProvider, jdbcType=VARCHAR}, user_local = #{user.local, jdbcType=BOOLEAN}, onboarded = #{user.onboarded, jdbcType=BOOLEAN}, + reset_password = #{user.resetPassword, jdbcType=BOOLEAN}, salt = #{user.salt, jdbcType=VARCHAR}, crypted_password = #{user.cryptedPassword, jdbcType=BIGINT}, hash_method = #{user.hashMethod, jdbcType=VARCHAR}, diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index e4effd33c0b..c3278219308 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -981,7 +981,8 @@ CREATE TABLE "USERS"( "HOMEPAGE_PARAMETER" VARCHAR(40), "LAST_CONNECTION_DATE" BIGINT, "CREATED_AT" BIGINT, - "UPDATED_AT" BIGINT + "UPDATED_AT" BIGINT, + "RESET_PASSWORD" BOOLEAN NOT NULL ); ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN"); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java index a3e1672e96c..41989df68b3 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java @@ -307,6 +307,35 @@ public class UserDaoTest { return dto; } + @Test + public void insert_user_with_default_values() { + UserDto userDto = new UserDto() + .setLogin("john") + .setName("John") + .setEmail("jo@hn.com") + .setExternalLogin("john-1") + .setExternalIdentityProvider("sonarqube") + .setExternalId("EXT_ID"); + underTest.insert(db.getSession(), userDto); + db.getSession().commit(); + + UserDto user = underTest.selectActiveUserByLogin(session, "john"); + assertThat(user).isNotNull(); + assertThat(user.getUuid()).isNotNull(); + assertThat(user.isActive()).isTrue(); + assertThat(user.isOnboarded()).isFalse(); + assertThat(user.isResetPassword()).isFalse(); + assertThat(user.isLocal()).isTrue(); + assertThat(user.isRoot()).isFalse(); + + assertThat(user.getScmAccountsAsList()).isEmpty(); + assertThat(user.getScmAccounts()).isNull(); + assertThat(user.getHashMethod()).isNull(); + assertThat(user.getLastConnectionDate()).isNull(); + assertThat(user.getHomepageType()).isNull(); + assertThat(user.getHomepageParameter()).isNull(); + } + @Test public void insert_user() { long date = DateUtils.parseDate("2014-06-20").getTime(); @@ -318,6 +347,7 @@ public class UserDaoTest { .setScmAccounts(",jo.hn,john2,") .setActive(true) .setOnboarded(true) + .setResetPassword(true) .setSalt("1234") .setCryptedPassword("abcd") .setHashMethod("SHA1") @@ -340,6 +370,7 @@ public class UserDaoTest { assertThat(user.getEmail()).isEqualTo("jo@hn.com"); assertThat(user.isActive()).isTrue(); assertThat(user.isOnboarded()).isTrue(); + assertThat(user.isResetPassword()).isTrue(); assertThat(user.getScmAccounts()).isEqualTo(",jo.hn,john2,"); assertThat(user.getSalt()).isEqualTo("1234"); assertThat(user.getCryptedPassword()).isEqualTo("abcd"); @@ -372,7 +403,8 @@ public class UserDaoTest { .setEmail("jo@hn.com") .setActive(true) .setLocal(true) - .setOnboarded(false)); + .setOnboarded(false) + .setResetPassword(false)); underTest.update(db.getSession(), newUserDto() .setUuid(user.getUuid()) @@ -382,6 +414,7 @@ public class UserDaoTest { .setScmAccounts(",jo.hn,john2,johndoo,") .setActive(false) .setOnboarded(true) + .setResetPassword(true) .setSalt("12345") .setCryptedPassword("abcde") .setHashMethod("BCRYPT") @@ -401,6 +434,7 @@ public class UserDaoTest { assertThat(reloaded.getEmail()).isEqualTo("jodoo@hn.com"); assertThat(reloaded.isActive()).isFalse(); assertThat(reloaded.isOnboarded()).isTrue(); + assertThat(reloaded.isResetPassword()).isTrue(); assertThat(reloaded.getScmAccounts()).isEqualTo(",jo.hn,john2,johndoo,"); assertThat(reloaded.getSalt()).isEqualTo("12345"); assertThat(reloaded.getCryptedPassword()).isEqualTo("abcde"); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsers.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsers.java new file mode 100644 index 00000000000..100afd4ae7e --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsers.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.BooleanColumnDef; +import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder; + +public class AddResetPasswordColumnToUsers extends DdlChange { + private static final String TABLE = "users"; + private static final String NEW_COLUMN = "reset_password"; + + public AddResetPasswordColumnToUsers(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + BooleanColumnDef column = newBooleanColumnDefBuilder() + .setColumnName(NEW_COLUMN) + .setIsNullable(true) + .build(); + context.execute(new AddColumnsBuilder(getDialect(), TABLE) + .addColumn(column) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java index f826e79eb6e..f4bd257b29a 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java @@ -53,6 +53,9 @@ public class DbVersion86 implements DbVersion { .add(4121, "Create index for 'app_branch_project_branch'", AddIndexToApplicationBranchProjs.class) .add(4122, "Migrate view definitions from xml to db", MigrateApplicationDefinitionsFromXmlToDb.class) - ; + + .add(4123, "Add 'reset_password' column to 'users' table", AddResetPasswordColumnToUsers.class) + .add(4124, "Populate 'reset_password' column with default value", PopulateResetPasswordDefaultValue.class) + .add(4125, "Make 'reset_password' column in 'users' table not nullable", MakeResetPasswordColumnNotNull.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNull.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNull.java new file mode 100644 index 00000000000..021fad5aa9e --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNull.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.BooleanColumnDef; +import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder; + +public class MakeResetPasswordColumnNotNull extends DdlChange { + private static final String TABLE = "users"; + private static final String RESET_PASSWORD_COLUMN_NAME = "reset_password"; + + private static final BooleanColumnDef columnDefinition = newBooleanColumnDefBuilder() + .setColumnName(RESET_PASSWORD_COLUMN_NAME) + .setIsNullable(false) + .build(); + + public MakeResetPasswordColumnNotNull(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(new AlterColumnsBuilder(getDialect(), TABLE) + .updateColumn(columnDefinition) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValue.java new file mode 100644 index 00000000000..b1224294722 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValue.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +public class PopulateResetPasswordDefaultValue extends DataChange { + + private final System2 system2; + + public PopulateResetPasswordDefaultValue(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select uuid from users where reset_password is null"); + massUpdate.update("update users set reset_password = ?, updated_at = ? where uuid = ?"); + + massUpdate.execute((row, update) -> { + update.setBoolean(1, false) + .setLong(2, system2.now()) + .setString(3, row.getString(1)); + return true; + }); + + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest.java new file mode 100644 index 00000000000..46a9d0ab425 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import java.util.Objects; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.MigrationStep; + +import static java.sql.Types.BOOLEAN; +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; + +public class AddResetPasswordColumnToUsersTest { + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(AddResetPasswordColumnToUsersTest.class, "schema.sql"); + + private MigrationStep underTest = new AddResetPasswordColumnToUsers(db.database()); + + @Test + public void execute_on_empty_db() throws SQLException { + db.assertColumnDoesNotExist("users", "reset_password"); + underTest.execute(); + db.assertColumnDefinition("users", "reset_password", BOOLEAN, null, true); + } + + @Test + public void execute_on_db_with_user_rows() throws SQLException { + insertUser("uuid-1"); + insertUser("uuid-2"); + insertUser("uuid-3"); + db.assertColumnDoesNotExist("users", "reset_password"); + underTest.execute(); + db.assertColumnDefinition("users", "reset_password", BOOLEAN, null, true); + + assertThatAllUsersResetPasswordFlagAreNotSet(); + } + + private void assertThatAllUsersResetPasswordFlagAreNotSet() { + assertThat(db.select("select reset_password from users") + .stream() + .map(r -> r.get("RESET_PASSWORD")) + .map(this::getBooleanValue) + .filter(Objects::nonNull) + .collect(toList())).isEmpty(); + } + + private Boolean getBooleanValue(@Nullable Object value) { + return value == null ? null : Boolean.parseBoolean(value.toString()); + } + + private void insertUser(String uuid) { + db.executeInsert("users", + "UUID", uuid, + "LOGIN", uuid + "login", + "EXTERNAL_LOGIN", uuid + "-external-login", + "EXTERNAL_IDENTITY_PROVIDER", "sonarqube", + "EXTERNAL_ID", uuid + "-external-id", + "IS_ROOT", false, + "ONBOARDED", false); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest.java new file mode 100644 index 00000000000..862754d6d9e --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest.java @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.MigrationStep; + +import static java.sql.Types.BOOLEAN; + +public class MakeResetPasswordColumnNotNullTest { + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(MakeResetPasswordColumnNotNullTest.class, "schema.sql"); + + private MigrationStep underTest = new MakeResetPasswordColumnNotNull(db.database()); + + @Test + public void execute_on_empty_db() throws SQLException { + db.assertColumnDefinition("users", "reset_password", BOOLEAN, null, true); + underTest.execute(); + db.assertColumnDefinition("users", "reset_password", BOOLEAN, null, false); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest.java new file mode 100644 index 00000000000..2993cee776f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest.java @@ -0,0 +1,121 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.platform.db.migration.version.v86; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import org.assertj.core.groups.Tuple; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.MigrationStep; + +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class PopulateResetPasswordDefaultValueTest { + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(PopulateResetPasswordDefaultValueTest.class, "schema.sql"); + + private final TestSystem2 system2 = new TestSystem2(); + private final long NOW = 1606375781L; + + private MigrationStep underTest = new PopulateResetPasswordDefaultValue(db.database(), system2); + + @Before + public void before() { + system2.setNow(NOW); + } + + @Test + public void execute_on_empty_db() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable("users")).isZero(); + } + + @Test + public void execute_on_db_with_user_rows() throws SQLException { + insertUser("uuid-1", null, NOW); + insertUser("uuid-2", null, NOW); + insertUser("uuid-3", null, NOW); + + long expectedUpdatedRowTime = 2_000_000_000L; + system2.setNow(expectedUpdatedRowTime); + + underTest.execute(); + + assertThatUserResetPasswordFlagIsEqualTo( + tuple("uuid-1", false, expectedUpdatedRowTime), + tuple("uuid-2", false, expectedUpdatedRowTime), + tuple("uuid-3", false, expectedUpdatedRowTime)); + } + + @Test + public void does_not_update_already_set_flags() throws SQLException { + insertUser("uuid-1", true, NOW); + insertUser("uuid-2", false, NOW); + insertUser("uuid-3", null, NOW); + + long expectedUpdatedRowTime = 2_000_000_000L; + system2.setNow(expectedUpdatedRowTime); + + underTest.execute(); + + insertUser("uuid-4", null, NOW); + // re-entrant + underTest.execute(); + + assertThatUserResetPasswordFlagIsEqualTo( + tuple("uuid-1", true, NOW), + tuple("uuid-2", false, NOW), + tuple("uuid-3", false, expectedUpdatedRowTime), + tuple("uuid-4", false, expectedUpdatedRowTime)); + } + + private void assertThatUserResetPasswordFlagIsEqualTo(Tuple... tuples) { + assertThat(db.select("select uuid, reset_password, updated_at from users") + .stream() + .map(r -> tuple(r.get("UUID"), getBooleanValue(r.get("RESET_PASSWORD")), r.get("UPDATED_AT"))) + .collect(toList())) + .containsExactlyInAnyOrder(tuples); + } + + private Boolean getBooleanValue(@Nullable Object value) { + return value == null ? null : Boolean.parseBoolean(value.toString()); + } + + private void insertUser(String uuid, @Nullable Boolean resetPassword, long updatedAt) { + db.executeInsert("users", + "UUID", uuid, + "LOGIN", uuid + "login", + "EXTERNAL_LOGIN", uuid + "-external-login", + "EXTERNAL_IDENTITY_PROVIDER", "sonarqube", + "EXTERNAL_ID", uuid + "-external-id", + "IS_ROOT", false, + "RESET_PASSWORD", resetPassword, + "ONBOARDED", false, + "UPDATED_AT", updatedAt); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest/schema.sql new file mode 100644 index 00000000000..542deb026c8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest/schema.sql @@ -0,0 +1,28 @@ +CREATE TABLE "USERS"( + "UUID" VARCHAR(255) NOT NULL, + "LOGIN" VARCHAR(255) NOT NULL, + "NAME" VARCHAR(200), + "EMAIL" VARCHAR(100), + "CRYPTED_PASSWORD" VARCHAR(100), + "SALT" VARCHAR(40), + "HASH_METHOD" VARCHAR(10), + "ACTIVE" BOOLEAN DEFAULT TRUE, + "SCM_ACCOUNTS" VARCHAR(4000), + "EXTERNAL_LOGIN" VARCHAR(255) NOT NULL, + "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100) NOT NULL, + "EXTERNAL_ID" VARCHAR(255) NOT NULL, + "IS_ROOT" BOOLEAN NOT NULL, + "USER_LOCAL" BOOLEAN, + "ONBOARDED" BOOLEAN NOT NULL, + "HOMEPAGE_TYPE" VARCHAR(40), + "HOMEPAGE_PARAMETER" VARCHAR(40), + "LAST_CONNECTION_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT +); +ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN"); +CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_ID"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_LOGIN"); + diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest/schema.sql new file mode 100644 index 00000000000..be6b1ef0e77 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest/schema.sql @@ -0,0 +1,29 @@ +CREATE TABLE "USERS"( + "UUID" VARCHAR(255) NOT NULL, + "LOGIN" VARCHAR(255) NOT NULL, + "NAME" VARCHAR(200), + "EMAIL" VARCHAR(100), + "CRYPTED_PASSWORD" VARCHAR(100), + "SALT" VARCHAR(40), + "HASH_METHOD" VARCHAR(10), + "ACTIVE" BOOLEAN DEFAULT TRUE, + "SCM_ACCOUNTS" VARCHAR(4000), + "EXTERNAL_LOGIN" VARCHAR(255) NOT NULL, + "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100) NOT NULL, + "EXTERNAL_ID" VARCHAR(255) NOT NULL, + "IS_ROOT" BOOLEAN NOT NULL, + "USER_LOCAL" BOOLEAN, + "ONBOARDED" BOOLEAN NOT NULL, + "RESET_PASSWORD" BOOLEAN, + "HOMEPAGE_TYPE" VARCHAR(40), + "HOMEPAGE_PARAMETER" VARCHAR(40), + "LAST_CONNECTION_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT +); +ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN"); +CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_ID"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_LOGIN"); + diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest/schema.sql new file mode 100644 index 00000000000..be6b1ef0e77 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest/schema.sql @@ -0,0 +1,29 @@ +CREATE TABLE "USERS"( + "UUID" VARCHAR(255) NOT NULL, + "LOGIN" VARCHAR(255) NOT NULL, + "NAME" VARCHAR(200), + "EMAIL" VARCHAR(100), + "CRYPTED_PASSWORD" VARCHAR(100), + "SALT" VARCHAR(40), + "HASH_METHOD" VARCHAR(10), + "ACTIVE" BOOLEAN DEFAULT TRUE, + "SCM_ACCOUNTS" VARCHAR(4000), + "EXTERNAL_LOGIN" VARCHAR(255) NOT NULL, + "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100) NOT NULL, + "EXTERNAL_ID" VARCHAR(255) NOT NULL, + "IS_ROOT" BOOLEAN NOT NULL, + "USER_LOCAL" BOOLEAN, + "ONBOARDED" BOOLEAN NOT NULL, + "RESET_PASSWORD" BOOLEAN, + "HOMEPAGE_TYPE" VARCHAR(40), + "HOMEPAGE_PARAMETER" VARCHAR(40), + "LAST_CONNECTION_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT +); +ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN"); +CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_ID"); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER", "EXTERNAL_LOGIN"); +