+
+Thank you!
+The bitwarden Team
diff --git a/src/Core/MailTemplates/ChangeEmail.cshtml b/src/Core/MailTemplates/Razor/ChangeEmail.cshtml
similarity index 100%
rename from src/Core/MailTemplates/ChangeEmail.cshtml
rename to src/Core/MailTemplates/Razor/ChangeEmail.cshtml
diff --git a/src/Core/MailTemplates/ChangeEmail.text.cshtml b/src/Core/MailTemplates/Razor/ChangeEmail.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/ChangeEmail.text.cshtml
rename to src/Core/MailTemplates/Razor/ChangeEmail.text.cshtml
diff --git a/src/Core/MailTemplates/ChangeEmailAlreadyExists.cshtml b/src/Core/MailTemplates/Razor/ChangeEmailAlreadyExists.cshtml
similarity index 100%
rename from src/Core/MailTemplates/ChangeEmailAlreadyExists.cshtml
rename to src/Core/MailTemplates/Razor/ChangeEmailAlreadyExists.cshtml
diff --git a/src/Core/MailTemplates/ChangeEmailAlreadyExists.text.cshtml b/src/Core/MailTemplates/Razor/ChangeEmailAlreadyExists.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/ChangeEmailAlreadyExists.text.cshtml
rename to src/Core/MailTemplates/Razor/ChangeEmailAlreadyExists.text.cshtml
diff --git a/src/Core/MailTemplates/MasterPasswordHint.cshtml b/src/Core/MailTemplates/Razor/MasterPasswordHint.cshtml
similarity index 100%
rename from src/Core/MailTemplates/MasterPasswordHint.cshtml
rename to src/Core/MailTemplates/Razor/MasterPasswordHint.cshtml
diff --git a/src/Core/MailTemplates/MasterPasswordHint.text.cshtml b/src/Core/MailTemplates/Razor/MasterPasswordHint.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/MasterPasswordHint.text.cshtml
rename to src/Core/MailTemplates/Razor/MasterPasswordHint.text.cshtml
diff --git a/src/Core/MailTemplates/NoMasterPasswordHint.cshtml b/src/Core/MailTemplates/Razor/NoMasterPasswordHint.cshtml
similarity index 100%
rename from src/Core/MailTemplates/NoMasterPasswordHint.cshtml
rename to src/Core/MailTemplates/Razor/NoMasterPasswordHint.cshtml
diff --git a/src/Core/MailTemplates/NoMasterPasswordHint.text.cshtml b/src/Core/MailTemplates/Razor/NoMasterPasswordHint.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/NoMasterPasswordHint.text.cshtml
rename to src/Core/MailTemplates/Razor/NoMasterPasswordHint.text.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserAccepted.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserAccepted.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserAccepted.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserAccepted.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserAccepted.text.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserAccepted.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserAccepted.text.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserAccepted.text.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserConfirmed.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserConfirmed.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserConfirmed.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserConfirmed.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserConfirmed.text.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserConfirmed.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserConfirmed.text.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserConfirmed.text.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserInvited.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserInvited.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserInvited.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserInvited.cshtml
diff --git a/src/Core/MailTemplates/OrganizationUserInvited.text.cshtml b/src/Core/MailTemplates/Razor/OrganizationUserInvited.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/OrganizationUserInvited.text.cshtml
rename to src/Core/MailTemplates/Razor/OrganizationUserInvited.text.cshtml
diff --git a/src/Core/MailTemplates/TwoFactorEmail.cshtml b/src/Core/MailTemplates/Razor/TwoFactorEmail.cshtml
similarity index 100%
rename from src/Core/MailTemplates/TwoFactorEmail.cshtml
rename to src/Core/MailTemplates/Razor/TwoFactorEmail.cshtml
diff --git a/src/Core/MailTemplates/TwoFactorEmail.text.cshtml b/src/Core/MailTemplates/Razor/TwoFactorEmail.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/TwoFactorEmail.text.cshtml
rename to src/Core/MailTemplates/Razor/TwoFactorEmail.text.cshtml
diff --git a/src/Core/MailTemplates/VerifyDelete.cshtml b/src/Core/MailTemplates/Razor/VerifyDelete.cshtml
similarity index 100%
rename from src/Core/MailTemplates/VerifyDelete.cshtml
rename to src/Core/MailTemplates/Razor/VerifyDelete.cshtml
diff --git a/src/Core/MailTemplates/VerifyDelete.text.cshtml b/src/Core/MailTemplates/Razor/VerifyDelete.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/VerifyDelete.text.cshtml
rename to src/Core/MailTemplates/Razor/VerifyDelete.text.cshtml
diff --git a/src/Core/MailTemplates/VerifyEmail.cshtml b/src/Core/MailTemplates/Razor/VerifyEmail.cshtml
similarity index 66%
rename from src/Core/MailTemplates/VerifyEmail.cshtml
rename to src/Core/MailTemplates/Razor/VerifyEmail.cshtml
index 49edc5f125..691ea8c012 100644
--- a/src/Core/MailTemplates/VerifyEmail.cshtml
+++ b/src/Core/MailTemplates/Razor/VerifyEmail.cshtml
@@ -3,6 +3,7 @@
Layout = "_BasicMailLayout";
}
- Verify this email address for your bitwarden account by clicking the following link:
+ Verify this email address for your bitwarden account by clicking the following link. If you did not request this email to
+ verify a bitwarden account, you can safely ignore it.
@Model.Url
diff --git a/src/Core/MailTemplates/VerifyEmail.text.cshtml b/src/Core/MailTemplates/Razor/VerifyEmail.text.cshtml
similarity index 53%
rename from src/Core/MailTemplates/VerifyEmail.text.cshtml
rename to src/Core/MailTemplates/Razor/VerifyEmail.text.cshtml
index d599edc251..1a949ae20c 100644
--- a/src/Core/MailTemplates/VerifyEmail.text.cshtml
+++ b/src/Core/MailTemplates/Razor/VerifyEmail.text.cshtml
@@ -3,6 +3,8 @@
Layout = "_BasicMailLayout.text";
}
Verify this email address for your bitwarden
-account by clicking the following link:
+account by clicking the following link. If you
+did not request this email to verify a bitwarden
+account, you can safely ignore it.
@Raw(Model.Url)
diff --git a/src/Core/MailTemplates/Welcome.cshtml b/src/Core/MailTemplates/Razor/Welcome.cshtml
similarity index 100%
rename from src/Core/MailTemplates/Welcome.cshtml
rename to src/Core/MailTemplates/Razor/Welcome.cshtml
diff --git a/src/Core/MailTemplates/Welcome.text.cshtml b/src/Core/MailTemplates/Razor/Welcome.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/Welcome.text.cshtml
rename to src/Core/MailTemplates/Razor/Welcome.text.cshtml
diff --git a/src/Core/MailTemplates/_BasicMailLayout.cshtml b/src/Core/MailTemplates/Razor/_BasicMailLayout.cshtml
similarity index 100%
rename from src/Core/MailTemplates/_BasicMailLayout.cshtml
rename to src/Core/MailTemplates/Razor/_BasicMailLayout.cshtml
diff --git a/src/Core/MailTemplates/_BasicMailLayout.text.cshtml b/src/Core/MailTemplates/Razor/_BasicMailLayout.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/_BasicMailLayout.text.cshtml
rename to src/Core/MailTemplates/Razor/_BasicMailLayout.text.cshtml
diff --git a/src/Core/MailTemplates/_MailLayout.cshtml b/src/Core/MailTemplates/Razor/_MailLayout.cshtml
similarity index 100%
rename from src/Core/MailTemplates/_MailLayout.cshtml
rename to src/Core/MailTemplates/Razor/_MailLayout.cshtml
diff --git a/src/Core/MailTemplates/_MailLayout.text.cshtml b/src/Core/MailTemplates/Razor/_MailLayout.text.cshtml
similarity index 100%
rename from src/Core/MailTemplates/_MailLayout.text.cshtml
rename to src/Core/MailTemplates/Razor/_MailLayout.text.cshtml
diff --git a/src/Core/Services/Implementations/MarkdownMailService.cs b/src/Core/Services/Implementations/MarkdownMailService.cs
new file mode 100644
index 0000000000..12af1755f3
--- /dev/null
+++ b/src/Core/Services/Implementations/MarkdownMailService.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Bit.Core.Models.Table;
+using Bit.Core.Models.Mail;
+using System.IO;
+using System.Net;
+using System.Reflection;
+
+namespace Bit.Core.Services
+{
+ public class MarkdownMailService : IMailService
+ {
+ private const string Namespace = "Bit.Core.MailTemplates.Markdown";
+
+ private readonly GlobalSettings _globalSettings;
+ private readonly IMailDeliveryService _mailDeliveryService;
+
+ public MarkdownMailService(
+ GlobalSettings globalSettings,
+ IMailDeliveryService mailDeliveryService)
+ {
+ _globalSettings = globalSettings;
+ _mailDeliveryService = mailDeliveryService;
+ }
+
+ public async Task SendVerifyEmailEmailAsync(string email, Guid userId, string token)
+ {
+ var model = new Dictionary
+ {
+ ["url"] = string.Format("{0}/verify-email?userId={1}&token={2}",
+ _globalSettings.BaseServiceUri.Vault, userId, WebUtility.UrlEncode(token))
+ };
+
+ var message = await CreateMessageAsync("Verify Your Email", email, "VerifyEmail", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token)
+ {
+ var model = new Dictionary
+ {
+ ["url"] = string.Format("{0}/verify-recover-delete?userId={1}&token={2}&email={3}",
+ _globalSettings.BaseServiceUri.Vault,
+ userId,
+ WebUtility.UrlEncode(token),
+ WebUtility.UrlEncode(email)),
+ ["email"] = WebUtility.HtmlEncode(email)
+ };
+
+ var message = await CreateMessageAsync("Delete Your Account", email, "VerifyDelete", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
+ {
+ var model = new Dictionary
+ {
+ ["fromEmail"] = WebUtility.HtmlEncode(fromEmail),
+ ["toEmail"] = WebUtility.HtmlEncode(toEmail),
+ };
+
+ var message = await CreateMessageAsync("Your Email Change", toEmail, "ChangeEmailAlreadyExists", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendChangeEmailEmailAsync(string newEmailAddress, string token)
+ {
+ var model = new Dictionary
+ {
+ ["token"] = token
+ };
+
+ var message = await CreateMessageAsync("Your Email Change", newEmailAddress, "ChangeEmail", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendTwoFactorEmailAsync(string email, string token)
+ {
+ var model = new Dictionary
+ {
+ ["token"] = token
+ };
+
+ var message = await CreateMessageAsync("Your Two-step Login Verification Code", email, "TwoFactorEmail", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendMasterPasswordHintEmailAsync(string email, string hint)
+ {
+ var model = new Dictionary
+ {
+ ["hint"] = WebUtility.HtmlEncode(hint),
+ ["vaultUrl"] = _globalSettings.BaseServiceUri.Vault
+ };
+
+ var message = await CreateMessageAsync("Your Master Password Hint", email, "MasterPasswordHint", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendNoMasterPasswordHintEmailAsync(string email)
+ {
+ var message = await CreateMessageAsync("Your Master Password Hint", email, "NoMasterPasswordHint", null);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail,
+ IEnumerable adminEmails)
+ {
+ var model = new Dictionary
+ {
+ ["userEmail"] = WebUtility.HtmlEncode(userEmail),
+ ["organizationName"] = WebUtility.HtmlEncode(organizationName)
+ };
+
+ var message = await CreateMessageAsync($"User {userEmail} Has Accepted Invite", adminEmails,
+ "OrganizationUserAccepted", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendOrganizationConfirmedEmailAsync(string organizationName, string email)
+ {
+ var model = new Dictionary
+ {
+ ["organizationName"] = WebUtility.HtmlEncode(organizationName)
+ };
+
+ var message = await CreateMessageAsync($"You Have Been Confirmed To {organizationName}", email,
+ "OrganizationUserConfirmed", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
+ {
+ var model = new Dictionary
+ {
+ ["organizationName"] = WebUtility.HtmlEncode(organizationName),
+ ["url"] = string.Format("{0}/accept-organization?organizationId={1}&organizationUserId={2}" +
+ "&email={3}&organizationName={4}&token={5}",
+ _globalSettings.BaseServiceUri.Vault,
+ orgUser.OrganizationId,
+ orgUser.Id,
+ WebUtility.UrlEncode(orgUser.Email),
+ WebUtility.UrlEncode(organizationName),
+ WebUtility.UrlEncode(token))
+ };
+
+ var message = await CreateMessageAsync($"Join {organizationName}", orgUser.Email, "OrganizationUserInvited", model);
+ message.MetaData.Add("SendGridBypassListManagement", true);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendWelcomeEmailAsync(User user)
+ {
+ var model = new Dictionary
+ {
+ ["vaultUrl"] = _globalSettings.BaseServiceUri.Vault
+ };
+
+ var message = await CreateMessageAsync("Welcome", user.Email, "Welcome", model);
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ private async Task CreateMessageAsync(string subject, string toEmail, string fileName,
+ Dictionary model)
+ {
+ return await CreateMessageAsync(subject, new List { toEmail }, fileName, model);
+ }
+
+ private async Task CreateMessageAsync(string subject, IEnumerable toEmails, string fileName,
+ Dictionary model)
+ {
+ var message = new MailMessage
+ {
+ ToEmails = toEmails,
+ Subject = subject,
+ MetaData = new Dictionary()
+ };
+
+ var assembly = typeof(MarkdownMailService).GetTypeInfo().Assembly;
+ using(var s = assembly.GetManifestResourceStream($"{Namespace}.{fileName}.md"))
+ using(var sr = new StreamReader(s))
+ {
+ var markdown = await sr.ReadToEndAsync();
+
+ if(model != null)
+ {
+ foreach(var prop in model)
+ {
+ markdown = markdown.Replace($"{{{{{prop.Key}}}}}", prop.Value);
+ }
+ }
+
+ message.HtmlContent = CommonMark.CommonMarkConverter.Convert(markdown);
+ message.TextContent = markdown;
+ }
+
+ return message;
+ }
+ }
+}
diff --git a/src/Core/Services/Implementations/RazorViewMailService.cs b/src/Core/Services/Implementations/RazorMailService.cs
similarity index 98%
rename from src/Core/Services/Implementations/RazorViewMailService.cs
rename to src/Core/Services/Implementations/RazorMailService.cs
index 5b6dc0db7a..76136bd0ca 100644
--- a/src/Core/Services/Implementations/RazorViewMailService.cs
+++ b/src/Core/Services/Implementations/RazorMailService.cs
@@ -10,20 +10,20 @@ using System.Net;
namespace Bit.Core.Services
{
- public class RazorViewMailService : IMailService
+ public class RazorMailService : IMailService
{
private readonly GlobalSettings _globalSettings;
private readonly IRazorLightEngine _engine;
private readonly IMailDeliveryService _mailDeliveryService;
- public RazorViewMailService(
+ public RazorMailService(
GlobalSettings globalSettings,
IMailDeliveryService mailDeliveryService)
{
_globalSettings = globalSettings;
_mailDeliveryService = mailDeliveryService;
- var manager = new CustomEmbeddedResourceTemplateManager("Bit.Core.MailTemplates");
+ var manager = new CustomEmbeddedResourceTemplateManager("Bit.Core.MailTemplates.Razor");
var core = new EngineCore(manager, EngineConfiguration.Default);
var pageFactory = new DefaultPageFactory(core.KeyCompile);
var lookup = new DefaultPageLookup(pageFactory);
@@ -151,8 +151,8 @@ namespace Bit.Core.Services
WebVaultUrl = _globalSettings.BaseServiceUri.Vault,
SiteName = _globalSettings.SiteName
};
- message.HtmlContent = _engine.Parse("OrganizationUserInvited", model);
- message.TextContent = _engine.Parse("OrganizationUserInvited.text", model);
+ message.HtmlContent = _engine.Parse("OrganizationUserAccepted", model);
+ message.TextContent = _engine.Parse("OrganizationUserAccepted.text", model);
await _mailDeliveryService.SendEmailAsync(message);
}
diff --git a/src/Core/Services/Implementations/SendGridTemplateMailService.cs b/src/Core/Services/Implementations/SendGridTemplateMailService.cs
index 5a83671f76..55e864cbf5 100644
--- a/src/Core/Services/Implementations/SendGridTemplateMailService.cs
+++ b/src/Core/Services/Implementations/SendGridTemplateMailService.cs
@@ -88,7 +88,7 @@ namespace Bit.Core.Services
AddSubstitution(message, "{{fromEmail}}", fromEmail);
AddSubstitution(message, "{{toEmail}}", toEmail);
- AddCategories(message, new List { AdministrativeCategoryName, "Change Email Alrady Exists" });
+ AddCategories(message, new List { AdministrativeCategoryName, "Change Email Already Exists" });
await _mailDeliveryService.SendEmailAsync(message);
}
diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs
index c1b497de56..e0b201058f 100644
--- a/src/Core/Utilities/ServiceCollectionExtensions.cs
+++ b/src/Core/Utilities/ServiceCollectionExtensions.cs
@@ -54,7 +54,15 @@ namespace Bit.Core.Utilities
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
{
- services.AddSingleton();
+ if(globalSettings.SelfHosted)
+ {
+ services.AddSingleton();
+ }
+ else
+ {
+ services.AddSingleton();
+ }
+
services.AddSingleton();
if(CoreHelpers.SettingHasValue(globalSettings.Mail.SendGridApiKey))