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:
@ -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>
|
||||
|
Reference in New Issue
Block a user