mirror of
https://github.com/bitwarden/server.git
synced 2025-07-12 13:19:01 -05:00
[PM-12607] Move key rotation & validators to km ownership (#4941)
* Move key rotation & validators to km ownership * Fix build errors * Fix build errors * Fix import ordering * Update validator namespace * Move key rotation data to km ownership * Fix linting * Fix namespaces * Fix namespace * Fix namespaces * Move rotateuserkeycommandtests to km ownership
This commit is contained in:
@ -1,19 +0,0 @@
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Auth.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,6 @@
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.KeyManagement.UserKey;
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.KeyManagement.UserKey;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
#nullable enable
|
||||
|
@ -1,29 +0,0 @@
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace Bit.Core.Auth.UserFeatures.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,120 +0,0 @@
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
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.Auth.UserFeatures.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;
|
||||
}
|
||||
}
|
@ -5,12 +5,12 @@ using Bit.Core.Auth.UserFeatures.Registration.Implementations;
|
||||
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey.Implementations;
|
||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword;
|
||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||
using Bit.Core.Auth.UserFeatures.WebAuthnLogin;
|
||||
using Bit.Core.Auth.UserFeatures.WebAuthnLogin.Implementations;
|
||||
using Bit.Core.KeyManagement.UserKey;
|
||||
using Bit.Core.KeyManagement.UserKey.Implementations;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
Reference in New Issue
Block a user