diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs
new file mode 100644
index 000000000..c28185566
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs
@@ -0,0 +1,15 @@
+{{#>FullHtmlLayout}}
+
+
+
+ Your user account has been removed from the {{OrganizationName}} organization because you do not have two-step login configured. Before you can re-join this organization you need to set up two-step login on your user account.
+ |
+
+
+
+ Learn how to enable two-step login on your user account at
+ https://help.bitwarden.com/article/setup-two-step-login/
+ |
+
+
+{{/FullHtmlLayout}}
diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs
new file mode 100644
index 000000000..a79afb588
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs
@@ -0,0 +1,7 @@
+{{#>BasicTextLayout}}
+Your user account has been removed from the {{OrganizationName}} organization because you do not have two-step login
+configured. Before you can re-join this organization you need to set up two-step login on your user account.
+
+Learn how to enable two-step login on your user account at
+
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/Models/Mail/OrganizationUserRemovedForPolicyTwoStepViewModel.cs b/src/Core/Models/Mail/OrganizationUserRemovedForPolicyTwoStepViewModel.cs
new file mode 100644
index 000000000..10beaa5d7
--- /dev/null
+++ b/src/Core/Models/Mail/OrganizationUserRemovedForPolicyTwoStepViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail
+{
+ public class OrganizationUserRemovedForPolicyTwoStepViewModel : BaseMailModel
+ {
+ public string OrganizationName { get; set; }
+ }
+}
diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs
index c44d86855..72c4e5b01 100644
--- a/src/Core/Services/IMailService.cs
+++ b/src/Core/Services/IMailService.cs
@@ -19,6 +19,7 @@ namespace Bit.Core.Services
Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail,
IEnumerable adminEmails);
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
+ Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email);
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List items,
bool mentionInvoices);
diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs
index 8c7a223ae..2bd6bcc84 100644
--- a/src/Core/Services/Implementations/HandlebarsMailService.cs
+++ b/src/Core/Services/Implementations/HandlebarsMailService.cs
@@ -186,6 +186,20 @@ namespace Bit.Core.Services
await _mailDeliveryService.SendEmailAsync(message);
}
+ public async Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email)
+ {
+ var message = CreateDefaultMessage($"You have been removed from {organizationName}", email);
+ var model = new OrganizationUserRemovedForPolicyTwoStepViewModel
+ {
+ OrganizationName = CoreHelpers.SanitizeForEmail(organizationName),
+ WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
+ SiteName = _globalSettings.SiteName
+ };
+ await AddMessageContentAsync(message, "OrganizationUserRemovedForPolicyTwoStep", model);
+ message.Category = "OrganizationUserRemovedForPolicyTwoStep";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
public async Task SendWelcomeEmailAsync(User user)
{
var message = CreateDefaultMessage("Welcome", user.Email);
diff --git a/src/Core/Services/Implementations/PolicyService.cs b/src/Core/Services/Implementations/PolicyService.cs
index 361966276..fa2c5164b 100644
--- a/src/Core/Services/Implementations/PolicyService.cs
+++ b/src/Core/Services/Implementations/PolicyService.cs
@@ -13,17 +13,20 @@ namespace Bit.Core.Services
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IPolicyRepository _policyRepository;
+ private readonly IMailService _mailService;
public PolicyService(
IEventService eventService,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
- IPolicyRepository policyRepository)
+ IPolicyRepository policyRepository,
+ IMailService mailService)
{
_eventService = eventService;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_policyRepository = policyRepository;
+ _mailService = mailService;
}
public async Task SaveAsync(Policy policy, IUserService userService, IOrganizationService organizationService,
@@ -52,6 +55,7 @@ namespace Bit.Core.Services
{
if(currentPolicy.Type == Enums.PolicyType.TwoFactorAuthentication)
{
+ Organization organization = null;
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(
policy.OrganizationId);
foreach(var orgUser in orgUsers.Where(ou =>
@@ -60,8 +64,14 @@ namespace Bit.Core.Services
{
if(orgUser.UserId != savingUserId && !await userService.TwoFactorIsEnabledAsync(orgUser))
{
+ if(organization == null)
+ {
+ organization = await _organizationRepository.GetByIdAsync(policy.OrganizationId);
+ }
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
savingUserId);
+ await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
+ organization.Name, orgUser.Email);
}
}
}
diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs
index ce221b672..0ccdfffbf 100644
--- a/src/Core/Services/Implementations/UserService.cs
+++ b/src/Core/Services/Implementations/UserService.cs
@@ -669,6 +669,9 @@ namespace Bit.Core.Services
if(!ownerOrgs.Contains(policy.OrganizationId))
{
await organizationService.DeleteUserAsync(policy.OrganizationId, user.Id);
+ var organization = await _organizationRepository.GetByIdAsync(policy.OrganizationId);
+ await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
+ organization.Name, user.Email);
}
}
}
diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs
index 5fb3f0c11..bdbb742d7 100644
--- a/src/Core/Services/NoopImplementations/NoopMailService.cs
+++ b/src/Core/Services/NoopImplementations/NoopMailService.cs
@@ -47,6 +47,11 @@ namespace Bit.Core.Services
return Task.FromResult(0);
}
+ public Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email)
+ {
+ return Task.FromResult(0);
+ }
+
public Task SendTwoFactorEmailAsync(string email, string token)
{
return Task.FromResult(0);