From 7e920b955c7e22c52542cbfde60a6454e9ca9d30 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 13 Mar 2019 16:19:00 -0400 Subject: [PATCH] amazon ses mail delivery service --- src/Admin/appsettings.json | 6 ++++ src/Api/appsettings.json | 6 ++++ src/Billing/appsettings.json | 6 ++++ src/Core/GlobalSettings.cs | 1 + src/Core/Models/Mail/MailMessage.cs | 1 + .../AmazonSesMailDeliveryService.cs | 18 ++++++----- .../Implementations/HandlebarsMailService.cs | 32 +++++++++---------- .../SendGridMailDeliveryService.cs | 4 +-- .../Utilities/ServiceCollectionExtensions.cs | 4 +++ src/Events/appsettings.json | 5 +++ src/Identity/appsettings.json | 6 ++++ src/Notifications/appsettings.json | 5 +++ 12 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/Admin/appsettings.json b/src/Admin/appsettings.json index 5221d0a716..f889b30e3e 100644 --- a/src/Admin/appsettings.json +++ b/src/Admin/appsettings.json @@ -21,6 +21,7 @@ }, "mail": { "sendGridApiKey": "SECRET", + "amazonConfigSetName": "Email", "replyToEmail": "hello@bitwarden.com" }, "identityServer": { @@ -42,6 +43,11 @@ "notificationHub": { "connectionString": "SECRET", "hubName": "SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } }, "adminSettings": { diff --git a/src/Api/appsettings.json b/src/Api/appsettings.json index 0d06cc3f9b..3b3c0fc638 100644 --- a/src/Api/appsettings.json +++ b/src/Api/appsettings.json @@ -21,6 +21,7 @@ }, "mail": { "sendGridApiKey": "SECRET", + "amazonConfigSetName": "Email", "replyToEmail": "hello@bitwarden.com" }, "identityServer": { @@ -67,6 +68,11 @@ "production": false, "base58Secret": "SECRET", "notificationUrl": "https://bitwarden.com/SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } }, "IpRateLimitOptions": { diff --git a/src/Billing/appsettings.json b/src/Billing/appsettings.json index 19da86dccc..1945612186 100644 --- a/src/Billing/appsettings.json +++ b/src/Billing/appsettings.json @@ -21,6 +21,7 @@ }, "mail": { "sendGridApiKey": "SECRET", + "amazonConfigSetName": "Email", "replyToEmail": "hello@bitwarden.com" }, "identityServer": { @@ -56,6 +57,11 @@ "production": false, "base58Secret": "SECRET", "notificationUrl": "https://bitwarden.com/SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } }, "billingSettings": { diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index d97d7d0923..17a689b9b1 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -108,6 +108,7 @@ namespace Bit.Core { public string ReplyToEmail { get; set; } public string SendGridApiKey { get; set; } + public string AmazonConfigSetName { get; set; } public SmtpSettings Smtp { get; set; } = new SmtpSettings(); public class SmtpSettings diff --git a/src/Core/Models/Mail/MailMessage.cs b/src/Core/Models/Mail/MailMessage.cs index d94cd04acf..4d1ae7e0e4 100644 --- a/src/Core/Models/Mail/MailMessage.cs +++ b/src/Core/Models/Mail/MailMessage.cs @@ -9,6 +9,7 @@ namespace Bit.Core.Models.Mail public IEnumerable BccEmails { get; set; } public string HtmlContent { get; set; } public string TextContent { get; set; } + public string Category { get; set; } public IDictionary MetaData { get; set; } } } diff --git a/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs index ad0e610a4a..7276a4731f 100644 --- a/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs +++ b/src/Core/Services/Implementations/AmazonSesMailDeliveryService.cs @@ -19,6 +19,7 @@ namespace Bit.Core.Services private readonly AmazonSimpleEmailServiceClient _client; private readonly string _source; private readonly string _senderTag; + private readonly string _configSetName; public AmazonSesMailDeliveryService( GlobalSettings globalSettings, @@ -44,7 +45,11 @@ namespace Bit.Core.Services _client = new AmazonSimpleEmailServiceClient(globalSettings.Amazon.AccessKeyId, globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(globalSettings.Amazon.Region)); _source = $"\"{globalSettings.SiteName}\" <{globalSettings.Mail.ReplyToEmail}>"; - _senderTag = $"Server: {globalSettings.ProjectName}"; + _senderTag = $"Server_{globalSettings.ProjectName}"; + if(!string.IsNullOrWhiteSpace(_globalSettings.Mail.AmazonConfigSetName)) + { + _configSetName = _globalSettings.Mail.AmazonConfigSetName; + } } public void Dispose() @@ -56,7 +61,7 @@ namespace Bit.Core.Services { var request = new SendEmailRequest { - ConfigurationSetName = "Email", + ConfigurationSetName = _configSetName, Source = _source, Destination = new Destination { @@ -91,11 +96,9 @@ namespace Bit.Core.Services request.Destination.BccAddresses = message.BccEmails.ToList(); } - if(message.MetaData?.ContainsKey("SendGridCategories") ?? false) + if(!string.IsNullOrWhiteSpace(message.Category)) { - var cats = (message.MetaData["SendGridCategories"] as List) - .Select(c => new MessageTag { Name = "Category", Value = c }); - request.Tags.AddRange(cats); + request.Tags.Add(new MessageTag { Name = "Category", Value = message.Category }); } try @@ -104,7 +107,7 @@ namespace Bit.Core.Services } catch(Exception e) { - _logger.LogWarning(e, "Failed to send email."); + _logger.LogWarning(e, "Failed to send email. Re-retying..."); await SendAsync(request, true); throw e; } @@ -117,7 +120,6 @@ namespace Bit.Core.Services // wait and try again await Task.Delay(2000); } - await _client.SendEmailAsync(request); } } diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index d9b249d6cc..7dc7555296 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -43,7 +43,7 @@ namespace Bit.Core.Services }; await AddMessageContentAsync(message, "VerifyEmail", model); message.MetaData.Add("SendGridBypassListManagement", true); - message.MetaData.Add("SendGridCategories", new List { "VerifyEmail" }); + message.Category = "VerifyEmail"; await _mailDeliveryService.SendEmailAsync(message); } @@ -61,7 +61,7 @@ namespace Bit.Core.Services }; await AddMessageContentAsync(message, "VerifyDelete", model); message.MetaData.Add("SendGridBypassListManagement", true); - message.MetaData.Add("SendGridCategories", new List { "VerifyDelete" }); + message.Category = "VerifyDelete"; await _mailDeliveryService.SendEmailAsync(message); } @@ -76,7 +76,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "ChangeEmailAlreadyExists", model); - message.MetaData.Add("SendGridCategories", new List { "ChangeEmailAlreadyExists" }); + message.Category = "ChangeEmailAlreadyExists"; await _mailDeliveryService.SendEmailAsync(message); } @@ -91,7 +91,7 @@ namespace Bit.Core.Services }; await AddMessageContentAsync(message, "ChangeEmail", model); message.MetaData.Add("SendGridBypassListManagement", true); - message.MetaData.Add("SendGridCategories", new List { "ChangeEmail" }); + message.Category = "ChangeEmail"; await _mailDeliveryService.SendEmailAsync(message); } @@ -106,7 +106,7 @@ namespace Bit.Core.Services }; await AddMessageContentAsync(message, "TwoFactorEmail", model); message.MetaData.Add("SendGridBypassListManagement", true); - message.MetaData.Add("SendGridCategories", new List { "TwoFactorEmail" }); + message.Category = "TwoFactorEmail"; await _mailDeliveryService.SendEmailAsync(message); } @@ -120,7 +120,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "MasterPasswordHint", model); - message.MetaData.Add("SendGridCategories", new List { "MasterPasswordHint" }); + message.Category = "MasterPasswordHint"; await _mailDeliveryService.SendEmailAsync(message); } @@ -133,7 +133,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "NoMasterPasswordHint", model); - message.MetaData.Add("SendGridCategories", new List { "NoMasterPasswordHint" }); + message.Category = "NoMasterPasswordHint"; await _mailDeliveryService.SendEmailAsync(message); } @@ -149,7 +149,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "OrganizationUserAccepted", model); - message.MetaData.Add("SendGridCategories", new List { "OrganizationUserAccepted" }); + message.Category = "OrganizationUserAccepted"; await _mailDeliveryService.SendEmailAsync(message); } @@ -163,7 +163,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "OrganizationUserConfirmed", model); - message.MetaData.Add("SendGridCategories", new List { "OrganizationUserConfirmed" }); + message.Category = "OrganizationUserConfirmed"; await _mailDeliveryService.SendEmailAsync(message); } @@ -182,7 +182,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "OrganizationUserInvited", model); - message.MetaData.Add("SendGridCategories", new List { "OrganizationUserInvited" }); + message.Category = "OrganizationUserInvited"; await _mailDeliveryService.SendEmailAsync(message); } @@ -195,7 +195,7 @@ namespace Bit.Core.Services SiteName = _globalSettings.SiteName }; await AddMessageContentAsync(message, "Welcome", model); - message.MetaData.Add("SendGridCategories", new List { "Welcome" }); + message.Category = "Welcome"; await _mailDeliveryService.SendEmailAsync(message); } @@ -214,7 +214,7 @@ namespace Bit.Core.Services Url = url.ToString() }; await AddMessageContentAsync(message, "PasswordlessSignIn", model); - message.MetaData.Add("SendGridCategories", new List { "PasswordlessSignIn" }); + message.Category = "PasswordlessSignIn"; await _mailDeliveryService.SendEmailAsync(message); } @@ -232,7 +232,7 @@ namespace Bit.Core.Services MentionInvoices = mentionInvoices }; await AddMessageContentAsync(message, "InvoiceUpcoming", model); - message.MetaData.Add("SendGridCategories", new List { "InvoiceUpcoming" }); + message.Category = "InvoiceUpcoming"; await _mailDeliveryService.SendEmailAsync(message); } @@ -247,7 +247,7 @@ namespace Bit.Core.Services MentionInvoices = mentionInvoices }; await AddMessageContentAsync(message, "PaymentFailed", model); - message.MetaData.Add("SendGridCategories", new List { "PaymentFailed" }); + message.Category = "PaymentFailed"; await _mailDeliveryService.SendEmailAsync(message); } @@ -261,7 +261,7 @@ namespace Bit.Core.Services Amount = amount }; await AddMessageContentAsync(message, "AddedCredit", model); - message.MetaData.Add("SendGridCategories", new List { "AddedCredit" }); + message.Category = "AddedCredit"; await _mailDeliveryService.SendEmailAsync(message); } @@ -279,7 +279,7 @@ namespace Bit.Core.Services IpAddress = ip }; await AddMessageContentAsync(message, "NewDeviceLoggedIn", model); - message.MetaData.Add("SendGridCategories", new List { "NewDeviceLoggedIn" }); + message.Category = "NewDeviceLoggedIn"; await _mailDeliveryService.SendEmailAsync(message); } diff --git a/src/Core/Services/Implementations/SendGridMailDeliveryService.cs b/src/Core/Services/Implementations/SendGridMailDeliveryService.cs index 0c3b894ae8..b510af13db 100644 --- a/src/Core/Services/Implementations/SendGridMailDeliveryService.cs +++ b/src/Core/Services/Implementations/SendGridMailDeliveryService.cs @@ -58,9 +58,9 @@ namespace Bit.Core.Services } var cats = new List { "Bitwarden Server" }; - if(message.MetaData?.ContainsKey("SendGridCategories") ?? false) + if(!string.IsNullOrWhiteSpace(message.Category)) { - cats.AddRange(message.MetaData["SendGridCategories"] as List); + cats.Add(message.Category); } sendGridMessage.AddCategories(cats); diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 6a3a96a161..c9374451d3 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -87,6 +87,10 @@ namespace Bit.Core.Utilities { services.AddSingleton(); } + else if(CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret)) + { + services.AddSingleton(); + } else if(CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Host)) { services.AddSingleton(); diff --git a/src/Events/appsettings.json b/src/Events/appsettings.json index eacfc1892c..b1298a4f53 100644 --- a/src/Events/appsettings.json +++ b/src/Events/appsettings.json @@ -32,6 +32,11 @@ }, "sentry": { "dsn": "SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } } } diff --git a/src/Identity/appsettings.json b/src/Identity/appsettings.json index 24f173eb3e..62c887701a 100644 --- a/src/Identity/appsettings.json +++ b/src/Identity/appsettings.json @@ -21,6 +21,7 @@ }, "mail": { "sendGridApiKey": "SECRET", + "amazonConfigSetName": "Email", "replyToEmail": "hello@bitwarden.com" }, "identityServer": { @@ -58,6 +59,11 @@ "merchantId": "SECRET", "publicKey": "SECRET", "privateKey": "SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } }, "IpRateLimitOptions": { diff --git a/src/Notifications/appsettings.json b/src/Notifications/appsettings.json index 5989e80bf2..6a6e6578d4 100644 --- a/src/Notifications/appsettings.json +++ b/src/Notifications/appsettings.json @@ -32,6 +32,11 @@ }, "sentry": { "dsn": "SECRET" + }, + "amazon": { + "accessKeyId": "SECRET", + "accessKeySecret": "SECRET", + "region": "SECRET" } } }