"EMAIL" VARCHAR(100),
"CRYPTED_PASSWORD" VARCHAR(100),
"SALT" VARCHAR(40),
+ "HASH_METHOD" VARCHAR(10),
"ACTIVE" BOOLEAN DEFAULT TRUE,
"SCM_ACCOUNTS" VARCHAR(4000),
"EXTERNAL_IDENTITY" VARCHAR(255),
private String externalIdentityProvider;
private String cryptedPassword;
private String salt;
+ private String hashMethod;
private Long createdAt;
private Long updatedAt;
private String homepageType;
return this;
}
+ @CheckForNull
+ public String getHashMethod() {
+ return hashMethod;
+ }
+
+ public UserDto setHashMethod(String hashMethod) {
+ this.hashMethod = hashMethod;
+ return this;
+ }
+
public Long getCreatedAt() {
return createdAt;
}
u.scm_accounts as "scmAccounts",
u.salt as "salt",
u.crypted_password as "cryptedPassword",
+ u.hash_method as "hashMethod",
u.external_identity as "externalIdentity",
u.external_identity_provider as "externalIdentityProvider",
u.user_local as "local",
user_local,
salt,
crypted_password,
+ hash_method,
is_root,
onboarded,
created_at,
#{user.local,jdbcType=BOOLEAN},
#{user.salt,jdbcType=VARCHAR},
#{user.cryptedPassword,jdbcType=VARCHAR},
+ #{user.hashMethod,jdbcType=VARCHAR},
#{user.root,jdbcType=BOOLEAN},
#{user.onboarded,jdbcType=BOOLEAN},
#{now,jdbcType=BIGINT},
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddHashMethodToUsersTable extends DdlChange {
+
+ public AddHashMethodToUsersTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), "users")
+ .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName("hash_method")
+ .setIsNullable(true)
+ .setLimit(10)
+ .build())
+ .build());
+ }
+
+}
public void addSteps(MigrationStepRegistry registry) {
registry
.add(2100, "Increase size of CRYPTED_PASSWORD", IncreaseCryptedPasswordSize.class)
+ .add(2101, "Add HASH_METHOD to table users", AddHashMethodToUsersTable.class)
+ .add(2102, "Populate HASH_METHOD on table users", PopulateHashMethodOnUsers.class)
;
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+public class PopulateHashMethodOnUsers extends DataChange {
+
+ private final System2 system2;
+
+ public PopulateHashMethodOnUsers(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareUpsert("UPDATE users SET hash_method=?, updated_at=? WHERE crypted_password IS NOT NULL")
+ .setString(1, "SHA1")
+ .setLong(2, system2.now())
+ .execute()
+ .commit();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddHashMethodToUsersTableTest {
+ @Rule
+ public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddHashMethodToUsersTableTest.class, "users.sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddHashMethodToUsersTable underTest = new AddHashMethodToUsersTable(dbTester.database());
+
+ @Test
+ public void column_is_added_to_table() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertColumnDefinition("users", "hash_method", VARCHAR, 10, true);
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 1);
+ verifyMigrationCount(underTest, 3);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import java.util.stream.IntStream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.CoreDbTester;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PopulateHashMethodOnUsersTest {
+
+ private static final long PAST = 5_000_000_000L;
+ private static final long NOW = 10_000_000_000L;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateHashMethodOnUsersTest.class, "users.sql");
+
+ private System2 system2 = new TestSystem2().setNow(NOW);
+
+ private PopulateHashMethodOnUsers underTest = new PopulateHashMethodOnUsers(db.database(), system2);
+
+ @Test
+ public void should_update_only_local_users() throws SQLException {
+ IntStream.range(0, 99).forEach(i -> insertLocalUser(null));
+ IntStream.range(0, 100).forEach(i -> insertExternalUser());
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(id) from users where hash_method='SHA1'")).isEqualTo(99);
+ assertThat(db.countSql("select count(id) from users where hash_method is not null and crypted_password is null")).isEqualTo(0);
+ }
+
+ @Test
+ public void should_add_sha1_in_hash_method() throws SQLException {
+ IntStream.range(0, 99).forEach(i -> insertLocalUser(null));
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(id) from users where hash_method <> 'SHA1'")).isEqualTo(0);
+ }
+
+ @Test
+ public void is_reentrant() throws SQLException {
+ IntStream.range(0, 99).forEach(i -> insertLocalUser(null));
+ IntStream.range(0, 100).forEach(i -> insertExternalUser());
+
+ underTest.execute();
+ underTest.execute();
+
+ assertThat(db.countSql("select count(id) from users where hash_method='SHA1'")).isEqualTo(99);
+ assertThat(db.countSql("select count(id) from users where hash_method is not null and crypted_password is null")).isEqualTo(0);
+ }
+
+ private void insertExternalUser() {
+ insertUser(randomAlphanumeric(10), null, null, null, randomAlphanumeric(20), randomAlphanumeric(20));
+ }
+
+ private void insertLocalUser(@Nullable String hashMethod) {
+ insertUser(randomAlphanumeric(10), randomAlphanumeric(10), randomAlphanumeric(10), hashMethod, null, null);
+ }
+
+ private void insertUser(String login, String cryptedPassword, String salt, String hashMethod,
+ @Nullable String externalIdentity, @Nullable String externalIdentityProvider) {
+ db.executeInsert("USERS",
+ "LOGIN", login,
+ "CRYPTED_PASSWORD", cryptedPassword,
+ "SALT", salt,
+ "HASH_METHOD", salt,
+ "EXTERNAL_IDENTITY", externalIdentity,
+ "EXTERNAL_IDENTITY_PROVIDER", externalIdentityProvider,
+ "IS_ROOT", false,
+ "ONBOARDED", false);
+ }
+}
--- /dev/null
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255),
+ "NAME" VARCHAR(200),
+ "EMAIL" VARCHAR(100),
+ "CRYPTED_PASSWORD" VARCHAR(100),
+ "SALT" VARCHAR(40),
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" VARCHAR(4000),
+ "EXTERNAL_IDENTITY" VARCHAR(255),
+ "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100),
+ "IS_ROOT" BOOLEAN NOT NULL,
+ "USER_LOCAL" BOOLEAN,
+ "ONBOARDED" BOOLEAN NOT NULL,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "HOMEPAGE_TYPE" VARCHAR(40),
+ "HOMEPAGE_PARAMETER" VARCHAR(40)
+);
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
--- /dev/null
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255),
+ "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_IDENTITY" VARCHAR(255),
+ "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100),
+ "IS_ROOT" BOOLEAN NOT NULL,
+ "USER_LOCAL" BOOLEAN,
+ "ONBOARDED" BOOLEAN NOT NULL,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "HOMEPAGE_TYPE" VARCHAR(40),
+ "HOMEPAGE_PARAMETER" VARCHAR(40)
+);
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");