mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
[PM-3797 Part 5] Add reset password keys to key rotation (#3445)
* Add reset password validator with tests * add organization user rotation methods to repository - move organization user TVP helper to admin console ownership * rename account recovery to reset password * formatting * move registration of RotateUserKeyCommand to Core and make internal * add admin console ValidatorServiceCollectionExtensions
This commit is contained in:
@ -0,0 +1,143 @@
|
||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
using Bit.Api.AdminConsole.Validators;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.AdminConsole.Validators;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationUserRotationValidatorTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_Success_ReturnsValid(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user,
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys)
|
||||
{
|
||||
var existingUserResetPassword = resetPasswordKeys
|
||||
.Select(a =>
|
||||
new OrganizationUser
|
||||
{
|
||||
Id = new Guid(),
|
||||
ResetPasswordKey = a.ResetPasswordKey,
|
||||
OrganizationId = a.OrganizationId
|
||||
}).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id)
|
||||
.Returns(existingUserResetPassword);
|
||||
|
||||
var result = await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys);
|
||||
|
||||
Assert.Equal(result.Select(r => r.OrganizationId), resetPasswordKeys.Select(a => a.OrganizationId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_NullResetPasswordKeys_ReturnsEmptyList(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user)
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys = null;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_NoOrgUsers_ReturnsEmptyList(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user,
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id)
|
||||
.Returns(new List<OrganizationUser>()); // Return an empty list
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_MissingResetPassword_Throws(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user,
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys)
|
||||
{
|
||||
var existingUserResetPassword = resetPasswordKeys
|
||||
.Select(a =>
|
||||
new OrganizationUser
|
||||
{
|
||||
Id = new Guid(),
|
||||
ResetPasswordKey = a.ResetPasswordKey,
|
||||
OrganizationId = a.OrganizationId
|
||||
}).ToList();
|
||||
existingUserResetPassword.Add(new OrganizationUser
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
ResetPasswordKey = "Missing ResetPasswordKey"
|
||||
});
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id)
|
||||
.Returns(existingUserResetPassword);
|
||||
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_ResetPasswordDoesNotBelongToUser_NotReturned(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user,
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys)
|
||||
{
|
||||
var existingUserResetPassword = resetPasswordKeys
|
||||
.Select(a =>
|
||||
new OrganizationUser
|
||||
{
|
||||
Id = new Guid(),
|
||||
ResetPasswordKey = a.ResetPasswordKey,
|
||||
OrganizationId = a.OrganizationId
|
||||
}).ToList();
|
||||
existingUserResetPassword.RemoveAt(0);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id)
|
||||
.Returns(existingUserResetPassword);
|
||||
|
||||
var result = await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys);
|
||||
|
||||
Assert.DoesNotContain(result, c => c.Id == resetPasswordKeys.First().OrganizationId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_AttemptToSetKeyToNull_Throws(
|
||||
SutProvider<OrganizationUserRotationValidator> sutProvider, User user,
|
||||
IEnumerable<ResetPasswordWithOrgIdRequestModel> resetPasswordKeys)
|
||||
{
|
||||
var existingUserResetPassword = resetPasswordKeys
|
||||
.Select(a =>
|
||||
new OrganizationUser
|
||||
{
|
||||
Id = new Guid(),
|
||||
ResetPasswordKey = a.ResetPasswordKey,
|
||||
OrganizationId = a.OrganizationId
|
||||
}).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id)
|
||||
.Returns(existingUserResetPassword);
|
||||
resetPasswordKeys.First().ResetPasswordKey = null;
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.ValidateAsync(user, resetPasswordKeys));
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
using Bit.Api.Auth.Controllers;
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
@ -60,6 +61,9 @@ public class AccountsControllerTests : IDisposable
|
||||
private readonly IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>> _sendValidator;
|
||||
private readonly IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>, IEnumerable<EmergencyAccess>>
|
||||
_emergencyAccessValidator;
|
||||
private readonly IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
||||
IReadOnlyList<OrganizationUser>>
|
||||
_resetPasswordValidator;
|
||||
|
||||
|
||||
public AccountsControllerTests()
|
||||
@ -88,6 +92,9 @@ public class AccountsControllerTests : IDisposable
|
||||
_sendValidator = Substitute.For<IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>>>();
|
||||
_emergencyAccessValidator = Substitute.For<IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>,
|
||||
IEnumerable<EmergencyAccess>>>();
|
||||
_resetPasswordValidator = Substitute
|
||||
.For<IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
||||
IReadOnlyList<OrganizationUser>>>();
|
||||
|
||||
_sut = new AccountsController(
|
||||
_globalSettings,
|
||||
@ -110,7 +117,8 @@ public class AccountsControllerTests : IDisposable
|
||||
_cipherValidator,
|
||||
_folderValidator,
|
||||
_sendValidator,
|
||||
_emergencyAccessValidator
|
||||
_emergencyAccessValidator,
|
||||
_resetPasswordValidator
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user