1
0
mirror of https://github.com/bitwarden/server synced 2026-02-14 15:33:35 +00:00

feat: add delete by GranteeId and allow for multiple grantors to be contacted.

This commit is contained in:
Ike Kottlowski
2026-01-21 18:00:33 -05:00
parent fc54737823
commit 07648a5992
2 changed files with 59 additions and 17 deletions

View File

@@ -14,18 +14,18 @@ public class DeleteEmergencyAccessCommand(
/// <inheritdoc />
public async Task<EmergencyAccessDetails> DeleteByIdGrantorIdAsync(Guid emergencyAccessId, Guid grantorId)
{
var emergencyAccess = await _emergencyAccessRepository.GetDetailsByIdGrantorIdAsync(emergencyAccessId, grantorId);
var emergencyAccessDetails = await _emergencyAccessRepository.GetDetailsByIdGrantorIdAsync(emergencyAccessId, grantorId);
if (emergencyAccess == null || emergencyAccess.GrantorId != grantorId)
if (emergencyAccessDetails == null || emergencyAccessDetails.GrantorId != grantorId)
{
throw new BadRequestException("Emergency Access not valid.");
}
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
var (grantorEmails, granteeEmails) = await DeleteEmergencyAccessAsync([emergencyAccessDetails]);
// Send notification email to grantor
await SendEmergencyAccessRemoveGranteesEmailAsync(emergencyAccess.GrantorEmail, [emergencyAccess.GranteeName]);
return emergencyAccess;
await SendEmergencyAccessRemoveGranteesEmailAsync(grantorEmails, granteeEmails);
return emergencyAccessDetails;
}
/// <inheritdoc />
@@ -33,34 +33,69 @@ public class DeleteEmergencyAccessCommand(
{
var emergencyAccessDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(grantorId);
// if there is nothing return an empty array and do not send an email
if (emergencyAccessDetails == null || emergencyAccessDetails.Count == 0)
{
return emergencyAccessDetails;
}
var (grantorEmails, granteeEmails) = await DeleteEmergencyAccessAsync(emergencyAccessDetails);
// Send notification email to grantor
await SendEmergencyAccessRemoveGranteesEmailAsync(grantorEmails, granteeEmails); ;
return emergencyAccessDetails;
}
/// <inheritdoc />
public async Task<ICollection<EmergencyAccessDetails>?> DeleteAllByGranteeIdAsync(Guid granteeId)
{
var emergencyAccessDetails = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(granteeId);
// if there is nothing return an empty array
if (emergencyAccessDetails == null || emergencyAccessDetails.Count == 0)
{
return emergencyAccessDetails;
}
foreach (var details in emergencyAccessDetails)
{
var emergencyAccess = details.ToEmergencyAccess();
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
}
var (grantorEmails, granteeEmails) = await DeleteEmergencyAccessAsync(emergencyAccessDetails);
// Send notification email to grantor
await SendEmergencyAccessRemoveGranteesEmailAsync(
emergencyAccessDetails.FirstOrDefault()?.GrantorEmail ?? string.Empty,
[.. emergencyAccessDetails.Select(e => e.GranteeName)]);
// Send notification email to grantor(s)
await SendEmergencyAccessRemoveGranteesEmailAsync(grantorEmails, granteeEmails);
return emergencyAccessDetails;
}
private async Task SendEmergencyAccessRemoveGranteesEmailAsync(string grantorEmail, string[] granteeNames)
private async Task<(HashSet<string> grantorEmails, HashSet<string> granteeEmails)> DeleteEmergencyAccessAsync(IEnumerable<EmergencyAccessDetails> emergencyAccessDetails)
{
var grantorEmails = new HashSet<string>();
var granteeEmails = new HashSet<string>();
foreach (var details in emergencyAccessDetails)
{
var emergencyAccess = details.ToEmergencyAccess();
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
granteeEmails.Add(details.GranteeEmail ?? string.Empty);
grantorEmails.Add(details.GrantorEmail);
}
return (grantorEmails, granteeEmails);
}
/// <summary>
/// Sends an email notification to the grantor about removed grantees.
/// </summary>
/// <param name="grantorEmails">The email addresses of the grantors to notify when deleting by grantee</param>
/// <param name="formattedGranteeIdentifiers">The formatted identifiers of the removed grantees to include in the email</param>
/// <returns></returns>
private async Task SendEmergencyAccessRemoveGranteesEmailAsync(IEnumerable<string> grantorEmails, IEnumerable<string> formattedGranteeIdentifiers)
{
var email = new EmergencyAccessRemoveGranteesMail
{
ToEmails = [grantorEmail],
ToEmails = grantorEmails,
View = new EmergencyAccessRemoveGranteesMailView
{
RemovedGranteeNames = granteeNames
RemovedGranteeEmails = formattedGranteeIdentifiers
}
};

View File

@@ -25,4 +25,11 @@ public interface IDeleteEmergencyAccessCommand
/// <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);
/// <summary>
/// Deletes all emergency access records for the specified grantee.
/// </summary>
/// <param name="granteeId">The ID of the grantee user whose emergency access records should be deleted.</param>
/// <returns>A collection of the deleted emergency access records.</returns>
Task<ICollection<EmergencyAccessDetails>?> DeleteAllByGranteeIdAsync(Guid granteeId);
}