From 8d544421735bafca4921980aa896f849b6249daa Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 13 Mar 2019 14:07:08 -0400 Subject: [PATCH] ses mail delivery service --- src/Core/Core.csproj | 1 + src/Core/GlobalSettings.cs | 8 ++ .../AmazonSesMailDeliveryService.cs | 124 ++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 2b16c26789..6df6a66ea0 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index 5693349534..d97d7d0923 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -32,6 +32,7 @@ namespace Bit.Core public virtual DuoSettings Duo { get; set; } = new DuoSettings(); public virtual BraintreeSettings Braintree { get; set; } = new BraintreeSettings(); public virtual BitPaySettings BitPay { get; set; } = new BitPaySettings(); + public virtual AmazonSettings Amazon { get; set; } = new AmazonSettings(); public class BaseServiceUriSettings { @@ -200,5 +201,12 @@ namespace Bit.Core public string Key { get; set; } public string IdentityUri { get; set; } } + + public class AmazonSettings + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + public string Region { get; set; } + } } } diff --git a/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs new file mode 100644 index 0000000000..ad0e610a4a --- /dev/null +++ b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Models.Mail; +using System.Linq; +using Amazon.SimpleEmail; +using Amazon; +using Amazon.SimpleEmail.Model; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; + +namespace Bit.Core.Services +{ + public class AmazonSesMailDeliveryService : IMailDeliveryService, IDisposable + { + private readonly GlobalSettings _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly ILogger _logger; + private readonly AmazonSimpleEmailServiceClient _client; + private readonly string _source; + private readonly string _senderTag; + + public AmazonSesMailDeliveryService( + GlobalSettings globalSettings, + IHostingEnvironment hostingEnvironment, + ILogger logger) + { + if(string.IsNullOrWhiteSpace(globalSettings.Amazon?.AccessKeyId)) + { + throw new ArgumentNullException(nameof(globalSettings.Amazon.AccessKeyId)); + } + if(string.IsNullOrWhiteSpace(globalSettings.Amazon?.AccessKeySecret)) + { + throw new ArgumentNullException(nameof(globalSettings.Amazon.AccessKeySecret)); + } + if(string.IsNullOrWhiteSpace(globalSettings.Amazon?.Region)) + { + throw new ArgumentNullException(nameof(globalSettings.Amazon.Region)); + } + + _globalSettings = globalSettings; + _hostingEnvironment = hostingEnvironment; + _logger = logger; + _client = new AmazonSimpleEmailServiceClient(globalSettings.Amazon.AccessKeyId, + globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(globalSettings.Amazon.Region)); + _source = $"\"{globalSettings.SiteName}\" <{globalSettings.Mail.ReplyToEmail}>"; + _senderTag = $"Server: {globalSettings.ProjectName}"; + } + + public void Dispose() + { + _client?.Dispose(); + } + + public async Task SendEmailAsync(MailMessage message) + { + var request = new SendEmailRequest + { + ConfigurationSetName = "Email", + Source = _source, + Destination = new Destination + { + ToAddresses = message.ToEmails.ToList() + }, + Message = new Message + { + Subject = new Content(message.Subject), + Body = new Body + { + Html = new Content + { + Charset = "UTF-8", + Data = message.HtmlContent + }, + Text = new Content + { + Charset = "UTF-8", + Data = message.TextContent + } + } + }, + Tags = new List + { + new MessageTag { Name = "Environment", Value = _hostingEnvironment.EnvironmentName }, + new MessageTag { Name = "Sender", Value = _senderTag } + } + }; + + if(message.BccEmails?.Any() ?? false) + { + request.Destination.BccAddresses = message.BccEmails.ToList(); + } + + if(message.MetaData?.ContainsKey("SendGridCategories") ?? false) + { + var cats = (message.MetaData["SendGridCategories"] as List) + .Select(c => new MessageTag { Name = "Category", Value = c }); + request.Tags.AddRange(cats); + } + + try + { + await SendAsync(request, false); + } + catch(Exception e) + { + _logger.LogWarning(e, "Failed to send email."); + await SendAsync(request, true); + throw e; + } + } + + private async Task SendAsync(SendEmailRequest request, bool retry) + { + if(retry) + { + // wait and try again + await Task.Delay(2000); + } + + await _client.SendEmailAsync(request); + } + } +}