Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-14406] Security Task Notifications #5344

Open
wants to merge 75 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
69da981
[PM-14378] Introduce GetCipherPermissionsForOrganization query for Da…
shane-melton Dec 6, 2024
c85a930
[PM-14378] Introduce GetCipherPermissionsForOrganization method for E…
shane-melton Dec 6, 2024
db5bd64
[PM-14378] Add integration tests for new repository method
shane-melton Dec 6, 2024
a03ddee
[PM-14378] Introduce IGetCipherPermissionsForUserQuery CQRS query
shane-melton Dec 6, 2024
532dd07
[PM-14378] Introduce SecurityTaskOperationRequirement
shane-melton Dec 6, 2024
5c38328
[PM-14378] Introduce SecurityTaskAuthorizationHandler.cs
shane-melton Dec 6, 2024
19a1814
[PM-14378] Introduce SecurityTaskOrganizationAuthorizationHandler.cs
shane-melton Dec 6, 2024
df3e424
[PM-14378] Register new authorization handlers
shane-melton Dec 6, 2024
78ea8b5
[PM-14378] Formatting
shane-melton Dec 6, 2024
4d80238
[PM-14378] Add unit tests for GetCipherPermissionsForUserQuery
shane-melton Dec 11, 2024
b40d144
[PM-15378] Cleanup SecurityTaskAuthorizationHandler and add tests
shane-melton Dec 12, 2024
ca15550
[PM-14378] Add tests for SecurityTaskOrganizationAuthorizationHandler
shane-melton Dec 12, 2024
bcf3210
[PM-14378] Formatting
shane-melton Dec 12, 2024
021634c
[PM-14378] Update date in migration file
shane-melton Dec 12, 2024
d222923
Merge branch 'main' into vault/pm-14378/security-task-auth-handler
shane-melton Dec 12, 2024
6eb33cf
Merge branch 'main' into vault/pm-14378/security-task-auth-handler
shane-melton Dec 12, 2024
b10df9b
[PM-14378] Add missing awaits
shane-melton Dec 13, 2024
3a04e88
Added bulk create request model
gbubemismith Dec 13, 2024
9cef90b
Merge branch 'main' into vault/pm-14378/security-task-auth-handler
shane-melton Dec 16, 2024
d00b25b
Created sproc to create bulk security tasks
gbubemismith Dec 20, 2024
3198ea7
Renamed tasks to SecurityTasksInput
gbubemismith Dec 20, 2024
4fabb8a
Added create many implementation for sqlserver and ef core
gbubemismith Dec 20, 2024
ef37e9d
removed trailing comma
gbubemismith Dec 23, 2024
0edb923
created ef implementatin for create many and added integration test
gbubemismith Dec 23, 2024
2684901
Refactored request model
gbubemismith Dec 23, 2024
0adb3e3
Refactored request model
gbubemismith Dec 23, 2024
ba848ce
created create many tasks command interface and class
gbubemismith Dec 23, 2024
74b25da
Merge commit 'refs/pull/5039/head' of https://github.com/bitwarden/se…
gbubemismith Dec 23, 2024
7738ce4
added security authorization handler work temp
gbubemismith Dec 23, 2024
67f3215
Added the implementation for the create manys tasks command
gbubemismith Dec 23, 2024
d2bed09
Added comment
gbubemismith Dec 23, 2024
6aaba7e
Changed return to return list of created security tasks
gbubemismith Dec 23, 2024
04c7c56
Registered command
gbubemismith Dec 23, 2024
6df6132
Completed bulk create action
gbubemismith Dec 23, 2024
e3352f4
Added unit tests for the command
gbubemismith Dec 23, 2024
e7abb09
removed hard coded table name
gbubemismith Dec 23, 2024
8868b3c
Fixed lint issue
gbubemismith Dec 23, 2024
72b121d
Added JsonConverter attribute to allow enum value to be passed as string
gbubemismith Dec 27, 2024
fff7707
Fixed merge conflicts
gbubemismith Jan 9, 2025
770fa34
Removed makshift security task operations
gbubemismith Jan 9, 2025
239a191
Fixed references
gbubemismith Jan 9, 2025
714666b
Removed old migration
gbubemismith Jan 9, 2025
a3ffb5f
Rebased
gbubemismith Jan 9, 2025
a99afb6
[PM-14378] Introduce GetCipherPermissionsForOrganization query for Da…
shane-melton Dec 6, 2024
4cfbd3d
[PM-14378] Introduce GetCipherPermissionsForOrganization method for E…
shane-melton Dec 6, 2024
2481ca1
[PM-14378] Add unit tests for GetCipherPermissionsForUserQuery
shane-melton Dec 11, 2024
b4e890a
Completed bulk create action
gbubemismith Dec 23, 2024
c1c7f3f
Fixed conflicts
gbubemismith Jan 9, 2025
6740471
Merge branch 'main' into vault/PM-14381
gbubemismith Jan 9, 2025
897285f
bumped migration version
gbubemismith Jan 9, 2025
e6c7722
Fixed lint issue
gbubemismith Jan 9, 2025
6f2e408
initial commit of `CipherOrganizationPermission_GetManyByUserId`
nick-livefront Jan 21, 2025
8022148
create queries to get all of the security tasks that are actionable b…
nick-livefront Jan 24, 2025
c239f19
rename query
nick-livefront Jan 24, 2025
945f2db
return the user's email from the query as well
nick-livefront Jan 24, 2025
2652f80
Add email notification for at-risk passwords
nick-livefront Jan 27, 2025
7585106
Merge branch 'main' of https://github.com/bitwarden/server into vault…
nick-livefront Jan 27, 2025
b067638
Removed complex sql data type in favour of json string
gbubemismith Jan 27, 2025
7e82d62
Merge branch 'main' into vault/PM-14381
gbubemismith Jan 27, 2025
e59946c
Register IGetTasksForOrganizationQuery
gbubemismith Jan 27, 2025
2b4c9a9
Fixed lint issue
gbubemismith Jan 27, 2025
28fb1ed
add push notification for security tasks
nick-livefront Jan 27, 2025
2ef25f5
Removed tasks grouping
gbubemismith Jan 28, 2025
b4ac978
Fixed linting
gbubemismith Jan 28, 2025
a9629c8
Removed unused code
gbubemismith Jan 28, 2025
05561f1
Removed unused code
gbubemismith Jan 28, 2025
fbc86fb
update entity framework to match stored procedure plus testing
nick-livefront Jan 29, 2025
5664944
update date of migration and remove orderby
nick-livefront Jan 29, 2025
6cce985
add push service to security task controller
nick-livefront Jan 29, 2025
cadd8d4
Merge branch 'vault/PM-14381' of https://github.com/bitwarden/server …
nick-livefront Jan 29, 2025
9ac2bc6
rename `SyncSecurityTasksCreated` to `SyncNotification`
nick-livefront Jan 29, 2025
447a60a
remove duplicate return
nick-livefront Jan 29, 2025
fb725b2
remove unused directive
nick-livefront Jan 29, 2025
1c9037d
remove unneeded new notification type
nick-livefront Jan 30, 2025
b43b131
use `createNotificationCommand` to alert all platforms
nick-livefront Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/Api/Vault/Controllers/SecurityTaskController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
using Bit.Api.Models.Response;
using Bit.Api.Vault.Models.Request;
using Bit.Api.Vault.Models.Response;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.NotificationCenter.Commands.Interfaces;
using Bit.Core.NotificationCenter.Entities;
using Bit.Core.NotificationCenter.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Commands.Interfaces;
Expand All @@ -20,17 +26,32 @@
private readonly IGetTaskDetailsForUserQuery _getTaskDetailsForUserQuery;
private readonly IMarkTaskAsCompleteCommand _markTaskAsCompleteCommand;
private readonly IGetTasksForOrganizationQuery _getTasksForOrganizationQuery;
private readonly ICreateManyTasksCommand _createManyTasksCommand;
private readonly IGetSecurityTasksNotificationDetailsQuery _getSecurityTasksNotificationDetailsQuery;
private readonly IOrganizationRepository _organizationRepository;
private readonly IMailService _mailService;
private readonly ICreateNotificationCommand _createNotificationCommand;

public SecurityTaskController(
IUserService userService,
IGetTaskDetailsForUserQuery getTaskDetailsForUserQuery,
IMarkTaskAsCompleteCommand markTaskAsCompleteCommand,
IGetTasksForOrganizationQuery getTasksForOrganizationQuery)
IGetTasksForOrganizationQuery getTasksForOrganizationQuery,
ICreateManyTasksCommand createManyTasksCommand,
IGetSecurityTasksNotificationDetailsQuery getSecurityTasksNotificationDetailsQuery,
IOrganizationRepository organizationRepository,
IMailService mailService,
ICreateNotificationCommand createNotificationCommand)

Check warning on line 44 in src/Api/Vault/Controllers/SecurityTaskController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/SecurityTaskController.cs#L40-L44

Added lines #L40 - L44 were not covered by tests
{
_userService = userService;
_getTaskDetailsForUserQuery = getTaskDetailsForUserQuery;
_markTaskAsCompleteCommand = markTaskAsCompleteCommand;
_getTasksForOrganizationQuery = getTasksForOrganizationQuery;
_createManyTasksCommand = createManyTasksCommand;
_getSecurityTasksNotificationDetailsQuery = getSecurityTasksNotificationDetailsQuery;
_organizationRepository = organizationRepository;
_mailService = mailService;
_createNotificationCommand = createNotificationCommand;

Check warning on line 54 in src/Api/Vault/Controllers/SecurityTaskController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/SecurityTaskController.cs#L51-L54

Added lines #L51 - L54 were not covered by tests
}

/// <summary>
Expand Down Expand Up @@ -71,4 +92,35 @@
var response = securityTasks.Select(x => new SecurityTasksResponseModel(x)).ToList();
return new ListResponseModel<SecurityTasksResponseModel>(response);
}

/// <summary>
/// Bulk create security tasks for an organization.
/// </summary>
/// <param name="orgId"></param>
/// <param name="model"></param>
/// <returns>A list response model containing the security tasks created for the organization.</returns>
[HttpPost("{orgId:guid}/bulk-create")]
public async Task<ListResponseModel<SecurityTasksResponseModel>> BulkCreateTasks(Guid orgId,
[FromBody] BulkCreateSecurityTasksRequestModel model)
{
var securityTasks = await _createManyTasksCommand.CreateAsync(orgId, model.Tasks);

var userTaskCount = await _getSecurityTasksNotificationDetailsQuery.GetNotificationDetailsByManyIds(orgId, securityTasks);
var organization = await _organizationRepository.GetByIdAsync(orgId);
await _mailService.SendBulkSecurityTaskNotificationsAsync(organization.Name, userTaskCount);

Check warning on line 110 in src/Api/Vault/Controllers/SecurityTaskController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/SecurityTaskController.cs#L108-L110

Added lines #L108 - L110 were not covered by tests
foreach (var task in userTaskCount)
{
var notification = new Notification
{
UserId = task.UserId,
OrganizationId = orgId,
Priority = Priority.Informational,
ClientType = ClientType.Browser,
Comment on lines +117 to +118
Copy link
Contributor Author

@nick-livefront nick-livefront Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unsure of what properties to set here, this felt like a good starting point for the task notifications.

Copy link
Member

@shane-melton shane-melton Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question, these seem reasonable to me though!

❓ Is this creating multiple notifications per user per task? I haven't dug too deep into the PR yet, but this is what we're going for now and we'll likely need to update this to include the TaskId in the Notification (this is being added in PM-14590 [Server] Modify Notification database table

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this sends a single notification (push and email) to an individual. Are you saying that we want to have a notification for each security task that the user can action on? That will be kind of noisy, no? X number of emails for each applicable task.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be a notification row in our backend database associated to a task for a variety of reasons to make management of the tasks better, but that is not to imply that communications such as push, email, etc. are 1:1 -- it would be per bulk operation.

};
await _createNotificationCommand.CreateAsync(notification);
}

Check warning on line 121 in src/Api/Vault/Controllers/SecurityTaskController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/SecurityTaskController.cs#L112-L121

Added lines #L112 - L121 were not covered by tests

var response = securityTasks.Select(x => new SecurityTasksResponseModel(x)).ToList();
return new ListResponseModel<SecurityTasksResponseModel>(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Bit.Core.Vault.Models.Api;

namespace Bit.Api.Vault.Models.Request;

public class BulkCreateSecurityTasksRequestModel
{
public IEnumerable<SecurityTaskCreateRequest> Tasks { get; set; }
}
61 changes: 61 additions & 0 deletions src/Core/MailTemplates/Handlebars/Layouts/SecurityTasks.html.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{{#>FullUpdatedHtmlLayout}}
<table border="0" cellpadding="0" cellspacing="0" width="100%"
style="background-color: #175DDC;padding-top:25px;padding-bottom:15px;">
<tr>
<td align="center" valign="top" width="70%" class="templateColumnContainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%"
style="padding-left:30px; padding-right: 5px; padding-top: 20px;">
<tr>
<td
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 24px; color: #ffffff; line-height: 32px; font-weight: 500; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
{{OrgName}} has identified {{TaskCount}} critical login{{#if TaskCountPlural}}s{{/if}} that require{{#unless
TaskCountPlural}}s{{/unless}} a
password change
</td>
</tr>
</table>
</td>
<td align="right" valign="bottom" class="templateColumnContainer" style="padding-right: 15px;">
<img width="140" height="140" align="right" valign="bottom"
style="width: 140px; height:140px; font-size: 0; vertical-align: bottom; text-align: right;" alt=''
src='https://assets.bitwarden.com/email/v1/business-warning.png' />
</td>
</tr>
</table>

{{>@partial-block}}

<table width="100%" style="display:table; background-color: #FBFBFB; vertical-align: middle; padding:30px" border="0"
cellpadding="0" cellspacing="0" valign="middle">
<tr>
<td width="70%" class="footer-text" style="padding-right: 20px;">
<table align="left" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-style: normal; font-weight: 400; font-size: 16px; line-height: 24px;">
<p
style="margin: 0; padding: 0; margin-bottom: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-style: normal; font-weight: 600; font-size: 20px; line-height: 28px;">
We’re here for you!</p>
If you have any questions, search the Bitwarden <a
style="text-decoration: none; color: #175DDC; font-weight: 600;"
href="https://bitwarden.com/help/">Help</a> site or <a
style="text-decoration: none; color: #175DDC; font-weight: 600;"
href="https://bitwarden.com/contact/">contact us</a>.
</td>
</tr>
</table>
</td>
<td width="30%">
<table align="right" valign="bottom" class="footer-image" border="0" cellpadding="0" cellspacing="0"
style="padding-left: 40px;">
<tr>
<td>
<img width="94" height="77" src="https://assets.bitwarden.com/email/v1/chat.png"
style="width: 94px; height: 77px;" alt="" />
</td>
</tr>
</table>
</td>
</tr>
</table>
{{/FullUpdatedHtmlLayout}}
12 changes: 12 additions & 0 deletions src/Core/MailTemplates/Handlebars/Layouts/SecurityTasks.text.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{#>FullTextLayout}}
{{OrgName}} has identified {{TaskCount}} critical login{{#if TaskCountPlural}}s{{/if}} that require{{#unless
TaskCountPlural}}s{{/unless}} a
password change

{{>@partial-block}}

We’re here for you!
If you have any questions, search the Bitwarden Help site or contact us.
- https://bitwarden.com/help/
- https://bitwarden.com/contact/
{{/FullTextLayout}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{#>SecurityTasksHtmlLayout}}
<table width="100%" border="0" style="display: block; padding: 30px;" align="center" cellpadding="0" cellspacing="0">
<tr>
<td
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-style: normal; font-weight: 400; font-size: 16px; line-height: 24px;">
Keep you and your organization's data safe by changing passwords that are weak, reused, or have been exposed in a
data breach.
</td>
</tr>
<tr>
<td
style="padding-top: 24px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-style: normal; font-weight: 400; font-size: 16px; line-height: 24px;">
Launch the Bitwarden extension to review your at-risk passwords.
</td>
</tr>
</table>
<table width="100%" border="0" cellpadding="0" cellspacing="0"
style="display: table; width:100%; padding-bottom: 35px; text-align: center;" align="center">
<tr>
<td display="display: table-cell">
<a href="{{ReviewPasswordsUrl}}" clicktracking=off target="_blank"
style="display: inline-block; color: #ffffff; text-decoration: none; text-align: center; cursor: pointer; border-radius: 999px; background-color: #175DDC; border-color: #175DDC; border-style: solid; border-width: 10px 20px; margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
Review at-risk passwords
</a>
</td>
</tr>
</table>
{{/SecurityTasksHtmlLayout}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{#>SecurityTasksHtmlLayout}}
Keep you and your organization's data safe by changing passwords that are weak, reused, or have been exposed in a data
breach.

Launch the Bitwarden extension to review your at-risk passwords.

Review at-risk passwords ({{{ReviewPasswordsUrl}}})
{{/SecurityTasksHtmlLayout}}
12 changes: 12 additions & 0 deletions src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Bit.Core.Models.Mail;

public class SecurityTaskNotificationViewModel : BaseMailModel
{
public string OrgName { get; set; }

Check warning on line 5 in src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs#L5

Added line #L5 was not covered by tests

public int TaskCount { get; set; }

Check warning on line 7 in src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs#L7

Added line #L7 was not covered by tests

public bool TaskCountPlural => TaskCount != 1;

Check warning on line 9 in src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs#L9

Added line #L9 was not covered by tests

public string ReviewPasswordsUrl => $"{WebVaultUrl}/browser-extension-prompt";

Check warning on line 11 in src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Models/Mail/SecurityTaskNotificationViewModel.cs#L11

Added line #L11 was not covered by tests
}
3 changes: 2 additions & 1 deletion src/Core/Services/IMailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Bit.Core.Entities;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Mail;
using Bit.Core.Vault.Models.Data;

namespace Bit.Core.Services;

Expand Down Expand Up @@ -96,6 +97,6 @@ Task SendProviderUpdatePaymentMethod(
Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId,
string organizationName);
Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList);
Task SendBulkSecurityTaskNotificationsAsync(string orgName, IEnumerable<UserSecurityTasksCount> securityTaskNotificaitons);
Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName);
}

24 changes: 23 additions & 1 deletion src/Core/Services/Implementations/HandlebarsMailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down Expand Up @@ -644,6 +645,10 @@
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) =>
{
Expand Down Expand Up @@ -1169,6 +1174,24 @@
await _mailDeliveryService.SendEmailAsync(message);
}

public async Task SendBulkSecurityTaskNotificationsAsync(string orgName, IEnumerable<UserSecurityTasksCount> securityTaskNotificaitons)
{

Check warning on line 1178 in src/Core/Services/Implementations/HandlebarsMailService.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Services/Implementations/HandlebarsMailService.cs#L1178

Added line #L1178 was not covered by tests
MailQueueMessage CreateMessage(UserSecurityTasksCount notification)
{

Check warning on line 1180 in src/Core/Services/Implementations/HandlebarsMailService.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Services/Implementations/HandlebarsMailService.cs#L1180

Added line #L1180 was not covered by tests
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());
}

Check warning on line 1193 in src/Core/Services/Implementations/HandlebarsMailService.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Services/Implementations/HandlebarsMailService.cs#L1182-L1193

Added lines #L1182 - L1193 were not covered by tests

public async Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName)
{
var templateName = _globalSettings.SelfHosted ?
Expand All @@ -1191,4 +1214,3 @@
return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false);
}
}

7 changes: 6 additions & 1 deletion src/Core/Services/NoopImplementations/NoopMailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Bit.Core.Entities;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Mail;
using Bit.Core.Vault.Models.Data;

namespace Bit.Core.Services;

Expand Down Expand Up @@ -321,5 +322,9 @@
{
return Task.FromResult(0);
}
}

public Task SendBulkSecurityTaskNotificationsAsync(string orgName, IEnumerable<UserSecurityTasksCount> securityTaskNotificaitons)
{
return Task.FromResult(0);
}

Check warning on line 329 in src/Core/Services/NoopImplementations/NoopMailService.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Services/NoopImplementations/NoopMailService.cs#L327-L329

Added lines #L327 - L329 were not covered by tests
}
16 changes: 0 additions & 16 deletions src/Core/Vault/Authorization/SecurityTaskOperations.cs

This file was deleted.

65 changes: 65 additions & 0 deletions src/Core/Vault/Commands/CreateManyTasksCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using Bit.Core.Vault.Authorization.SecurityTasks;
using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Enums;
using Bit.Core.Vault.Models.Api;
using Bit.Core.Vault.Repositories;
using Microsoft.AspNetCore.Authorization;

namespace Bit.Core.Vault.Commands;

public class CreateManyTasksCommand : ICreateManyTasksCommand
{
private readonly IAuthorizationService _authorizationService;
private readonly ICurrentContext _currentContext;
private readonly ISecurityTaskRepository _securityTaskRepository;

public CreateManyTasksCommand(
ISecurityTaskRepository securityTaskRepository,
IAuthorizationService authorizationService,
ICurrentContext currentContext)
{
_securityTaskRepository = securityTaskRepository;
_authorizationService = authorizationService;
_currentContext = currentContext;
}

/// <inheritdoc />
public async Task<ICollection<SecurityTask>> CreateAsync(Guid organizationId,
IEnumerable<SecurityTaskCreateRequest> tasks)
{
if (!_currentContext.UserId.HasValue)
{
throw new NotFoundException();
}

var tasksList = tasks?.ToList();

if (tasksList is null || tasksList.Count == 0)
{
throw new BadRequestException("No tasks provided.");
}

var securityTasks = tasksList.Select(t => new SecurityTask
{
OrganizationId = organizationId,
CipherId = t.CipherId,
Type = t.Type,
Status = SecurityTaskStatus.Pending
}).ToList();

// Verify authorization for each task
foreach (var task in securityTasks)
{
await _authorizationService.AuthorizeOrThrowAsync(
_currentContext.HttpContext.User,
task,
SecurityTaskOperations.Create);
}

return await _securityTaskRepository.CreateManyAsync(securityTasks);
}
}
Loading
Loading