diff options
author | Antoine Vigneau <antoine.vigneau@sonarsource.com> | 2023-05-02 17:22:50 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-05-11 20:03:14 +0000 |
commit | 6f601b7241ece842321d2c5cd1e1a75b28e2f850 (patch) | |
tree | 474d15e7ec47295cec6ac203ed45976e039414e2 | |
parent | 8643251e97d9975814a28a348e76b4a3f8d38ec9 (diff) | |
download | sonarqube-6f601b7241ece842321d2c5cd1e1a75b28e2f850.tar.gz sonarqube-6f601b7241ece842321d2c5cd1e1a75b28e2f850.zip |
SONAR-19085 Create and update GitHub groups
9 files changed, 277 insertions, 9 deletions
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/user/ExternalGroupDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/user/ExternalGroupDaoIT.java index 26d47127977..6de7ba2c279 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/user/ExternalGroupDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/user/ExternalGroupDaoIT.java @@ -21,6 +21,7 @@ package org.sonar.db.user; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.sonar.db.DbSession; @@ -30,6 +31,10 @@ import static org.assertj.core.api.Assertions.assertThat; public class ExternalGroupDaoIT { + private static final String GROUP_UUID = "uuid"; + private static final String EXTERNAL_ID = "external_id"; + private static final String EXTERNAL_IDENTITY_PROVIDER = "external_identity_provider"; + @Rule public final DbTester db = DbTester.create(); @@ -41,17 +46,28 @@ public class ExternalGroupDaoIT { @Test public void insert_savesExternalGroup() { - GroupDto localGroup = insertGroup("12345"); + GroupDto localGroup = insertGroup(GROUP_UUID); insertGroup("67689"); - ExternalGroupDto externalGroupDto = externalGroup("12345", "providerId"); + ExternalGroupDto externalGroupDto = externalGroup(GROUP_UUID, EXTERNAL_IDENTITY_PROVIDER); underTest.insert(dbSession, externalGroupDto); - List<ExternalGroupDto> savedGroups = underTest.selectByIdentityProvider(dbSession, "providerId"); + List<ExternalGroupDto> savedGroups = underTest.selectByIdentityProvider(dbSession, EXTERNAL_IDENTITY_PROVIDER); assertThat(savedGroups) .hasSize(1) .contains(createExternalGroupDto(localGroup.getName(), externalGroupDto)); } @Test + public void selectByGroupUuid_shouldReturnExternalGroup() { + ExternalGroupDto expectedExternalGroupDto = new ExternalGroupDto(GROUP_UUID, EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER); + underTest.insert(dbSession, expectedExternalGroupDto); + + Optional<ExternalGroupDto> actualExternalGroupDto = underTest.selectByGroupUuid(dbSession, GROUP_UUID); + + assertThat(actualExternalGroupDto).isPresent(); + compareExpectedAndActualExternalGroupDto(expectedExternalGroupDto, actualExternalGroupDto.get()); + } + + @Test public void selectByIdentityProvider_returnOnlyGroupForTheIdentityProvider() { List<ExternalGroupDto> expectedGroups = createAndInsertExternalGroupDtos("provider1", 3); createAndInsertExternalGroupDtos("provider2", 1); @@ -60,6 +76,25 @@ public class ExternalGroupDaoIT { } @Test + public void selectByExternalIdAndIdentityProvider_shouldReturnOnlyMatchingExternalGroup() { + ExternalGroupDto expectedExternalGroupDto = new ExternalGroupDto(GROUP_UUID, EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER); + underTest.insert(dbSession, expectedExternalGroupDto); + underTest.insert(dbSession, new ExternalGroupDto(GROUP_UUID + "1", EXTERNAL_ID, "another_external_identity_provider")); + underTest.insert(dbSession, new ExternalGroupDto(GROUP_UUID + "2", "another_external_id", EXTERNAL_IDENTITY_PROVIDER)); + underTest.insert(dbSession, new ExternalGroupDto(GROUP_UUID + "3", "whatever", "whatever")); + + Optional<ExternalGroupDto> actualExternalGroupDto = underTest.selectByExternalIdAndIdentityProvider(dbSession, EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER); + + compareExpectedAndActualExternalGroupDto(expectedExternalGroupDto, actualExternalGroupDto.get()); + } + + private void compareExpectedAndActualExternalGroupDto(ExternalGroupDto expectedExternalGroupDto, ExternalGroupDto actualExternalGroupDto) { + assertThat(actualExternalGroupDto) + .usingRecursiveComparison() + .isEqualTo(expectedExternalGroupDto); + } + + @Test public void deleteByGroupUuid_deletesTheGroup() { List<ExternalGroupDto> insertedGroups = createAndInsertExternalGroupDtos("provider1", 3); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupDao.java index 9980aaa60ce..743236638fb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupDao.java @@ -20,6 +20,7 @@ package org.sonar.db.user; import java.util.List; +import java.util.Optional; import org.sonar.db.Dao; import org.sonar.db.DbSession; @@ -29,6 +30,10 @@ public class ExternalGroupDao implements Dao { mapper(dbSession).insert(externalGroupDto); } + public Optional<ExternalGroupDto> selectByGroupUuid(DbSession dbSession, String groupUuid) { + return mapper(dbSession).selectByGroupUuid(groupUuid); + } + public List<ExternalGroupDto> selectByIdentityProvider(DbSession dbSession, String identityProvider) { return mapper(dbSession).selectByIdentityProvider(identityProvider); } @@ -37,6 +42,10 @@ public class ExternalGroupDao implements Dao { mapper(dbSession).deleteByGroupUuid(groupUuid); } + public Optional<ExternalGroupDto> selectByExternalIdAndIdentityProvider(DbSession dbSession, String externalId, String identityProvider) { + return mapper(dbSession).selectByExternalIdAndIdentityProvider(externalId, identityProvider); + } + private static ExternalGroupMapper mapper(DbSession session) { return session.getMapper(ExternalGroupMapper.class); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupMapper.java index b456be15a66..908aae7664f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/ExternalGroupMapper.java @@ -20,15 +20,18 @@ package org.sonar.db.user; import java.util.List; +import java.util.Optional; import org.apache.ibatis.annotations.Param; public interface ExternalGroupMapper { void insert(ExternalGroupDto externalGroupDto); - List<ExternalGroupDto> selectByIdentityProvider(String identityProvider); + Optional<ExternalGroupDto> selectByGroupUuid(@Param("groupUuid") String groupUuid); - void deleteByGroupUuid(@Param("groupUuid") String groupUuid); + List<ExternalGroupDto> selectByIdentityProvider(@Param("identityProvider") String identityProvider); + Optional<ExternalGroupDto> selectByExternalIdAndIdentityProvider(@Param("externalId") String externalId, @Param("identityProvider") String identityProvider); + void deleteByGroupUuid(@Param("groupUuid") String groupUuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupQuery.java index a8cf01456dc..d169534193b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupQuery.java @@ -30,7 +30,7 @@ public class GroupQuery { private final String searchText; private final String isManagedSqlClause; - private GroupQuery(@Nullable String searchText, @Nullable String isManagedSqlClause) { + GroupQuery(@Nullable String searchText, @Nullable String isManagedSqlClause) { this.searchText = searchTextToSearchTextSql(searchText); this.isManagedSqlClause = isManagedSqlClause; } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/ExternalGroupMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/ExternalGroupMapper.xml index be2db7475b4..a63497ece33 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/ExternalGroupMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/ExternalGroupMapper.xml @@ -4,6 +4,12 @@ <mapper namespace="org.sonar.db.user.ExternalGroupMapper"> + <sql id="externalGroupColumns"> + eg.group_uuid as groupUuid, + eg.external_group_id as external_id, + eg.external_identity_provider as externalIdentityProvider + </sql> + <insert id="insert" useGeneratedKeys="false" parameterType="ExternalGroup"> insert into external_groups ( group_uuid, @@ -16,17 +22,30 @@ ) </insert> + <select id="selectByGroupUuid" parameterType="String" resultType="ExternalGroup"> + SELECT + <include refid="externalGroupColumns"/> + FROM external_groups eg + WHERE eg.group_uuid=#{groupUuid,jdbcType=VARCHAR} + </select> + <select id="selectByIdentityProvider" parameterType="String" resultType="ExternalGroup"> SELECT - eg.group_uuid as groupUuid, - eg.external_group_id as external_id, - eg.external_identity_provider as externalIdentityProvider, + <include refid="externalGroupColumns"/>, g.name as name FROM external_groups eg LEFT JOIN groups g ON eg.group_uuid = g.uuid WHERE eg.external_identity_provider=#{identityProvider,jdbcType=VARCHAR} </select> + <select id="selectByExternalIdAndIdentityProvider" parameterType="String" resultType="ExternalGroup"> + SELECT + <include refid="externalGroupColumns"/> + FROM external_groups eg + WHERE eg.external_group_id=#{externalId,jdbcType=VARCHAR} + AND eg.external_identity_provider=#{identityProvider,jdbcType=VARCHAR} + </select> + <delete id="deleteByGroupUuid" parameterType="String"> delete from external_groups where group_uuid = #{groupUuid, jdbcType=VARCHAR} </delete> diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java index 45a2815630b..4513d2608e3 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java @@ -163,6 +163,12 @@ public class UserDbTester { db.commit(); } + public ExternalGroupDto insertExternalGroup(ExternalGroupDto dto) { + db.getDbClient().externalGroupDao().insert(db.getSession(), dto); + db.commit(); + return dto; + } + public ScimGroupDto insertScimGroup(GroupDto dto) { ScimGroupDto result = db.getDbClient().scimGroupDao().enableScimForGroup(db.getSession(), dto.getUuid()); db.commit(); @@ -184,6 +190,14 @@ public class UserDbTester { return db.getDbClient().groupDao().selectByName(db.getSession(), name); } + public int countAllGroups() { + return db.getDbClient().groupDao().countByQuery(db.getSession(), new GroupQuery(null, null)); + } + + public Optional<ExternalGroupDto> selectExternalGroupByGroupUuid(String groupUuid) { + return db.getDbClient().externalGroupDao().selectByGroupUuid(db.getSession(), groupUuid); + } + // GROUP MEMBERSHIP public UserGroupDto insertMember(GroupDto group, UserDto user) { diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/ExternalGroupServiceIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/ExternalGroupServiceIT.java new file mode 100644 index 00000000000..a355dbfd164 --- /dev/null +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/ExternalGroupServiceIT.java @@ -0,0 +1,94 @@ +/* + * 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.usergroups.ws; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.user.ExternalGroupDto; +import org.sonar.db.user.GroupDto; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ExternalGroupServiceIT { + + private static final String GROUP_NAME = "GROUP_NAME"; + private static final String EXTERNAL_ID = "EXTERNAL_ID"; + private static final String EXTERNAL_IDENTITY_PROVIDER = "EXTERNAL_IDENTITY_PROVIDER"; + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + private final DbClient dbClient = dbTester.getDbClient(); + private final DbSession dbSession = dbTester.getSession(); + private final GroupService groupService = new GroupService(dbClient, UuidFactoryFast.getInstance()); + + private final ExternalGroupService externalGroupService = new ExternalGroupService(dbClient, groupService); + + @Test + public void createOrUpdateExternalGroup_whenNewGroup_shouldCreateIt() { + externalGroupService.createOrUpdateExternalGroup(dbSession, new GroupRegistration(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER, GROUP_NAME)); + + assertGroupAndExternalGroup(); + } + + @Test + public void createOrUpdateExternalGroup_whenExistingLocalGroup_shouldMatchAndMakeItExternal() { + dbTester.users().insertGroup(GROUP_NAME); + + externalGroupService.createOrUpdateExternalGroup(dbSession, new GroupRegistration(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER, GROUP_NAME)); + + assertThat(dbTester.users().countAllGroups()).isEqualTo(1); + assertGroupAndExternalGroup(); + } + + @Test + public void createOrUpdateExternalGroup_whenExistingExternalGroup_shouldUpdate() { + dbTester.users().insertDefaultGroup(); + GroupDto existingGroupDto = dbTester.users().insertGroup(GROUP_NAME); + dbTester.users().insertExternalGroup(new ExternalGroupDto(existingGroupDto.getUuid(), EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER)); + + String updatedGroupName = "updated_" + GROUP_NAME; + externalGroupService.createOrUpdateExternalGroup(dbSession, new GroupRegistration(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER, updatedGroupName)); + + Optional<GroupDto> groupDto = dbTester.users().selectGroup(updatedGroupName); + assertThat(groupDto) + .isPresent().get() + .extracting(GroupDto::getName) + .isEqualTo(updatedGroupName); + } + + private void assertGroupAndExternalGroup() { + Optional<GroupDto> groupDto = dbTester.users().selectGroup(GROUP_NAME); + assertThat(groupDto) + .isPresent().get() + .extracting(GroupDto::getName).isEqualTo(GROUP_NAME); + + assertThat((dbTester.users().selectExternalGroupByGroupUuid(groupDto.get().getUuid()))) + .isPresent().get() + .extracting(ExternalGroupDto::externalId, ExternalGroupDto::externalIdentityProvider) + .containsExactly(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/ExternalGroupService.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/ExternalGroupService.java new file mode 100644 index 00000000000..65ead0b66d5 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/ExternalGroupService.java @@ -0,0 +1,71 @@ +/* + * 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.usergroups.ws; + +import java.util.Optional; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.user.ExternalGroupDto; +import org.sonar.db.user.GroupDto; + +public class ExternalGroupService { + + private final DbClient dbClient; + private final GroupService groupService; + + public ExternalGroupService(DbClient dbClient, GroupService groupService) { + this.dbClient = dbClient; + this.groupService = groupService; + } + + public void createOrUpdateExternalGroup(DbSession dbSession, GroupRegistration groupRegistration) { + Optional<GroupDto> groupDto = retrieveGroupByItsExternalInformation(dbSession, groupRegistration); + if (groupDto.isPresent()) { + updateExternalGroup(dbSession, groupDto.get(), groupRegistration.name()); + } else { + createOrMatchExistingLocalGroup(dbSession, groupRegistration); + } + } + + private Optional<GroupDto> retrieveGroupByItsExternalInformation(DbSession dbSession, GroupRegistration groupRegistration) { + Optional<ExternalGroupDto> externalGroupDto = + dbClient.externalGroupDao().selectByExternalIdAndIdentityProvider(dbSession, groupRegistration.externalId(), groupRegistration.externalIdentityProvider()); + return externalGroupDto.flatMap(existingExternalGroupDto -> Optional.ofNullable(dbClient.groupDao().selectByUuid(dbSession, existingExternalGroupDto.groupUuid()))); + } + + private void updateExternalGroup(DbSession dbSession, GroupDto groupDto, String newName) { + groupService.updateGroup(dbSession, groupDto, newName); + } + + private void createOrMatchExistingLocalGroup(DbSession dbSession, GroupRegistration groupRegistration) { + GroupDto groupDto = findOrCreateLocalGroup(dbSession, groupRegistration); + createExternalGroup(dbSession, groupDto.getUuid(), groupRegistration); + } + + private GroupDto findOrCreateLocalGroup(DbSession dbSession, GroupRegistration groupRegistration) { + Optional<GroupDto> groupDto = groupService.findGroup(dbSession, groupRegistration.name()); + return groupDto.orElseGet(() -> groupService.createGroup(dbSession, groupRegistration.name(), null)); + } + + private void createExternalGroup(DbSession dbSession, String groupUuid, GroupRegistration groupRegistration) { + dbClient.externalGroupDao().insert(dbSession, new ExternalGroupDto(groupUuid, groupRegistration.externalId(), groupRegistration.externalIdentityProvider())); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupRegistration.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupRegistration.java new file mode 100644 index 00000000000..cafa26e362b --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupRegistration.java @@ -0,0 +1,23 @@ +/* + * 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.usergroups.ws; + +public record GroupRegistration(String externalId, String externalIdentityProvider, String name) { +} |