1d3818e725
- dms-lxc.sh: Proxmox-Host-Installer (unprivilegierter LXC, Debian 13, Docker), curl-Self-Download, Multi-Domain-DKIM, SnappyMail-Provisionierung, PVE-Firewall - Stack: docker-mailserver, Node-Admin-API (Supabase-Auth), React-Admin-UI (OPENBUREAU-Look), SnappyMail (Shibui-Theme), Rspamd-Web-UI, docker-socket-proxy - Admin: Postfächer/Aliase/Catch-all/Quota, editierbare Domains+Settings, Server (Quota/Queue über abgesicherte Bridge), Status & DNS - Hardening: no-new-privileges, Whitelisted exec-Bridge, Rspamd-Passwort, .env chmod 600, PVE-CT-Firewall, generisch/teilbar (keine festen Domains) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.5 KiB
JavaScript
61 lines
2.5 KiB
JavaScript
// ---------------------------------------------------------------------------
|
|
// auth.js — Supabase-Token-Prüfung (wie in OPENBUREAU)
|
|
//
|
|
// Die React-Admin-UI loggt sich per Supabase ein und schickt das Access-Token
|
|
// als Authorization: Bearer <token>. Hier validieren wir es gegen Supabase
|
|
// und prüfen, ob die E-Mail in ADMIN_ALLOWED_EMAILS steht.
|
|
// ---------------------------------------------------------------------------
|
|
import { createClient } from '@supabase/supabase-js';
|
|
|
|
const SUPABASE_URL = process.env.SUPABASE_URL;
|
|
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
|
|
const ALLOWED = (process.env.ADMIN_ALLOWED_EMAILS || '')
|
|
.split(',')
|
|
.map((s) => s.trim().toLowerCase())
|
|
.filter(Boolean);
|
|
|
|
// Nur für lokale Tests: hebt die Auth komplett auf. Standardmäßig AUS.
|
|
// Wird ausschließlich im docker-compose.local.yml gesetzt, niemals im Deploy.
|
|
const AUTH_DISABLED = process.env.AUTH_DISABLED === 'true';
|
|
if (AUTH_DISABLED) {
|
|
console.warn('[auth] ⚠ AUTH_DISABLED=true — KEINE Authentifizierung! Nur für lokale Tests verwenden.');
|
|
}
|
|
|
|
// Supabase-Client nur erstellen, wenn Auth aktiv und konfiguriert ist
|
|
// (createClient wirft bei leerer URL — würde sonst den Start verhindern).
|
|
let supabase = null;
|
|
if (!AUTH_DISABLED) {
|
|
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
|
console.warn('[auth] SUPABASE_URL / SUPABASE_ANON_KEY nicht gesetzt — Auth wird fehlschlagen.');
|
|
} else {
|
|
supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function requireAdmin(req, res, next) {
|
|
try {
|
|
if (AUTH_DISABLED) {
|
|
req.user = { email: 'dev@local.test' };
|
|
return next();
|
|
}
|
|
if (!supabase) return res.status(500).json({ error: 'Auth nicht konfiguriert (SUPABASE_URL/ANON_KEY fehlen).' });
|
|
const header = req.headers.authorization || '';
|
|
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
|
|
if (!token) return res.status(401).json({ error: 'Kein Token.' });
|
|
|
|
const { data, error } = await supabase.auth.getUser(token);
|
|
if (error || !data?.user) return res.status(401).json({ error: 'Token ungültig.' });
|
|
|
|
const email = (data.user.email || '').toLowerCase();
|
|
if (ALLOWED.length && !ALLOWED.includes(email)) {
|
|
return res.status(403).json({ error: 'Kein Admin-Zugriff für diese E-Mail.' });
|
|
}
|
|
req.user = { email };
|
|
next();
|
|
} catch (e) {
|
|
res.status(500).json({ error: 'Auth-Fehler: ' + e.message });
|
|
}
|
|
}
|