]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18532 Add support for group membership DELETE in /api/scim/v2/Groups
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>
Mon, 27 Feb 2023 16:07:40 +0000 (17:07 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 22 Mar 2023 20:04:06 +0000 (20:04 +0000)
server/sonar-db-dao/src/it/java/org/sonar/db/user/UserGroupDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserGroupMapper.xml
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/user/UserDbTester.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/DeleteActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/DeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupService.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/UserGroupsModule.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/GroupServiceTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/UserGroupsModuleTest.java

index a537b80da4889a7fe9137e8525ddff61f9dcac01..dcca13431143b5ec1d4668489ef07cf7fbeac872 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.db.user;
 
+import java.util.List;
 import java.util.Set;
 import org.junit.Rule;
 import org.junit.Test;
@@ -117,4 +118,47 @@ public class UserGroupDaoIT {
     assertThat(dbTester.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbTester.getSession(), user1.getUuid())).isEmpty();
     assertThat(dbTester.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbTester.getSession(), user2.getUuid())).containsOnly(group1.getUuid(), group2.getUuid());
   }
+
+  @Test
+  public void deleteFromGroupByUserUuids_shouldOnlyRemoveSpecificUsersFromOneGroup() {
+    GroupDto group1 = dbTester.users().insertGroup();
+    GroupDto group2 = dbTester.users().insertGroup();
+
+    UserDto user1 = dbTester.users().insertUser();
+    UserDto user2 = dbTester.users().insertUser();
+    UserDto user3 = dbTester.users().insertUser();
+
+    dbTester.users().insertMembers(group1, user1, user2, user3);
+    dbTester.users().insertMembers(group2, user1, user2, user3);
+
+    underTest.deleteFromGroupByUserUuids(dbSession, group1, Set.of(user1, user2));
+
+    assertThat(dbTester.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbTester.getSession(), user1.getUuid())).containsOnly(group2.getUuid());
+    assertThat(dbTester.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbTester.getSession(), user2.getUuid())).containsOnly(group2.getUuid());
+    assertThat(dbTester.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbTester.getSession(), user3.getUuid())).containsAll(List.of(group1.getUuid(), group2.getUuid()));
+  }
+
+  @Test
+  public void selectAllScimUsersByGroupUuid_shouldOnlySelectScimUsersFromOneSpecificGroup() {
+    GroupDto scimGroup = dbTester.users().insertGroup();
+    dbTester.users().insertScimGroup(scimGroup);
+    GroupDto nonScimGroup = dbTester.users().insertGroup();
+
+    UserDto scimUser1 = dbTester.users().insertUser();
+    dbTester.users().insertScimUser(scimUser1);
+    UserDto scimUser2 = dbTester.users().insertUser();
+    dbTester.users().insertScimUser(scimUser2);
+    UserDto scimUser3 = dbTester.users().insertUser();
+    dbTester.users().insertScimUser(scimUser3);
+    UserDto nonScimUser = dbTester.users().insertUser();
+
+    dbTester.users().insertMembers(scimGroup, scimUser1, scimUser2, nonScimUser);
+    dbTester.users().insertMembers(nonScimGroup, scimUser3, nonScimUser);
+
+    Set<UserDto> scimUserDtos = underTest.selectScimMembersByGroupUuid(dbSession, scimGroup);
+    assertThat(scimUserDtos.stream().map(UserDto::getUuid).toList()).containsOnly(scimUser1.getUuid(), scimUser2.getUuid());
+
+    Set<UserDto> nonScimUserDtos = underTest.selectScimMembersByGroupUuid(dbSession, nonScimGroup);
+    assertThat(nonScimUserDtos.stream().map(UserDto::getUuid).toList()).containsOnly(scimUser3.getUuid());
+  }
 }
index 9d12ae9358d239e835efaf52f0c8a1684ae07193..520a695c310afc1497fb9c4bf3066f8ce398bf1b 100644 (file)
@@ -66,7 +66,22 @@ public class UserGroupDao implements Dao {
     }
   }
 
+  public void deleteFromGroupByUserUuids(DbSession dbSession, GroupDto groupDto, Set<UserDto> userDtos) {
+    int deletedRows = mapper(dbSession).deleteFromGroupByUserUuids(groupDto.getUuid(), userDtos.stream()
+      .map(UserDto::getUuid)
+      .toList());
+
+    if (deletedRows > 0) {
+      userDtos.forEach(userDto -> auditPersister.deleteUserFromGroup(dbSession, new UserGroupNewValue(userDto)));
+    }
+  }
+
+  public Set<UserDto> selectScimMembersByGroupUuid(DbSession dbSession, GroupDto groupDto) {
+    return mapper(dbSession).selectScimMembersByGroupUuid(groupDto.getUuid());
+  }
+
   private static UserGroupMapper mapper(DbSession session) {
     return session.getMapper(UserGroupMapper.class);
   }
+
 }
index 4c0b1268f1abad09c86c65af64c692257de33518..190db7fa5cbd5ccdf01718888f9f732c299a2e54 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.db.user;
 
+import java.util.List;
 import java.util.Set;
 import org.apache.ibatis.annotations.Param;
 
@@ -33,4 +34,8 @@ public interface UserGroupMapper {
   int deleteByGroupUuid(@Param("groupUuid") String groupUuid);
 
   int deleteByUserUuid(@Param("userUuid") String userUuid);
+
+  int deleteFromGroupByUserUuids(@Param("groupUuid") String groupUuid, @Param("userUuids") List<String> userUuids);
+
+  Set<UserDto> selectScimMembersByGroupUuid(@Param("groupUuid") String groupUuid);
 }
index 8151f29ea8360bfc6841d501b0fec5bfcb787809..30a0c99e3146abd554eb7c3b30722c1d06c428c8 100644 (file)
@@ -3,6 +3,29 @@
 
 <mapper namespace="org.sonar.db.user.UserGroupMapper">
 
+    <sql id="userColumns">
+      u.uuid as uuid,
+      u.login as login,
+      u.name as name,
+      u.email as email,
+      u.active as "active",
+      u.scm_accounts as "scmAccounts",
+      u.salt as "salt",
+      u.crypted_password as "cryptedPassword",
+      u.hash_method as "hashMethod",
+      u.external_id as "externalId",
+      u.external_login as "externalLogin",
+      u.external_identity_provider as "externalIdentityProvider",
+      u.user_local as "local",
+      u.reset_password as "resetPassword",
+      u.homepage_type as "homepageType",
+      u.homepage_parameter as "homepageParameter",
+      u.last_connection_date as "lastConnectionDate",
+      u.last_sonarlint_connection as "lastSonarlintConnectionDate",
+      u.created_at as "createdAt",
+      u.updated_at as "updatedAt"
+    </sql>
+
   <insert id="insert" parameterType="UserGroup" useGeneratedKeys="false">
     insert into groups_users (
     user_uuid,
     where gu.group_uuid=#{groupUuid,jdbcType=VARCHAR}
   </select>
 
+  <select id="selectScimMembersByGroupUuid" parameterType="string" resultType="User">
+    select
+    <include refid="userColumns"/>
+    from users u
+    inner join scim_users su on su.user_uuid = u.uuid
+    inner join groups_users gu on gu.user_uuid = u.uuid
+    where gu.group_uuid = #{groupUuid,jdbcType=VARCHAR}
+  </select>
+
   <delete id="delete" parameterType="map">
     delete from groups_users
     where user_uuid = #{userUuid,jdbcType=VARCHAR} and
     DELETE FROM groups_users WHERE user_uuid=#{userUuid,jdbcType=VARCHAR}
   </delete>
 
+  <delete id="deleteFromGroupByUserUuids" parameterType="string">
+    delete
+    from
+      groups_users
+    where
+      group_uuid = #{groupUuid, jdbcType=VARCHAR}
+    and user_uuid in
+    <foreach collection="userUuids" open="(" close=")" item="uuid" separator=",">
+      #{uuid, jdbcType=VARCHAR}
+    </foreach>
+  </delete>
 </mapper>
index 40f77ebcecd8245faeef674ef4e7dbaa47ae7b76..6664dae2eba1ad30cc2296f8a58ae1edaa08c626 100644 (file)
@@ -77,6 +77,12 @@ public class UserDbTester {
     return insertUser(dto);
   }
 
+  public ScimUserDto insertScimUser(UserDto userDto) {
+    ScimUserDto scimUserDto = dbClient.scimUserDao().enableScimForUser(db.getSession(), userDto.getUuid());
+    db.commit();
+    return scimUserDto;
+  }
+
   @SafeVarargs
   public final UserDto insertDisabledUser(Consumer<UserDto>... populators) {
     UserDto dto = UserTesting.newDisabledUser();
index 858ea93d32e06dbfd8bc15dace00fc99fafba369..c949c686c314eafa5ae735847c309e13c173e045 100644 (file)
@@ -35,7 +35,6 @@ import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.usergroups.DefaultGroupFinder;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
@@ -54,7 +53,8 @@ public class DeleteActionIT {
   public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
 
   private final ComponentDbTester componentTester = new ComponentDbTester(db);
-  private final WsActionTester ws = new WsActionTester(new DeleteAction(db.getDbClient(), userSession, newGroupWsSupport()));
+  private final GroupService groupService = new GroupService(db.getDbClient());
+  private final WsActionTester ws = new WsActionTester(new DeleteAction(db.getDbClient(), userSession, groupService));
 
   @Test
   public void verify_definition() {
@@ -280,9 +280,4 @@ public class DeleteActionIT {
   private TestRequest newRequest() {
     return ws.newRequest();
   }
-
-  private GroupWsSupport newGroupWsSupport() {
-    return new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()));
-  }
-
 }
index d8cb25807c948cb674a708541dc3ef9a70e65700..e9da2748bda070afb44a3151211f8d5c4434872b 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.user.GroupDto;
 import org.sonar.server.user.UserSession;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.defineGroupWsParameters;
@@ -39,12 +38,12 @@ public class DeleteAction implements UserGroupsWsAction {
 
   private final DbClient dbClient;
   private final UserSession userSession;
-  private final GroupWsSupport support;
+  private final GroupService groupService;
 
-  public DeleteAction(DbClient dbClient, UserSession userSession, GroupWsSupport support) {
+  public DeleteAction(DbClient dbClient, UserSession userSession, GroupService groupService) {
     this.dbClient = dbClient;
     this.userSession = userSession;
-    this.support = support;
+    this.groupService = groupService;
   }
 
   @Override
@@ -66,40 +65,13 @@ public class DeleteAction implements UserGroupsWsAction {
   @Override
   public void handle(Request request, Response response) throws Exception {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      GroupDto group = support.findGroupDto(dbSession, request);
       userSession.checkPermission(GlobalPermission.ADMINISTER);
 
-      support.checkGroupIsNotDefault(dbSession, group);
-      checkNotTryingToDeleteLastAdminGroup(dbSession, group);
-      removeGroupPermissions(dbSession, group);
-      removeFromPermissionTemplates(dbSession, group);
-      removeGroupMembers(dbSession, group);
-      dbClient.qProfileEditGroupsDao().deleteByGroup(dbSession, group);
-      dbClient.qualityGateGroupPermissionsDao().deleteByGroup(dbSession, group);
-      dbClient.scimGroupDao().deleteByGroupUuid(dbSession, group.getUuid());
-      dbClient.groupDao().deleteByUuid(dbSession, group.getUuid(), group.getName());
+      GroupDto groupDto = groupService.findGroupDtoOrThrow(dbSession, request.mandatoryParam(PARAM_GROUP_NAME));
+      groupService.delete(dbSession, groupDto);
 
       dbSession.commit();
       response.noContent();
     }
   }
-
-  private void checkNotTryingToDeleteLastAdminGroup(DbSession dbSession, GroupDto group) {
-    int remaining = dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingGroup(dbSession,
-      GlobalPermission.ADMINISTER.getKey(), group.getUuid());
-
-    checkArgument(remaining > 0, "The last system admin group cannot be deleted");
-  }
-
-  private void removeGroupPermissions(DbSession dbSession, GroupDto group) {
-    dbClient.roleDao().deleteGroupRolesByGroupUuid(dbSession, group.getUuid());
-  }
-
-  private void removeFromPermissionTemplates(DbSession dbSession, GroupDto group) {
-    dbClient.permissionTemplateDao().deleteByGroup(dbSession, group.getUuid(), group.getName());
-  }
-
-  private void removeGroupMembers(DbSession dbSession, GroupDto group) {
-    dbClient.userGroupDao().deleteByGroupUuid(dbSession, group.getUuid(), group.getName());
-  }
 }
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupService.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/GroupService.java
new file mode 100644 (file)
index 0000000..629c3d8
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.Set;
+import org.sonar.api.security.DefaultGroups;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+
+@ServerSide
+public class GroupService {
+
+  private final DbClient dbClient;
+
+  public GroupService(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  public GroupDto findGroupDtoOrThrow(DbSession dbSession, String groupName) {
+    return dbClient.groupDao()
+      .selectByName(dbSession, groupName)
+      .orElseThrow(() -> new NotFoundException(format("No group with name '%s'", groupName)));
+  }
+
+  public void delete(DbSession dbSession, GroupDto group) {
+    checkGroupIsNotDefault(dbSession, group);
+    checkNotTryingToDeleteLastAdminGroup(dbSession, group);
+
+    removeGroupPermissions(dbSession, group);
+    removeGroupFromPermissionTemplates(dbSession, group);
+    removeGroupMembers(dbSession, group);
+    removeGroupFromQualityProfileEdit(dbSession, group);
+    removeGroupFromQualityGateEdit(dbSession, group);
+    removeGroupScimLink(dbSession, group);
+    removeGroup(dbSession, group);
+  }
+
+  void checkGroupIsNotDefault(DbSession dbSession, GroupDto groupDto) {
+    GroupDto defaultGroup = findDefaultGroup(dbSession);
+    checkArgument(!defaultGroup.getUuid().equals(groupDto.getUuid()), "Default group '%s' cannot be used to perform this action", groupDto.getName());
+  }
+
+  private GroupDto findDefaultGroup(DbSession dbSession) {
+    return dbClient.groupDao().selectByName(dbSession, DefaultGroups.USERS)
+      .orElseThrow(() -> new IllegalStateException("Default group cannot be found"));
+  }
+
+  private void checkNotTryingToDeleteLastAdminGroup(DbSession dbSession, GroupDto group) {
+    int remaining = dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingGroup(dbSession,
+      GlobalPermission.ADMINISTER.getKey(), group.getUuid());
+
+    checkArgument(remaining > 0, "The last system admin group cannot be deleted");
+  }
+
+  private void removeGroupPermissions(DbSession dbSession, GroupDto group) {
+    dbClient.roleDao().deleteGroupRolesByGroupUuid(dbSession, group.getUuid());
+  }
+
+  private void removeGroupFromPermissionTemplates(DbSession dbSession, GroupDto group) {
+    dbClient.permissionTemplateDao().deleteByGroup(dbSession, group.getUuid(), group.getName());
+  }
+
+  private void removeGroupMembers(DbSession dbSession, GroupDto group) {
+    dbClient.userGroupDao().deleteByGroupUuid(dbSession, group.getUuid(), group.getName());
+  }
+
+  private void removeGroupFromQualityProfileEdit(DbSession dbSession, GroupDto group) {
+    dbClient.qProfileEditGroupsDao().deleteByGroup(dbSession, group);
+  }
+
+  private void removeGroupFromQualityGateEdit(DbSession dbSession, GroupDto group) {
+    dbClient.qualityGateGroupPermissionsDao().deleteByGroup(dbSession, group);
+  }
+
+  private void removeGroupScimLink(DbSession dbSession, GroupDto group) {
+    dbClient.scimGroupDao().deleteByGroupUuid(dbSession, group.getUuid());
+  }
+
+  private void removeGroup(DbSession dbSession, GroupDto group) {
+    dbClient.groupDao().deleteByUuid(dbSession, group.getUuid(), group.getName());
+  }
+
+  public void deleteScimMembersByGroup(DbSession dbSession, GroupDto groupDto) {
+    Set<UserDto> scimUsers = dbClient.userGroupDao().selectScimMembersByGroupUuid(dbSession, groupDto);
+    dbClient.userGroupDao().deleteFromGroupByUserUuids(dbSession, groupDto, scimUsers);
+  }
+}
index a47e8a1a2854e61cb3a8aed0469ffe755f141ed2..9ca4ba906925725832b6cd37c6c86f90919d477d 100644 (file)
@@ -28,6 +28,7 @@ public class UserGroupsModule extends Module {
     add(
       UserGroupsWs.class,
       GroupWsSupport.class,
+      GroupService.class,
       // actions
       SearchAction.class,
       CreateAction.class,
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/GroupServiceTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/usergroups/ws/GroupServiceTest.java
new file mode 100644 (file)
index 0000000..cc13153
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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 java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.api.security.DefaultGroups;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.permission.AuthorizationDao;
+import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.permission.template.PermissionTemplateDao;
+import org.sonar.db.qualitygate.QualityGateGroupPermissionsDao;
+import org.sonar.db.qualityprofile.QProfileEditGroupsDao;
+import org.sonar.db.scim.ScimGroupDao;
+import org.sonar.db.user.GroupDao;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.RoleDao;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserGroupDao;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GroupServiceTest {
+  private static final String GROUP_NAME = "GROUP_NAME";
+  private static final String GROUP_UUID = "GROUP_UUID";
+  @Mock
+  private DbSession dbSession;
+  @Mock
+  private DbClient dbClient;
+  @InjectMocks
+  private GroupService groupService;
+
+  @Before
+  public void setUp() {
+    mockNeededDaos();
+  }
+
+  @Test
+  public void findGroupDtoOrThrow_whenGroupExists_returnsIt() {
+    GroupDto groupDto = mockGroupDto();
+
+    when(dbClient.groupDao().selectByName(dbSession, GROUP_NAME))
+      .thenReturn(Optional.of(groupDto));
+
+    assertThat(groupService.findGroupDtoOrThrow(dbSession, GROUP_NAME))
+      .isEqualTo(groupDto);
+  }
+
+  @Test
+  public void findGroupDtoOrThrow_whenGroupDoesntExist_throw() {
+    when(dbClient.groupDao().selectByName(dbSession, GROUP_NAME))
+      .thenReturn(Optional.empty());
+
+    assertThatThrownBy(() -> groupService.findGroupDtoOrThrow(dbSession, GROUP_NAME))
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage(format("No group with name '%s'", GROUP_NAME));
+  }
+
+  @Test
+  public void delete_whenNotDefaultAndNotLastAdminGroup_deleteGroup() {
+    GroupDto groupDto = mockGroupDto();
+
+    when(dbClient.groupDao().selectByName(dbSession, DefaultGroups.USERS))
+      .thenReturn(Optional.of(new GroupDto().setUuid("another_group_uuid")));
+    when(dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingGroup(dbSession, GlobalPermission.ADMINISTER.getKey(), groupDto.getUuid()))
+      .thenReturn(2);
+
+    groupService.delete(dbSession, groupDto);
+
+    verifyGroupDelete(dbSession, groupDto);
+  }
+
+  @Test
+  public void delete_whenDefaultGroup_throwAndDontDeleteGroup() {
+    GroupDto groupDto = mockGroupDto();
+
+    when(dbClient.groupDao().selectByName(dbSession, DefaultGroups.USERS))
+      .thenReturn(Optional.of(groupDto));
+
+    assertThatThrownBy(() -> groupService.delete(dbSession, groupDto))
+      .isInstanceOf(IllegalArgumentException.class)
+      .hasMessage(format("Default group '%s' cannot be used to perform this action", GROUP_NAME));
+
+    verifyNoGroupDelete(dbSession, groupDto);
+  }
+
+  @Test
+  public void delete_whenLastAdminGroup_throwAndDontDeleteGroup() {
+    GroupDto groupDto = mockGroupDto();
+
+    when(dbClient.groupDao().selectByName(dbSession, DefaultGroups.USERS))
+      .thenReturn(Optional.of(new GroupDto().setUuid("another_group_uuid"))); // We must pass the default group check
+    when(dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingGroup(dbSession, GlobalPermission.ADMINISTER.getKey(), groupDto.getUuid()))
+      .thenReturn(0);
+
+    assertThatThrownBy(() -> groupService.delete(dbSession, groupDto))
+      .isInstanceOf(IllegalArgumentException.class)
+      .hasMessage("The last system admin group cannot be deleted");
+
+    verifyNoGroupDelete(dbSession, groupDto);
+  }
+
+  @Test
+  public void deleteAllScimUsersByGroup_() {
+    GroupDto groupDto = mockGroupDto();
+    Set<UserDto> userDtos = Set.of(new UserDto(), new UserDto());
+
+    when(dbClient.userGroupDao().selectScimMembersByGroupUuid(dbSession, groupDto))
+      .thenReturn(userDtos);
+
+    groupService.deleteScimMembersByGroup(dbSession, groupDto);
+
+    verify(dbClient.userGroupDao()).deleteFromGroupByUserUuids(dbSession, groupDto, userDtos);
+  }
+
+  private void mockNeededDaos() {
+    when(dbClient.authorizationDao()).thenReturn(mock(AuthorizationDao.class));
+    when(dbClient.roleDao()).thenReturn(mock(RoleDao.class));
+    when(dbClient.permissionTemplateDao()).thenReturn(mock(PermissionTemplateDao.class));
+    when(dbClient.userGroupDao()).thenReturn(mock(UserGroupDao.class));
+    when(dbClient.qProfileEditGroupsDao()).thenReturn(mock(QProfileEditGroupsDao.class));
+    when(dbClient.qualityGateGroupPermissionsDao()).thenReturn(mock(QualityGateGroupPermissionsDao.class));
+    when(dbClient.scimGroupDao()).thenReturn(mock(ScimGroupDao.class));
+    when(dbClient.groupDao()).thenReturn(mock(GroupDao.class));
+  }
+
+  private static GroupDto mockGroupDto() {
+    GroupDto groupDto = mock(GroupDto.class);
+    when(groupDto.getName()).thenReturn(GROUP_NAME);
+    when(groupDto.getUuid()).thenReturn(GROUP_UUID);
+    return groupDto;
+  }
+
+  private void verifyNoGroupDelete(DbSession dbSession, GroupDto groupDto) {
+    verify(dbClient.roleDao(), never()).deleteGroupRolesByGroupUuid(dbSession, groupDto.getUuid());
+    verify(dbClient.permissionTemplateDao(), never()).deleteByGroup(dbSession, groupDto.getUuid(), groupDto.getName());
+    verify(dbClient.userGroupDao(), never()).deleteByGroupUuid(dbSession, groupDto.getUuid(), groupDto.getName());
+    verify(dbClient.qProfileEditGroupsDao(), never()).deleteByGroup(dbSession, groupDto);
+    verify(dbClient.qualityGateGroupPermissionsDao(), never()).deleteByGroup(dbSession, groupDto);
+    verify(dbClient.scimGroupDao(), never()).deleteByGroupUuid(dbSession, groupDto.getUuid());
+    verify(dbClient.groupDao(), never()).deleteByUuid(dbSession, groupDto.getUuid(), groupDto.getName());
+  }
+
+  private void verifyGroupDelete(DbSession dbSession, GroupDto groupDto) {
+    verify(dbClient.roleDao()).deleteGroupRolesByGroupUuid(dbSession, groupDto.getUuid());
+    verify(dbClient.permissionTemplateDao()).deleteByGroup(dbSession, groupDto.getUuid(), groupDto.getName());
+    verify(dbClient.userGroupDao()).deleteByGroupUuid(dbSession, groupDto.getUuid(), groupDto.getName());
+    verify(dbClient.qProfileEditGroupsDao()).deleteByGroup(dbSession, groupDto);
+    verify(dbClient.qualityGateGroupPermissionsDao()).deleteByGroup(dbSession, groupDto);
+    verify(dbClient.scimGroupDao()).deleteByGroupUuid(dbSession, groupDto.getUuid());
+    verify(dbClient.groupDao()).deleteByUuid(dbSession, groupDto.getUuid(), groupDto.getName());
+  }
+}
index ed788a1f57d928b525117e2bfa632dd42ff8f995..c90261141e0c07d513f7660513f51e60e6021754 100644 (file)
@@ -29,6 +29,6 @@ public class UserGroupsModuleTest {
   public void verify_count_of_added_components() {
     ListContainer container = new ListContainer();
     new UserGroupsModule().configure(container);
-    assertThat(container.getAddedObjects()).hasSize(9);
+    assertThat(container.getAddedObjects()).isNotEmpty();
   }
 }