2024-11-21 10:17:04 -08:00
|
|
|
|
using Bit.Core.Auth.Repositories;
|
2023-12-05 12:05:51 -05:00
|
|
|
|
using Bit.Core.Entities;
|
2024-11-21 10:17:04 -08:00
|
|
|
|
using Bit.Core.KeyManagement.Models.Data;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
using Bit.Core.Repositories;
|
|
|
|
|
|
using Bit.Core.Services;
|
2023-12-12 11:58:34 -05:00
|
|
|
|
using Bit.Core.Tools.Repositories;
|
2023-12-06 08:46:36 -05:00
|
|
|
|
using Bit.Core.Vault.Repositories;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
|
|
|
2024-11-21 10:17:04 -08:00
|
|
|
|
namespace Bit.Core.KeyManagement.UserKey.Implementations;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
|
2023-12-12 11:58:34 -05:00
|
|
|
|
/// <inheritdoc />
|
2023-11-09 14:56:08 -05:00
|
|
|
|
public class RotateUserKeyCommand : IRotateUserKeyCommand
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IUserService _userService;
|
|
|
|
|
|
private readonly IUserRepository _userRepository;
|
2023-12-06 08:46:36 -05:00
|
|
|
|
private readonly ICipherRepository _cipherRepository;
|
|
|
|
|
|
private readonly IFolderRepository _folderRepository;
|
2023-12-12 11:58:34 -05:00
|
|
|
|
private readonly ISendRepository _sendRepository;
|
2023-12-05 12:05:51 -05:00
|
|
|
|
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
|
2023-12-14 15:05:19 -05:00
|
|
|
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
private readonly IPushNotificationService _pushService;
|
|
|
|
|
|
private readonly IdentityErrorDescriber _identityErrorDescriber;
|
2024-06-17 20:46:57 +02:00
|
|
|
|
private readonly IWebAuthnCredentialRepository _credentialRepository;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
|
2023-12-12 11:58:34 -05:00
|
|
|
|
/// <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>
|
2023-11-09 14:56:08 -05:00
|
|
|
|
public RotateUserKeyCommand(IUserService userService, IUserRepository userRepository,
|
2023-12-12 11:58:34 -05:00
|
|
|
|
ICipherRepository cipherRepository, IFolderRepository folderRepository, ISendRepository sendRepository,
|
2023-12-14 15:05:19 -05:00
|
|
|
|
IEmergencyAccessRepository emergencyAccessRepository, IOrganizationUserRepository organizationUserRepository,
|
2024-06-17 20:46:57 +02:00
|
|
|
|
IPushNotificationService pushService, IdentityErrorDescriber errors, IWebAuthnCredentialRepository credentialRepository)
|
2023-11-09 14:56:08 -05:00
|
|
|
|
{
|
|
|
|
|
|
_userService = userService;
|
|
|
|
|
|
_userRepository = userRepository;
|
2023-12-06 08:46:36 -05:00
|
|
|
|
_cipherRepository = cipherRepository;
|
|
|
|
|
|
_folderRepository = folderRepository;
|
2023-12-12 11:58:34 -05:00
|
|
|
|
_sendRepository = sendRepository;
|
2023-12-05 12:05:51 -05:00
|
|
|
|
_emergencyAccessRepository = emergencyAccessRepository;
|
2023-12-14 15:05:19 -05:00
|
|
|
|
_organizationUserRepository = organizationUserRepository;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
_pushService = pushService;
|
|
|
|
|
|
_identityErrorDescriber = errors;
|
2024-06-17 20:46:57 +02:00
|
|
|
|
_credentialRepository = credentialRepository;
|
2023-11-09 14:56:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2023-12-05 12:05:51 -05:00
|
|
|
|
public async Task<IdentityResult> RotateUserKeyAsync(User user, RotateUserKeyData model)
|
2023-11-09 14:56:08 -05:00
|
|
|
|
{
|
2023-12-05 12:05:51 -05:00
|
|
|
|
if (user == null)
|
2023-11-09 14:56:08 -05:00
|
|
|
|
{
|
2023-12-05 12:05:51 -05:00
|
|
|
|
throw new ArgumentNullException(nameof(user));
|
2023-11-09 14:56:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-05 12:05:51 -05:00
|
|
|
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
2023-11-09 14:56:08 -05:00
|
|
|
|
{
|
|
|
|
|
|
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var now = DateTime.UtcNow;
|
2023-12-05 12:05:51 -05:00
|
|
|
|
user.RevisionDate = user.AccountRevisionDate = now;
|
|
|
|
|
|
user.LastKeyRotationDate = now;
|
|
|
|
|
|
user.SecurityStamp = Guid.NewGuid().ToString();
|
|
|
|
|
|
user.Key = model.Key;
|
|
|
|
|
|
user.PrivateKey = model.PrivateKey;
|
2023-12-14 15:05:19 -05:00
|
|
|
|
if (model.Ciphers.Any() || model.Folders.Any() || model.Sends.Any() || model.EmergencyAccesses.Any() ||
|
2024-06-17 20:46:57 +02:00
|
|
|
|
model.OrganizationUsers.Any() || model.WebAuthnKeys.Any())
|
2023-11-09 14:56:08 -05:00
|
|
|
|
{
|
|
|
|
|
|
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = new();
|
2023-12-06 08:46:36 -05:00
|
|
|
|
|
|
|
|
|
|
if (model.Ciphers.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(_cipherRepository.UpdateForKeyRotation(user.Id, model.Ciphers));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (model.Folders.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(_folderRepository.UpdateForKeyRotation(user.Id, model.Folders));
|
|
|
|
|
|
}
|
2023-12-12 11:58:34 -05:00
|
|
|
|
|
|
|
|
|
|
if (model.Sends.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(_sendRepository.UpdateForKeyRotation(user.Id, model.Sends));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-14 15:05:19 -05:00
|
|
|
|
if (model.EmergencyAccesses.Any())
|
2023-12-05 12:05:51 -05:00
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(
|
2023-12-14 15:05:19 -05:00
|
|
|
|
_emergencyAccessRepository.UpdateForKeyRotation(user.Id, model.EmergencyAccesses));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (model.OrganizationUsers.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(
|
|
|
|
|
|
_organizationUserRepository.UpdateForKeyRotation(user.Id, model.OrganizationUsers));
|
2023-12-05 12:05:51 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-17 20:46:57 +02:00
|
|
|
|
if (model.WebAuthnKeys.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
saveEncryptedDataActions.Add(_credentialRepository.UpdateKeysForRotationAsync(user.Id, model.WebAuthnKeys));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-05 12:05:51 -05:00
|
|
|
|
await _userRepository.UpdateUserKeyAndEncryptedDataAsync(user, saveEncryptedDataActions);
|
2023-11-09 14:56:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-12-05 12:05:51 -05:00
|
|
|
|
await _userRepository.ReplaceAsync(user);
|
2023-11-09 14:56:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-05 12:05:51 -05:00
|
|
|
|
await _pushService.PushLogOutAsync(user.Id, excludeCurrentContextFromPush: true);
|
2023-11-09 14:56:08 -05:00
|
|
|
|
return IdentityResult.Success;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|