mirror of
https://github.com/bitwarden/server.git
synced 2025-06-25 05:08:48 -05:00
Remove key rotation v1 (#5939)
This commit is contained in:
parent
70703cb3b0
commit
34580f0472
@ -1,34 +1,21 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Response;
|
||||||
using Bit.Api.AdminConsole.Models.Response;
|
|
||||||
using Bit.Api.Auth.Models.Request;
|
|
||||||
using Bit.Api.Auth.Models.Request.Accounts;
|
using Bit.Api.Auth.Models.Request.Accounts;
|
||||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
|
||||||
using Bit.Api.KeyManagement.Validators;
|
|
||||||
using Bit.Api.Models.Request.Accounts;
|
using Bit.Api.Models.Request.Accounts;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Api.Tools.Models.Request;
|
|
||||||
using Bit.Api.Vault.Models.Request;
|
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||||
using Bit.Core.Auth.Models.Data;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.KeyManagement.Models.Data;
|
|
||||||
using Bit.Core.KeyManagement.UserKey;
|
|
||||||
using Bit.Core.Models.Api.Response;
|
using Bit.Core.Models.Api.Response;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -45,22 +32,9 @@ public class AccountsController : Controller
|
|||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
||||||
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
||||||
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
|
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
|
||||||
private readonly IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> _folderValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>> _sendValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>, IEnumerable<EmergencyAccess>>
|
|
||||||
_emergencyAccessValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
|
||||||
IReadOnlyList<OrganizationUser>>
|
|
||||||
_organizationUserValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
|
|
||||||
_webauthnKeyValidator;
|
|
||||||
|
|
||||||
|
|
||||||
public AccountsController(
|
public AccountsController(
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
@ -69,17 +43,8 @@ public class AccountsController : Controller
|
|||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
|
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
|
||||||
ITdeOffboardingPasswordCommand tdeOffboardingPasswordCommand,
|
ITdeOffboardingPasswordCommand tdeOffboardingPasswordCommand,
|
||||||
IRotateUserKeyCommand rotateUserKeyCommand,
|
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService
|
||||||
IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> cipherValidator,
|
|
||||||
IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> folderValidator,
|
|
||||||
IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>> sendValidator,
|
|
||||||
IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>, IEnumerable<EmergencyAccess>>
|
|
||||||
emergencyAccessValidator,
|
|
||||||
IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>, IReadOnlyList<OrganizationUser>>
|
|
||||||
organizationUserValidator,
|
|
||||||
IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>> webAuthnKeyValidator
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
@ -89,15 +54,8 @@ public class AccountsController : Controller
|
|||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
_setInitialMasterPasswordCommand = setInitialMasterPasswordCommand;
|
_setInitialMasterPasswordCommand = setInitialMasterPasswordCommand;
|
||||||
_tdeOffboardingPasswordCommand = tdeOffboardingPasswordCommand;
|
_tdeOffboardingPasswordCommand = tdeOffboardingPasswordCommand;
|
||||||
_rotateUserKeyCommand = rotateUserKeyCommand;
|
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_cipherValidator = cipherValidator;
|
|
||||||
_folderValidator = folderValidator;
|
|
||||||
_sendValidator = sendValidator;
|
|
||||||
_emergencyAccessValidator = emergencyAccessValidator;
|
|
||||||
_organizationUserValidator = organizationUserValidator;
|
|
||||||
_webauthnKeyValidator = webAuthnKeyValidator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -313,45 +271,6 @@ public class AccountsController : Controller
|
|||||||
throw new BadRequestException(ModelState);
|
throw new BadRequestException(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Replaced by the safer rotate-user-account-keys endpoint.")]
|
|
||||||
[HttpPost("key")]
|
|
||||||
public async Task PostKey([FromBody] UpdateKeyRequestModel model)
|
|
||||||
{
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new UnauthorizedAccessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var dataModel = new RotateUserKeyData
|
|
||||||
{
|
|
||||||
MasterPasswordHash = model.MasterPasswordHash,
|
|
||||||
Key = model.Key,
|
|
||||||
PrivateKey = model.PrivateKey,
|
|
||||||
Ciphers = await _cipherValidator.ValidateAsync(user, model.Ciphers),
|
|
||||||
Folders = await _folderValidator.ValidateAsync(user, model.Folders),
|
|
||||||
Sends = await _sendValidator.ValidateAsync(user, model.Sends),
|
|
||||||
EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.EmergencyAccessKeys),
|
|
||||||
OrganizationUsers = await _organizationUserValidator.ValidateAsync(user, model.ResetPasswordKeys),
|
|
||||||
WebAuthnKeys = await _webauthnKeyValidator.ValidateAsync(user, model.WebAuthnKeys)
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _rotateUserKeyCommand.RotateUserKeyAsync(user, dataModel);
|
|
||||||
|
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var error in result.Errors)
|
|
||||||
{
|
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(2000);
|
|
||||||
throw new BadRequestException(ModelState);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("security-stamp")]
|
[HttpPost("security-stamp")]
|
||||||
public async Task PostSecurityStamp([FromBody] SecretVerificationRequestModel model)
|
public async Task PostSecurityStamp([FromBody] SecretVerificationRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,6 @@ public static class UserServiceCollectionExtensions
|
|||||||
|
|
||||||
public static void AddUserKeyCommands(this IServiceCollection services, IGlobalSettings globalSettings)
|
public static void AddUserKeyCommands(this IServiceCollection services, IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
services.AddScoped<IRotateUserKeyCommand, RotateUserKeyCommand>();
|
|
||||||
services.AddScoped<IRotateUserAccountKeysCommand, RotateUserAccountKeysCommand>();
|
services.AddScoped<IRotateUserAccountKeysCommand, RotateUserAccountKeysCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Models.Data;
|
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
|
|
||||||
namespace Bit.Core.KeyManagement.Models.Data;
|
|
||||||
|
|
||||||
public class RotateUserKeyData
|
|
||||||
{
|
|
||||||
public string MasterPasswordHash { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
public string PrivateKey { get; set; }
|
|
||||||
public IEnumerable<Cipher> Ciphers { get; set; }
|
|
||||||
public IEnumerable<Folder> Folders { get; set; }
|
|
||||||
public IReadOnlyList<Send> Sends { get; set; }
|
|
||||||
public IEnumerable<EmergencyAccess> EmergencyAccesses { get; set; }
|
|
||||||
public IReadOnlyList<OrganizationUser> OrganizationUsers { get; set; }
|
|
||||||
public IEnumerable<WebAuthnLoginRotateKeyData> WebAuthnKeys { get; set; }
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.KeyManagement.Models.Data;
|
using Bit.Core.KeyManagement.Models.Data;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
|
||||||
namespace Bit.Core.KeyManagement.UserKey;
|
namespace Bit.Core.KeyManagement.UserKey;
|
||||||
|
|
||||||
@ -18,3 +19,12 @@ public interface IRotateUserAccountKeysCommand
|
|||||||
/// <exception cref="InvalidOperationException">User KDF settings and email must match the model provided settings.</exception>
|
/// <exception cref="InvalidOperationException">User KDF settings and email must match the model provided settings.</exception>
|
||||||
Task<IdentityResult> RotateUserAccountKeysAsync(User user, RotateUserAccountKeysData model);
|
Task<IdentityResult> RotateUserAccountKeysAsync(User user, RotateUserAccountKeysData model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A type used to implement updates to the database for key rotations. Each domain that requires an update of encrypted
|
||||||
|
/// data during a key rotation should use this to implement its own database call. The user repository loops through
|
||||||
|
/// these during a key rotation.
|
||||||
|
/// <para>Note: connection and transaction are only used for Dapper. They won't be available in EF</para>
|
||||||
|
/// </summary>
|
||||||
|
public delegate Task UpdateEncryptedDataForKeyRotation(SqlConnection connection = null,
|
||||||
|
SqlTransaction transaction = null);
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.KeyManagement.Models.Data;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.Data.SqlClient;
|
|
||||||
|
|
||||||
namespace Bit.Core.KeyManagement.UserKey;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Responsible for rotation of a user key and updating database with re-encrypted data
|
|
||||||
/// </summary>
|
|
||||||
public interface IRotateUserKeyCommand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a new user key and updates all encrypted data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="model">All necessary information for rotation. Warning: Any encrypted data not included will be lost.</param>
|
|
||||||
/// <returns>An IdentityResult for verification of the master password hash</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">User must be provided.</exception>
|
|
||||||
Task<IdentityResult> RotateUserKeyAsync(User user, RotateUserKeyData model);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A type used to implement updates to the database for key rotations. Each domain that requires an update of encrypted
|
|
||||||
/// data during a key rotation should use this to implement its own database call. The user repository loops through
|
|
||||||
/// these during a key rotation.
|
|
||||||
/// <para>Note: connection and transaction are only used for Dapper. They won't be available in EF</para>
|
|
||||||
/// </summary>
|
|
||||||
public delegate Task UpdateEncryptedDataForKeyRotation(SqlConnection connection = null,
|
|
||||||
SqlTransaction transaction = null);
|
|
@ -1,121 +0,0 @@
|
|||||||
using Bit.Core.Auth.Repositories;
|
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.KeyManagement.Models.Data;
|
|
||||||
using Bit.Core.Platform.Push;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Tools.Repositories;
|
|
||||||
using Bit.Core.Vault.Repositories;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
|
|
||||||
namespace Bit.Core.KeyManagement.UserKey.Implementations;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public class RotateUserKeyCommand : IRotateUserKeyCommand
|
|
||||||
{
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly ICipherRepository _cipherRepository;
|
|
||||||
private readonly IFolderRepository _folderRepository;
|
|
||||||
private readonly ISendRepository _sendRepository;
|
|
||||||
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
||||||
private readonly IPushNotificationService _pushService;
|
|
||||||
private readonly IdentityErrorDescriber _identityErrorDescriber;
|
|
||||||
private readonly IWebAuthnCredentialRepository _credentialRepository;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a new <see cref="RotateUserKeyCommand"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userService">Master password hash validation</param>
|
|
||||||
/// <param name="userRepository">Updates user keys and re-encrypted data if needed</param>
|
|
||||||
/// <param name="cipherRepository">Provides a method to update re-encrypted cipher data</param>
|
|
||||||
/// <param name="folderRepository">Provides a method to update re-encrypted folder data</param>
|
|
||||||
/// <param name="sendRepository">Provides a method to update re-encrypted send data</param>
|
|
||||||
/// <param name="emergencyAccessRepository">Provides a method to update re-encrypted emergency access data</param>
|
|
||||||
/// <param name="pushService">Logs out user from other devices after successful rotation</param>
|
|
||||||
/// <param name="errors">Provides a password mismatch error if master password hash validation fails</param>
|
|
||||||
public RotateUserKeyCommand(IUserService userService, IUserRepository userRepository,
|
|
||||||
ICipherRepository cipherRepository, IFolderRepository folderRepository, ISendRepository sendRepository,
|
|
||||||
IEmergencyAccessRepository emergencyAccessRepository, IOrganizationUserRepository organizationUserRepository,
|
|
||||||
IPushNotificationService pushService, IdentityErrorDescriber errors, IWebAuthnCredentialRepository credentialRepository)
|
|
||||||
{
|
|
||||||
_userService = userService;
|
|
||||||
_userRepository = userRepository;
|
|
||||||
_cipherRepository = cipherRepository;
|
|
||||||
_folderRepository = folderRepository;
|
|
||||||
_sendRepository = sendRepository;
|
|
||||||
_emergencyAccessRepository = emergencyAccessRepository;
|
|
||||||
_organizationUserRepository = organizationUserRepository;
|
|
||||||
_pushService = pushService;
|
|
||||||
_identityErrorDescriber = errors;
|
|
||||||
_credentialRepository = credentialRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<IdentityResult> RotateUserKeyAsync(User user, RotateUserKeyData model)
|
|
||||||
{
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
|
||||||
{
|
|
||||||
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
user.RevisionDate = user.AccountRevisionDate = now;
|
|
||||||
user.LastKeyRotationDate = now;
|
|
||||||
user.SecurityStamp = Guid.NewGuid().ToString();
|
|
||||||
user.Key = model.Key;
|
|
||||||
user.PrivateKey = model.PrivateKey;
|
|
||||||
if (model.Ciphers.Any() || model.Folders.Any() || model.Sends.Any() || model.EmergencyAccesses.Any() ||
|
|
||||||
model.OrganizationUsers.Any() || model.WebAuthnKeys.Any())
|
|
||||||
{
|
|
||||||
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = new();
|
|
||||||
|
|
||||||
if (model.Ciphers.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(_cipherRepository.UpdateForKeyRotation(user.Id, model.Ciphers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.Folders.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(_folderRepository.UpdateForKeyRotation(user.Id, model.Folders));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.Sends.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(_sendRepository.UpdateForKeyRotation(user.Id, model.Sends));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.EmergencyAccesses.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(
|
|
||||||
_emergencyAccessRepository.UpdateForKeyRotation(user.Id, model.EmergencyAccesses));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.OrganizationUsers.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(
|
|
||||||
_organizationUserRepository.UpdateForKeyRotation(user.Id, model.OrganizationUsers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.WebAuthnKeys.Any())
|
|
||||||
{
|
|
||||||
saveEncryptedDataActions.Add(_credentialRepository.UpdateKeysForRotationAsync(user.Id, model.WebAuthnKeys));
|
|
||||||
}
|
|
||||||
|
|
||||||
await _userRepository.UpdateUserKeyAndEncryptedDataAsync(user, saveEncryptedDataActions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _userRepository.ReplaceAsync(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _pushService.PushLogOutAsync(user.Id, excludeCurrentContextFromPush: true);
|
|
||||||
return IdentityResult.Success;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +1,16 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
|
||||||
using Bit.Api.Auth.Controllers;
|
using Bit.Api.Auth.Controllers;
|
||||||
using Bit.Api.Auth.Models.Request;
|
|
||||||
using Bit.Api.Auth.Models.Request.Accounts;
|
using Bit.Api.Auth.Models.Request.Accounts;
|
||||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
|
||||||
using Bit.Api.KeyManagement.Validators;
|
|
||||||
using Bit.Api.Tools.Models.Request;
|
|
||||||
using Bit.Api.Vault.Models.Request;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||||
using Bit.Core.Auth.Models.Data;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.KeyManagement.UserKey;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@ -39,23 +28,10 @@ public class AccountsControllerTests : IDisposable
|
|||||||
private readonly IProviderUserRepository _providerUserRepository;
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
||||||
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
|
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
|
||||||
private readonly IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> _folderValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>> _sendValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>, IEnumerable<EmergencyAccess>>
|
|
||||||
_emergencyAccessValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
|
||||||
IReadOnlyList<OrganizationUser>>
|
|
||||||
_resetPasswordValidator;
|
|
||||||
private readonly IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
|
|
||||||
_webauthnKeyRotationValidator;
|
|
||||||
|
|
||||||
|
|
||||||
public AccountsControllerTests()
|
public AccountsControllerTests()
|
||||||
{
|
{
|
||||||
_userService = Substitute.For<IUserService>();
|
_userService = Substitute.For<IUserService>();
|
||||||
@ -64,21 +40,9 @@ public class AccountsControllerTests : IDisposable
|
|||||||
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
||||||
_policyService = Substitute.For<IPolicyService>();
|
_policyService = Substitute.For<IPolicyService>();
|
||||||
_setInitialMasterPasswordCommand = Substitute.For<ISetInitialMasterPasswordCommand>();
|
_setInitialMasterPasswordCommand = Substitute.For<ISetInitialMasterPasswordCommand>();
|
||||||
_rotateUserKeyCommand = Substitute.For<IRotateUserKeyCommand>();
|
|
||||||
_twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
|
_twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
|
||||||
_tdeOffboardingPasswordCommand = Substitute.For<ITdeOffboardingPasswordCommand>();
|
_tdeOffboardingPasswordCommand = Substitute.For<ITdeOffboardingPasswordCommand>();
|
||||||
_featureService = Substitute.For<IFeatureService>();
|
_featureService = Substitute.For<IFeatureService>();
|
||||||
_cipherValidator =
|
|
||||||
Substitute.For<IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>>>();
|
|
||||||
_folderValidator =
|
|
||||||
Substitute.For<IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>>>();
|
|
||||||
_sendValidator = Substitute.For<IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>>>();
|
|
||||||
_emergencyAccessValidator = Substitute.For<IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>,
|
|
||||||
IEnumerable<EmergencyAccess>>>();
|
|
||||||
_webauthnKeyRotationValidator = Substitute.For<IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>>();
|
|
||||||
_resetPasswordValidator = Substitute
|
|
||||||
.For<IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
|
||||||
IReadOnlyList<OrganizationUser>>>();
|
|
||||||
|
|
||||||
_sut = new AccountsController(
|
_sut = new AccountsController(
|
||||||
_organizationService,
|
_organizationService,
|
||||||
@ -88,15 +52,8 @@ public class AccountsControllerTests : IDisposable
|
|||||||
_policyService,
|
_policyService,
|
||||||
_setInitialMasterPasswordCommand,
|
_setInitialMasterPasswordCommand,
|
||||||
_tdeOffboardingPasswordCommand,
|
_tdeOffboardingPasswordCommand,
|
||||||
_rotateUserKeyCommand,
|
|
||||||
_twoFactorIsEnabledQuery,
|
_twoFactorIsEnabledQuery,
|
||||||
_featureService,
|
_featureService
|
||||||
_cipherValidator,
|
|
||||||
_folderValidator,
|
|
||||||
_sendValidator,
|
|
||||||
_emergencyAccessValidator,
|
|
||||||
_resetPasswordValidator,
|
|
||||||
_webauthnKeyRotationValidator
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Repositories;
|
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.KeyManagement.Models.Data;
|
|
||||||
using Bit.Core.KeyManagement.UserKey.Implementations;
|
|
||||||
using Bit.Core.Platform.Push;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Test.Common.AutoFixture;
|
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using NSubstitute;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Bit.Core.Test.KeyManagement.UserKey;
|
|
||||||
|
|
||||||
[SutProviderCustomize]
|
|
||||||
public class RotateUserKeyCommandTests
|
|
||||||
{
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RotateUserKeyAsync_Success(SutProvider<RotateUserKeyCommand> sutProvider, User user,
|
|
||||||
RotateUserKeyData model)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IUserService>().CheckPasswordAsync(user, model.MasterPasswordHash)
|
|
||||||
.Returns(true);
|
|
||||||
foreach (var webauthnCred in model.WebAuthnKeys)
|
|
||||||
{
|
|
||||||
var dbWebauthnCred = new WebAuthnCredential
|
|
||||||
{
|
|
||||||
EncryptedPublicKey = "encryptedPublicKey",
|
|
||||||
EncryptedUserKey = "encryptedUserKey"
|
|
||||||
};
|
|
||||||
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetByIdAsync(webauthnCred.Id, user.Id)
|
|
||||||
.Returns(dbWebauthnCred);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.RotateUserKeyAsync(user, model);
|
|
||||||
|
|
||||||
Assert.Equal(IdentityResult.Success, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RotateUserKeyAsync_InvalidMasterPasswordHash_ReturnsFailedIdentityResult(
|
|
||||||
SutProvider<RotateUserKeyCommand> sutProvider, User user, RotateUserKeyData model)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IUserService>().CheckPasswordAsync(user, model.MasterPasswordHash)
|
|
||||||
.Returns(false);
|
|
||||||
foreach (var webauthnCred in model.WebAuthnKeys)
|
|
||||||
{
|
|
||||||
var dbWebauthnCred = new WebAuthnCredential
|
|
||||||
{
|
|
||||||
EncryptedPublicKey = "encryptedPublicKey",
|
|
||||||
EncryptedUserKey = "encryptedUserKey"
|
|
||||||
};
|
|
||||||
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetByIdAsync(webauthnCred.Id, user.Id)
|
|
||||||
.Returns(dbWebauthnCred);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.RotateUserKeyAsync(user, model);
|
|
||||||
|
|
||||||
Assert.False(result.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RotateUserKeyAsync_LogsOutUser(
|
|
||||||
SutProvider<RotateUserKeyCommand> sutProvider, User user, RotateUserKeyData model)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IUserService>().CheckPasswordAsync(user, model.MasterPasswordHash)
|
|
||||||
.Returns(true);
|
|
||||||
foreach (var webauthnCred in model.WebAuthnKeys)
|
|
||||||
{
|
|
||||||
var dbWebauthnCred = new WebAuthnCredential
|
|
||||||
{
|
|
||||||
EncryptedPublicKey = "encryptedPublicKey",
|
|
||||||
EncryptedUserKey = "encryptedUserKey"
|
|
||||||
};
|
|
||||||
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetByIdAsync(webauthnCred.Id, user.Id)
|
|
||||||
.Returns(dbWebauthnCred);
|
|
||||||
}
|
|
||||||
|
|
||||||
await sutProvider.Sut.RotateUserKeyAsync(user, model);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPushNotificationService>().ReceivedWithAnyArgs()
|
|
||||||
.PushLogOutAsync(default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user