1
0
mirror of https://github.com/bitwarden/server synced 2025-12-22 19:23:45 +00: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:
Bernd Schoolmann
2024-11-21 10:17:04 -08:00
committed by GitHub
parent 92b94fd4ee
commit fae8692d2a
42 changed files with 66 additions and 71 deletions

View File

@@ -3,7 +3,7 @@ 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.WebAuthn;
using Bit.Api.Auth.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Response;
@@ -18,7 +18,6 @@ using Bit.Core.Auth.Entities;
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.UserKey;
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Services;
@@ -26,6 +25,8 @@ using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
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.Business;
using Bit.Core.Repositories;

View File

@@ -1,54 +0,0 @@
using Bit.Api.Auth.Models.Request;
using Bit.Core.Auth.Entities;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
namespace Bit.Api.Auth.Validators;
public class EmergencyAccessRotationValidator : IRotationValidator<IEnumerable<EmergencyAccessWithIdRequestModel>,
IEnumerable<EmergencyAccess>>
{
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
private readonly IUserService _userService;
public EmergencyAccessRotationValidator(IEmergencyAccessRepository emergencyAccessRepository,
IUserService userService)
{
_emergencyAccessRepository = emergencyAccessRepository;
_userService = userService;
}
public async Task<IEnumerable<EmergencyAccess>> ValidateAsync(User user,
IEnumerable<EmergencyAccessWithIdRequestModel> emergencyAccessKeys)
{
var result = new List<EmergencyAccess>();
var existing = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(user.Id);
if (existing == null || existing.Count == 0)
{
return result;
}
// Exclude any emergency access that has not been confirmed yet.
existing = existing.Where(ea => ea.KeyEncrypted != null).ToList();
foreach (var ea in existing)
{
var emergencyAccess = emergencyAccessKeys.FirstOrDefault(c => c.Id == ea.Id);
if (emergencyAccess == null)
{
throw new BadRequestException("All existing emergency access keys must be included in the rotation.");
}
if (emergencyAccess.KeyEncrypted == null)
{
throw new BadRequestException("Emergency access keys cannot be set to null during rotation.");
}
result.Add(emergencyAccess.ToEmergencyAccess(ea));
}
return result;
}
}

View File

@@ -1,22 +0,0 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
namespace Bit.Api.Auth.Validators;
/// <summary>
/// A consistent interface for domains to validate re-encrypted data before saved to database. Some examples are:<br/>
/// - All available encrypted data is accounted for<br/>
/// - All provided encrypted data belongs to the user
/// </summary>
/// <typeparam name="T">Request model</typeparam>
/// <typeparam name="R">Domain model</typeparam>
public interface IRotationValidator<T, R>
{
/// <summary>
/// Validates re-encrypted data before being saved to database.
/// </summary>
/// <param name="user">Request model</param>
/// <param name="data">Domain model</param>
/// <exception cref="BadRequestException">Throws if data fails validation</exception>
Task<R> ValidateAsync(User user, T data);
}

View File

@@ -1,55 +0,0 @@
using Bit.Api.Auth.Models.Request.WebAuthn;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
namespace Bit.Api.Auth.Validators;
public class WebAuthnLoginKeyRotationValidator : IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
{
private readonly IWebAuthnCredentialRepository _webAuthnCredentialRepository;
public WebAuthnLoginKeyRotationValidator(IWebAuthnCredentialRepository webAuthnCredentialRepository)
{
_webAuthnCredentialRepository = webAuthnCredentialRepository;
}
public async Task<IEnumerable<WebAuthnLoginRotateKeyData>> ValidateAsync(User user, IEnumerable<WebAuthnLoginRotateKeyRequestModel> keysToRotate)
{
// 2024-06: Remove after 3 releases, for backward compatibility
if (keysToRotate == null)
{
return new List<WebAuthnLoginRotateKeyData>();
}
var result = new List<WebAuthnLoginRotateKeyData>();
var existing = await _webAuthnCredentialRepository.GetManyByUserIdAsync(user.Id);
if (existing == null || !existing.Any())
{
return result;
}
foreach (var ea in existing)
{
var keyToRotate = keysToRotate.FirstOrDefault(c => c.Id == ea.Id);
if (keyToRotate == null)
{
throw new BadRequestException("All existing webauthn prf keys must be included in the rotation.");
}
if (keyToRotate.EncryptedUserKey == null)
{
throw new BadRequestException("WebAuthn prf keys must have user-key during rotation.");
}
if (keyToRotate.EncryptedPublicKey == null)
{
throw new BadRequestException("WebAuthn prf keys must have public-key during rotation.");
}
result.Add(keyToRotate.ToWebAuthnRotateKeyData());
}
return result;
}
}