mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
[AC-2328] Add a Bulk OrganizationUsersController.GetResetPasswordDetails endpoint (#4079)
* Add new stored procedure for reading reset password details for multiple organization user IDs * Add method IOrganizationUserRepository.GetManyResetPasswordDetailsByOrganizationUserAsync * Add new API endpoint for getting reset password details for multiple organization users * Add unit tests for bulk OrganizationUsersController.GetResetPasswordDetails * Add alias to sql query result column * Add constructor for automatic mapping * Fix http method type for endpoint * dotnet format * Simplify the constructor in the OrganizationUserResetPasswordDetails * Refactor stored procedure and repository method names for retrieving account recovery details * Add integration tests for GetManyAccountRecoveryDetailsByOrganizationUserAsync * Lock endpoint behind BulkDeviceApproval feature flag * Update feature flag key value
This commit is contained in:
@ -471,6 +471,45 @@ public class OrganizationUsersControllerTests
|
||||
Assert.False(customUserResponse.Permissions.DeleteAssignedCollections);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetAccountRecoveryDetails_ReturnsDetails(
|
||||
Guid organizationId,
|
||||
OrganizationUserBulkRequestModel bulkRequestModel,
|
||||
ICollection<OrganizationUserResetPasswordDetails> resetPasswordDetails,
|
||||
SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageResetPassword(organizationId).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAccountRecoveryDetailsByOrganizationUserAsync(organizationId, bulkRequestModel.Ids)
|
||||
.Returns(resetPasswordDetails);
|
||||
|
||||
var response = await sutProvider.Sut.GetAccountRecoveryDetails(organizationId, bulkRequestModel);
|
||||
|
||||
Assert.Equal(resetPasswordDetails.Count, response.Data.Count());
|
||||
Assert.True(response.Data.All(r =>
|
||||
resetPasswordDetails.Any(ou =>
|
||||
ou.OrganizationUserId == r.OrganizationUserId &&
|
||||
ou.Kdf == r.Kdf &&
|
||||
ou.KdfIterations == r.KdfIterations &&
|
||||
ou.KdfMemory == r.KdfMemory &&
|
||||
ou.KdfParallelism == r.KdfParallelism &&
|
||||
ou.ResetPasswordKey == r.ResetPasswordKey &&
|
||||
ou.EncryptedPrivateKey == r.EncryptedPrivateKey)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetAccountRecoveryDetails_WithoutManageResetPasswordPermission_Throws(
|
||||
Guid organizationId,
|
||||
OrganizationUserBulkRequestModel bulkRequestModel,
|
||||
SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageResetPassword(organizationId).Returns(false);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetAccountRecoveryDetails(organizationId, bulkRequestModel));
|
||||
}
|
||||
|
||||
private void Put_Setup(SutProvider<OrganizationUsersController> sutProvider, OrganizationAbility organizationAbility,
|
||||
OrganizationUser organizationUser, Guid savingUserId, OrganizationUserUpdateRequestModel model, bool authorizeAll)
|
||||
{
|
||||
|
@ -95,4 +95,85 @@ public class OrganizationUserRepositoryTests
|
||||
Assert.NotEqual(updatedUser1.AccountRevisionDate, user1.AccountRevisionDate);
|
||||
Assert.NotEqual(updatedUser2.AccountRevisionDate, user2.AccountRevisionDate);
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetManyAccountRecoveryDetailsByOrganizationUserAsync_Works(IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
var user1 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User 1",
|
||||
Email = $"test+{Guid.NewGuid()}@example.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 1,
|
||||
KdfMemory = 2,
|
||||
KdfParallelism = 3
|
||||
});
|
||||
|
||||
var user2 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User 2",
|
||||
Email = $"test+{Guid.NewGuid()}@example.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
Kdf = KdfType.Argon2id,
|
||||
KdfIterations = 4,
|
||||
KdfMemory = 5,
|
||||
KdfParallelism = 6
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
BillingEmail = user1.Email, // TODO: EF does not enforce this being NOT NULl
|
||||
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
|
||||
PrivateKey = "privatekey",
|
||||
});
|
||||
|
||||
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user1.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
ResetPasswordKey = "resetpasswordkey1",
|
||||
});
|
||||
|
||||
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user2.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
ResetPasswordKey = "resetpasswordkey2",
|
||||
});
|
||||
|
||||
var recoveryDetails = await organizationUserRepository.GetManyAccountRecoveryDetailsByOrganizationUserAsync(
|
||||
organization.Id,
|
||||
new[]
|
||||
{
|
||||
orgUser1.Id,
|
||||
orgUser2.Id,
|
||||
});
|
||||
|
||||
Assert.NotNull(recoveryDetails);
|
||||
Assert.Equal(2, recoveryDetails.Count());
|
||||
Assert.Contains(recoveryDetails, r =>
|
||||
r.OrganizationUserId == orgUser1.Id &&
|
||||
r.Kdf == KdfType.PBKDF2_SHA256 &&
|
||||
r.KdfIterations == 1 &&
|
||||
r.KdfMemory == 2 &&
|
||||
r.KdfParallelism == 3 &&
|
||||
r.ResetPasswordKey == "resetpasswordkey1" &&
|
||||
r.EncryptedPrivateKey == "privatekey");
|
||||
Assert.Contains(recoveryDetails, r =>
|
||||
r.OrganizationUserId == orgUser2.Id &&
|
||||
r.Kdf == KdfType.Argon2id &&
|
||||
r.KdfIterations == 4 &&
|
||||
r.KdfMemory == 5 &&
|
||||
r.KdfParallelism == 6 &&
|
||||
r.ResetPasswordKey == "resetpasswordkey2" &&
|
||||
r.EncryptedPrivateKey == "privatekey");
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user