Emails: Add Expires header (#20285)
* Add `Expiry` header to emails, set default expiry to 30 days and allow user config via `appsettings` * Remove `IsSmtpExpirationConfigured` as it will always have a value * Check for `emailExpiration` value * Removed `EmailExpiration` default value as it should be opt-in * Simplify SMTP email expiration condition * Fix APICompat issue * Add implementation to `NotImplementedEmailSender` * Rename `emailExpiration` to `expires` to match the SMTP header * Obsolete interfaces without `expires` parameter, delegate to an existing method. * Set expiry TimeSpan values from user configurable settings with defaults * Fix formating * Handle breaking changes, add obsoletion messages and simplify interfaces. * Fix default of invite expires timespan (was being parsed as 72 days not 72 hours). --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
@@ -181,6 +181,11 @@ public class GlobalSettings
|
||||
/// </summary>
|
||||
public bool IsSmtpServerConfigured => !string.IsNullOrWhiteSpace(Smtp?.Host);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether SMTP expiry is configured.
|
||||
/// </summary>
|
||||
public bool IsSmtpExpiryConfigured => Smtp?.EmailExpiration != null && Smtp?.EmailExpiration.HasValue == true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is a physical pickup directory configured.
|
||||
/// </summary>
|
||||
|
||||
@@ -34,6 +34,9 @@ public class SecuritySettings
|
||||
internal const string StaticAuthorizeCallbackLogoutPathName = "/umbraco/logout";
|
||||
internal const string StaticAuthorizeCallbackErrorPathName = "/umbraco/error";
|
||||
|
||||
internal const string StaticPasswordResetEmailExpiry = "01:00:00";
|
||||
internal const string StaticUserInviteEmailExpiry = "3.00:00:00";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to keep the user logged in.
|
||||
/// </summary>
|
||||
@@ -159,4 +162,16 @@ public class SecuritySettings
|
||||
/// </summary>
|
||||
[DefaultValue(StaticAuthorizeCallbackErrorPathName)]
|
||||
public string AuthorizeCallbackErrorPathName { get; set; } = StaticAuthorizeCallbackErrorPathName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expiry time for password reset emails.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticPasswordResetEmailExpiry)]
|
||||
public TimeSpan PasswordResetEmailExpiry { get; set; } = TimeSpan.Parse(StaticPasswordResetEmailExpiry);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expiry time for user invite emails.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticUserInviteEmailExpiry)]
|
||||
public TimeSpan UserInviteEmailExpiry { get; set; } = TimeSpan.Parse(StaticUserInviteEmailExpiry);
|
||||
}
|
||||
|
||||
@@ -96,4 +96,9 @@ public class SmtpSettings : ValidatableEntryBase
|
||||
/// Gets or sets a value for the SMTP password.
|
||||
/// </summary>
|
||||
public string? Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the time until an email expires.
|
||||
/// </summary>
|
||||
public TimeSpan? EmailExpiration { get; set; }
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class EmailNotificationMethod : NotificationMethodBase
|
||||
var subject = _textService?.Localize("healthcheck", "scheduledHealthCheckEmailSubject", new[] { host });
|
||||
|
||||
EmailMessage mailMessage = CreateMailMessage(subject, message);
|
||||
Task? task = _emailSender?.SendAsync(mailMessage, Constants.Web.EmailTypes.HealthCheck);
|
||||
Task? task = _emailSender?.SendAsync(mailMessage, Constants.Web.EmailTypes.HealthCheck, false, null);
|
||||
if (task is not null)
|
||||
{
|
||||
await task;
|
||||
|
||||
@@ -7,9 +7,28 @@ namespace Umbraco.Cms.Core.Mail;
|
||||
/// </summary>
|
||||
public interface IEmailSender
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a message asynchronously.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the overload with expires parameter. Scheduled for removal in Umbraco 18.")]
|
||||
Task SendAsync(EmailMessage message, string emailType);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message asynchronously.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the overload with expires parameter. Scheduled for removal in Umbraco 18.")]
|
||||
Task SendAsync(EmailMessage message, string emailType, bool enableNotification);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message asynchronously.
|
||||
/// </summary>
|
||||
Task SendAsync(EmailMessage message, string emailType, bool enableNotification = false, TimeSpan? expires = null)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
=> SendAsync(message, emailType, enableNotification);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
/// <summary>
|
||||
/// Verifies if the email sender is configured to send emails.
|
||||
/// </summary>
|
||||
bool CanSendRequiredEmail();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ internal sealed class NotImplementedEmailSender : IEmailSender
|
||||
throw new NotImplementedException(
|
||||
"To send an Email ensure IEmailSender is implemented with a custom implementation");
|
||||
|
||||
public Task SendAsync(EmailMessage message, string emailType, bool enableNotification, TimeSpan? expires) =>
|
||||
throw new NotImplementedException(
|
||||
"To send an Email ensure IEmailSender is implemented with a custom implementation");
|
||||
|
||||
public bool CanSendRequiredEmail()
|
||||
=> throw new NotImplementedException(
|
||||
"To send an Email ensure IEmailSender is implemented with a custom implementation");
|
||||
|
||||
@@ -557,7 +557,7 @@ public class NotificationService : INotificationService
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.LogDebug("Begin processing notifications.");
|
||||
}
|
||||
@@ -569,9 +569,9 @@ public class NotificationService : INotificationService
|
||||
{
|
||||
try
|
||||
{
|
||||
_emailSender.SendAsync(request.Mail, Constants.Web.EmailTypes.Notification).GetAwaiter()
|
||||
_emailSender.SendAsync(request.Mail, Constants.Web.EmailTypes.Notification, false, null).GetAwaiter()
|
||||
.GetResult();
|
||||
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.LogDebug("Notification '{Action}' sent to {Username} ({Email})", request.Action, request.UserName, request.Email);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user