using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers; [SutProviderCustomize] public class RemoveOrganizationUserCommandTests { [Theory, BitAutoData] public async Task RemoveUser_Success( [OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser, [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var currentContext = sutProvider.GetDependency(); organizationUser.OrganizationId = deletingUser.OrganizationId; organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser); organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser); currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true); await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId); await organizationUserRepository.Received(1).DeleteAsync(organizationUser); await sutProvider.GetDependency().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed); } [Theory] [BitAutoData] public async Task RemoveUser_NotFound_ThrowsException(SutProvider sutProvider, Guid organizationId, Guid organizationUserId) { await Assert.ThrowsAsync(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null)); } [Theory] [BitAutoData] public async Task RemoveUser_MismatchingOrganizationId_ThrowsException( SutProvider sutProvider, Guid organizationId, Guid organizationUserId) { sutProvider.GetDependency() .GetByIdAsync(organizationUserId) .Returns(new OrganizationUser { Id = organizationUserId, OrganizationId = Guid.NewGuid() }); await Assert.ThrowsAsync(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null)); } [Theory, BitAutoData] public async Task RemoveUser_InvalidUser_ThrowsException( OrganizationUser organizationUser, OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(Guid.NewGuid(), organizationUser.Id, deletingUser.UserId)); Assert.Contains("User not found.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUser_RemoveYourself_ThrowsException(OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, deletingUser.Id, deletingUser.UserId)); Assert.Contains("You cannot remove yourself.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUser_NonOwnerRemoveOwner_ThrowsException( [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser, [OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var currentContext = sutProvider.GetDependency(); organizationUser.OrganizationId = deletingUser.OrganizationId; organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser); currentContext.OrganizationAdmin(deletingUser.OrganizationId).Returns(true); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId)); Assert.Contains("Only owners can delete other owners.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUser_RemovingLastOwner_ThrowsException( [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser, OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var hasConfirmedOwnersExceptQuery = sutProvider.GetDependency(); organizationUser.OrganizationId = deletingUser.OrganizationId; organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser); hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync( deletingUser.OrganizationId, Arg.Is>(i => i.Contains(organizationUser.Id)), Arg.Any()) .Returns(false); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, null)); Assert.Contains("Organization must have at least one confirmed owner.", exception.Message); hasConfirmedOwnersExceptQuery .Received(1) .HasConfirmedOwnersExceptAsync( organizationUser.OrganizationId, Arg.Is>(i => i.Contains(organizationUser.Id)), true); } [Theory, BitAutoData] public async Task RemoveUser_WithEventSystemUser_Success( [OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser); await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser); await sutProvider.GetDependency().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser); } [Theory, BitAutoData] public async Task RemoveUser_ByUserId_Success( [OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); organizationUserRepository .GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value) .Returns(organizationUser); sutProvider.GetDependency() .HasConfirmedOwnersExceptAsync( organizationUser.OrganizationId, Arg.Is>(i => i.Contains(organizationUser.Id)), Arg.Any()) .Returns(true); await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.UserId.Value); await sutProvider.GetDependency().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed); } [Theory, BitAutoData] public async Task RemoveUser_ByUserId_NotFound_ThrowsException(SutProvider sutProvider, Guid organizationId, Guid userId) { await Assert.ThrowsAsync(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, userId)); } [Theory, BitAutoData] public async Task RemoveUser_ByUserId_InvalidUser_ThrowsException(OrganizationUser organizationUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); organizationUserRepository.GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value).Returns(organizationUser); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(Guid.NewGuid(), organizationUser.UserId.Value)); Assert.Contains("User not found.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUser_ByUserId_RemovingLastOwner_ThrowsException( [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var hasConfirmedOwnersExceptQuery = sutProvider.GetDependency(); organizationUserRepository.GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value).Returns(organizationUser); hasConfirmedOwnersExceptQuery .HasConfirmedOwnersExceptAsync( organizationUser.OrganizationId, Arg.Is>(i => i.Contains(organizationUser.Id)), Arg.Any()) .Returns(false); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.UserId.Value)); Assert.Contains("Organization must have at least one confirmed owner.", exception.Message); hasConfirmedOwnersExceptQuery .Received(1) .HasConfirmedOwnersExceptAsync( organizationUser.OrganizationId, Arg.Is>(i => i.Contains(organizationUser.Id)), Arg.Any()); } [Theory, BitAutoData] public async Task RemoveUsers_FilterInvalid_ThrowsException(OrganizationUser organizationUser, OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var organizationUsers = new[] { organizationUser }; var organizationUserIds = organizationUsers.Select(u => u.Id); organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId)); Assert.Contains("Users invalid.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUsers_RemoveYourself_ThrowsException( OrganizationUser deletingUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var organizationUsers = new[] { deletingUser }; var organizationUserIds = organizationUsers.Select(u => u.Id); organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers); sutProvider.GetDependency() .HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any>()) .Returns(true); var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId); Assert.Contains("You cannot remove yourself.", result[0].Item2); } [Theory, BitAutoData] public async Task RemoveUsers_NonOwnerRemoveOwner_ThrowsException( [OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser, [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, [OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser2, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId; var organizationUsers = new[] { orgUser1 }; var organizationUserIds = organizationUsers.Select(u => u.Id); organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers); sutProvider.GetDependency() .HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any>()) .Returns(true); var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId); Assert.Contains("Only owners can delete other owners.", result[0].Item2); } [Theory, BitAutoData] public async Task RemoveUsers_LastOwner_ThrowsException( [OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var organizationUsers = new[] { orgUser }; var organizationUserIds = organizationUsers.Select(u => u.Id); organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers); organizationUserRepository.GetManyByOrganizationAsync(orgUser.OrganizationId, OrganizationUserType.Owner).Returns(organizationUsers); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RemoveUsersAsync(orgUser.OrganizationId, organizationUserIds, null)); Assert.Contains("Organization must have at least one confirmed owner.", exception.Message); } [Theory, BitAutoData] public async Task RemoveUsers_Success( [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser, [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2, SutProvider sutProvider) { var organizationUserRepository = sutProvider.GetDependency(); var currentContext = sutProvider.GetDependency(); orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId; var organizationUsers = new[] { orgUser1, orgUser2 }; var organizationUserIds = organizationUsers.Select(u => u.Id); organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers); organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser); sutProvider.GetDependency() .HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any>()) .Returns(true); currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true); await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId); } }