1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 23:52:50 -05:00

[PM-13362] Add private key regeneration endpoint (#4929)

* Add new RegenerateUserAsymmetricKeysCommand

* add new command tests

* Add regen controller

* Add regen controller tests

* add feature flag

* Add push notification to sync new asymmetric keys to other devices
This commit is contained in:
Thomas Avery
2024-12-16 12:01:09 -06:00
committed by GitHub
parent d88a103fbc
commit 7637cbe12a
11 changed files with 641 additions and 0 deletions

View File

@ -160,6 +160,7 @@ public static class FeatureFlagKeys
public const string PM12443RemovePagingLogic = "pm-12443-remove-paging-logic";
public const string SelfHostLicenseRefactor = "pm-11516-self-host-license-refactor";
public const string PromoteProviderServiceUserTool = "pm-15128-promote-provider-service-user-tool";
public const string PrivateKeyRegeneration = "pm-12241-private-key-regeneration";
public static List<string> GetAllKeys()
{

View File

@ -0,0 +1,13 @@
#nullable enable
using Bit.Core.Auth.Models.Data;
using Bit.Core.Entities;
using Bit.Core.KeyManagement.Models.Data;
namespace Bit.Core.KeyManagement.Commands.Interfaces;
public interface IRegenerateUserAsymmetricKeysCommand
{
Task RegenerateKeysAsync(UserAsymmetricKeys userAsymmetricKeys,
ICollection<OrganizationUser> usersOrganizationAccounts,
ICollection<EmergencyAccessDetails> designatedEmergencyAccess);
}

View File

@ -0,0 +1,71 @@
#nullable enable
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Commands.Interfaces;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.KeyManagement.Repositories;
using Bit.Core.Services;
using Microsoft.Extensions.Logging;
namespace Bit.Core.KeyManagement.Commands;
public class RegenerateUserAsymmetricKeysCommand : IRegenerateUserAsymmetricKeysCommand
{
private readonly ICurrentContext _currentContext;
private readonly ILogger<RegenerateUserAsymmetricKeysCommand> _logger;
private readonly IUserAsymmetricKeysRepository _userAsymmetricKeysRepository;
private readonly IPushNotificationService _pushService;
public RegenerateUserAsymmetricKeysCommand(
ICurrentContext currentContext,
IUserAsymmetricKeysRepository userAsymmetricKeysRepository,
IPushNotificationService pushService,
ILogger<RegenerateUserAsymmetricKeysCommand> logger)
{
_currentContext = currentContext;
_logger = logger;
_userAsymmetricKeysRepository = userAsymmetricKeysRepository;
_pushService = pushService;
}
public async Task RegenerateKeysAsync(UserAsymmetricKeys userAsymmetricKeys,
ICollection<OrganizationUser> usersOrganizationAccounts,
ICollection<EmergencyAccessDetails> designatedEmergencyAccess)
{
var userId = _currentContext.UserId;
if (!userId.HasValue ||
userAsymmetricKeys.UserId != userId.Value ||
usersOrganizationAccounts.Any(ou => ou.UserId != userId) ||
designatedEmergencyAccess.Any(dea => dea.GranteeId != userId))
{
throw new NotFoundException();
}
var inOrganizations = usersOrganizationAccounts.Any(ou =>
ou.Status is OrganizationUserStatusType.Confirmed or OrganizationUserStatusType.Revoked);
var hasDesignatedEmergencyAccess = designatedEmergencyAccess.Any(x =>
x.Status is EmergencyAccessStatusType.Confirmed or EmergencyAccessStatusType.RecoveryApproved
or EmergencyAccessStatusType.RecoveryInitiated);
_logger.LogInformation(
"User asymmetric keys regeneration requested. UserId: {userId} OrganizationMembership: {inOrganizations} DesignatedEmergencyAccess: {hasDesignatedEmergencyAccess} DeviceType: {deviceType}",
userAsymmetricKeys.UserId, inOrganizations, hasDesignatedEmergencyAccess, _currentContext.DeviceType);
// For now, don't regenerate asymmetric keys for user's with organization membership and designated emergency access.
if (inOrganizations || hasDesignatedEmergencyAccess)
{
throw new BadRequestException("Key regeneration not supported for this user.");
}
await _userAsymmetricKeysRepository.RegenerateUserAsymmetricKeysAsync(userAsymmetricKeys);
_logger.LogInformation(
"User's asymmetric keys regenerated. UserId: {userId} OrganizationMembership: {inOrganizations} DesignatedEmergencyAccess: {hasDesignatedEmergencyAccess} DeviceType: {deviceType}",
userAsymmetricKeys.UserId, inOrganizations, hasDesignatedEmergencyAccess, _currentContext.DeviceType);
await _pushService.PushSyncSettingsAsync(userId.Value);
}
}

View File

@ -0,0 +1,18 @@
using Bit.Core.KeyManagement.Commands;
using Bit.Core.KeyManagement.Commands.Interfaces;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.KeyManagement;
public static class KeyManagementServiceCollectionExtensions
{
public static void AddKeyManagementServices(this IServiceCollection services)
{
services.AddKeyManagementCommands();
}
private static void AddKeyManagementCommands(this IServiceCollection services)
{
services.AddScoped<IRegenerateUserAsymmetricKeysCommand, RegenerateUserAsymmetricKeysCommand>();
}
}