From 36bbe0a193c9831b6010fab778fdd44688982bbb Mon Sep 17 00:00:00 2001 From: vegard Date: Thu, 19 Mar 2026 01:55:37 +0000 Subject: [PATCH] Fjern frase-sjekk, legg til domene-alias i synops-mail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fjernet "Kjære vaktmester"-krav: avsender-verifisering via auth_identities.email er tilstrekkelig spam-filter - Domene-alias: mottaker-username oppslås i auth_identities uavhengig av domene. vegard@synops.no, vegard@sidelinja.org, vegard@vegard.info ruter til samme bruker Co-Authored-By: Claude Opus 4.6 (1M context) --- tools/synops-mail/src/main.rs | 55 +++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/tools/synops-mail/src/main.rs b/tools/synops-mail/src/main.rs index dbfc286..da8a4fb 100644 --- a/tools/synops-mail/src/main.rs +++ b/tools/synops-mail/src/main.rs @@ -30,7 +30,7 @@ use std::process::{Command, Stdio}; const DEFAULT_FROM: &str = "vaktmester@synops.no"; const DEFAULT_FROM_NAME: &str = "Synops Vaktmester"; const DEFAULT_CONFIG: &str = "/srv/synops/config/msmtp/msmtprc"; -const DEFAULT_PHRASE: &str = "Kjære vaktmester"; +// Frase-sjekk fjernet — avsender-verifisering via auth_identities er nok. /// Synops epost-verktøy: send og motta epost. #[derive(Parser)] @@ -72,9 +72,6 @@ struct Cli { #[arg(long)] sender: Option, - /// Aktiveringsfrase som epost-body må starte med (default: "Kjære vaktmester") - #[arg(long, env = "SYNOPS_MAIL_PHRASE", default_value = DEFAULT_PHRASE)] - phrase: String, } fn main() { @@ -226,7 +223,7 @@ async fn run_receive(cli: &Cli) { tracing::info!(subject = %subject, body_len = body.len(), "epost parset"); - // Sjekk 1: Avsender-epost matcher auth_identities.email? + // Verifiser avsender: epost matcher auth_identities.email? let db = match synops_common::db::connect().await { Ok(pool) => pool, Err(e) => { @@ -253,7 +250,7 @@ async fn run_receive(cli: &Cli) { let user_node_id = match user_node_id { Some(id) => { - tracing::info!(user_node_id = %id, "avsender verifisert"); + tracing::info!(user_node_id = %id, "avsender verifisert via epost"); id } None => { @@ -265,18 +262,44 @@ async fn run_receive(cli: &Cli) { } }; - // Sjekk 2: Body starter med aktiveringsfrasen? - let body_trimmed = body.trim(); - if !body_trimmed.starts_with(&cli.phrase) { + // Domene-alias: mottaker-username oppslås uavhengig av domene. + // vegard@synops.no, vegard@sidelinja.org, vegard@vegard.info + // ruter alle til samme bruker. + let recipient_username = recipient + .split('@') + .next() + .unwrap_or("") + .trim() + .to_lowercase(); + + let target_node_id: Option = match sqlx::query_scalar( + "SELECT node_id FROM auth_identities WHERE LOWER(username) = $1", + ) + .bind(&recipient_username) + .fetch_optional(&db) + .await + { + Ok(id) => id, + Err(e) => { + tracing::warn!("username-oppslag feilet: {e}"); + None + } + }; + + // Hvis mottaker-username matcher en bruker, bruk den. + // Ellers havner noden hos avsender (epost til seg selv / vaktmester). + let owner_node_id = target_node_id.unwrap_or(user_node_id); + if target_node_id.is_some() { tracing::info!( - phrase = %cli.phrase, - body_start = &body_trimmed[..body_trimmed.len().min(50)], - "epost mangler aktiveringsfrase — forkaster" + recipient_username = %recipient_username, + owner = %owner_node_id, + "mottaker funnet via username" ); - return; } - // Begge sjekker bestått — opprett content-node + let body_trimmed = body.trim(); + + // Avsender verifisert — opprett content-node let node_id = uuid::Uuid::now_v7(); let title = if subject.is_empty() { format!("Epost fra {}", sender_email) @@ -299,14 +322,14 @@ async fn run_receive(cli: &Cli) { .bind(&title) .bind(body_trimmed) .bind(&metadata) - .bind(user_node_id) + .bind(owner_node_id) .execute(&db) .await { Ok(_) => { tracing::info!( node_id = %node_id, - user = %user_node_id, + owner = %owner_node_id, title = %title, "content-node opprettet fra epost" );