From: Antoine Vigneau Date: Wed, 22 Feb 2023 13:44:24 +0000 (+0100) Subject: SONAR-18525 SCIM Group data model (Mapper, DTO, DAO, Migrations) X-Git-Tag: 10.0.0.68432~116 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e5fa399151a9c5edbc3d023d73224b09cbac6266;p=sonarqube.git SONAR-18525 SCIM Group data model (Mapper, DTO, DAO, Migrations) --- diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index 058a80af47d..16c98a0e284 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -99,6 +99,7 @@ public final class SqTables { "rule_repositories", "scanner_analysis_cache", "schema_migrations", + "scim_groups", "scim_users", "session_tokens", "snapshots", diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index ccc02ce1b88..c3598e3b832 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -81,6 +81,7 @@ import org.sonar.db.rule.RuleDao; import org.sonar.db.rule.RuleRepositoryDao; import org.sonar.db.scannercache.ScannerAnalysisCacheDao; import org.sonar.db.schemamigration.SchemaMigrationDao; +import org.sonar.db.scim.ScimGroupDao; import org.sonar.db.scim.ScimUserDao; import org.sonar.db.source.FileSourceDao; import org.sonar.db.user.GroupDao; @@ -163,6 +164,7 @@ public class DaoModule extends Module { SamlMessageIdDao.class, ScannerAnalysisCacheDao.class, SchemaMigrationDao.class, + ScimGroupDao.class, ScimUserDao.class, SnapshotDao.class, SessionTokensDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index 0caa22c4ac8..906edafaee2 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -81,6 +81,7 @@ import org.sonar.db.rule.RuleDao; import org.sonar.db.rule.RuleRepositoryDao; import org.sonar.db.scannercache.ScannerAnalysisCacheDao; import org.sonar.db.schemamigration.SchemaMigrationDao; +import org.sonar.db.scim.ScimGroupDao; import org.sonar.db.scim.ScimUserDao; import org.sonar.db.source.FileSourceDao; import org.sonar.db.user.GroupDao; @@ -174,6 +175,7 @@ public class DbClient { private final ProjectBadgeTokenDao projectBadgeTokenDao; private final ScannerAnalysisCacheDao scannerAnalysisCacheDao; private final ScimUserDao scimUserDao; + private final ScimGroupDao scimGroupDao; public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) { this.database = database; @@ -257,6 +259,7 @@ public class DbClient { applicationProjectsDao = getDao(map, ApplicationProjectsDao.class); scannerAnalysisCacheDao = getDao(map, ScannerAnalysisCacheDao.class); scimUserDao = getDao(map, ScimUserDao.class); + scimGroupDao = getDao(map, ScimGroupDao.class); } public DbSession openSession(boolean batch) { @@ -568,4 +571,9 @@ public class DbClient { public ScimUserDao scimUserDao() { return scimUserDao; } + + public ScimGroupDao scimGroupDao() { + return scimGroupDao; + } } + diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index 54647f71c9c..78dca8e791c 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -142,6 +142,7 @@ import org.sonar.db.rule.RuleRepositoryMapper; import org.sonar.db.scannercache.ScannerAnalysisCacheMapper; import org.sonar.db.schemamigration.SchemaMigrationDto; import org.sonar.db.schemamigration.SchemaMigrationMapper; +import org.sonar.db.scim.ScimGroupMapper; import org.sonar.db.scim.ScimUserMapper; import org.sonar.db.source.FileSourceMapper; import org.sonar.db.user.GroupDto; @@ -311,6 +312,7 @@ public class MyBatis { SamlMessageIdMapper.class, ScannerAnalysisCacheMapper.class, SchemaMigrationMapper.class, + ScimGroupMapper.class, ScimUserMapper.class, SessionTokenMapper.class, SnapshotMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDao.java new file mode 100644 index 00000000000..fad37c6c455 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDao.java @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.scim; + +import java.util.List; +import java.util.Optional; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; + +public class ScimGroupDao implements Dao { + private final UuidFactory uuidFactory; + + public ScimGroupDao(UuidFactory uuidFactory) { + this.uuidFactory = uuidFactory; + } + + public List findAll(DbSession dbSession) { + return mapper(dbSession).findAll(); + } + + public Optional findByScimUuid(DbSession dbSession, String scimGroupUuid) { + return Optional.ofNullable(mapper(dbSession).findByScimUuid(scimGroupUuid)); + } + + public Optional findByGroupUuid(DbSession dbSession, String groupUuid) { + return Optional.ofNullable(mapper(dbSession).findByGroupUuid(groupUuid)); + } + + public ScimGroupDto enableScimForGroup(DbSession dbSession, String groupUuid) { + ScimGroupDto scimGroupDto = new ScimGroupDto(uuidFactory.create(), groupUuid); + mapper(dbSession).insert(scimGroupDto); + return scimGroupDto; + } + + public void deleteByGroupUuid(DbSession dbSession, String groupUuid) { + mapper(dbSession).deleteByGroupUuid(groupUuid); + } + + public void deleteByScimUuid(DbSession dbSession, String scimUuid) { + mapper(dbSession).deleteByScimUuid(scimUuid); + } + + private static ScimGroupMapper mapper(DbSession session) { + return session.getMapper(ScimGroupMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDto.java new file mode 100644 index 00000000000..961c53a3529 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDto.java @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.scim; + +public class ScimGroupDto { + + private final String scimGroupUuid; + private final String groupUuid; + + public ScimGroupDto(String scimGroupUuid, String groupUuid) { + this.scimGroupUuid = scimGroupUuid; + this.groupUuid = groupUuid; + } + + public String getScimGroupUuid() { + return scimGroupUuid; + } + + public String getGroupUuid() { + return groupUuid; + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupMapper.java new file mode 100644 index 00000000000..538181eeae2 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupMapper.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.scim; + +import java.util.List; +import javax.annotation.CheckForNull; +import org.apache.ibatis.annotations.Param; + +public interface ScimGroupMapper { + + List findAll(); + + @CheckForNull + ScimGroupDto findByScimUuid(@Param("scimGroupUuid") String scimGroupUuid); + + @CheckForNull + ScimGroupDto findByGroupUuid(@Param("groupUuid") String groupUuid); + + void insert(@Param("scimGroupDto") ScimGroupDto scimGroupDto); + + void deleteByGroupUuid(@Param("groupUuid") String groupUuid); + + void deleteByScimUuid(@Param("scimUuid") String scimUuid); +} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/scim/ScimGroupMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/scim/ScimGroupMapper.xml new file mode 100644 index 00000000000..cb58c126cea --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/scim/ScimGroupMapper.xml @@ -0,0 +1,52 @@ + + + + + + + scim_uuid as scimGroupUuid, + group_uuid as groupUuid + + + + + + + + + + insert into scim_groups ( + scim_uuid, + group_uuid + ) values ( + #{scimGroupDto.scimGroupUuid,jdbcType=VARCHAR}, + #{scimGroupDto.groupUuid,jdbcType=VARCHAR} + ) + + + + delete from scim_groups where group_uuid = #{groupUuid, jdbcType=VARCHAR} + + + + delete from scim_groups where scim_uuid = #{scimUuid, 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 cdab891a083..0662a2a6a89 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -914,6 +914,13 @@ CREATE TABLE "SCANNER_ANALYSIS_CACHE"( ); ALTER TABLE "SCANNER_ANALYSIS_CACHE" ADD CONSTRAINT "PK_SCANNER_ANALYSIS_CACHE" PRIMARY KEY("BRANCH_UUID"); +CREATE TABLE "SCIM_GROUPS"( + "SCIM_UUID" CHARACTER VARYING(40) NOT NULL, + "GROUP_UUID" CHARACTER VARYING(40) NOT NULL +); +ALTER TABLE "SCIM_GROUPS" ADD CONSTRAINT "PK_SCIM_GROUPS" PRIMARY KEY("SCIM_UUID"); +CREATE UNIQUE INDEX "UNIQ_SCIM_GROUP_UUID" ON "SCIM_GROUPS"("GROUP_UUID" NULLS FIRST); + CREATE TABLE "SCIM_USERS"( "SCIM_UUID" CHARACTER VARYING(40) NOT NULL, "USER_UUID" CHARACTER VARYING(40) NOT NULL diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/scim/ScimGroupDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/scim/ScimGroupDaoTest.java new file mode 100644 index 00000000000..95fc808f037 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/scim/ScimGroupDaoTest.java @@ -0,0 +1,137 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.scim; + +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.db.DbTester; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Fail.fail; +import static org.assertj.core.groups.Tuple.tuple; + +@RunWith(DataProviderRunner.class) +public class ScimGroupDaoTest { + @Rule + public DbTester db = DbTester.create(); + private final ScimGroupDao scimGroupDao = db.getDbClient().scimGroupDao(); + + @Test + public void findAll_ifNoData_returnsEmptyList() { + assertThat(scimGroupDao.findAll(db.getSession())).isEmpty(); + } + + @Test + public void findAll_returnsAllEntries() { + ScimGroupDto scimGroup1 = insertScimGroup(); + ScimGroupDto scimGroup2 = insertScimGroup(); + + List underTest = scimGroupDao.findAll(db.getSession()); + + assertThat(underTest).hasSize(2) + .extracting(ScimGroupDto::getGroupUuid, ScimGroupDto::getScimGroupUuid) + .containsExactlyInAnyOrder( + tuple(scimGroup1.getGroupUuid(), scimGroup1.getScimGroupUuid()), + tuple(scimGroup2.getGroupUuid(), scimGroup2.getScimGroupUuid()) + ); + } + + @Test + public void findByScimUuid_whenScimUuidNotFound_shouldReturnEmptyOptional() { + assertThat(scimGroupDao.findByScimUuid(db.getSession(), "unknownId")).isEmpty(); + } + + @Test + public void findByScimUuid_whenScimUuidFound_shouldReturnDto() { + ScimGroupDto scimGroupDto = insertScimGroup(); + insertScimGroup(); + + ScimGroupDto underTest = scimGroupDao.findByScimUuid(db.getSession(), scimGroupDto.getScimGroupUuid()) + .orElseGet(() -> fail("Group not found")); + + assertThat(underTest.getScimGroupUuid()).isEqualTo(scimGroupDto.getScimGroupUuid()); + assertThat(underTest.getGroupUuid()).isEqualTo(scimGroupDto.getGroupUuid()); + } + + @Test + public void findByGroupUuid_whenScimUuidNotFound_shouldReturnEmptyOptional() { + assertThat(scimGroupDao.findByGroupUuid(db.getSession(), "unknownId")).isEmpty(); + } + + @Test + public void findByGroupUuid_whenScimUuidFound_shouldReturnDto() { + ScimGroupDto scimGroupDto = insertScimGroup(); + insertScimGroup(); + + ScimGroupDto underTest = scimGroupDao.findByGroupUuid(db.getSession(), scimGroupDto.getGroupUuid()) + .orElseGet(() -> fail("Group not found")); + + assertThat(underTest.getScimGroupUuid()).isEqualTo(scimGroupDto.getScimGroupUuid()); + assertThat(underTest.getGroupUuid()).isEqualTo(scimGroupDto.getGroupUuid()); + } + + @Test + public void enableScimForGroup_addsGroupToScimGroups() { + ScimGroupDto underTest = scimGroupDao.enableScimForGroup(db.getSession(), "sqGroup1"); + + assertThat(underTest.getScimGroupUuid()).isNotBlank(); + ScimGroupDto scimGroupDto = scimGroupDao.findByScimUuid(db.getSession(), underTest.getScimGroupUuid()).orElseThrow(); + assertThat(underTest.getScimGroupUuid()).isEqualTo(scimGroupDto.getScimGroupUuid()); + assertThat(underTest.getGroupUuid()).isEqualTo(scimGroupDto.getGroupUuid()); + } + + @Test + public void deleteByGroupUuid_shouldDeleteScimGroup() { + ScimGroupDto scimGroupDto = insertScimGroup(); + + scimGroupDao.deleteByGroupUuid(db.getSession(), scimGroupDto.getGroupUuid()); + + assertThat(scimGroupDao.findAll(db.getSession())).isEmpty(); + } + + @Test + public void deleteByScimUuid_shouldDeleteScimGroup() { + ScimGroupDto scimGroupDto1 = insertScimGroup(); + ScimGroupDto scimGroupDto2 = insertScimGroup(); + + scimGroupDao.deleteByScimUuid(db.getSession(), scimGroupDto1.getScimGroupUuid()); + + List remainingGroups = scimGroupDao.findAll(db.getSession()); + assertThat(remainingGroups).hasSize(1); + + ScimGroupDto remainingGroup = remainingGroups.get(0); + assertThat(remainingGroup.getScimGroupUuid()).isEqualTo(scimGroupDto2.getScimGroupUuid()); + assertThat(remainingGroup.getGroupUuid()).isEqualTo(scimGroupDto2.getGroupUuid()); + } + + @Test + public void deleteFromGroupUuid_shouldNotFail_whenNoGroup() { + assertThatCode(() -> scimGroupDao.deleteByGroupUuid(db.getSession(), randomAlphanumeric(6))).doesNotThrowAnyException(); + } + + private ScimGroupDto insertScimGroup() { + return scimGroupDao.enableScimForGroup(db.getSession(), randomAlphanumeric(40)); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTable.java new file mode 100644 index 00000000000..a0fb98bb028 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTable.java @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v100; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.CreateTableChange; + +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +public class CreateScimGroupsTable extends CreateTableChange { + static final String TABLE_NAME = "scim_groups"; + + public CreateScimGroupsTable(Database db) { + super(db, TABLE_NAME); + } + + @Override + public void execute(Context context, String tableName) throws SQLException { + context.execute(new CreateTableBuilder(getDialect(), tableName) + .addPkColumn(newVarcharColumnDefBuilder().setColumnName("scim_uuid").setIsNullable(false).setLimit(UUID_SIZE).build()) + .addColumn(newVarcharColumnDefBuilder().setColumnName("group_uuid").setIsNullable(false).setLimit(UUID_SIZE).build()) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuid.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuid.java new file mode 100644 index 00000000000..a70ee752126 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuid.java @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v100; + +import com.google.common.annotations.VisibleForTesting; +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.version.v100.CreateScimGroupsTable.TABLE_NAME; + +public class CreateUniqueIndexForScimGroupsUuid extends DdlChange { + + @VisibleForTesting + static final String COLUMN_NAME = "group_uuid"; + + @VisibleForTesting + static final String INDEX_NAME = "uniq_scim_group_uuid"; + + public CreateUniqueIndexForScimGroupsUuid(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + createUserUuidUniqueIndex(context, connection); + } + } + + private static void createUserUuidUniqueIndex(Context context, Connection connection) { + if (!DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) { + context.execute(new CreateIndexBuilder() + .setTable(TABLE_NAME) + .setName(INDEX_NAME) + .addColumn(COLUMN_NAME) + .setUnique(true) + .build()); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/DbVersion100.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/DbVersion100.java index 9d0015944c8..331d3b25b24 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/DbVersion100.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/DbVersion100.java @@ -51,6 +51,8 @@ public class DbVersion100 implements DbVersion { .add(10_0_007, "Drop column 'root_uuid' in the 'Components' table", DropRootUuidInComponents.class) .add(10_0_008, "Update value of 'user_local' in the 'users' table", UpdateUserLocalValueInUsers.class) .add(10_0_009, "Make column 'user_local' not nullable in the 'users' table", MakeColumnUserLocalNotNullableInUsers.class) + .add(10_0_010, "Create 'scim_groups' table", CreateScimGroupsTable.class) + .add(10_0_011, "Create unique index on scim_groups.group_uuid", CreateUniqueIndexForScimGroupsUuid.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTableTest.java new file mode 100644 index 00000000000..466cc51c2cf --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTableTest.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v100; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.version.v100.CreateScimGroupsTable.TABLE_NAME; + +public class CreateScimGroupsTableTest { + @Rule + public final CoreDbTester db = CoreDbTester.createEmpty(); + + private final DdlChange underTest = new CreateScimGroupsTable(db.database()); + + @Test + public void migration_should_create_a_table() throws SQLException { + db.assertTableDoesNotExist(TABLE_NAME); + + underTest.execute(); + + db.assertTableExists(TABLE_NAME); + db.assertColumnDefinition(TABLE_NAME, "scim_uuid", Types.VARCHAR, UUID_SIZE, false); + db.assertColumnDefinition(TABLE_NAME, "group_uuid", Types.VARCHAR, UUID_SIZE, false); + db.assertPrimaryKey(TABLE_NAME, "pk_scim_groups", "scim_uuid"); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertTableDoesNotExist(TABLE_NAME); + + underTest.execute(); + // re-entrant + underTest.execute(); + + db.assertTableExists(TABLE_NAME); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest.java new file mode 100644 index 00000000000..fab009df53f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v100; + +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.DdlChange; + +import static org.sonar.server.platform.db.migration.version.v100.CreateScimGroupsTable.TABLE_NAME; +import static org.sonar.server.platform.db.migration.version.v100.CreateUniqueIndexForScimGroupsUuid.COLUMN_NAME; +import static org.sonar.server.platform.db.migration.version.v100.CreateUniqueIndexForScimGroupsUuid.INDEX_NAME; + +public class CreateUniqueIndexForScimGroupsUuidTest { + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateUniqueIndexForScimGroupsUuidTest.class, "schema.sql"); + + private final DdlChange underTest = new CreateUniqueIndexForScimGroupsUuid(db.database()); + + @Test + public void migration_should_create_index() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + + underTest.execute(); + + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + + underTest.execute(); + underTest.execute(); + + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest/schema.sql new file mode 100644 index 00000000000..7028259571b --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE "SCIM_GROUPS"( + "SCIM_UUID" CHARACTER VARYING(40) NOT NULL, + "GROUP_UUID" CHARACTER VARYING(40) NOT NULL +); +ALTER TABLE "SCIM_GROUPS" ADD CONSTRAINT "PK_SCIM_GROUPS" PRIMARY KEY("SCIM_UUID");