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.
return this;
}
+ public boolean isResetPassword() {
+ return resetPassword;
+ }
+
+ public UserDto setResetPassword(boolean resetPassword) {
+ this.resetPassword = resetPassword;
+ return this;
+ }
+
@CheckForNull
public Long getLastConnectionDate() {
return lastConnectionDate;
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},
"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");
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();
.setScmAccounts(",jo.hn,john2,")
.setActive(true)
.setOnboarded(true)
+ .setResetPassword(true)
.setSalt("1234")
.setCryptedPassword("abcd")
.setHashMethod("SHA1")
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");
.setEmail("jo@hn.com")
.setActive(true)
.setLocal(true)
- .setOnboarded(false));
+ .setOnboarded(false)
+ .setResetPassword(false));
underTest.update(db.getSession(), newUserDto()
.setUuid(user.getUuid())
.setScmAccounts(",jo.hn,john2,johndoo,")
.setActive(false)
.setOnboarded(true)
+ .setResetPassword(true)
.setSalt("12345")
.setCryptedPassword("abcde")
.setHashMethod("BCRYPT")
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");
--- /dev/null
+/*
+ * 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());
+ }
+}
.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);
}
}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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;
+ });
+
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+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");
+
--- /dev/null
+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");
+
--- /dev/null
+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");
+