1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-17 07:30:59 -05:00

[PM-14406] Security Task Notifications (#5344)

* initial commit of `CipherOrganizationPermission_GetManyByUserId`

* create queries to get all of the security tasks that are actionable by a user

- A task is "actionable" when the user has manage permissions for that cipher

* rename query

* return the user's email from the query as well

* Add email notification for at-risk passwords

- Added email layouts for security tasks

* add push notification for security tasks

* update entity framework to match stored procedure plus testing

* update date of migration and remove orderby

* add push service to security task controller

* rename `SyncSecurityTasksCreated` to `SyncNotification`

* remove duplicate return

* remove unused directive

* remove unneeded new notification type

* use `createNotificationCommand` to alert all platforms

* return the cipher id that is associated with the security task and store the security task id on the notification entry

* Add `TaskId` to the output model of `GetUserSecurityTasksByCipherIdsAsync`

* move notification logic to command

* use TaskId from `_getSecurityTasksNotificationDetailsQuery`

* add service

* only push last notification for each user

* formatting

* refactor `CreateNotificationCommand` parameter to `sendPush`

* flip boolean in test

* update interface to match usage

* do not push any of the security related notifications to the user

* add `PendingSecurityTasks` push type

* add push notification for pending security tasks
This commit is contained in:
Nick Krantz
2025-02-27 08:34:42 -06:00
committed by GitHub
parent a2e665cb96
commit 1267332b5b
35 changed files with 893 additions and 8 deletions

View File

@ -0,0 +1,82 @@
using Bit.Core.Enums;
using Bit.Core.NotificationCenter.Commands.Interfaces;
using Bit.Core.NotificationCenter.Entities;
using Bit.Core.NotificationCenter.Enums;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Queries;
public class CreateManyTaskNotificationsCommand : ICreateManyTaskNotificationsCommand
{
private readonly IGetSecurityTasksNotificationDetailsQuery _getSecurityTasksNotificationDetailsQuery;
private readonly IOrganizationRepository _organizationRepository;
private readonly IMailService _mailService;
private readonly ICreateNotificationCommand _createNotificationCommand;
private readonly IPushNotificationService _pushNotificationService;
public CreateManyTaskNotificationsCommand(
IGetSecurityTasksNotificationDetailsQuery getSecurityTasksNotificationDetailsQuery,
IOrganizationRepository organizationRepository,
IMailService mailService,
ICreateNotificationCommand createNotificationCommand,
IPushNotificationService pushNotificationService)
{
_getSecurityTasksNotificationDetailsQuery = getSecurityTasksNotificationDetailsQuery;
_organizationRepository = organizationRepository;
_mailService = mailService;
_createNotificationCommand = createNotificationCommand;
_pushNotificationService = pushNotificationService;
}
public async Task CreateAsync(Guid orgId, IEnumerable<SecurityTask> securityTasks)
{
var securityTaskCiphers = await _getSecurityTasksNotificationDetailsQuery.GetNotificationDetailsByManyIds(orgId, securityTasks);
// Get the number of tasks for each user
var userTaskCount = securityTaskCiphers.GroupBy(x => x.UserId).Select(x => new UserSecurityTasksCount
{
UserId = x.Key,
Email = x.First().Email,
TaskCount = x.Count()
}).ToList();
var organization = await _organizationRepository.GetByIdAsync(orgId);
await _mailService.SendBulkSecurityTaskNotificationsAsync(organization.Name, userTaskCount);
// Break securityTaskCiphers into separate lists by user Id
var securityTaskCiphersByUser = securityTaskCiphers.GroupBy(x => x.UserId)
.ToDictionary(g => g.Key, g => g.ToList());
foreach (var userId in securityTaskCiphersByUser.Keys)
{
// Get the security tasks by the user Id
var userSecurityTaskCiphers = securityTaskCiphersByUser[userId];
// Process each user's security task ciphers
for (int i = 0; i < userSecurityTaskCiphers.Count; i++)
{
var userSecurityTaskCipher = userSecurityTaskCiphers[i];
// Create a notification for the user with the associated task
var notification = new Notification
{
UserId = userSecurityTaskCipher.UserId,
OrganizationId = orgId,
Priority = Priority.Informational,
ClientType = ClientType.Browser,
TaskId = userSecurityTaskCipher.TaskId
};
await _createNotificationCommand.CreateAsync(notification, false);
}
// Notify the user that they have pending security tasks
await _pushNotificationService.PushPendingSecurityTasksAsync(userId);
}
}
}