]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19965 Add ITs for UserService::deactivate.
authorWojtek Wajerowicz <115081248+wojciech-wajerowicz-sonarsource@users.noreply.github.com>
Fri, 28 Jul 2023 13:45:27 +0000 (15:45 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 28 Jul 2023 20:03:15 +0000 (20:03 +0000)
server/sonar-webserver-common/src/it/java/org/sonar/server/common/user/service/UserServiceIT.java

index 3c65ea743afed07c116cb2814dd2010c40a415d6..0083f73827ba27c27fc157a36001cd7f707f7d27 100644 (file)
@@ -32,20 +32,37 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.api.web.UserRole;
 import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.alm.setting.AlmSettingDto;
 import org.sonar.db.audit.NoOpAuditPersister;
+import org.sonar.db.ce.CeTaskMessageType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.permission.template.PermissionTemplateDto;
+import org.sonar.db.permission.template.PermissionTemplateUserDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.db.property.PropertyQuery;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.scim.ScimUserDao;
 import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.SessionTokenDto;
+import org.sonar.db.user.UserDismissedMessageDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.authentication.CredentialsLocalAuthentication;
 import org.sonar.server.common.SearchResults;
 import org.sonar.server.common.avatar.AvatarResolverImpl;
 import org.sonar.server.common.management.ManagedInstanceChecker;
+import org.sonar.server.common.user.UserAnonymizer;
 import org.sonar.server.common.user.UserDeactivator;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.management.ManagedInstanceService;
+import org.sonar.server.user.ExternalIdentity;
 import org.sonar.server.user.NewUserNotifier;
 import org.sonar.server.user.UserUpdater;
 import org.sonar.server.usergroups.DefaultGroupFinder;
@@ -63,9 +80,8 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.sonar.db.property.PropertyTesting.newUserPropertyDto;
 import static org.sonar.db.user.UserTesting.newUserDto;
 
 public class UserServiceIT {
@@ -75,9 +91,14 @@ public class UserServiceIT {
 
   @Rule
   public DbTester db = DbTester.create();
+  private final DbSession dbSession = db.getSession();
+
   private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
   private final ManagedInstanceChecker managedInstanceChecker = mock(ManagedInstanceChecker.class);
-  private final UserDeactivator userDeactivator = mock(UserDeactivator.class);
+
+  private final UserAnonymizer userAnonymizer = new UserAnonymizer(db.getDbClient(), () -> "anonymized");
+
+  private final UserDeactivator userDeactivator = new UserDeactivator(db.getDbClient(), userAnonymizer);
   private final MapSettings settings = new MapSettings().setProperty("sonar.internal.pbkdf2.iterations", "1");
   private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient(), settings.asConfig());
   private final UserUpdater userUpdater = new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), new DefaultGroupFinder(db.getDbClient()),
@@ -402,42 +423,261 @@ public class UserServiceIT {
   }
 
   @Test
-  public void deactivate_whenUserIsNotFound_shouldThrowNotFoundException() {
-    assertThatThrownBy(() -> userService.deactivate("userToDelete", false))
-      .isInstanceOf(NotFoundException.class)
-      .hasMessage("User 'userToDelete' not found");
+  public void deactivate_user_and_delete_their_related_data() {
+    createAdminUser();
+    UserDto user = db.users().insertUser(u -> u
+      .setLogin("ada.lovelace")
+      .setEmail("ada.lovelace@noteg.com")
+      .setName("Ada Lovelace")
+      .setScmAccounts(singletonList("al")));
+
+    userService.deactivate(user.getLogin(), false);
+
+    verifyThatUserIsDeactivated(user.getLogin());
+  }
+
+  @Test
+  public void anonymize_user_if_anonymize_is_true() {
+    createAdminUser();
+    UserDto user = db.users().insertUser(u -> u
+      .setLogin("ada.lovelace")
+      .setEmail("ada.lovelace@noteg.com")
+      .setName("Ada Lovelace")
+      .setScmAccounts(singletonList("al")));
+
+    userService.deactivate(user.getLogin(), true);
+
+    verifyThatUserIsDeactivated("anonymized");
+    verifyThatUserIsAnomymized("anonymized");
   }
 
   @Test
-  public void deactivate_whenInstanceIsManagedAndUserIsManaged_shouldThrowBadRequestException() {
+  public void deactivate_user_deletes_their_group_membership() {
+    createAdminUser();
     UserDto user = db.users().insertUser();
-    BadRequestException badRequestException = BadRequestException.create("Not allowed");
-    doThrow(badRequestException).when(managedInstanceChecker).throwIfUserIsManaged(any(), eq(user.getUuid()));
-    assertThatThrownBy(() -> userService.deactivate(user.getLogin(), false))
-      .isEqualTo(badRequestException);
+    GroupDto group1 = db.users().insertGroup();
+    db.users().insertGroup();
+    db.users().insertMember(group1, user);
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().groupMembershipDao().selectGroupUuidsByUserUuid(dbSession, user.getUuid())).isEmpty();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_tokens() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    db.users().insertToken(user);
+    db.users().insertToken(user);
+    db.commit();
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().userTokenDao().selectByUser(dbSession, user)).isEmpty();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_properties() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
+    db.properties().insertProperty(newUserPropertyDto(user), null, null, null, user.getLogin());
+    db.properties().insertProperty(newUserPropertyDto(user), null, null, null, user.getLogin());
+    db.properties().insertProperty(newUserPropertyDto(user).setEntityUuid(project.uuid()), project.getKey(),
+      project.name(), project.qualifier(), user.getLogin());
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().propertiesDao().selectByQuery(PropertyQuery.builder().setUserUuid(user.getUuid()).build(), dbSession)).isEmpty();
+    assertThat(db.getDbClient().propertiesDao().selectByQuery(PropertyQuery.builder().setUserUuid(user.getUuid()).setEntityUuid(project.uuid()).build(), dbSession)).isEmpty();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_permissions() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
+    db.users().insertGlobalPermissionOnUser(user, GlobalPermission.SCAN);
+    db.users().insertGlobalPermissionOnUser(user, GlobalPermission.ADMINISTER_QUALITY_PROFILES);
+    db.users().insertProjectPermissionOnUser(user, UserRole.USER, project);
+    db.users().insertProjectPermissionOnUser(user, UserRole.CODEVIEWER, project);
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().userPermissionDao().selectGlobalPermissionsOfUser(dbSession, user.getUuid())).isEmpty();
+    assertThat(db.getDbClient().userPermissionDao().selectEntityPermissionsOfUser(dbSession, user.getUuid(), project.uuid())).isEmpty();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_permission_templates() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    PermissionTemplateDto template = db.permissionTemplates().insertTemplate();
+    PermissionTemplateDto anotherTemplate = db.permissionTemplates().insertTemplate();
+    db.permissionTemplates().addUserToTemplate(template.getUuid(), user.getUuid(), UserRole.USER, template.getName(), user.getLogin());
+    db.permissionTemplates().addUserToTemplate(anotherTemplate.getUuid(), user.getUuid(), UserRole.CODEVIEWER, anotherTemplate.getName(), user.getLogin());
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().permissionTemplateDao().selectUserPermissionsByTemplateId(dbSession, template.getUuid())).extracting(PermissionTemplateUserDto::getUserUuid)
+      .isEmpty();
+    assertThat(db.getDbClient().permissionTemplateDao().selectUserPermissionsByTemplateId(dbSession, anotherTemplate.getUuid())).extracting(PermissionTemplateUserDto::getUserUuid)
+      .isEmpty();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_qprofiles_permissions() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    QProfileDto profile = db.qualityProfiles().insert();
+    db.qualityProfiles().addUserPermission(profile, user);
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(dbSession, profile, user)).isFalse();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_default_assignee_settings() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
+    ComponentDto anotherProject = db.components().insertPrivateProject().getMainBranchComponent();
+    db.properties().insertProperty(new PropertyDto().setKey("sonar.issues.defaultAssigneeLogin").setValue(user.getLogin())
+      .setEntityUuid(project.uuid()), project.getKey(), project.name(), project.qualifier(), user.getLogin());
+    db.properties().insertProperty(new PropertyDto().setKey("sonar.issues.defaultAssigneeLogin").setValue(user.getLogin())
+      .setEntityUuid(anotherProject.uuid()), anotherProject.getKey(), anotherProject.name(), anotherProject.qualifier(), user.getLogin());
+    db.properties().insertProperty(new PropertyDto().setKey("other").setValue(user.getLogin())
+      .setEntityUuid(anotherProject.uuid()), anotherProject.getKey(), anotherProject.name(), anotherProject.qualifier(), user.getLogin());
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().propertiesDao().selectByQuery(PropertyQuery.builder().setKey("sonar.issues.defaultAssigneeLogin").build(), db.getSession())).isEmpty();
+    assertThat(db.getDbClient().propertiesDao().selectByQuery(PropertyQuery.builder().build(), db.getSession())).extracting(PropertyDto::getKey).containsOnly("other");
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_qgate_permissions() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    QualityGateDto qualityGate = db.qualityGates().insertQualityGate();
+    db.qualityGates().addUserPermission(qualityGate, user);
+    assertThat(db.countRowsOfTable("qgate_user_permissions")).isOne();
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.countRowsOfTable("qgate_user_permissions")).isZero();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_alm_pat() {
+    createAdminUser();
+    AlmSettingDto almSettingDto = db.almSettings().insertBitbucketAlmSetting();
+    UserDto user = db.users().insertUser();
+    db.almPats().insert(p -> p.setUserUuid(user.getUuid()), p -> p.setAlmSettingUuid(almSettingDto.getUuid()));
+    UserDto anotherUser = db.users().insertUser();
+    db.almPats().insert(p -> p.setUserUuid(anotherUser.getUuid()), p -> p.setAlmSettingUuid(almSettingDto.getUuid()));
 
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().almPatDao().selectByUserAndAlmSetting(dbSession, user.getUuid(), almSettingDto)).isEmpty();
+    assertThat(db.getDbClient().almPatDao().selectByUserAndAlmSetting(dbSession, anotherUser.getUuid(), almSettingDto)).isNotNull();
   }
 
   @Test
-  public void deactivate_whenAnonymizeIsFalse_shouldDeactivateUser() {
+  public void deactivate_user_deletes_their_session_tokens() {
+    createAdminUser();
     UserDto user = db.users().insertUser();
+    SessionTokenDto sessionToken1 = db.users().insertSessionToken(user);
+    SessionTokenDto sessionToken2 = db.users().insertSessionToken(user);
+    UserDto anotherUser = db.users().insertUser();
+    SessionTokenDto sessionToken3 = db.users().insertSessionToken(anotherUser);
 
     userService.deactivate(user.getLogin(), false);
-    verify(managedInstanceChecker).throwIfUserIsManaged(any(), eq(user.getUuid()));
 
-    verify(userDeactivator).deactivateUser(any(), eq(user.getLogin()));
-    verify(userDeactivator, never()).deactivateUserWithAnonymization(any(), eq(user.getLogin()));
+    assertThat(db.getDbClient().sessionTokensDao().selectByUuid(dbSession, sessionToken1.getUuid())).isNotPresent();
+    assertThat(db.getDbClient().sessionTokensDao().selectByUuid(dbSession, sessionToken2.getUuid())).isNotPresent();
+    assertThat(db.getDbClient().sessionTokensDao().selectByUuid(dbSession, sessionToken3.getUuid())).isPresent();
+  }
+
+  @Test
+  public void deactivate_user_deletes_their_dismissed_messages() {
+    createAdminUser();
+    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
+    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
+    UserDto user = db.users().insertUser();
+
+    db.users().insertUserDismissedMessage(user, project1, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+    db.users().insertUserDismissedMessage(user, project2, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+    UserDto anotherUser = db.users().insertUser();
+    UserDismissedMessageDto msg3 = db.users().insertUserDismissedMessage(anotherUser, project1, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+    UserDismissedMessageDto msg4 = db.users().insertUserDismissedMessage(anotherUser, project2, CeTaskMessageType.SUGGEST_DEVELOPER_EDITION_UPGRADE);
+
+    userService.deactivate(user.getLogin(), false);
+
+    assertThat(db.getDbClient().userDismissedMessagesDao().selectByUser(dbSession, user)).isEmpty();
+    assertThat(db.getDbClient().userDismissedMessagesDao().selectByUser(dbSession, anotherUser))
+      .extracting(UserDismissedMessageDto::getUuid)
+      .containsExactlyInAnyOrder(msg3.getUuid(), msg4.getUuid());
+  }
+
+  @Test
+  public void fail_if_user_does_not_exist() {
+
+    assertThatThrownBy(() -> {
+      userService.deactivate("someone", false);
+    })
+      .isInstanceOf(NotFoundException.class)
+      .hasMessage("User 'someone' not found");
+  }
+
+  @Test
+  public void fail_to_deactivate_last_administrator() {
+    UserDto admin = db.users().insertUser();
+    db.users().insertGlobalPermissionOnUser(admin, GlobalPermission.ADMINISTER);
+
+    assertThatThrownBy(() -> {
+      userService.deactivate(admin.getLogin(), false);
+    })
+      .isInstanceOf(BadRequestException.class)
+      .hasMessage("User is last administrator, and cannot be deactivated");
   }
 
   @Test
-  public void deactivate_whenAnonymizeIsTrue_shouldDeactivateUserWithAnonymization() {
+  public void administrators_can_be_deactivated_if_there_are_still_other_administrators() {
+    UserDto admin = createAdminUser();
+
+    UserDto anotherAdmin = createAdminUser();
+
+    userService.deactivate(admin.getLogin(), false);
+
+    verifyThatUserIsDeactivated(admin.getLogin());
+    verifyThatUserExists(anotherAdmin.getLogin());
+  }
+
+  @Test
+  public void anonymizeUser_whenSamlAndScimUser_shouldDeleteScimMapping() {
+    createAdminUser();
     UserDto user = db.users().insertUser();
+    db.getDbClient().scimUserDao().enableScimForUser(dbSession, user.getUuid());
+    db.commit();
 
     userService.deactivate(user.getLogin(), true);
-    verify(managedInstanceChecker).throwIfUserIsManaged(any(), eq(user.getUuid()));
 
-    verify(userDeactivator).deactivateUserWithAnonymization(any(), eq(user.getLogin()));
-    verify(userDeactivator, never()).deactivateUser(any(), eq(user.getLogin()));
+    assertThat(db.getDbClient().scimUserDao().findByUserUuid(dbSession, user.getUuid())).isEmpty();
+  }
+
+  @Test
+  public void handle_whenUserManagedAndInstanceManaged_shouldThrow() {
+    createAdminUser();
+    UserDto user = db.users().insertUser();
+    doThrow(new IllegalStateException("User managed")).when(managedInstanceChecker).throwIfUserIsManaged(any(), eq(user.getUuid()));
+
+    String login = user.getLogin();
+    assertThatThrownBy(() -> userService.deactivate(login, false))
+      .isInstanceOf(IllegalStateException.class)
+      .hasMessage("User managed");
   }
 
   @Test
@@ -573,14 +813,14 @@ public class UserServiceIT {
 
   @Test
   public void createUser_whenDuplicatesInScmAccounts_shouldFail() {
-      UserCreateRequest userCreateRequest = UserCreateRequest.builder()
-        .setLogin("john")
-        .setName("John")
-        .setEmail("john@email.com")
-        .setScmAccounts(List.of("admin", "admin"))
-        .setPassword("1234")
-        .setLocal(true)
-        .build();
+    UserCreateRequest userCreateRequest = UserCreateRequest.builder()
+      .setLogin("john")
+      .setName("John")
+      .setEmail("john@email.com")
+      .setScmAccounts(List.of("admin", "admin"))
+      .setPassword("1234")
+      .setLocal(true)
+      .build();
 
     assertThatThrownBy(() -> userService.createUser(userCreateRequest))
       .isInstanceOf(IllegalArgumentException.class)
@@ -653,4 +893,32 @@ public class UserServiceIT {
       .isEqualTo(badRequestException);
   }
 
+  private void verifyThatUserIsDeactivated(String login) {
+    Optional<UserDto> user = db.users().selectUserByLogin(login);
+    assertThat(user).isPresent();
+    assertThat(user.get().isActive()).isFalse();
+    assertThat(user.get().getEmail()).isNull();
+    assertThat(user.get().getSortedScmAccounts()).isEmpty();
+  }
+
+  private void verifyThatUserIsAnomymized(String login) {
+    Optional<UserDto> user = db.users().selectUserByLogin(login);
+    assertThat(user).isPresent();
+    assertThat(user.get().getName()).isEqualTo(login);
+    assertThat(user.get().getExternalLogin()).isEqualTo(login);
+    assertThat(user.get().getExternalId()).isEqualTo(login);
+    assertThat(user.get().getExternalIdentityProvider()).isEqualTo(ExternalIdentity.SQ_AUTHORITY);
+  }
+
+  private UserDto createAdminUser() {
+    UserDto admin = db.users().insertUser();
+    db.users().insertGlobalPermissionOnUser(admin, GlobalPermission.ADMINISTER);
+    db.commit();
+    return admin;
+  }
+
+  private void verifyThatUserExists(String login) {
+    assertThat(db.users().selectUserByLogin(login)).isPresent();
+  }
+
 }