1
0
mirror of https://github.com/bitwarden/server synced 2026-01-28 15:23:38 +00:00

feat: Add initial DeleteEmergencyContactCommand

This commit is contained in:
Ike Kottlowski
2026-01-15 17:41:21 -05:00
parent 8eff440e83
commit c05d904e85
3 changed files with 99 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
using Bit.Core.Auth.UserFeatures.EmergencyAccess.Mail;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Mail.Mailer;
using Bit.Core.Repositories;
namespace Bit.Core.Auth.UserFeatures.EmergencyAccess.Commands;
public class DeleteEmergencyAccessCommand(
IEmergencyAccessRepository _emergencyAccessRepository,
IMailer mailer) : IDeleteEmergencyAccessCommand
{
/// <inheritdoc />
public async Task<EmergencyAccessDetails> DeleteByIdGrantorIdAsync(Guid emergencyAccessId, Guid grantorId)
{
var emergencyAccess = await _emergencyAccessRepository.GetDetailsByIdGrantorIdAsync(emergencyAccessId, grantorId);
if (emergencyAccess == null || emergencyAccess.GrantorId != grantorId)
{
throw new BadRequestException("Emergency Access not valid.");
}
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
// Send notification email to grantor
await SendEmailAsync(emergencyAccess.GrantorEmail, [emergencyAccess.GranteeName]);
return emergencyAccess;
}
/// <inheritdoc />
public async Task<ICollection<EmergencyAccessDetails>> DeleteAllByGrantorIdAsync(Guid grantorId)
{
var emergencyAccessDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(grantorId);
foreach (var details in emergencyAccessDetails)
{
var emergencyAccess = details.ToEmergencyAccess();
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
}
// Send notification email to grantor
await SendEmailAsync(
emergencyAccessDetails.FirstOrDefault()?.GrantorEmail ?? string.Empty,
[.. emergencyAccessDetails.Select(e => e.GranteeName)]);
return emergencyAccessDetails;
}
private async Task SendEmailAsync(string grantorEmail, string[] granteeNames)
{
var email = new EmergencyAccessRemoveGranteesMail
{
ToEmails = [grantorEmail],
View = new EmergencyAccessRemoveGranteesMailView
{
RemovedGranteeNames = granteeNames
}
};
await mailer.SendEmail(email);
}
}

View File

@@ -0,0 +1,28 @@
using Bit.Core.Auth.Models.Data;
using Bit.Core.Exceptions;
namespace Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
/// <summary>
/// Command for deleting emergency access records based on the grantor's user ID.
/// </summary>
public interface IDeleteEmergencyAccessCommand
{
/// <summary>
/// Deletes a single emergency access record for the specified grantor.
/// </summary>
/// <param name="emergencyAccessId">The ID of the emergency access record to delete.</param>
/// <param name="grantorId">The ID of the grantor user who owns the emergency access record.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <exception cref="BadRequestException">
/// Thrown when the emergency access record is not found or does not belong to the specified grantor.
/// </exception>
Task<EmergencyAccessDetails> DeleteByIdGrantorIdAsync(Guid emergencyAccessId, Guid grantorId);
/// <summary>
/// Deletes all emergency access records for the specified grantor.
/// </summary>
/// <param name="grantorId">The ID of the grantor user whose emergency access records should be deleted.</param>
/// <returns>A collection of the deleted emergency access records.</returns>
Task<ICollection<EmergencyAccessDetails>> DeleteAllByGrantorIdAsync(Guid grantorId);
}

View File

@@ -1,5 +1,7 @@
using Bit.Core.Auth.Sso;
using Bit.Core.Auth.UserFeatures.DeviceTrust;
using Bit.Core.Auth.UserFeatures.EmergencyAccess.Commands;
using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
using Bit.Core.Auth.UserFeatures.Registration;
using Bit.Core.Auth.UserFeatures.Registration.Implementations;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
@@ -23,6 +25,7 @@ public static class UserServiceCollectionExtensions
{
services.AddScoped<IUserService, UserService>();
services.AddDeviceTrustCommands();
services.AddEmergencyAccessCommands();
services.AddUserPasswordCommands();
services.AddUserRegistrationCommands();
services.AddWebAuthnLoginCommands();
@@ -36,6 +39,11 @@ public static class UserServiceCollectionExtensions
services.AddScoped<IUntrustDevicesCommand, UntrustDevicesCommand>();
}
private static void AddEmergencyAccessCommands(this IServiceCollection services)
{
services.AddScoped<IDeleteEmergencyAccessCommand, DeleteEmergencyAccessCommand>();
}
public static void AddUserKeyCommands(this IServiceCollection services, IGlobalSettings globalSettings)
{
services.AddScoped<IRotateUserAccountKeysCommand, RotateUserAccountKeysCommand>();