mirror of
https://github.com/bitwarden/server
synced 2026-01-04 17:43:53 +00:00
when ciphers are soft deleted, complete any associated security tasks (#6492)
This commit is contained in:
@@ -35,4 +35,10 @@ public interface ISecurityTaskRepository : IRepository<SecurityTask, Guid>
|
||||
/// <param name="organizationId">The id of the organization</param>
|
||||
/// <returns>A collection of security task metrics</returns>
|
||||
Task<SecurityTaskMetrics> GetTaskMetricsAsync(Guid organizationId);
|
||||
|
||||
/// <summary>
|
||||
/// Marks all tasks associated with the respective ciphers as complete.
|
||||
/// </summary>
|
||||
/// <param name="cipherIds">Collection of cipher IDs</param>
|
||||
Task MarkAsCompleteByCipherIds(IEnumerable<Guid> cipherIds);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public class CipherService : ICipherService
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly ISecurityTaskRepository _securityTaskRepository;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IAttachmentStorageService _attachmentStorageService;
|
||||
private readonly IEventService _eventService;
|
||||
@@ -53,6 +54,7 @@ public class CipherService : ICipherService
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
ISecurityTaskRepository securityTaskRepository,
|
||||
IPushNotificationService pushService,
|
||||
IAttachmentStorageService attachmentStorageService,
|
||||
IEventService eventService,
|
||||
@@ -71,6 +73,7 @@ public class CipherService : ICipherService
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_collectionCipherRepository = collectionCipherRepository;
|
||||
_securityTaskRepository = securityTaskRepository;
|
||||
_pushService = pushService;
|
||||
_attachmentStorageService = attachmentStorageService;
|
||||
_eventService = eventService;
|
||||
@@ -724,6 +727,7 @@ public class CipherService : ICipherService
|
||||
cipherDetails.ArchivedDate = null;
|
||||
}
|
||||
|
||||
await _securityTaskRepository.MarkAsCompleteByCipherIds([cipherDetails.Id]);
|
||||
await _cipherRepository.UpsertAsync(cipherDetails);
|
||||
await _eventService.LogCipherEventAsync(cipherDetails, EventType.Cipher_SoftDeleted);
|
||||
|
||||
@@ -750,6 +754,8 @@ public class CipherService : ICipherService
|
||||
await _cipherRepository.SoftDeleteAsync(deletingCiphers.Select(c => c.Id), deletingUserId);
|
||||
}
|
||||
|
||||
await _securityTaskRepository.MarkAsCompleteByCipherIds(deletingCiphers.Select(c => c.Id));
|
||||
|
||||
var events = deletingCiphers.Select(c =>
|
||||
new Tuple<Cipher, EventType, DateTime?>(c, EventType.Cipher_SoftDeleted, null));
|
||||
foreach (var eventsBatch in events.Chunk(100))
|
||||
|
||||
@@ -85,4 +85,19 @@ public class SecurityTaskRepository : Repository<SecurityTask, Guid>, ISecurityT
|
||||
|
||||
return tasksList;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task MarkAsCompleteByCipherIds(IEnumerable<Guid> cipherIds)
|
||||
{
|
||||
if (!cipherIds.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var connection = new SqlConnection(ConnectionString);
|
||||
await connection.ExecuteAsync(
|
||||
$"[{Schema}].[SecurityTask_MarkCompleteByCipherIds]",
|
||||
new { CipherIds = cipherIds.ToGuidIdArrayTVP() },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +96,24 @@ public class SecurityTaskRepository : Repository<Core.Vault.Entities.SecurityTas
|
||||
|
||||
return metrics ?? new Core.Vault.Entities.SecurityTaskMetrics(0, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task MarkAsCompleteByCipherIds(IEnumerable<Guid> cipherIds)
|
||||
{
|
||||
if (!cipherIds.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var cipherIdsList = cipherIds.ToList();
|
||||
|
||||
await dbContext.SecurityTasks
|
||||
.Where(st => st.CipherId.HasValue && cipherIdsList.Contains(st.CipherId.Value) && st.Status != SecurityTaskStatus.Completed)
|
||||
.ExecuteUpdateAsync(st => st
|
||||
.SetProperty(s => s.Status, SecurityTaskStatus.Completed)
|
||||
.SetProperty(s => s.RevisionDate, DateTime.UtcNow));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE PROCEDURE [dbo].[SecurityTask_MarkCompleteByCipherIds]
|
||||
@CipherIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
UPDATE
|
||||
[dbo].[SecurityTask]
|
||||
SET
|
||||
[Status] = 1, -- completed
|
||||
[RevisionDate] = SYSUTCDATETIME()
|
||||
WHERE
|
||||
[CipherId] IN (SELECT [Id] FROM @CipherIds)
|
||||
AND [Status] <> 1 -- Not already completed
|
||||
END
|
||||
Reference in New Issue
Block a user