feat: email_send allowlist — block sends to non-whitelisted addresses
Reads home/{username}/email_allowlist.json (JSON array of addresses).
Fails safe: if file is missing or address not listed, send is blocked with
an informative message. home/ is gitignored; create the file manually per user.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,15 +6,40 @@ email_send uses the server SMTP config from .env (smtp_server, smtp_from_*).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
from config import settings
|
||||
from persona import get_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _email_allowlist(username: str) -> list[str]:
|
||||
"""Load the per-user email allowlist. Returns empty list if not configured."""
|
||||
path = settings.home_root() / username / "email_allowlist.json"
|
||||
try:
|
||||
return [a.lower().strip() for a in json.loads(path.read_text())]
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.warning("failed to read email_allowlist.json for %s: %s", username, e)
|
||||
return []
|
||||
|
||||
|
||||
async def email_send(to: str, subject: str, body: str) -> str:
|
||||
"""Send an email via the server's configured SMTP account."""
|
||||
username = get_user()
|
||||
allowlist = _email_allowlist(username)
|
||||
|
||||
if not allowlist:
|
||||
return (
|
||||
"Email blocked — no allowlist configured. "
|
||||
f"Add allowed addresses to home/{username}/email_allowlist.json as a JSON array."
|
||||
)
|
||||
if to.lower().strip() not in allowlist:
|
||||
return f"Email blocked — {to} is not in the allowlist for {username}."
|
||||
|
||||
from email_utils import send_email
|
||||
ok = await asyncio.to_thread(
|
||||
send_email,
|
||||
|
||||
Reference in New Issue
Block a user