mirror of
https://github.com/bitwarden/server
synced 2025-12-14 07:13:39 +00:00
[PM-15621] Refactor delete claimed user command (#6221)
- create vNext command - restructure command to simplify logic - move validation to a separate class - implement result types using OneOf library and demo their use here
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
using System.Net;
|
||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||
using Bit.Api.IntegrationTest.Factories;
|
||||
using Bit.Api.IntegrationTest.Helpers;
|
||||
using Bit.Api.Models.Request;
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
@@ -30,6 +33,10 @@ public class OrganizationUserControllerTests : IClassFixture<ApiApplicationFacto
|
||||
featureService
|
||||
.IsEnabled(FeatureFlagKeys.CreateDefaultLocation)
|
||||
.Returns(true);
|
||||
|
||||
featureService
|
||||
.IsEnabled(FeatureFlagKeys.DeleteClaimedUserAccountRefactor)
|
||||
.Returns(true);
|
||||
});
|
||||
_client = _factory.CreateClient();
|
||||
_loginHelper = new LoginHelper(_factory, _client);
|
||||
@@ -42,6 +49,91 @@ public class OrganizationUserControllerTests : IClassFixture<ApiApplicationFacto
|
||||
private Organization _organization = null!;
|
||||
private string _ownerEmail = null!;
|
||||
|
||||
[Fact]
|
||||
public async Task BulkDeleteAccount_Success()
|
||||
{
|
||||
var (userEmail, _) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory,
|
||||
_organization.Id, OrganizationUserType.Owner);
|
||||
|
||||
await _loginHelper.LoginAsync(userEmail);
|
||||
|
||||
var (_, orgUserToDelete) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.User);
|
||||
await OrganizationTestHelpers.CreateVerifiedDomainAsync(_factory, _organization.Id, "bitwarden.com");
|
||||
|
||||
var userRepository = _factory.GetService<IUserRepository>();
|
||||
var organizationUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
|
||||
Assert.NotNull(orgUserToDelete.UserId);
|
||||
Assert.NotNull(await userRepository.GetByIdAsync(orgUserToDelete.UserId.Value));
|
||||
Assert.NotNull(await organizationUserRepository.GetByIdAsync(orgUserToDelete.Id));
|
||||
|
||||
var request = new OrganizationUserBulkRequestModel
|
||||
{
|
||||
Ids = [orgUserToDelete.Id]
|
||||
};
|
||||
|
||||
var httpResponse = await _client.PostAsJsonAsync($"organizations/{_organization.Id}/users/delete-account", request);
|
||||
var content = await httpResponse.Content.ReadFromJsonAsync<ListResponseModel<OrganizationUserBulkResponseModel>>();
|
||||
Assert.Single(content.Data, r => r.Id == orgUserToDelete.Id && r.Error == string.Empty);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode);
|
||||
Assert.Null(await userRepository.GetByIdAsync(orgUserToDelete.UserId.Value));
|
||||
Assert.Null(await organizationUserRepository.GetByIdAsync(orgUserToDelete.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BulkDeleteAccount_MixedResults()
|
||||
{
|
||||
var (userEmail, _) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory,
|
||||
_organization.Id, OrganizationUserType.Admin);
|
||||
|
||||
await _loginHelper.LoginAsync(userEmail);
|
||||
|
||||
// Can delete users
|
||||
var (_, validOrgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.User);
|
||||
// Cannot delete owners
|
||||
var (_, invalidOrgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.Owner);
|
||||
await OrganizationTestHelpers.CreateVerifiedDomainAsync(_factory, _organization.Id, "bitwarden.com");
|
||||
|
||||
var userRepository = _factory.GetService<IUserRepository>();
|
||||
var organizationUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
|
||||
Assert.NotNull(validOrgUser.UserId);
|
||||
Assert.NotNull(invalidOrgUser.UserId);
|
||||
|
||||
var arrangedUsers =
|
||||
await userRepository.GetManyAsync([validOrgUser.UserId.Value, invalidOrgUser.UserId.Value]);
|
||||
Assert.Equal(2, arrangedUsers.Count());
|
||||
|
||||
var arrangedOrgUsers =
|
||||
await organizationUserRepository.GetManyAsync([validOrgUser.Id, invalidOrgUser.Id]);
|
||||
Assert.Equal(2, arrangedOrgUsers.Count);
|
||||
|
||||
var request = new OrganizationUserBulkRequestModel
|
||||
{
|
||||
Ids = [validOrgUser.Id, invalidOrgUser.Id]
|
||||
};
|
||||
|
||||
var httpResponse = await _client.PostAsJsonAsync($"organizations/{_organization.Id}/users/delete-account", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode);
|
||||
var debug = await httpResponse.Content.ReadAsStringAsync();
|
||||
var content = await httpResponse.Content.ReadFromJsonAsync<ListResponseModel<OrganizationUserBulkResponseModel>>();
|
||||
Assert.Equal(2, content.Data.Count());
|
||||
Assert.Contains(content.Data, r => r.Id == validOrgUser.Id && r.Error == string.Empty);
|
||||
Assert.Contains(content.Data, r =>
|
||||
r.Id == invalidOrgUser.Id &&
|
||||
string.Equals(r.Error, new CannotDeleteOwnersError().Message, StringComparison.Ordinal));
|
||||
|
||||
var actualUsers =
|
||||
await userRepository.GetManyAsync([validOrgUser.UserId.Value, invalidOrgUser.UserId.Value]);
|
||||
Assert.Single(actualUsers, u => u.Id == invalidOrgUser.UserId.Value);
|
||||
|
||||
var actualOrgUsers =
|
||||
await organizationUserRepository.GetManyAsync([validOrgUser.Id, invalidOrgUser.Id]);
|
||||
Assert.Single(actualOrgUsers, ou => ou.Id == invalidOrgUser.Id);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(OrganizationUserType.User)]
|
||||
[InlineData(OrganizationUserType.Custom)]
|
||||
@@ -57,11 +149,36 @@ public class OrganizationUserControllerTests : IClassFixture<ApiApplicationFacto
|
||||
Ids = new List<Guid> { Guid.NewGuid() }
|
||||
};
|
||||
|
||||
var httpResponse = await _client.PostAsJsonAsync($"organizations/{_organization.Id}/users/remove", request);
|
||||
var httpResponse = await _client.PostAsJsonAsync($"organizations/{_organization.Id}/users/delete-account", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, httpResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAccount_Success()
|
||||
{
|
||||
var (userEmail, _) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory,
|
||||
_organization.Id, OrganizationUserType.Owner);
|
||||
|
||||
await _loginHelper.LoginAsync(userEmail);
|
||||
|
||||
var (_, orgUserToDelete) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id, OrganizationUserType.User);
|
||||
await OrganizationTestHelpers.CreateVerifiedDomainAsync(_factory, _organization.Id, "bitwarden.com");
|
||||
|
||||
var userRepository = _factory.GetService<IUserRepository>();
|
||||
var organizationUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
|
||||
Assert.NotNull(orgUserToDelete.UserId);
|
||||
Assert.NotNull(await userRepository.GetByIdAsync(orgUserToDelete.UserId.Value));
|
||||
Assert.NotNull(await organizationUserRepository.GetByIdAsync(orgUserToDelete.Id));
|
||||
|
||||
var httpResponse = await _client.DeleteAsync($"organizations/{_organization.Id}/users/{orgUserToDelete.Id}/delete-account");
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode);
|
||||
Assert.Null(await userRepository.GetByIdAsync(orgUserToDelete.UserId.Value));
|
||||
Assert.Null(await organizationUserRepository.GetByIdAsync(orgUserToDelete.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(OrganizationUserType.User)]
|
||||
[InlineData(OrganizationUserType.Custom)]
|
||||
@@ -74,7 +191,7 @@ public class OrganizationUserControllerTests : IClassFixture<ApiApplicationFacto
|
||||
|
||||
var userToRemove = Guid.NewGuid();
|
||||
|
||||
var httpResponse = await _client.DeleteAsync($"organizations/{_organization.Id}/users/{userToRemove}");
|
||||
var httpResponse = await _client.DeleteAsync($"organizations/{_organization.Id}/users/{userToRemove}/delete-account");
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, httpResponse.StatusCode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user