case 4724 - SMTP certificate checked, password never returned

Mailer checks the SMTP server's certificate (trust and name; no revocation
check). New config setting AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE accepts any
certificate (boot warning, shown in server info, manual page).
The notification settings GET returns ******** instead of the SMTP
password; a PUT with ******** or empty keeps the stored password.
F30 and F31 fixed. Changelog and manual updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 19:09:02 -07:00
parent 94a6f3b653
commit 4141217c04
13 changed files with 98 additions and 7 deletions

View File

@@ -60,6 +60,10 @@ namespace AyaNova.Api.Controllers
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
//case 4724: the SMTP password never goes to the client (detached first so the mask can't be saved)
ct.Entry(o).State = EntityState.Detached;
o.SmtpPassword = GlobalOpsNotificationSettingsBiz.SMTP_PASSWORD_MASK;
return Ok(ApiOkResponse.Response(o));
}

View File

@@ -193,6 +193,7 @@ namespace AyaNova.Api.Controllers
AYANOVA_LOG_PATH = ServerBootConfig.AYANOVA_LOG_PATH,
AYANOVA_LOG_LEVEL = ServerBootConfig.AYANOVA_LOG_LEVEL,
AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG = ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG,
AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE = ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE,
serverinfo = ServerBootConfig.BOOT_DIAGNOSTIC_INFO,
dbserverinfo = ServerBootConfig.DBSERVER_DIAGNOSTIC_INFO
}));
@@ -315,6 +316,7 @@ namespace AyaNova.Api.Controllers
sbRet.AppendLine($"AYANOVA_LOG_PATH: {ServerBootConfig.AYANOVA_LOG_PATH}");
sbRet.AppendLine($"AYANOVA_LOG_LEVEL: {ServerBootConfig.AYANOVA_LOG_LEVEL}");
sbRet.AppendLine($"AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG: {ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG}");
sbRet.AppendLine($"AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE: {ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE}");
sbRet.AppendLine("#########################################################");
sbRet.AppendLine("SERVER DIAGNOSTICS");

View File

@@ -228,6 +228,9 @@ namespace AyaNova
if (ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG)
logger.Warn("AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG is enabled, this will cause the server to run very slowly");
if (ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE)
logger.Warn("AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE is enabled, the SMTP server's certificate is not checked");
//Log environmental settings
long UtilityFilesAvailableSpace = 0;

View File

@@ -10,6 +10,8 @@ namespace AyaNova.Biz
internal class GlobalOpsNotificationSettingsBiz : BizObject
{
//case 4724: shown to the client in place of the SMTP password, which never leaves the server
internal const string SMTP_PASSWORD_MASK = "********";
internal GlobalOpsNotificationSettingsBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
@@ -62,6 +64,9 @@ namespace AyaNova.Biz
if (dbObject == null)
throw new System.Exception("GlobalOpsNotificationSettings::PutAsync -> Settings object not found in database!!");
//case 4724: the client gets the mask, not the password; the mask or an empty value keeps the stored password
if (string.IsNullOrEmpty(putObject.SmtpPassword) || putObject.SmtpPassword == SMTP_PASSWORD_MASK)
putObject.SmtpPassword = dbObject.SmtpPassword;
CopyObject.Copy(putObject, dbObject, "Id");
ct.Entry(dbObject).OriginalValues["Concurrency"] = putObject.Concurrency;
Validate(dbObject);

View File

@@ -83,7 +83,12 @@ namespace AyaNova.Util
using (var client = new SmtpClient())
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
//case 4724: the server's certificate must be trusted and match the server name, unless the
//AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE setting says otherwise. No revocation check: it fails the
//connection whenever the certificate authority's revocation server can't be reached
client.CheckCertificateRevocation = false;
if (ServerBootConfig.AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
bool UseSSL = smtpSettings.ConnectionSecurity != Biz.NotifyMailSecurity.None;
await client.ConnectAsync(smtpSettings.SmtpServerAddress, smtpSettings.SmtpServerPort, UseSSL);

View File

@@ -108,6 +108,10 @@ namespace AyaNova.Util
//SECURITY
internal static string AYANOVA_SET_SUPERUSER_PW { get; set; }
//EMAIL
//case 4724: the SMTP server's certificate is checked unless this is set (a mail server with a self-signed or internal certificate)
internal static bool AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE { get; set; }
//HELPFUL INFORMATION FOR DIAGNOSTICS
internal static Dictionary<string, string> BOOT_DIAGNOSTIC_INFO { get; set; } =
new Dictionary<string, string>();
@@ -218,6 +222,10 @@ namespace AyaNova.Util
bTemp = config.GetValue<bool?>("AYANOVA_REMOVE_LICENSE_FROM_DB");
AYANOVA_REMOVE_LICENSE_FROM_DB = (null == bTemp) ? false : (bool)bTemp;
//EMAIL
bTemp = config.GetValue<bool?>("AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE");
AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE = (null == bTemp) ? false : (bool)bTemp;
//FOLDERS
string DataFolderPath = ActualFullPath(config.GetValue<string>("AYANOVA_DATA_PATH"));
string LogPath = ActualFullPath(config.GetValue<string>("AYANOVA_LOG_PATH"));