smtp: add notify_email field; fix admin notification sent to no-reply sender

This commit is contained in:
Pontoporeia
2026-04-30 12:19:33 +02:00
parent bdb68479d5
commit 33987c9b15
7 changed files with 81 additions and 42 deletions

11
TODO.md
View File

@@ -63,6 +63,17 @@
- [ ] Verify TCP reachability from XAMXAM VM to LDAP server (port 636)
- [ ] See `docs/LDAP_AUTH_PLAN.md` for full phase-by-phase plan
## SMTP notify_email fix
- [x] Migration 006: add `notify_email` column to `smtp_settings`
- [x] `SmtpRelay::getSettings()` — include `notify_email` in SELECT + defaults
- [x] `SmtpRelay::updateSettings()` — persist `notify_email`
- [x] `SmtpRelay::getNotifyEmail()` — returns `notify_email` ?? `from_email`
- [x] `request-access.php` — use `getNotifyEmail()` instead of `from_email` for admin notifications
- [x] `actions/settings.php` — wire `smtp_notify_email` POST field
- [x] Template: add "Adresse de notification admin" field to SMTP form
- [x] `schema.sql` — updated DDL
## SMTP credential validation
- [x] Add `SmtpProbeException` with `field` property for structured error classification

View File

@@ -0,0 +1,4 @@
-- Migration 006: add notify_email to smtp_settings
-- notify_email is the address that receives admin notifications (access requests, etc.)
-- It is separate from from_email (the sender/no-reply address).
ALTER TABLE smtp_settings ADD COLUMN notify_email TEXT NOT NULL DEFAULT '';

View File

@@ -34,12 +34,13 @@ if ($section === 'formulaire') {
App::flash('success', "Types de travaux mis à jour.");
} elseif ($section === 'smtp') {
$smtpData = [
'host' => $_POST['smtp_host'] ?? '',
'port' => $_POST['smtp_port'] ?? 587,
'encryption' => $_POST['smtp_encryption'] ?? 'tls',
'username' => $_POST['smtp_username'] ?? '',
'from_email' => $_POST['smtp_from_email'] ?? '',
'from_name' => $_POST['smtp_from_name'] ?? 'XAMXAM',
'host' => $_POST['smtp_host'] ?? '',
'port' => $_POST['smtp_port'] ?? 587,
'encryption' => $_POST['smtp_encryption'] ?? 'tls',
'username' => $_POST['smtp_username'] ?? '',
'from_email' => $_POST['smtp_from_email'] ?? '',
'from_name' => $_POST['smtp_from_name'] ?? 'XAMXAM',
'notify_email' => $_POST['smtp_notify_email'] ?? '',
];
// Only update password when user actually typed something.
$pwd = $_POST['smtp_password'] ?? '';

View File

@@ -188,9 +188,9 @@ try {
);
$plain = htmlToPlain($body);
$settings = SmtpRelay::getSettings($db);
if (!empty($settings['from_email'])) {
SmtpRelay::send($db, $settings['from_email'], $subject, $body, $plain);
$notifyEmail = SmtpRelay::getNotifyEmail($db);
if ($notifyEmail !== '') {
SmtpRelay::send($db, $notifyEmail, $subject, $body, $plain);
}
http_response_code(200);

View File

@@ -39,22 +39,34 @@ class SmtpRelay {
*/
public static function getSettings(Database $db): array {
$stmt = $db->getPDO()->query(
"SELECT host, port, encryption, username, password, from_email, from_name
"SELECT host, port, encryption, username, password, from_email, from_name, notify_email
FROM v_smtp_active LIMIT 1"
);
$row = $stmt->fetch();
return $row ?: [
'host' => '',
'port' => 587,
'encryption' => 'tls',
'username' => '',
'password' => '',
'from_email' => '',
'from_name' => 'XAMXAM',
'host' => '',
'port' => 587,
'encryption' => 'tls',
'username' => '',
'password' => '',
'from_email' => '',
'from_name' => 'XAMXAM',
'notify_email' => '',
];
}
/**
* Return the address that should receive admin notification emails.
* Uses notify_email when set, falls back to from_email.
*/
public static function getNotifyEmail(Database $db): string
{
$s = self::getSettings($db);
$notify = trim($s['notify_email'] ?? '');
return $notify !== '' ? $notify : trim($s['from_email'] ?? '');
}
/**
* Upsert SMTP settings.
*
@@ -71,25 +83,27 @@ class SmtpRelay {
$stmt = $db->getPDO()->prepare(
"UPDATE smtp_settings
SET host = :host,
port = :port,
encryption = :encryption,
username = :username,
password = :password,
from_email = :from_email,
from_name = :from_name,
updated_at = CURRENT_TIMESTAMP
SET host = :host,
port = :port,
encryption = :encryption,
username = :username,
password = :password,
from_email = :from_email,
from_name = :from_name,
notify_email = :notify_email,
updated_at = CURRENT_TIMESTAMP
WHERE id = 1"
);
$stmt->execute([
':host' => trim($merged['host']),
':port' => $port,
':encryption' => $encryption,
':username' => trim($merged['username']),
':password' => $merged['password'],
':from_email' => trim($merged['from_email']),
':from_name' => trim($merged['from_name']),
':host' => trim($merged['host']),
':port' => $port,
':encryption' => $encryption,
':username' => trim($merged['username']),
':password' => $merged['password'],
':from_email' => trim($merged['from_email']),
':from_name' => trim($merged['from_name']),
':notify_email' => trim($merged['notify_email'] ?? ''),
]);
}

View File

@@ -352,15 +352,16 @@ CREATE INDEX IF NOT EXISTS idx_share_links_active ON share_links(is_active);
-- Singleton row — id is always 1. Credentials stored in clear for now.
CREATE TABLE IF NOT EXISTS smtp_settings (
id INTEGER PRIMARY KEY CHECK (id = 1),
host TEXT NOT NULL DEFAULT '',
port INTEGER NOT NULL DEFAULT 587,
encryption TEXT NOT NULL DEFAULT 'tls', -- 'tls' | 'ssl' | 'none'
username TEXT NOT NULL DEFAULT '',
password TEXT NOT NULL DEFAULT '', -- stored in clear for now; encrypt later
from_email TEXT NOT NULL DEFAULT '',
from_name TEXT NOT NULL DEFAULT 'XAMXAM',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
id INTEGER PRIMARY KEY CHECK (id = 1),
host TEXT NOT NULL DEFAULT '',
port INTEGER NOT NULL DEFAULT 587,
encryption TEXT NOT NULL DEFAULT 'tls', -- 'tls' | 'ssl' | 'none'
username TEXT NOT NULL DEFAULT '',
password TEXT NOT NULL DEFAULT '', -- stored in clear for now; encrypt later
from_email TEXT NOT NULL DEFAULT '',
from_name TEXT NOT NULL DEFAULT 'XAMXAM',
notify_email TEXT NOT NULL DEFAULT '', -- recipient for admin notifications
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT OR IGNORE INTO smtp_settings (id) VALUES (1);

View File

@@ -265,16 +265,24 @@
<legend>Expéditeur par défaut</legend>
<div class="param-grid">
<div>
<label for="smtp_from_email">Adresse e-mail</label>
<label for="smtp_from_email">Adresse e-mail d'expédition</label>
<input type="email" id="smtp_from_email" name="smtp_from_email"
value="<?= htmlspecialchars($smtpSettings['from_email']) ?>"
placeholder="noreply@example.com">
<small>Adresse utilisée comme expéditeur (champ From:).</small>
</div>
<div>
<label for="smtp_from_name">Nom d'expéditeur</label>
<input type="text" id="smtp_from_name" name="smtp_from_name"
value="<?= htmlspecialchars($smtpSettings['from_name']) ?>">
</div>
<div>
<label for="smtp_notify_email">Adresse de notification admin</label>
<input type="email" id="smtp_notify_email" name="smtp_notify_email"
value="<?= htmlspecialchars($smtpSettings['notify_email'] ?? '') ?>"
placeholder="admin@example.com">
<small>Reçoit les notifications (demandes daccès, etc.). Si vide, utilise ladresse dexpédition.</small>
</div>
</div>
</fieldset>