mirror of
https://github.com/bitwarden/server
synced 2026-02-11 05:53:47 +00:00
[PM-26376] Emergency Access Delete Command (#6857)
* feat: Add initial DeleteEmergencyContactCommand * chore: remove nullable enable and add comments * test: add tests for new delete command * test: update tests to test IMailer was called. * feat: add delete by GranteeId and allow for multiple grantors to be contacted. * feat: add DeleteMany stored procedure for EmergencyAccess * test: add database tests for new SP * feat: commands use DeleteManyById for emergencyAccessDeletes * claude: send one email per grantor instead of a bulk email to all grantors. Modified tests to validate. * feat: change revision dates for confirmed grantees; * feat: add AccountRevisionDate bump for grantee users in the confirmed status * test: update integration test to validate only confirmed users are updated as well as proper deletion of emergency access
This commit is contained in:
@@ -42,4 +42,97 @@ public class EmergencyAccessRepositoriesTests
|
||||
Assert.NotNull(updatedGrantee);
|
||||
Assert.NotEqual(updatedGrantee.AccountRevisionDate, granteeUser.AccountRevisionDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates 3 Emergency Access records all connected to a single grantor, but separate grantees.
|
||||
/// All 3 records are then deleted in a single call to DeleteManyAsync.
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task DeleteManyAsync_DeletesMultipleGranteeRecords_UpdatesUserRevisionDates(
|
||||
IUserRepository userRepository,
|
||||
IEmergencyAccessRepository emergencyAccessRepository)
|
||||
{
|
||||
// Arrange
|
||||
var grantorUser = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test Grantor User",
|
||||
Email = $"test+grantor{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var confirmedGranteeUser1 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test Grantee User 1",
|
||||
Email = $"test+grantee{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var invitedGranteeUser2 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test Grantee User 2",
|
||||
Email = $"test+grantee{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
// The inmemory datetime has a precision issue, so we need to refresh the user to get the stored AccountRevisionDate
|
||||
invitedGranteeUser2 = await userRepository.GetByIdAsync(invitedGranteeUser2.Id);
|
||||
|
||||
var granteeUser3 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test Grantee User 3",
|
||||
Email = $"test+grantee{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var confirmedEmergencyAccess = await emergencyAccessRepository.CreateAsync(new EmergencyAccess
|
||||
{
|
||||
GrantorId = grantorUser.Id,
|
||||
GranteeId = confirmedGranteeUser1.Id,
|
||||
Status = EmergencyAccessStatusType.Confirmed,
|
||||
});
|
||||
|
||||
var invitedEmergencyAccess = await emergencyAccessRepository.CreateAsync(new EmergencyAccess
|
||||
{
|
||||
GrantorId = grantorUser.Id,
|
||||
GranteeId = invitedGranteeUser2.Id,
|
||||
Status = EmergencyAccessStatusType.Invited,
|
||||
});
|
||||
|
||||
var acceptedEmergencyAccess = await emergencyAccessRepository.CreateAsync(new EmergencyAccess
|
||||
{
|
||||
GrantorId = grantorUser.Id,
|
||||
GranteeId = granteeUser3.Id,
|
||||
Status = EmergencyAccessStatusType.Accepted,
|
||||
});
|
||||
|
||||
|
||||
// Act
|
||||
await emergencyAccessRepository.DeleteManyAsync([confirmedEmergencyAccess.Id, invitedEmergencyAccess.Id, acceptedEmergencyAccess.Id]);
|
||||
|
||||
// Assert
|
||||
// ensure Grantor records deleted
|
||||
var grantorEmergencyAccess = await emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(grantorUser.Id);
|
||||
Assert.Empty(grantorEmergencyAccess);
|
||||
|
||||
// ensure Grantee records deleted
|
||||
foreach (var grantee in (List<User>)[confirmedGranteeUser1, invitedGranteeUser2, granteeUser3])
|
||||
{
|
||||
var granteeEmergencyAccess = await emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(grantee.Id);
|
||||
Assert.Empty(granteeEmergencyAccess);
|
||||
}
|
||||
|
||||
// Only the Status.Confirmed grantee's AccountRevisionDate should be updated
|
||||
var updatedConfirmedGrantee = await userRepository.GetByIdAsync(confirmedGranteeUser1.Id);
|
||||
Assert.NotNull(updatedConfirmedGrantee);
|
||||
Assert.NotEqual(updatedConfirmedGrantee.AccountRevisionDate, confirmedGranteeUser1.AccountRevisionDate);
|
||||
|
||||
// Invited user should not have an updated AccountRevisionDate
|
||||
var updatedInvitedGrantee = await userRepository.GetByIdAsync(invitedGranteeUser2.Id);
|
||||
Assert.NotNull(updatedInvitedGrantee);
|
||||
Assert.Equal(updatedInvitedGrantee.AccountRevisionDate, invitedGranteeUser2.AccountRevisionDate);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user