ixes invalid registration email formats are still rejected before the user is created
All checks were successful
Dotnet build and test / build (pull_request) Successful in 10m5s

SMTP recipient rejections are logged and treated as a soft failure instead of crashing the request
This commit is contained in:
Paul Schneider 2026-08-29 17:21:40 +01:00
commit 40437982eb
Signed by: notazof
GPG key ID: 1DD5D838E5343B06
2 changed files with 85 additions and 1 deletions

View file

@ -1,9 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using MailKit.Net.Smtp;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MimeKit;
using Yavsc.Interface;
using Yavsc.Interfaces;
using Yavsc.Models.Relationship;
using Yavsc.Org.Tests.Fakes;
using Yavsc.Services;
using Yavsc.Settings;
using Yavsc.ViewModels.Account;
namespace Yavsc.Org.Tests
@ -80,5 +89,74 @@ namespace Yavsc.Org.Tests
Assert.Contains(results, r => r.MemberNames.Contains(nameof(RegisterModel.Email)));
}
[Fact]
public async Task SendEmailAsync_ignores_smtp_recipient_rejection()
{
var sender = new MailSender(
Options.Create(new SiteSettings
{
Title = "Test",
Authority = "example.com",
Owner = new StaticContact { Name = "Test Owner", EMail = "owner@example.com" }
}),
Options.Create(new SmtpSettings
{
Host = "smtp.test.local",
Port = 465,
UserName = "test-user",
Password = "secret"
}),
NullLoggerFactory.Instance,
new TestStringLocalizer(),
new RejectingSmtpClientFactory());
var result = await sender.SendEmailAsync(
"Alice",
"contact@pschneider.fr",
"Welcome",
"hello");
Assert.Equal(string.Empty, result);
}
private sealed class RejectingSmtpClientFactory : ISmtpClientFactory
{
public Yavsc.Interfaces.ISmtpClient CreateClient() => new RejectingSmtpClient();
}
private sealed class RejectingSmtpClient : Yavsc.Interfaces.ISmtpClient
{
public int Timeout { get; set; }
public void Connect(string host, int port, MailKit.Security.SecureSocketOptions options) { }
public void Authenticate(string userName, string password) { }
public Task SendAsync(MimeMessage message, CancellationToken cancellationToken = default)
{
throw new SmtpCommandException(
SmtpErrorCode.RecipientNotAccepted,
SmtpStatusCode.MailboxUnavailable,
"Recipient address rejected: User unknown in local recipient table");
}
public void Disconnect(bool quit) { }
public void Dispose() { }
}
private sealed class TestStringLocalizer : IStringLocalizer<MailSender>
{
public LocalizedString this[string name] => new(name, name);
public LocalizedString this[string name, params object[] arguments] => new(name, string.Format(CultureInfo.InvariantCulture, name, arguments));
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
=> Enumerable.Empty<LocalizedString>();
public LocalizedString GetString(string name)
=> new(name, name);
public LocalizedString GetString(string name, params object[] arguments)
=> new(name, string.Format(CultureInfo.InvariantCulture, name, arguments));
public IStringLocalizer WithCulture(CultureInfo culture)
=> this;
}
}
}

View file

@ -1,3 +1,4 @@
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -114,7 +115,7 @@ namespace Yavsc.Services
msg.MessageId = MimeKit.Utils.MimeUtils.GenerateMessageId(
siteSettings.Authority
);
using ISmtpClient sc = _smtpClientFactory.CreateClient();
using Yavsc.Interfaces.ISmtpClient sc = _smtpClientFactory.CreateClient();
{
sc.Timeout = 30000;
sc.Connect(
@ -139,6 +140,11 @@ namespace Yavsc.Services
logger.LogError(ex, "Refusing to send email because the recipient or sender address is malformed. To={To}, From={From}", email, siteSettings.Owner.EMail);
return string.Empty;
}
catch (SmtpCommandException ex)
{
logger.LogError(ex, "SMTP rejected the recipient or sender address. To={To}, Subject={Subject}, Status={Status}, Error={Error}", email, subject, ex.StatusCode, ex.Message);
return string.Empty;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to send email. To={To}, Subject={Subject}", email, subject);