mirror of
https://github.com/bitwarden/server
synced 2025-12-10 13:23:27 +00:00
[PM-20010] Fix purge logic to skip claimed user check for organization vault (#6107)
* Implement unit tests for PostPurge method in CiphersController to handle various scenarios * Refactor PostPurge method in CiphersController to use Guid for organizationId parameter and update related unit tests * Refactor PostPurge method in CiphersController to skip checking if user is claimed if its purging the org vault
This commit is contained in:
@@ -1113,7 +1113,7 @@ public class CiphersController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("purge")]
|
||||
public async Task PostPurge([FromBody] SecretVerificationRequestModel model, string organizationId = null)
|
||||
public async Task PostPurge([FromBody] SecretVerificationRequestModel model, Guid? organizationId = null)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
@@ -1128,24 +1128,22 @@ public class CiphersController : Controller
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
if (organizationId == null)
|
||||
{
|
||||
// Check if the user is claimed by any organization.
|
||||
if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
||||
{
|
||||
throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(organizationId))
|
||||
{
|
||||
await _cipherRepository.DeleteByUserIdAsync(user.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
var orgId = new Guid(organizationId);
|
||||
if (!await _currentContext.EditAnyCollection(orgId))
|
||||
if (!await _currentContext.EditAnyCollection(organizationId!.Value))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
await _cipherService.PurgeAsync(orgId);
|
||||
await _cipherService.PurgeAsync(organizationId!.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Vault.Controllers;
|
||||
using Bit.Api.Vault.Models;
|
||||
using Bit.Api.Vault.Models.Request;
|
||||
@@ -1789,5 +1790,123 @@ public class CiphersControllerTests
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_WhenUserNotFound_ThrowsUnauthorizedAccessException(
|
||||
SecretVerificationRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns((User)null);
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.PostPurge(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_WhenUserVerificationFails_ThrowsBadRequestException(
|
||||
User user,
|
||||
SecretVerificationRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(user, model.Secret)
|
||||
.Returns(false);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostPurge(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_UserPurge_WithClaimedUser_ThrowsBadRequestException(
|
||||
User user,
|
||||
SecretVerificationRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(user, model.Secret)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.IsClaimedByAnyOrganizationAsync(user.Id)
|
||||
.Returns(true);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostPurge(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_UserPurge_WithUnclaimedUser_Successful(
|
||||
User user,
|
||||
SecretVerificationRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(user, model.Secret)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.IsClaimedByAnyOrganizationAsync(user.Id)
|
||||
.Returns(false);
|
||||
|
||||
await sutProvider.Sut.PostPurge(model);
|
||||
|
||||
await sutProvider.GetDependency<ICipherRepository>()
|
||||
.Received(1)
|
||||
.DeleteByUserIdAsync(user.Id);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_OrganizationPurge_WithEditAnyCollectionPermission_Successful(
|
||||
User user,
|
||||
SecretVerificationRequestModel model,
|
||||
Guid organizationId,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(user, model.Secret)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.IsClaimedByAnyOrganizationAsync(user.Id)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAnyCollection(organizationId)
|
||||
.Returns(true);
|
||||
|
||||
await sutProvider.Sut.PostPurge(model, organizationId);
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>()
|
||||
.Received(1)
|
||||
.PurgeAsync(organizationId);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_OrganizationPurge_WithInsufficientPermissions_ThrowsNotFoundException(
|
||||
User user,
|
||||
Guid organizationId,
|
||||
SecretVerificationRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.VerifySecretAsync(user, model.Secret)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.IsClaimedByAnyOrganizationAsync(user.Id)
|
||||
.Returns(false);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAnyCollection(organizationId)
|
||||
.Returns(false);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PostPurge(model, organizationId));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user