Fjern frase-sjekk, legg til domene-alias i synops-mail

- 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) <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-19 01:55:37 +00:00
parent 25713c4482
commit 36bbe0a193

View file

@ -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<String>,
/// 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<uuid::Uuid> = 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"
);