]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18525 SCIM Group data model (Mapper, DTO, DAO, Migrations)
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>
Wed, 22 Feb 2023 13:44:24 +0000 (14:44 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 22 Mar 2023 20:04:06 +0000 (20:04 +0000)
16 files changed:
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDao.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/scim/ScimGroupMapper.java [new file with mode: 0644]
server/sonar-db-dao/src/main/resources/org/sonar/db/scim/ScimGroupMapper.xml [new file with mode: 0644]
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-dao/src/test/java/org/sonar/db/scim/ScimGroupDaoTest.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuid.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v100/DbVersion100.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateScimGroupsTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v100/CreateUniqueIndexForScimGroupsUuidTest/schema.sql [new file with mode: 0644]

index 058a80af47d96479a1ed31f7836a2bcfb27f46ad..16c98a0e2848dbe44d2556d4581f99d819207723 100644 (file)
@@ -99,6 +99,7 @@ public final class SqTables {
     "rule_repositories",
     "scanner_analysis_cache",
     "schema_migrations",
+    "scim_groups",
     "scim_users",
     "session_tokens",
     "snapshots",
index ccc02ce1b88cddd26b96908c344b883ece62d1b2..c3598e3b8328afed8e65eb36a0b69d5ae08e1f71 100644 (file)
@@ -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,
index 0caa22c4ac86595ebf4785aecbf0a13723ae8655..906edafaee23f3cb761cc81dbcc4e7e9fae1d81e 100644 (file)
@@ -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;
+  }
 }
+
index 54647f71c9c56f6f58cbd4eab3110fded9c2facb..78dca8e791c609bcdb9faf5730532be88aabc800 100644 (file)
@@ -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 (file)
index 0000000..fad37c6
--- /dev/null
@@ -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<ScimGroupDto> findAll(DbSession dbSession) {
+    return mapper(dbSession).findAll();
+  }
+
+  public Optional<ScimGroupDto> findByScimUuid(DbSession dbSession, String scimGroupUuid) {
+    return Optional.ofNullable(mapper(dbSession).findByScimUuid(scimGroupUuid));
+  }
+
+  public Optional<ScimGroupDto> 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 (file)
index 0000000..961c53a
--- /dev/null
@@ -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 (file)
index 0000000..538181e
--- /dev/null
@@ -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<ScimGroupDto> 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 (file)
index 0000000..cb58c12
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.scim.ScimGroupMapper">
+
+  <sql id="scimGroupsColumns">
+      scim_uuid as scimGroupUuid,
+      group_uuid as groupUuid
+  </sql>
+
+  <select id="findAll" resultType="org.sonar.db.scim.ScimGroupDto">
+    select
+    <include refid="scimGroupsColumns"/>
+      from scim_groups
+  </select>
+
+  <select id="findByScimUuid" parameterType="String" resultType="org.sonar.db.scim.ScimGroupDto">
+    select
+    <include refid="scimGroupsColumns"/>
+      from scim_groups
+    where
+      scim_uuid = #{scimGroupUuid,jdbcType=VARCHAR}
+  </select>
+
+  <select id="findByGroupUuid" parameterType="String" resultType="org.sonar.db.scim.ScimGroupDto">
+    select
+    <include refid="scimGroupsColumns"/>
+      from scim_groups
+    where
+      group_uuid = #{groupUuid,jdbcType=VARCHAR}
+  </select>
+
+  <insert id="insert" parameterType="map" useGeneratedKeys="false">
+    insert into scim_groups (
+      scim_uuid,
+      group_uuid
+    ) values (
+      #{scimGroupDto.scimGroupUuid,jdbcType=VARCHAR},
+      #{scimGroupDto.groupUuid,jdbcType=VARCHAR}
+    )
+  </insert>
+
+  <delete id="deleteByGroupUuid" parameterType="String">
+    delete from scim_groups where group_uuid = #{groupUuid, jdbcType=VARCHAR}
+  </delete>
+
+  <delete id="deleteByScimUuid" parameterType="String">
+    delete from scim_groups where scim_uuid = #{scimUuid, jdbcType=VARCHAR}
+  </delete>
+
+</mapper>
+
index cdab891a083e35812e9126b7fee647dcda04a710..0662a2a6a890b03738438942612bd0693fee1226 100644 (file)
@@ -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 (file)
index 0000000..95fc808
--- /dev/null
@@ -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<ScimGroupDto> 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<ScimGroupDto> 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 (file)
index 0000000..a0fb98b
--- /dev/null
@@ -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 (file)
index 0000000..a70ee75
--- /dev/null
@@ -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());
+    }
+  }
+}
index 9d0015944c8b2622d93e1db310cb4a92c673fd81..331d3b25b240da59a763c4178b24d16ae63e37fd 100644 (file)
@@ -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 (file)
index 0000000..466cc51
--- /dev/null
@@ -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 (file)
index 0000000..fab009d
--- /dev/null
@@ -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 (file)
index 0000000..7028259
--- /dev/null
@@ -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");