1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-06 18:42:49 -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

@ -15,6 +15,7 @@ using Bit.Core.Models.Mail.Provider;
using Bit.Core.SecretsManager.Models.Mail;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Core.Vault.Models.Data;
using HandlebarsDotNet;
namespace Bit.Core.Services;
@ -654,6 +655,10 @@ public class HandlebarsMailService : IMailService
Handlebars.RegisterTemplate("TitleContactUsHtmlLayout", titleContactUsHtmlLayoutSource);
var titleContactUsTextLayoutSource = await ReadSourceAsync("Layouts.TitleContactUs.text");
Handlebars.RegisterTemplate("TitleContactUsTextLayout", titleContactUsTextLayoutSource);
var securityTasksHtmlLayoutSource = await ReadSourceAsync("Layouts.SecurityTasks.html");
Handlebars.RegisterTemplate("SecurityTasksHtmlLayout", securityTasksHtmlLayoutSource);
var securityTasksTextLayoutSource = await ReadSourceAsync("Layouts.SecurityTasks.text");
Handlebars.RegisterTemplate("SecurityTasksTextLayout", securityTasksTextLayoutSource);
Handlebars.RegisterHelper("date", (writer, context, parameters) =>
{
@ -1196,9 +1201,26 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendBulkSecurityTaskNotificationsAsync(string orgName, IEnumerable<UserSecurityTasksCount> securityTaskNotificaitons)
{
MailQueueMessage CreateMessage(UserSecurityTasksCount notification)
{
var message = CreateDefaultMessage($"{orgName} has identified {notification.TaskCount} at-risk password{(notification.TaskCount.Equals(1) ? "" : "s")}", notification.Email);
var model = new SecurityTaskNotificationViewModel
{
OrgName = orgName,
TaskCount = notification.TaskCount,
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
};
message.Category = "SecurityTasksNotification";
return new MailQueueMessage(message, "SecurityTasksNotification", model);
}
var messageModels = securityTaskNotificaitons.Select(CreateMessage);
await EnqueueMailAsync(messageModels.ToList());
}
private static string GetUserIdentifier(string email, string userName)
{
return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false);
}
}