]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14175 Add 'reset_password' column to 'users' table
authorJacek <jacek.poreda@sonarsource.com>
Wed, 25 Nov 2020 15:19:57 +0000 (16:19 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 2 Dec 2020 20:06:57 +0000 (20:06 +0000)
 - add and populate 'reset_password' column in users table
 - add boolean 'resetPassword' to UserDto

14 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsers.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNull.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValue.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddResetPasswordColumnToUsersTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MakeResetPasswordColumnNotNullTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/PopulateResetPasswordDefaultValueTest/schema.sql [new file with mode: 0644]

index c81e64516bd07bfdcfbd16a194e521fb99b0017c..0f7a0b59f1cf4db286c9231349d65f4bb3c635b0 100644 (file)
@@ -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;
index a1b5e6eca7bb1c6a870d8ac5441f237828681ff7..8909e23aafac1c81bdd167145072925e44675965 100644 (file)
@@ -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",
         hash_method,
         is_root,
         onboarded,
+        reset_password,
         homepage_type,
         homepage_parameter,
         created_at,
         #{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},
         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},
index e4effd33c0bdcd95dd3487c1ec3beb54b93e3c10..c3278219308863d159af55d978602ecf96676d71 100644 (file)
@@ -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");
index a3e1672e96c203e1de2c6ca22813c16941495ae9..41989df68b32174c05f2c02d4133e2264cb25d5a 100644 (file)
@@ -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 (file)
index 0000000..100afd4
--- /dev/null
@@ -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());
+  }
+}
index f826e79eb6ec64c149e887df237f051388eaef2a..f4bd257b29a4aee24694821a64cea597d9a03623 100644 (file)
@@ -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 (file)
index 0000000..021fad5
--- /dev/null
@@ -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 (file)
index 0000000..b122429
--- /dev/null
@@ -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 (file)
index 0000000..46a9d0a
--- /dev/null
@@ -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 (file)
index 0000000..862754d
--- /dev/null
@@ -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 (file)
index 0000000..2993cee
--- /dev/null
@@ -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 (file)
index 0000000..542deb0
--- /dev/null
@@ -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 (file)
index 0000000..be6b1ef
--- /dev/null
@@ -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 (file)
index 0000000..be6b1ef
--- /dev/null
@@ -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");
+