1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12:49 -05:00

[PM-15637] Add Email Notification Templates and Logic for Device Approval Requests (#5270)

* Add device approval notification email templates

* Add DeviceApprovalRequestedViewModel for device approval notifications

* Add method to send device approval requested notification email

* Send email notification to Organization Admins when adding a new admin approval auth request

* Add tests for device approval notification email sending in AuthRequestServiceTests

* fix(email-templates): Remove unnecessary triple braces from user name variable in device approval notification emails

* Add feature flag for admin notifications on device approval requests

* Add logging for skipped admin notifications on device approval requests
This commit is contained in:
Rui Tomé
2025-01-27 10:59:46 +00:00
committed by GitHub
parent 3908edd08f
commit 9e718d7336
11 changed files with 249 additions and 1 deletions

View File

@ -7,6 +7,7 @@ using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -227,6 +228,14 @@ public class AuthRequestServiceTests
await sutProvider.GetDependency<IAuthRequestRepository>()
.Received()
.CreateAsync(createdAuthRequest);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendDeviceApprovalRequestedNotificationEmailAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Any<Guid>(),
Arg.Any<string>(),
Arg.Any<string>());
}
/// <summary>
@ -321,6 +330,115 @@ public class AuthRequestServiceTests
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogUserEventAsync(user.Id, EventType.User_RequestedDeviceApproval);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendDeviceApprovalRequestedNotificationEmailAsync(
Arg.Any<IEnumerable<string>>(),
Arg.Any<Guid>(),
Arg.Any<string>(),
Arg.Any<string>());
}
[Theory, BitAutoData]
public async Task CreateAuthRequestAsync_AdminApproval_WithAdminNotifications_CreatesForEachOrganization_SendsEmails(
SutProvider<AuthRequestService> sutProvider,
AuthRequestCreateRequestModel createModel,
User user,
OrganizationUser organizationUser1,
OrganizationUserUserDetails admin1,
OrganizationUser organizationUser2,
OrganizationUserUserDetails admin2,
OrganizationUserUserDetails admin3)
{
createModel.Type = AuthRequestType.AdminApproval;
user.Email = createModel.Email;
organizationUser1.UserId = user.Id;
organizationUser2.UserId = user.Id;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.DeviceApprovalRequestAdminNotifications)
.Returns(true);
sutProvider.GetDependency<IUserRepository>()
.GetByEmailAsync(user.Email)
.Returns(user);
sutProvider.GetDependency<ICurrentContext>()
.DeviceType
.Returns(DeviceType.ChromeExtension);
sutProvider.GetDependency<ICurrentContext>()
.UserId
.Returns(user.Id);
sutProvider.GetDependency<IGlobalSettings>()
.PasswordlessAuth.KnownDevicesOnly
.Returns(false);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByUserAsync(user.Id)
.Returns(new List<OrganizationUser>
{
organizationUser1,
organizationUser2,
});
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByMinimumRoleAsync(organizationUser1.OrganizationId, OrganizationUserType.Admin)
.Returns(
[
admin1,
]);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByMinimumRoleAsync(organizationUser2.OrganizationId, OrganizationUserType.Admin)
.Returns(
[
admin2,
admin3,
]);
sutProvider.GetDependency<IAuthRequestRepository>()
.CreateAsync(Arg.Any<AuthRequest>())
.Returns(c => c.ArgAt<AuthRequest>(0));
var authRequest = await sutProvider.Sut.CreateAuthRequestAsync(createModel);
Assert.Equal(organizationUser1.OrganizationId, authRequest.OrganizationId);
await sutProvider.GetDependency<IAuthRequestRepository>()
.Received(1)
.CreateAsync(Arg.Is<AuthRequest>(o => o.OrganizationId == organizationUser1.OrganizationId));
await sutProvider.GetDependency<IAuthRequestRepository>()
.Received(1)
.CreateAsync(Arg.Is<AuthRequest>(o => o.OrganizationId == organizationUser2.OrganizationId));
await sutProvider.GetDependency<IAuthRequestRepository>()
.Received(2)
.CreateAsync(Arg.Any<AuthRequest>());
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogUserEventAsync(user.Id, EventType.User_RequestedDeviceApproval);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendDeviceApprovalRequestedNotificationEmailAsync(
Arg.Is<IEnumerable<string>>(emails => emails.Count() == 1 && emails.Contains(admin1.Email)),
organizationUser1.OrganizationId,
user.Email,
user.Name);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendDeviceApprovalRequestedNotificationEmailAsync(
Arg.Is<IEnumerable<string>>(emails => emails.Count() == 2 && emails.Contains(admin2.Email) && emails.Contains(admin3.Email)),
organizationUser2.OrganizationId,
user.Email,
user.Name);
}
/// <summary>