diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommand.cs index 010f5de9bf..7b7d8003a3 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommand.cs @@ -154,6 +154,12 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz } } + if (orgUser.Type == OrganizationUserType.Admin && await _currentContext.OrganizationCustom(organizationId)) + { + throw new BadRequestException("Custom users can not delete admins."); + } + + if (!managementStatus.TryGetValue(orgUser.Id, out var isManaged) || !isManaged) { throw new BadRequestException("Member is not managed by the organization."); diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs index 9375a231ec..3568a2a2b9 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs @@ -25,7 +25,8 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand public const string UserNotFoundErrorMessage = "User not found."; public const string UsersInvalidErrorMessage = "Users invalid."; public const string RemoveYourselfErrorMessage = "You cannot remove yourself."; - public const string RemoveOwnerByNonOwnerErrorMessage = "Only owners can delete other owners."; + public const string RemoveOwnerByNonOwnerErrorMessage = "Only owners can remove other owners."; + public const string RemoveAdminByCustomUserErrorMessage = "Custom users can not remove admins."; public const string RemoveLastConfirmedOwnerErrorMessage = "Organization must have at least one confirmed owner."; public const string RemoveClaimedAccountErrorMessage = "Cannot remove member accounts claimed by the organization. To offboard a member, revoke or delete the account."; @@ -153,6 +154,11 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand } } + if (orgUser.Type == OrganizationUserType.Admin && await _currentContext.OrganizationCustom(orgUser.OrganizationId)) + { + throw new BadRequestException(RemoveAdminByCustomUserErrorMessage); + } + if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null) { var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(orgUser.OrganizationId, new[] { orgUser.Id }); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommandTests.cs index b21ae5459f..f8f6bdb60d 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteManagedOrganizationUserAccountCommandTests.cs @@ -131,6 +131,38 @@ public class DeleteManagedOrganizationUserAccountCommandTests .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); } + [Theory] + [BitAutoData] + public async Task DeleteUserAsync_WhenCustomUserDeletesAdmin_ThrowsException( + SutProvider sutProvider, User user, + [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Admin)] OrganizationUser organizationUser, + Guid deletingUserId) + { + // Arrange + organizationUser.UserId = user.Id; + + sutProvider.GetDependency() + .GetByIdAsync(organizationUser.Id) + .Returns(organizationUser); + + sutProvider.GetDependency().GetByIdAsync(user.Id) + .Returns(user); + + sutProvider.GetDependency() + .OrganizationCustom(organizationUser.OrganizationId) + .Returns(true); + + // Act + var exception = await Assert.ThrowsAsync(() => + sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId)); + + // Assert + Assert.Equal("Custom users can not delete admins.", exception.Message); + await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); + await sutProvider.GetDependency().Received(0) + .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + [Theory] [BitAutoData] public async Task DeleteUserAsync_DeletingOwnerWhenNotOwner_ThrowsException( diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs index 6ab8236b8e..a60850c5a9 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs @@ -171,6 +171,28 @@ public class RemoveOrganizationUserCommandTests Assert.Contains(RemoveOrganizationUserCommand.RemoveOwnerByNonOwnerErrorMessage, exception.Message); } + [Theory, BitAutoData] + public async Task RemoveUser_WhenCustomUserRemovesAdmin_ThrowsException( + [OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser organizationUser, + [OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser deletingUser, + SutProvider sutProvider) + { + // Arrange + organizationUser.OrganizationId = deletingUser.OrganizationId; + + sutProvider.GetDependency() + .GetByIdAsync(organizationUser.Id) + .Returns(organizationUser); + sutProvider.GetDependency() + .OrganizationCustom(organizationUser.OrganizationId) + .Returns(true); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUser.UserId)); + Assert.Contains(RemoveOrganizationUserCommand.RemoveAdminByCustomUserErrorMessage, exception.Message); + } + [Theory, BitAutoData] public async Task RemoveUser_WithDeletingUserId_RemovingLastOwner_ThrowsException( [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,