1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 13:08:17 -05:00

New mail services and implementations

This commit is contained in:
Kyle Spearrin 2017-05-30 17:19:46 -04:00
parent 72ac5c9f80
commit 07c5f45ae0
19 changed files with 549 additions and 160 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
@ -13,17 +13,29 @@
<RootNamespace>Bit.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="MailTemplates\MasterPasswordHint.text.cshtml" />
<EmbeddedResource Include="MailTemplates\NoMasterPasswordHint.text.cshtml" />
<EmbeddedResource Include="MailTemplates\NoMasterPasswordHint.cshtml" />
<EmbeddedResource Include="MailTemplates\MasterPasswordHint.cshtml" />
<EmbeddedResource Include="MailTemplates\Welcome.cshtml" />
<EmbeddedResource Include="MailTemplates\_BasicMailLayout.text.cshtml" />
<EmbeddedResource Include="MailTemplates\_BasicMailLayout.cshtml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="1.3.1" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="1.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.3" />
<PackageReference Include="Dapper" Version="1.50.2" />
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="1.0.8" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="RazorLight" Version="1.1.0" />
<PackageReference Include="Sendgrid" Version="9.2.0" />
<PackageReference Include="PushSharp" Version="4.0.10" />
<PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" />

View File

@ -0,0 +1,8 @@
@model Bit.Core.Models.Mail.MasterPasswordHintViewModel
@{
Layout = "_BasicMailLayout";
}
<p>You (or someone) recently requested your master password hint.</p>
<p>Your hint is: "@Model.Hint"</p>
<p>Login: <a href="@Model.WebVaultUrl" target="_blank">@Model.WebVaultUrl</a></p>
<p>If you did not request your master password hint you can safely ignore this email.</p>

View File

@ -0,0 +1,11 @@
@model Bit.Core.Models.Mail.MasterPasswordHintViewModel
@{
Layout = "_BasicMailLayout.text";
}
You (or someone) recently requested your master password hint.
Your hint is: "@Model.Hint"
Login: @Model.WebVaultUrl
If you did not request your master password hint you can safely ignore this email.

View File

@ -0,0 +1,9 @@
@model Bit.Core.Models.Mail.BaseMailModel
@{
Layout = "_BasicMailLayout";
}
<p>
You (or someone) recently requested your master password hint.
Unfortunately, your account does not have a master password hint.
</p>
<p>If you did not request your master password hint you can safely ignore this email.</p>

View File

@ -0,0 +1,7 @@
@model Bit.Core.Models.Mail.BaseMailModel
@{
Layout = "_BasicMailLayout";
}
You (or someone) recently requested your master password hint. Unfortunately, your account does not have a master password hint.
If you did not request your master password hint you can safely ignore this email.

View File

@ -0,0 +1,10 @@
@model Bit.Core.Models.Mail.BaseMailModel
@{
Layout = "_BasicMailLayout";
}
<html>
<head></head>
<body>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
@RenderBody()
Regards,<br />
The bitwarden team
</body>
</html>

View File

@ -0,0 +1,3 @@
@RenderBody()
Regards,
The bitwarden team

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models.Mail
{
public class BaseMailModel
{
public string SiteName { get; set; }
public string WebVaultUrl { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Bit.Core.Models.Mail
{
public class MailMessage
{
public string Subject { get; set; }
public IEnumerable<string> ToEmails { get; set; }
public string HtmlContent { get; set; }
public string TextContent { get; set; }
public IDictionary<string, object> MetaData { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Models.Mail
{
public class MasterPasswordHintViewModel : BaseMailModel
{
public string Hint { get; set; }
}
}

9
src/Core/Program.cs Normal file
View File

@ -0,0 +1,9 @@
namespace Bit.Core
{
public class Program
{
public static void Main(string[] args)
{
}
}
}

View File

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Bit.Core.Models.Mail;
namespace Bit.Core.Services
{
public interface IMailDeliveryService
{
Task SendEmailAsync(MailMessage message);
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table;
using RazorLight;
using Bit.Core.Models.Mail;
using RazorLight.Templating;
using System.IO;
namespace Bit.Core.Services
{
public class RazorMailService : IMailService
{
private readonly GlobalSettings _globalSettings;
private readonly IRazorLightEngine _engine;
private readonly IMailDeliveryService _mailDeliveryService;
public RazorMailService(
GlobalSettings globalSettings,
IMailDeliveryService mailDeliveryService)
{
_globalSettings = globalSettings;
_mailDeliveryService = mailDeliveryService;
var manager = new CustomEmbeddedResourceTemplateManager("Bit.Core.MailTemplates");
var core = new EngineCore(manager, EngineConfiguration.Default);
var pageFactory = new DefaultPageFactory(core.KeyCompile);
var lookup = new DefaultPageLookup(pageFactory);
_engine = new RazorLightEngine(core, lookup);
}
public Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
{
throw new NotImplementedException();
}
public Task SendChangeEmailEmailAsync(string newEmailAddress, string token)
{
throw new NotImplementedException();
}
public async Task SendMasterPasswordHintEmailAsync(string email, string hint)
{
var message = CreateDefaultMessage("Your Master Password Hint", email);
var model = new MasterPasswordHintViewModel
{
Hint = hint
};
message.HtmlContent = _engine.Parse("MasterPasswordHint", model);
message.TextContent = _engine.Parse("MasterPasswordHint.text", model);
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendNoMasterPasswordHintEmailAsync(string email)
{
var message = CreateDefaultMessage("Your Master Password Hint", email);
var model = new BaseMailModel();
message.HtmlContent = _engine.Parse("NoMasterPasswordHint", model);
message.TextContent = _engine.Parse("NoMasterPasswordHint.text", model);
await _mailDeliveryService.SendEmailAsync(message);
}
public Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail, IEnumerable<string> adminEmails)
{
throw new NotImplementedException();
}
public Task SendOrganizationConfirmedEmailAsync(string organizationName, string email)
{
throw new NotImplementedException();
}
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
{
throw new NotImplementedException();
}
public Task SendWelcomeEmailAsync(User user)
{
throw new NotImplementedException();
}
private MailMessage CreateDefaultMessage(string subject, string toEmail)
{
return CreateDefaultMessage(subject, new List<string> { toEmail });
}
private MailMessage CreateDefaultMessage(string subject, IEnumerable<string> toEmails)
{
var message = new MailMessage
{
MetaData = new Dictionary<string, object>(),
ToEmails = toEmails,
Subject = subject
};
return message;
}
public class CustomEmbeddedResourceTemplateManager : ITemplateManager
{
public CustomEmbeddedResourceTemplateManager(string rootNamespace)
{
if(rootNamespace == null)
{
throw new ArgumentNullException(nameof(rootNamespace));
}
Namespace = rootNamespace;
}
public string Namespace { get; }
public ITemplateSource Resolve(string key)
{
var assembly = GetType().Assembly;
using(var stream = assembly.GetManifestResourceStream(Namespace + "." + key + ".cshtml"))
{
if(stream == null)
{
throw new RazorLightException(string.Format("Couldn't load resource '{0}.{1}.cshtml'.", Namespace, key));
}
using(var reader = new StreamReader(stream))
{
return new LoadedTemplateSource(reader.ReadToEnd());
}
}
}
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SendGrid;
using SendGrid.Helpers.Mail;
using Bit.Core.Models.Mail;
using System.Linq;
namespace Bit.Core.Services
{
public class SendGridMailDeliveryService : IMailDeliveryService
{
private readonly GlobalSettings _globalSettings;
private readonly SendGridClient _client;
public SendGridMailDeliveryService(GlobalSettings globalSettings)
{
_globalSettings = globalSettings;
_client = new SendGridClient(_globalSettings.Mail.ApiKey);
}
public async Task SendEmailAsync(MailMessage message)
{
var sendGridMessage = new SendGridMessage
{
Subject = message.Subject,
From = new EmailAddress(_globalSettings.Mail.ReplyToEmail, _globalSettings.SiteName),
HtmlContent = message.HtmlContent,
PlainTextContent = message.TextContent,
};
sendGridMessage.AddTos(message.ToEmails.Select(e => new EmailAddress(e)).ToList());
if(message.MetaData.ContainsKey("SendGridTemplateId"))
{
sendGridMessage.HtmlContent = " ";
sendGridMessage.PlainTextContent = " ";
sendGridMessage.TemplateId = message.MetaData["SendGridTemplateId"].ToString();
}
if(message.MetaData.ContainsKey("SendGridSubstitutions"))
{
var subs = message.MetaData["SendGridSubstitutions"] as Dictionary<string, string>;
sendGridMessage.AddSubstitutions(subs);
}
if(message.MetaData.ContainsKey("SendGridCategories"))
{
var cats = message.MetaData["SendGridCategories"] as List<string>;
sendGridMessage.AddCategories(cats);
}
if(message.MetaData.ContainsKey("SendGridBypassListManagement"))
{
var bypass = message.MetaData["SendGridBypassListManagement"] as bool?;
sendGridMessage.SetBypassListManagement(bypass.GetValueOrDefault(false));
}
await _client.SendEmailAsync(sendGridMessage);
}
}
}

View File

@ -1,158 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Net;
using System.Linq;
namespace Bit.Core.Services
{
public class SendGridMailService : IMailService
{
private const string WelcomeTemplateId = "045f8ad5-5547-4fa2-8d3d-6d46e401164d";
private const string ChangeEmailAlreadyExistsTemplateId = "b69d2038-6ad9-4cf6-8f7f-7880921cba43";
private const string ChangeEmailTemplateId = "ec2c1471-8292-4f17-b6b6-8223d514f86e";
private const string NoMasterPasswordHintTemplateId = "136eb299-e102-495a-88bd-f96736eea159";
private const string MasterPasswordHintTemplateId = "be77cfde-95dd-4cb9-b5e0-8286b53885f1";
private const string OrganizationInviteTemplateId = "1eff5512-e36c-49a8-b9e2-2b215d6bbced";
private const string OrganizationAcceptedTemplateId = "28f7f741-598e-449c-85fe-601e1cc32ba3";
private const string OrganizationConfirmedTemplateId = "a8afe2a0-6161-4eb9-b40c-08a7f520ec50";
private const string AdministrativeCategoryName = "Administrative";
private const string MarketingCategoryName = "Marketing";
private readonly GlobalSettings _globalSettings;
private readonly SendGridClient _client;
public SendGridMailService(GlobalSettings globalSettings)
{
_globalSettings = globalSettings;
_client = new SendGridClient(_globalSettings.Mail.ApiKey);
}
public async Task SendWelcomeEmailAsync(User user)
{
var message = CreateDefaultMessage(WelcomeTemplateId);
message.Subject = "Welcome";
message.AddTo(new EmailAddress(user.Email));
message.AddCategories(new List<string> { AdministrativeCategoryName, "Welcome" });
await _client.SendEmailAsync(message);
}
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
{
var message = CreateDefaultMessage(ChangeEmailAlreadyExistsTemplateId);
message.Subject = "Your Email Change";
message.AddTo(new EmailAddress(toEmail));
message.AddSubstitution("{{fromEmail}}", fromEmail);
message.AddSubstitution("{{toEmail}}", toEmail);
message.AddCategories(new List<string> { AdministrativeCategoryName, "Change Email Alrady Exists" });
await _client.SendEmailAsync(message);
}
public async Task SendChangeEmailEmailAsync(string newEmailAddress, string token)
{
var message = CreateDefaultMessage(ChangeEmailTemplateId);
message.Subject = "Change Your Email";
message.AddTo(new EmailAddress(newEmailAddress));
message.AddSubstitution("{{token}}", Uri.EscapeDataString(token));
message.AddCategories(new List<string> { AdministrativeCategoryName, "Change Email" });
message.SetBypassListManagement(true);
await _client.SendEmailAsync(message);
}
public async Task SendNoMasterPasswordHintEmailAsync(string email)
{
var message = CreateDefaultMessage(NoMasterPasswordHintTemplateId);
message.Subject = "Your Master Password Hint";
message.AddTo(new EmailAddress(email));
message.AddCategories(new List<string> { AdministrativeCategoryName, "No Master Password Hint" });
await _client.SendEmailAsync(message);
}
public async Task SendMasterPasswordHintEmailAsync(string email, string hint)
{
var message = CreateDefaultMessage(MasterPasswordHintTemplateId);
message.Subject = "Your Master Password Hint";
message.AddTo(new EmailAddress(email));
message.AddSubstitution("{{hint}}", hint);
message.AddCategories(new List<string> { AdministrativeCategoryName, "Master Password Hint" });
await _client.SendEmailAsync(message);
}
public async Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
{
var message = CreateDefaultMessage(OrganizationInviteTemplateId);
message.Subject = $"Join {organizationName}";
message.AddTo(new EmailAddress(orgUser.Email));
message.AddSubstitution("{{organizationName}}", organizationName);
message.AddSubstitution("{{organizationId}}", orgUser.OrganizationId.ToString());
message.AddSubstitution("{{organizationUserId}}", orgUser.Id.ToString());
message.AddSubstitution("{{token}}", token);
message.AddSubstitution("{{email}}", WebUtility.UrlEncode(orgUser.Email));
message.AddSubstitution("{{organizationNameUrlEncoded}}", WebUtility.UrlEncode(organizationName));
message.AddCategories(new List<string> { AdministrativeCategoryName, "Organization User Invite" });
await _client.SendEmailAsync(message);
}
public async Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail,
IEnumerable<string> adminEmails)
{
var message = CreateDefaultMessage(OrganizationAcceptedTemplateId);
message.Subject = $"User {userEmail} Has Accepted Invite";
message.AddTos(adminEmails.Select(e => new EmailAddress(e)).ToList());
message.AddSubstitution("{{userEmail}}", userEmail);
message.AddSubstitution("{{organizationName}}", organizationName);
message.AddCategories(new List<string> { AdministrativeCategoryName, "Organization User Accepted" });
await _client.SendEmailAsync(message);
}
public async Task SendOrganizationConfirmedEmailAsync(string organizationName, string email)
{
var message = CreateDefaultMessage(OrganizationConfirmedTemplateId);
message.Subject = $"You Have Been Confirmed To {organizationName}";
message.AddTo(new EmailAddress(email));
message.AddSubstitution("{{organizationName}}", organizationName);
message.AddCategories(new List<string> { AdministrativeCategoryName, "Organization User Confirmed" });
await _client.SendEmailAsync(message);
}
private SendGridMessage CreateDefaultMessage(string templateId)
{
var message = new SendGridMessage
{
From = new EmailAddress(_globalSettings.Mail.ReplyToEmail, _globalSettings.SiteName),
HtmlContent = " ",
PlainTextContent = " "
};
if(!string.IsNullOrWhiteSpace(templateId))
{
message.TemplateId = templateId;
}
message.AddSubstitution("{{siteName}}", _globalSettings.SiteName);
message.AddSubstitution("{{baseVaultUri}}", _globalSettings.BaseVaultUri);
return message;
}
}
}

View File

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table;
using System.Net;
using Bit.Core.Models.Mail;
namespace Bit.Core.Services
{
public class SendGridTemplateMailService : IMailService
{
private const string WelcomeTemplateId = "045f8ad5-5547-4fa2-8d3d-6d46e401164d";
private const string ChangeEmailAlreadyExistsTemplateId = "b69d2038-6ad9-4cf6-8f7f-7880921cba43";
private const string ChangeEmailTemplateId = "ec2c1471-8292-4f17-b6b6-8223d514f86e";
private const string NoMasterPasswordHintTemplateId = "136eb299-e102-495a-88bd-f96736eea159";
private const string MasterPasswordHintTemplateId = "be77cfde-95dd-4cb9-b5e0-8286b53885f1";
private const string OrganizationInviteTemplateId = "1eff5512-e36c-49a8-b9e2-2b215d6bbced";
private const string OrganizationAcceptedTemplateId = "28f7f741-598e-449c-85fe-601e1cc32ba3";
private const string OrganizationConfirmedTemplateId = "a8afe2a0-6161-4eb9-b40c-08a7f520ec50";
private const string AdministrativeCategoryName = "Administrative";
private const string MarketingCategoryName = "Marketing";
private readonly GlobalSettings _globalSettings;
private readonly IMailDeliveryService _mailDeliveryService;
public SendGridTemplateMailService(
GlobalSettings globalSettings,
IMailDeliveryService mailDeliveryService)
{
_globalSettings = globalSettings;
_mailDeliveryService = mailDeliveryService;
}
public async Task SendWelcomeEmailAsync(User user)
{
var message = CreateDefaultMessage(
"Welcome",
user.Email,
WelcomeTemplateId);
AddCategories(message, new List<string> { AdministrativeCategoryName, "Welcome" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
{
var message = CreateDefaultMessage(
"Your Email Change",
toEmail,
ChangeEmailAlreadyExistsTemplateId);
AddSubstitution(message, "{{fromEmail}}", fromEmail);
AddSubstitution(message, "{{toEmail}}", toEmail);
AddCategories(message, new List<string> { AdministrativeCategoryName, "Change Email Alrady Exists" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendChangeEmailEmailAsync(string newEmailAddress, string token)
{
var message = CreateDefaultMessage(
"Your Email Change",
newEmailAddress,
ChangeEmailTemplateId);
AddSubstitution(message, "{{token}}", Uri.EscapeDataString(token));
AddCategories(message, new List<string> { AdministrativeCategoryName, "Change Email" });
message.MetaData.Add("SendGridBypassListManagement", true);
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendNoMasterPasswordHintEmailAsync(string email)
{
var message = CreateDefaultMessage(
"Your Master Password Hint",
email,
NoMasterPasswordHintTemplateId);
AddCategories(message, new List<string> { AdministrativeCategoryName, "No Master Password Hint" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendMasterPasswordHintEmailAsync(string email, string hint)
{
var message = CreateDefaultMessage(
"Your Master Password Hint",
email,
MasterPasswordHintTemplateId);
message.Subject = "Your Master Password Hint";
AddSubstitution(message, "{{hint}}", hint);
AddCategories(message, new List<string> { AdministrativeCategoryName, "Master Password Hint" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
{
var message = CreateDefaultMessage(
$"Join {organizationName}",
orgUser.Email,
OrganizationInviteTemplateId);
AddSubstitution(message, "{{organizationName}}", organizationName);
AddSubstitution(message, "{{organizationId}}", orgUser.OrganizationId.ToString());
AddSubstitution(message, "{{organizationUserId}}", orgUser.Id.ToString());
AddSubstitution(message, "{{token}}", token);
AddSubstitution(message, "{{email}}", WebUtility.UrlEncode(orgUser.Email));
AddSubstitution(message, "{{organizationNameUrlEncoded}}", WebUtility.UrlEncode(organizationName));
AddCategories(message, new List<string> { AdministrativeCategoryName, "Organization User Invite" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendOrganizationAcceptedEmailAsync(string organizationName, string userEmail,
IEnumerable<string> adminEmails)
{
var message = CreateDefaultMessage(
$"User {userEmail} Has Accepted Invite",
adminEmails,
OrganizationAcceptedTemplateId);
AddSubstitution(message, "{{userEmail}}", userEmail);
AddSubstitution(message, "{{organizationName}}", organizationName);
AddCategories(message, new List<string> { AdministrativeCategoryName, "Organization User Accepted" });
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendOrganizationConfirmedEmailAsync(string organizationName, string email)
{
var message = CreateDefaultMessage(
$"You Have Been Confirmed To {organizationName}",
email,
OrganizationConfirmedTemplateId);
AddSubstitution(message, "{{organizationName}}", organizationName);
AddCategories(message, new List<string> { AdministrativeCategoryName, "Organization User Confirmed" });
await _mailDeliveryService.SendEmailAsync(message);
}
private MailMessage CreateDefaultMessage(string subject, string toEmail, string templateId)
{
return CreateDefaultMessage(subject, new List<string> { toEmail }, templateId);
}
private MailMessage CreateDefaultMessage(string subject, IEnumerable<string> toEmails, string templateId)
{
var message = new MailMessage
{
HtmlContent = " ",
TextContent = " ",
MetaData = new Dictionary<string, object>(),
ToEmails = toEmails,
Subject = subject
};
if(!string.IsNullOrWhiteSpace(templateId))
{
message.MetaData.Add("SendGridTemplateId", templateId);
}
AddSubstitution(message, "{{siteName}}", _globalSettings.SiteName);
AddSubstitution(message, "{{baseVaultUri}}", _globalSettings.BaseVaultUri);
return message;
}
private void AddSubstitution(MailMessage message, string key, string value)
{
Dictionary<string, string> dict;
if(!message.MetaData.ContainsKey("SendGridSubstitutions"))
{
dict = new Dictionary<string, string>();
}
else
{
dict = message.MetaData["SendGridSubstitutions"] as Dictionary<string, string>;
}
dict.Add(key, value);
message.MetaData["SendGridSubstitutions"] = dict;
}
private void AddCategories(MailMessage message, List<string> categories)
{
List<string> cats;
if(!message.MetaData.ContainsKey("SendGridCategories"))
{
cats = categories;
}
else
{
cats = message.MetaData["SendGridCategories"] as List<string>;
cats.AddRange(categories);
}
message.MetaData["SendGridCategories"] = cats;
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Threading.Tasks;
using Bit.Core.Models.Mail;
namespace Bit.Core.Services
{
public class SmtpMailDeliveryService : IMailDeliveryService
{
private readonly GlobalSettings _globalSettings;
public SmtpMailDeliveryService(GlobalSettings globalSettings)
{
}
public Task SendEmailAsync(MailMessage message)
{
throw new NotImplementedException();
}
}
}

View File

@ -49,7 +49,9 @@ namespace Bit.Core.Utilities
public static void AddDefaultServices(this IServiceCollection services)
{
services.AddSingleton<IMailService, SendGridMailService>();
//services.AddSingleton<IMailService, SendGridTemplateMailService>();
services.AddSingleton<IMailService, RazorMailService>();
services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>();
services.AddSingleton<IPushNotificationService, NotificationHubPushNotificationService>();
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();