mirror of
https://github.com/bitwarden/server
synced 2026-02-12 06:23:28 +00:00
[PM-31745] Allow user to perm delete unassigned items (#6956)
* update DeleteAdmin to grab items that are unassigned and ciphersControllerTests
This commit is contained in:
@@ -976,14 +976,14 @@ public class CiphersController : Controller
|
||||
public async Task DeleteAdmin(Guid id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await GetByIdAsync(id, userId);
|
||||
var cipher = await GetByIdAsyncAdmin(id);
|
||||
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||
!await CanDeleteOrRestoreCipherAsAdminAsync(cipher.OrganizationId.Value, new[] { cipher.Id }))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _cipherService.DeleteAsync(cipher, userId, true);
|
||||
await _cipherService.DeleteAsync(new CipherDetails(cipher), userId, true);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/delete-admin")]
|
||||
|
||||
@@ -158,9 +158,9 @@ public class CiphersControllerTests
|
||||
[BitAutoData(OrganizationUserType.Custom, false, false)]
|
||||
public async Task CanEditCiphersAsAdminAsync_FlexibleCollections_Success(
|
||||
OrganizationUserType userType, bool allowAdminsAccessToAllItems, bool shouldSucceed,
|
||||
CurrentContextOrganization organization, Guid userId, CipherDetails cipherDetails, SutProvider<CiphersController> sutProvider)
|
||||
CurrentContextOrganization organization, Guid userId, CipherOrganizationDetails cipherOrgDetails, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
organization.Type = userType;
|
||||
if (userType == OrganizationUserType.Custom)
|
||||
{
|
||||
@@ -171,9 +171,9 @@ public class CiphersControllerTests
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId });
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails });
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherOrgDetails });
|
||||
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
|
||||
{
|
||||
@@ -183,13 +183,13 @@ public class CiphersControllerTests
|
||||
|
||||
if (shouldSucceed)
|
||||
{
|
||||
await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
|
||||
await sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id);
|
||||
await sutProvider.GetDependency<ICipherService>().ReceivedWithAnyArgs()
|
||||
.DeleteAsync(default, default);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id));
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id));
|
||||
await sutProvider.GetDependency<ICipherService>().DidNotReceiveWithAnyArgs()
|
||||
.DeleteAsync(default, default);
|
||||
}
|
||||
@@ -199,25 +199,23 @@ public class CiphersControllerTests
|
||||
[BitAutoData(OrganizationUserType.Owner)]
|
||||
[BitAutoData(OrganizationUserType.Admin)]
|
||||
public async Task DeleteAdmin_WithOwnerOrAdmin_WithManagePermission_DeletesCipher(
|
||||
OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId,
|
||||
OrganizationUserType organizationUserType, CipherOrganizationDetails cipherOrgDetails, Guid userId,
|
||||
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.UserId = null;
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
cipherDetails.Edit = true;
|
||||
cipherDetails.Manage = true;
|
||||
cipherOrgDetails.UserId = null;
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
|
||||
organization.Type = organizationUserType;
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId });
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyByUserIdAsync(userId)
|
||||
.Returns(new List<CipherDetails>
|
||||
{
|
||||
cipherDetails
|
||||
new CipherDetails(cipherOrgDetails) { Edit = true, Manage = true }
|
||||
});
|
||||
sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.GetOrganizationAbilityAsync(organization.Id)
|
||||
@@ -227,34 +225,35 @@ public class CiphersControllerTests
|
||||
LimitItemDeletion = true
|
||||
});
|
||||
|
||||
await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
|
||||
await sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true);
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(
|
||||
Arg.Is<CipherDetails>(c => c.Id == cipherOrgDetails.Id && c.OrganizationId == cipherOrgDetails.OrganizationId),
|
||||
userId,
|
||||
true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationUserType.Owner)]
|
||||
[BitAutoData(OrganizationUserType.Admin)]
|
||||
public async Task DeleteAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException(
|
||||
OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId,
|
||||
OrganizationUserType organizationUserType, CipherOrganizationDetails cipherOrgDetails, Guid userId,
|
||||
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.UserId = null;
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
cipherDetails.Edit = true;
|
||||
cipherDetails.Manage = false;
|
||||
cipherOrgDetails.UserId = null;
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
|
||||
organization.Type = organizationUserType;
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId });
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyByUserIdAsync(userId)
|
||||
.Returns(new List<CipherDetails>
|
||||
{
|
||||
cipherDetails
|
||||
new CipherDetails(cipherOrgDetails) { Edit = true, Manage = false }
|
||||
});
|
||||
sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.GetOrganizationAbilityAsync(organization.Id)
|
||||
@@ -264,7 +263,7 @@ public class CiphersControllerTests
|
||||
LimitItemDeletion = true
|
||||
});
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id));
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id));
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>().DidNotReceive().DeleteAsync(Arg.Any<CipherDetails>(), Arg.Any<Guid>(), Arg.Any<bool>());
|
||||
}
|
||||
@@ -273,21 +272,21 @@ public class CiphersControllerTests
|
||||
[BitAutoData(OrganizationUserType.Owner)]
|
||||
[BitAutoData(OrganizationUserType.Admin)]
|
||||
public async Task DeleteAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_DeletesCipher(
|
||||
OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId,
|
||||
OrganizationUserType organizationUserType, CipherOrganizationDetails cipherOrgDetails, Guid userId,
|
||||
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
organization.Type = organizationUserType;
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId });
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
|
||||
.Returns(new List<CipherOrganizationDetails>
|
||||
{
|
||||
new() { Id = cipherDetails.Id, OrganizationId = cipherDetails.OrganizationId }
|
||||
new() { Id = cipherOrgDetails.Id, OrganizationId = cipherOrgDetails.OrganizationId }
|
||||
});
|
||||
sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.GetOrganizationAbilityAsync(organization.Id)
|
||||
@@ -297,54 +296,65 @@ public class CiphersControllerTests
|
||||
LimitItemDeletion = true
|
||||
});
|
||||
|
||||
await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
|
||||
await sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true);
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(Arg.Is<CipherDetails>(c => c.Id == cipherOrgDetails.Id && c.OrganizationId == cipherOrgDetails.OrganizationId),
|
||||
userId,
|
||||
true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationUserType.Owner)]
|
||||
[BitAutoData(OrganizationUserType.Admin)]
|
||||
public async Task DeleteAdmin_WithAdminOrOwner_WithAccessToAllCollectionItems_DeletesCipher(
|
||||
OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId,
|
||||
OrganizationUserType organizationUserType, CipherOrganizationDetails cipherOrgDetails, Guid userId,
|
||||
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
|
||||
organization.Type = organizationUserType;
|
||||
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
organization.Type = organizationUserType;
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails });
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherOrgDetails });
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
|
||||
{
|
||||
Id = organization.Id,
|
||||
AllowAdminAccessToAllCollectionItems = true
|
||||
});
|
||||
|
||||
await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
|
||||
await sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true);
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(
|
||||
Arg.Is<CipherDetails>(c => c.Id == cipherOrgDetails.Id && c.OrganizationId == cipherOrgDetails.OrganizationId),
|
||||
userId,
|
||||
true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteAdmin_WithCustomUser_WithEditAnyCollectionTrue_DeletesCipher(
|
||||
CipherDetails cipherDetails, Guid userId,
|
||||
CipherOrganizationDetails cipherOrgDetails, Guid userId,
|
||||
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
cipherDetails.OrganizationId = organization.Id;
|
||||
cipherOrgDetails.OrganizationId = organization.Id;
|
||||
organization.Type = OrganizationUserType.Custom;
|
||||
organization.Permissions.EditAnyCollection = true;
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails });
|
||||
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherOrgDetails.Id).Returns(cipherOrgDetails);
|
||||
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherOrgDetails });
|
||||
|
||||
await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
|
||||
await sutProvider.Sut.DeleteAdmin(cipherOrgDetails.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true);
|
||||
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(
|
||||
Arg.Is<CipherDetails>(c => c.Id == cipherOrgDetails.Id && c.OrganizationId == cipherOrgDetails.OrganizationId),
|
||||
userId,
|
||||
true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
Reference in New Issue
Block a user