"""Send notification emails via Postfix (SMTP).""" from __future__ import annotations import os import smtplib from email.message import EmailMessage ROOT_NOTIFY_EMAIL = os.getenv("DESK_ROOT_NOTIFY_EMAIL", "admin@ligbox.com.br") DESK_PUBLIC_URL = os.getenv("DESK_PUBLIC_URL", "https://desk.ligbox.com.br") MAIL_FROM = os.getenv("DESK_MAIL_FROM", "ligbox-ops@ligbox.com.br") SMTP_HOST = os.getenv("DESK_SMTP_HOST", "10.10.10.122") SMTP_PORT = int(os.getenv("DESK_SMTP_PORT", "25")) def send_email(to: str, subject: str, body: str) -> bool: to = (to or "").strip() if not to: return False msg = EmailMessage() msg["From"] = MAIL_FROM msg["To"] = to msg["Subject"] = subject msg.set_content(body) try: with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=15) as smtp: smtp.send_message(msg) return True except Exception: return False def notify_root_registration_pending(email: str, request_id: int) -> bool: body = ( f"Novo pedido de cadastro Ligbox Ops Desk\n\n" f"E-mail: {email}\n" f"ID: {request_id}\n\n" f"Aprovar em: {DESK_PUBLIC_URL}/\n" f"(Menu Mensagens)\n" ) return send_email(ROOT_NOTIFY_EMAIL, f"[Ligbox Ops] Pedido de cadastro: {email}", body) def notify_candidate_approved(email: str, activation_url: str, role: str) -> bool: body = ( f"Seu pedido de acesso ao Ligbox Ops Desk foi APROVADO.\n\n" f"Perfil atribuído: {role}\n\n" f"Ative sua conta (complete 2 de 3 fatores: e-mail, telefone ou app 2FA):\n{activation_url}\n\n" f"Este link expira em 48 horas.\n" ) return send_email(email, "[Ligbox Ops] Conta aprovada — ative agora", body) def notify_candidate_rejected(email: str, reason: str | None = None) -> bool: body = "Seu pedido de acesso ao Ligbox Ops Desk foi rejeitado." if reason: body += f"\n\nMotivo: {reason}" return send_email(email, "[Ligbox Ops] Pedido de cadastro rejeitado", body) def send_otp_email(email: str, code: str, purpose: str) -> bool: body = ( f"Código de verificação Ligbox Ops Desk\n\n" f"Finalidade: {purpose}\n" f"Código: {code}\n\n" f"Válido por 10 minutos.\n" ) return send_email(email, f"[Ligbox Ops] Código: {code}", body) def mask_email(email: str) -> str: email = (email or "").strip() if "@" not in email: return email local, domain = email.split("@", 1) if len(local) <= 2: masked_local = local[0] + "***" else: masked_local = local[0] + "***" + local[-1] return f"{masked_local}@{domain}" def notify_mfa_recovery_started(username: str, email: str) -> bool: body = ( f"Recuperação de 2FA iniciada no Ligbox Ops Desk\n\n" f"Utilizador: {username}\n" f"E-mail de verificação: {email}\n\n" f"Se não foi você, contacte o root imediatamente.\n" ) return send_email( ROOT_NOTIFY_EMAIL, f"[Ligbox Ops] Recuperação 2FA: {username}", body, ) def notify_mfa_recovery_completed(username: str) -> bool: body = ( f"Recuperação de 2FA concluída no Ligbox Ops Desk\n\n" f"Utilizador: {username}\n" f"Novo autenticador configurado e códigos de backup gerados.\n" ) return send_email( ROOT_NOTIFY_EMAIL, f"[Ligbox Ops] 2FA reconfigurado: {username}", body, ) def notify_admin_2fa_reset(target_username: str, target_email: str, admin_username: str) -> bool: body = ( f"O administrador {admin_username} resetou o 2FA da conta:\n\n" f"Utilizador: {target_username}\n" f"E-mail: {target_email}\n\n" f"O utilizador pode entrar só com senha e reconfigurar o autenticador em:\n" f"{DESK_PUBLIC_URL}/login.html\n" f"(Perdi acesso ao autenticador)\n" ) send_email(target_email, "[Ligbox Ops] 2FA resetado pelo administrador", body) return send_email( ROOT_NOTIFY_EMAIL, f"[Ligbox Ops] Admin resetou 2FA: {target_username}", body, ) def send_backup_codes_email(email: str, codes: list[str]) -> bool: lines = "\n".join(f" • {c}" for c in codes) body = ( f"Códigos de backup — Ligbox Ops Desk\n\n" f"Guarde estes códigos em local seguro. Cada código só pode ser usado uma vez.\n\n" f"{lines}\n\n" f"Use-os no login se perder acesso ao aplicativo autenticador.\n" ) return send_email(email, "[Ligbox Ops] Códigos de backup 2FA", body)