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

amazon ses mail delivery service

This commit is contained in:
Kyle Spearrin 2019-03-13 16:19:00 -04:00
parent 8d54442173
commit 7e920b955c
12 changed files with 68 additions and 26 deletions

View File

@ -21,6 +21,7 @@
}, },
"mail": { "mail": {
"sendGridApiKey": "SECRET", "sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email",
"replyToEmail": "hello@bitwarden.com" "replyToEmail": "hello@bitwarden.com"
}, },
"identityServer": { "identityServer": {
@ -42,6 +43,11 @@
"notificationHub": { "notificationHub": {
"connectionString": "SECRET", "connectionString": "SECRET",
"hubName": "SECRET" "hubName": "SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
}, },
"adminSettings": { "adminSettings": {

View File

@ -21,6 +21,7 @@
}, },
"mail": { "mail": {
"sendGridApiKey": "SECRET", "sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email",
"replyToEmail": "hello@bitwarden.com" "replyToEmail": "hello@bitwarden.com"
}, },
"identityServer": { "identityServer": {
@ -67,6 +68,11 @@
"production": false, "production": false,
"base58Secret": "SECRET", "base58Secret": "SECRET",
"notificationUrl": "https://bitwarden.com/SECRET" "notificationUrl": "https://bitwarden.com/SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
}, },
"IpRateLimitOptions": { "IpRateLimitOptions": {

View File

@ -21,6 +21,7 @@
}, },
"mail": { "mail": {
"sendGridApiKey": "SECRET", "sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email",
"replyToEmail": "hello@bitwarden.com" "replyToEmail": "hello@bitwarden.com"
}, },
"identityServer": { "identityServer": {
@ -56,6 +57,11 @@
"production": false, "production": false,
"base58Secret": "SECRET", "base58Secret": "SECRET",
"notificationUrl": "https://bitwarden.com/SECRET" "notificationUrl": "https://bitwarden.com/SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
}, },
"billingSettings": { "billingSettings": {

View File

@ -108,6 +108,7 @@ namespace Bit.Core
{ {
public string ReplyToEmail { get; set; } public string ReplyToEmail { get; set; }
public string SendGridApiKey { get; set; } public string SendGridApiKey { get; set; }
public string AmazonConfigSetName { get; set; }
public SmtpSettings Smtp { get; set; } = new SmtpSettings(); public SmtpSettings Smtp { get; set; } = new SmtpSettings();
public class SmtpSettings public class SmtpSettings

View File

@ -9,6 +9,7 @@ namespace Bit.Core.Models.Mail
public IEnumerable<string> BccEmails { get; set; } public IEnumerable<string> BccEmails { get; set; }
public string HtmlContent { get; set; } public string HtmlContent { get; set; }
public string TextContent { get; set; } public string TextContent { get; set; }
public string Category { get; set; }
public IDictionary<string, object> MetaData { get; set; } public IDictionary<string, object> MetaData { get; set; }
} }
} }

View File

@ -19,6 +19,7 @@ namespace Bit.Core.Services
private readonly AmazonSimpleEmailServiceClient _client; private readonly AmazonSimpleEmailServiceClient _client;
private readonly string _source; private readonly string _source;
private readonly string _senderTag; private readonly string _senderTag;
private readonly string _configSetName;
public AmazonSesMailDeliveryService( public AmazonSesMailDeliveryService(
GlobalSettings globalSettings, GlobalSettings globalSettings,
@ -44,7 +45,11 @@ namespace Bit.Core.Services
_client = new AmazonSimpleEmailServiceClient(globalSettings.Amazon.AccessKeyId, _client = new AmazonSimpleEmailServiceClient(globalSettings.Amazon.AccessKeyId,
globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(globalSettings.Amazon.Region)); globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(globalSettings.Amazon.Region));
_source = $"\"{globalSettings.SiteName}\" <{globalSettings.Mail.ReplyToEmail}>"; _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() public void Dispose()
@ -56,7 +61,7 @@ namespace Bit.Core.Services
{ {
var request = new SendEmailRequest var request = new SendEmailRequest
{ {
ConfigurationSetName = "Email", ConfigurationSetName = _configSetName,
Source = _source, Source = _source,
Destination = new Destination Destination = new Destination
{ {
@ -91,11 +96,9 @@ namespace Bit.Core.Services
request.Destination.BccAddresses = message.BccEmails.ToList(); request.Destination.BccAddresses = message.BccEmails.ToList();
} }
if(message.MetaData?.ContainsKey("SendGridCategories") ?? false) if(!string.IsNullOrWhiteSpace(message.Category))
{ {
var cats = (message.MetaData["SendGridCategories"] as List<string>) request.Tags.Add(new MessageTag { Name = "Category", Value = message.Category });
.Select(c => new MessageTag { Name = "Category", Value = c });
request.Tags.AddRange(cats);
} }
try try
@ -104,7 +107,7 @@ namespace Bit.Core.Services
} }
catch(Exception e) catch(Exception e)
{ {
_logger.LogWarning(e, "Failed to send email."); _logger.LogWarning(e, "Failed to send email. Re-retying...");
await SendAsync(request, true); await SendAsync(request, true);
throw e; throw e;
} }
@ -117,7 +120,6 @@ namespace Bit.Core.Services
// wait and try again // wait and try again
await Task.Delay(2000); await Task.Delay(2000);
} }
await _client.SendEmailAsync(request); await _client.SendEmailAsync(request);
} }
} }

View File

@ -43,7 +43,7 @@ namespace Bit.Core.Services
}; };
await AddMessageContentAsync(message, "VerifyEmail", model); await AddMessageContentAsync(message, "VerifyEmail", model);
message.MetaData.Add("SendGridBypassListManagement", true); message.MetaData.Add("SendGridBypassListManagement", true);
message.MetaData.Add("SendGridCategories", new List<string> { "VerifyEmail" }); message.Category = "VerifyEmail";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -61,7 +61,7 @@ namespace Bit.Core.Services
}; };
await AddMessageContentAsync(message, "VerifyDelete", model); await AddMessageContentAsync(message, "VerifyDelete", model);
message.MetaData.Add("SendGridBypassListManagement", true); message.MetaData.Add("SendGridBypassListManagement", true);
message.MetaData.Add("SendGridCategories", new List<string> { "VerifyDelete" }); message.Category = "VerifyDelete";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -76,7 +76,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "ChangeEmailAlreadyExists", model); await AddMessageContentAsync(message, "ChangeEmailAlreadyExists", model);
message.MetaData.Add("SendGridCategories", new List<string> { "ChangeEmailAlreadyExists" }); message.Category = "ChangeEmailAlreadyExists";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -91,7 +91,7 @@ namespace Bit.Core.Services
}; };
await AddMessageContentAsync(message, "ChangeEmail", model); await AddMessageContentAsync(message, "ChangeEmail", model);
message.MetaData.Add("SendGridBypassListManagement", true); message.MetaData.Add("SendGridBypassListManagement", true);
message.MetaData.Add("SendGridCategories", new List<string> { "ChangeEmail" }); message.Category = "ChangeEmail";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -106,7 +106,7 @@ namespace Bit.Core.Services
}; };
await AddMessageContentAsync(message, "TwoFactorEmail", model); await AddMessageContentAsync(message, "TwoFactorEmail", model);
message.MetaData.Add("SendGridBypassListManagement", true); message.MetaData.Add("SendGridBypassListManagement", true);
message.MetaData.Add("SendGridCategories", new List<string> { "TwoFactorEmail" }); message.Category = "TwoFactorEmail";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -120,7 +120,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "MasterPasswordHint", model); await AddMessageContentAsync(message, "MasterPasswordHint", model);
message.MetaData.Add("SendGridCategories", new List<string> { "MasterPasswordHint" }); message.Category = "MasterPasswordHint";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -133,7 +133,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "NoMasterPasswordHint", model); await AddMessageContentAsync(message, "NoMasterPasswordHint", model);
message.MetaData.Add("SendGridCategories", new List<string> { "NoMasterPasswordHint" }); message.Category = "NoMasterPasswordHint";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -149,7 +149,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "OrganizationUserAccepted", model); await AddMessageContentAsync(message, "OrganizationUserAccepted", model);
message.MetaData.Add("SendGridCategories", new List<string> { "OrganizationUserAccepted" }); message.Category = "OrganizationUserAccepted";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -163,7 +163,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "OrganizationUserConfirmed", model); await AddMessageContentAsync(message, "OrganizationUserConfirmed", model);
message.MetaData.Add("SendGridCategories", new List<string> { "OrganizationUserConfirmed" }); message.Category = "OrganizationUserConfirmed";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -182,7 +182,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "OrganizationUserInvited", model); await AddMessageContentAsync(message, "OrganizationUserInvited", model);
message.MetaData.Add("SendGridCategories", new List<string> { "OrganizationUserInvited" }); message.Category = "OrganizationUserInvited";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -195,7 +195,7 @@ namespace Bit.Core.Services
SiteName = _globalSettings.SiteName SiteName = _globalSettings.SiteName
}; };
await AddMessageContentAsync(message, "Welcome", model); await AddMessageContentAsync(message, "Welcome", model);
message.MetaData.Add("SendGridCategories", new List<string> { "Welcome" }); message.Category = "Welcome";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -214,7 +214,7 @@ namespace Bit.Core.Services
Url = url.ToString() Url = url.ToString()
}; };
await AddMessageContentAsync(message, "PasswordlessSignIn", model); await AddMessageContentAsync(message, "PasswordlessSignIn", model);
message.MetaData.Add("SendGridCategories", new List<string> { "PasswordlessSignIn" }); message.Category = "PasswordlessSignIn";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -232,7 +232,7 @@ namespace Bit.Core.Services
MentionInvoices = mentionInvoices MentionInvoices = mentionInvoices
}; };
await AddMessageContentAsync(message, "InvoiceUpcoming", model); await AddMessageContentAsync(message, "InvoiceUpcoming", model);
message.MetaData.Add("SendGridCategories", new List<string> { "InvoiceUpcoming" }); message.Category = "InvoiceUpcoming";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -247,7 +247,7 @@ namespace Bit.Core.Services
MentionInvoices = mentionInvoices MentionInvoices = mentionInvoices
}; };
await AddMessageContentAsync(message, "PaymentFailed", model); await AddMessageContentAsync(message, "PaymentFailed", model);
message.MetaData.Add("SendGridCategories", new List<string> { "PaymentFailed" }); message.Category = "PaymentFailed";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -261,7 +261,7 @@ namespace Bit.Core.Services
Amount = amount Amount = amount
}; };
await AddMessageContentAsync(message, "AddedCredit", model); await AddMessageContentAsync(message, "AddedCredit", model);
message.MetaData.Add("SendGridCategories", new List<string> { "AddedCredit" }); message.Category = "AddedCredit";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -279,7 +279,7 @@ namespace Bit.Core.Services
IpAddress = ip IpAddress = ip
}; };
await AddMessageContentAsync(message, "NewDeviceLoggedIn", model); await AddMessageContentAsync(message, "NewDeviceLoggedIn", model);
message.MetaData.Add("SendGridCategories", new List<string> { "NewDeviceLoggedIn" }); message.Category = "NewDeviceLoggedIn";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }

View File

@ -58,9 +58,9 @@ namespace Bit.Core.Services
} }
var cats = new List<string> { "Bitwarden Server" }; var cats = new List<string> { "Bitwarden Server" };
if(message.MetaData?.ContainsKey("SendGridCategories") ?? false) if(!string.IsNullOrWhiteSpace(message.Category))
{ {
cats.AddRange(message.MetaData["SendGridCategories"] as List<string>); cats.Add(message.Category);
} }
sendGridMessage.AddCategories(cats); sendGridMessage.AddCategories(cats);

View File

@ -87,6 +87,10 @@ namespace Bit.Core.Utilities
{ {
services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>(); services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>();
} }
else if(CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret))
{
services.AddSingleton<IMailDeliveryService, AmazonSesMailDeliveryService>();
}
else if(CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Host)) else if(CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Host))
{ {
services.AddSingleton<IMailDeliveryService, MailKitSmtpMailDeliveryService>(); services.AddSingleton<IMailDeliveryService, MailKitSmtpMailDeliveryService>();

View File

@ -32,6 +32,11 @@
}, },
"sentry": { "sentry": {
"dsn": "SECRET" "dsn": "SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
} }
} }

View File

@ -21,6 +21,7 @@
}, },
"mail": { "mail": {
"sendGridApiKey": "SECRET", "sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email",
"replyToEmail": "hello@bitwarden.com" "replyToEmail": "hello@bitwarden.com"
}, },
"identityServer": { "identityServer": {
@ -58,6 +59,11 @@
"merchantId": "SECRET", "merchantId": "SECRET",
"publicKey": "SECRET", "publicKey": "SECRET",
"privateKey": "SECRET" "privateKey": "SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
}, },
"IpRateLimitOptions": { "IpRateLimitOptions": {

View File

@ -32,6 +32,11 @@
}, },
"sentry": { "sentry": {
"dsn": "SECRET" "dsn": "SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
} }
} }
} }