mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-25427] Allow reading mail templates from disk (#6123)
* Allow reading mail templates from self host disk * Update src/Core/Services/Implementations/HandlebarsMailService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Core/Services/Implementations/HandlebarsMailService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * tweak logic * some error handling reading templates from disk * fix: broken test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ using Bit.Core.Vault.Models.Data;
|
||||
using Core.Auth.Enums;
|
||||
using HandlebarsDotNet;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
@@ -39,6 +40,7 @@ public class HandlebarsMailService : IMailService
|
||||
private readonly IMailDeliveryService _mailDeliveryService;
|
||||
private readonly IMailEnqueuingService _mailEnqueuingService;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly ILogger<HandlebarsMailService> _logger;
|
||||
private readonly Dictionary<string, HandlebarsTemplate<object, object>> _templateCache = new();
|
||||
|
||||
private bool _registeredHelpersAndPartials = false;
|
||||
@@ -47,12 +49,14 @@ public class HandlebarsMailService : IMailService
|
||||
GlobalSettings globalSettings,
|
||||
IMailDeliveryService mailDeliveryService,
|
||||
IMailEnqueuingService mailEnqueuingService,
|
||||
IDistributedCache distributedCache)
|
||||
IDistributedCache distributedCache,
|
||||
ILogger<HandlebarsMailService> logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_mailDeliveryService = mailDeliveryService;
|
||||
_mailEnqueuingService = mailEnqueuingService;
|
||||
_distributedCache = distributedCache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task SendVerifyEmailEmailAsync(string email, Guid userId, string token)
|
||||
@@ -708,6 +712,12 @@ public class HandlebarsMailService : IMailService
|
||||
|
||||
private async Task<string?> ReadSourceAsync(string templateName)
|
||||
{
|
||||
var diskSource = await ReadSourceFromDiskAsync(templateName);
|
||||
if (!string.IsNullOrWhiteSpace(diskSource))
|
||||
{
|
||||
return diskSource;
|
||||
}
|
||||
|
||||
var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly;
|
||||
var fullTemplateName = $"{Namespace}.{templateName}.hbs";
|
||||
if (!assembly.GetManifestResourceNames().Any(f => f == fullTemplateName))
|
||||
@@ -721,6 +731,42 @@ public class HandlebarsMailService : IMailService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> ReadSourceFromDiskAsync(string templateName)
|
||||
{
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
var templateFileSuffix = ".html";
|
||||
if (templateName.EndsWith(".txt"))
|
||||
{
|
||||
templateFileSuffix = ".txt";
|
||||
}
|
||||
else if (!templateName.EndsWith(".html"))
|
||||
{
|
||||
// unexpected suffix
|
||||
return null;
|
||||
}
|
||||
var suffixPosition = templateName.LastIndexOf(templateFileSuffix);
|
||||
var templateNameNoSuffix = templateName.Substring(0, suffixPosition);
|
||||
var templatePathNoSuffix = templateNameNoSuffix.Replace(".", "/");
|
||||
var diskPath = $"{_globalSettings.MailTemplateDirectory}/{templatePathNoSuffix}{templateFileSuffix}.hbs";
|
||||
var directory = Path.GetDirectoryName(diskPath);
|
||||
if (Directory.Exists(directory) && File.Exists(diskPath))
|
||||
{
|
||||
var fileContents = await File.ReadAllTextAsync(diskPath);
|
||||
return fileContents;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to read mail template from disk.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task RegisterHelpersAndPartialsAsync()
|
||||
{
|
||||
if (_registeredHelpersAndPartials)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Bit.Core.Settings;
|
||||
|
||||
public class GlobalSettings : IGlobalSettings
|
||||
{
|
||||
private string _mailTemplateDirectory;
|
||||
private string _logDirectory;
|
||||
private string _licenseDirectory;
|
||||
|
||||
@@ -37,6 +38,11 @@ public class GlobalSettings : IGlobalSettings
|
||||
get => BuildDirectory(_licenseDirectory, "/core/licenses");
|
||||
set => _licenseDirectory = value;
|
||||
}
|
||||
public virtual string MailTemplateDirectory
|
||||
{
|
||||
get => BuildDirectory(_mailTemplateDirectory, "/mail-templates");
|
||||
set => _mailTemplateDirectory = value;
|
||||
}
|
||||
public string LicenseCertificatePassword { get; set; }
|
||||
public virtual string PushRelayBaseUri { get; set; }
|
||||
public virtual string InternalIdentityKey { get; set; }
|
||||
|
||||
@@ -23,6 +23,7 @@ public class HandlebarsMailServiceTests
|
||||
private readonly IMailDeliveryService _mailDeliveryService;
|
||||
private readonly IMailEnqueuingService _mailEnqueuingService;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly ILogger<HandlebarsMailService> _logger;
|
||||
|
||||
public HandlebarsMailServiceTests()
|
||||
{
|
||||
@@ -30,12 +31,14 @@ public class HandlebarsMailServiceTests
|
||||
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
|
||||
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
|
||||
_distributedCache = Substitute.For<IDistributedCache>();
|
||||
_logger = Substitute.For<ILogger<HandlebarsMailService>>();
|
||||
|
||||
_sut = new HandlebarsMailService(
|
||||
_globalSettings,
|
||||
_mailDeliveryService,
|
||||
_mailEnqueuingService,
|
||||
_distributedCache
|
||||
_distributedCache,
|
||||
_logger
|
||||
);
|
||||
}
|
||||
|
||||
@@ -217,8 +220,9 @@ public class HandlebarsMailServiceTests
|
||||
|
||||
var mailDeliveryService = new MailKitSmtpMailDeliveryService(globalSettings, Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>());
|
||||
var distributedCache = Substitute.For<IDistributedCache>();
|
||||
var logger = Substitute.For<ILogger<HandlebarsMailService>>();
|
||||
|
||||
var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService(), distributedCache);
|
||||
var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService(), distributedCache, logger);
|
||||
|
||||
var sendMethods = typeof(IMailService).GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.Name.StartsWith("Send") && m.Name != "SendEnqueuedMailMessageAsync");
|
||||
|
||||
Reference in New Issue
Block a user