mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
[PM-3797 Part 1] Layout new key rotation methods (#3425)
* layout new key rotation methods - add endpoint with request model - add command with data model - add repository method * layout new key rotation methods - add endpoint with request model - add command with data model - add repository method * formatting * rename account recovery to reset password * fix tests * remove extra endpoint * rename account recovery to reset password * fix tests and formatting * register db calls in command, removing list from user repo * formatting
This commit is contained in:
19
src/Core/Auth/Models/Data/RotateUserKeyData.cs
Normal file
19
src/Core/Auth/Models/Data/RotateUserKeyData.cs
Normal file
@ -0,0 +1,19 @@
|
||||
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 User User { get; set; }
|
||||
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 IEnumerable<Send> Sends { get; set; }
|
||||
public IEnumerable<EmergencyAccess> EmergencyAccessKeys { get; set; }
|
||||
public IEnumerable<OrganizationUser> ResetPasswordKeys { get; set; }
|
||||
}
|
18
src/Core/Auth/UserFeatures/UserKey/IRotateUserKeyCommand.cs
Normal file
18
src/Core/Auth/UserFeatures/UserKey/IRotateUserKeyCommand.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace Bit.Core.Auth.UserFeatures.UserKey;
|
||||
|
||||
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(RotateUserKeyData model);
|
||||
}
|
||||
|
||||
public delegate Task UpdateEncryptedDataForKeyRotation(SqlTransaction transaction = null);
|
@ -0,0 +1,61 @@
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Auth.UserFeatures.UserKey.Implementations;
|
||||
|
||||
public class RotateUserKeyCommand : IRotateUserKeyCommand
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IdentityErrorDescriber _identityErrorDescriber;
|
||||
|
||||
public RotateUserKeyCommand(IUserService userService, IUserRepository userRepository,
|
||||
IPushNotificationService pushService, IdentityErrorDescriber errors)
|
||||
{
|
||||
_userService = userService;
|
||||
_userRepository = userRepository;
|
||||
_pushService = pushService;
|
||||
_identityErrorDescriber = errors;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IdentityResult> RotateUserKeyAsync(RotateUserKeyData model)
|
||||
{
|
||||
if (model.User == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(model.User));
|
||||
}
|
||||
|
||||
if (!await _userService.CheckPasswordAsync(model.User, model.MasterPasswordHash))
|
||||
{
|
||||
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
model.User.RevisionDate = model.User.AccountRevisionDate = now;
|
||||
model.User.LastKeyRotationDate = now;
|
||||
model.User.SecurityStamp = Guid.NewGuid().ToString();
|
||||
model.User.Key = model.Key;
|
||||
model.User.PrivateKey = model.PrivateKey;
|
||||
if (model.Ciphers.Any() || model.Folders.Any() || model.Sends.Any() || model.EmergencyAccessKeys.Any() ||
|
||||
model.ResetPasswordKeys.Any())
|
||||
{
|
||||
List<UpdateEncryptedDataForKeyRotation> saveEncryptedDataActions = new();
|
||||
// if (model.Ciphers.Any())
|
||||
// {
|
||||
// saveEncryptedDataActions.Add(_cipherRepository.SaveRotatedData);
|
||||
// }
|
||||
await _userRepository.UpdateUserKeyAndEncryptedDataAsync(model.User, saveEncryptedDataActions);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _userRepository.ReplaceAsync(model.User);
|
||||
}
|
||||
|
||||
await _pushService.PushLogOutAsync(model.User.Id, excludeCurrentContextFromPush: true);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
}
|
@ -65,6 +65,7 @@ public static class FeatureFlagKeys
|
||||
public const string ItemShare = "item-share";
|
||||
public const string BillingPlansUpgrade = "billing-plans-upgrade";
|
||||
public const string BillingStarterPlan = "billing-starter-plan";
|
||||
public const string KeyRotationImprovements = "key-rotation-improvements";
|
||||
|
||||
public static List<string> GetAllKeys()
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Repositories;
|
||||
@ -15,4 +16,13 @@ public interface IUserRepository : IRepository<User, Guid>
|
||||
Task UpdateStorageAsync(Guid id);
|
||||
Task UpdateRenewalReminderDateAsync(Guid id, DateTime renewalReminderDate);
|
||||
Task<IEnumerable<User>> GetManyAsync(IEnumerable<Guid> ids);
|
||||
/// <summary>
|
||||
/// Sets a new user key and updates all encrypted data.
|
||||
/// <para>Warning: Any user key encrypted data not included will be lost.</para>
|
||||
/// </summary>
|
||||
/// <param name="user">The user to update</param>
|
||||
/// <param name="updateDataActions">Registered database calls to update re-encrypted data.</param>
|
||||
[Obsolete("Intended for future improvements to key rotation. Do not use.")]
|
||||
Task UpdateUserKeyAndEncryptedDataAsync(User user,
|
||||
IEnumerable<UpdateEncryptedDataForKeyRotation> updateDataActions);
|
||||
}
|
||||
|
Reference in New Issue
Block a user