feat(email): generic webhook for all notification emails, hide SMTP UI
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
- Renamed the password-setup webhook (email_webhook.*, /system/email-webhook-config) and made it the single transport for all notification emails. - New webhook_email_service.py shared by password_setup_service and notification_service. - Wired test validated/rejected, campaign completed, and new-MITRE-technique notifications through the webhook (previously dead SMTP code, never triggered). - Added POST /system/email-webhook-test to send a real test email. - Hid the Email/SMTP settings tab from the UI (SMTP code kept intact, unused). - Redact email_webhook.api_key on config export.
This commit is contained in:
@@ -41,6 +41,7 @@ _REDACTED_KEYS = {
|
||||
"smtp.password",
|
||||
"jira.admin_api_token",
|
||||
"tempo.admin_token",
|
||||
"email_webhook.api_key",
|
||||
# Older/alternate key names kept defensively in case a prior schema
|
||||
# version wrote under these instead.
|
||||
"jira.api_token",
|
||||
|
||||
@@ -344,60 +344,93 @@ def update_jira_config(
|
||||
)
|
||||
|
||||
|
||||
class PasswordWebhookConfigOut(BaseModel):
|
||||
class EmailWebhookConfigOut(BaseModel):
|
||||
configured: bool
|
||||
url: str
|
||||
api_key_set: bool
|
||||
|
||||
|
||||
class PasswordWebhookConfigUpdate(BaseModel):
|
||||
class EmailWebhookConfigUpdate(BaseModel):
|
||||
url: str
|
||||
# Optional — omit or send "" to leave the currently-stored key unchanged.
|
||||
api_key: Optional[str] = None
|
||||
|
||||
|
||||
@router.get("/password-webhook-config", response_model=PasswordWebhookConfigOut)
|
||||
def get_password_webhook_config(
|
||||
class EmailWebhookTestRequest(BaseModel):
|
||||
to: str
|
||||
|
||||
|
||||
@router.get("/email-webhook-config", response_model=EmailWebhookConfigOut)
|
||||
def get_email_webhook_config(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_role("admin")),
|
||||
):
|
||||
"""Return the configured webhook URL used to email set-password links.
|
||||
"""Return the configured webhook used to send every platform notification
|
||||
email (password setup/reset, test validated, campaign completed, etc.).
|
||||
|
||||
The API key itself is never returned, only whether one is set.
|
||||
|
||||
**Requires** the ``admin`` role.
|
||||
"""
|
||||
from app.services.password_setup_service import get_password_webhook_api_key, get_password_webhook_url
|
||||
from app.services.webhook_email_service import get_webhook_api_key, get_webhook_url
|
||||
|
||||
url = get_password_webhook_url(db) or ""
|
||||
api_key_set = bool(get_password_webhook_api_key(db))
|
||||
return PasswordWebhookConfigOut(configured=bool(url), url=url, api_key_set=api_key_set)
|
||||
url = get_webhook_url(db) or ""
|
||||
api_key_set = bool(get_webhook_api_key(db))
|
||||
return EmailWebhookConfigOut(configured=bool(url), url=url, api_key_set=api_key_set)
|
||||
|
||||
|
||||
@router.patch("/password-webhook-config", response_model=PasswordWebhookConfigOut)
|
||||
def update_password_webhook_config(
|
||||
payload: PasswordWebhookConfigUpdate,
|
||||
@router.patch("/email-webhook-config", response_model=EmailWebhookConfigOut)
|
||||
def update_email_webhook_config(
|
||||
payload: EmailWebhookConfigUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_role("admin")),
|
||||
):
|
||||
"""Set the webhook URL (and optionally API key) used to email set-password links.
|
||||
"""Set the webhook URL (and optionally API key) used to send all
|
||||
platform notification emails.
|
||||
|
||||
**Requires** the ``admin`` role.
|
||||
"""
|
||||
from app.services.password_setup_service import (
|
||||
get_password_webhook_api_key,
|
||||
get_password_webhook_url,
|
||||
set_password_webhook_api_key,
|
||||
set_password_webhook_url,
|
||||
from app.services.webhook_email_service import (
|
||||
get_webhook_api_key,
|
||||
get_webhook_url,
|
||||
set_webhook_api_key,
|
||||
set_webhook_url,
|
||||
)
|
||||
|
||||
set_password_webhook_url(db, payload.url)
|
||||
set_webhook_url(db, payload.url)
|
||||
if payload.api_key:
|
||||
set_password_webhook_api_key(db, payload.api_key)
|
||||
set_webhook_api_key(db, payload.api_key)
|
||||
db.commit()
|
||||
url = get_password_webhook_url(db) or ""
|
||||
api_key_set = bool(get_password_webhook_api_key(db))
|
||||
return PasswordWebhookConfigOut(configured=bool(url), url=url, api_key_set=api_key_set)
|
||||
url = get_webhook_url(db) or ""
|
||||
api_key_set = bool(get_webhook_api_key(db))
|
||||
return EmailWebhookConfigOut(configured=bool(url), url=url, api_key_set=api_key_set)
|
||||
|
||||
|
||||
@router.post("/email-webhook-test")
|
||||
def test_email_webhook(
|
||||
payload: EmailWebhookTestRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_role("admin")),
|
||||
) -> dict:
|
||||
"""Send a real test notification through the configured email webhook.
|
||||
|
||||
**Requires** the ``admin`` role.
|
||||
"""
|
||||
from app.services.webhook_email_service import send_webhook_email
|
||||
|
||||
sent = send_webhook_email(
|
||||
db,
|
||||
to=payload.to,
|
||||
subject="Aegis Email Webhook Test",
|
||||
message=(
|
||||
"This is a test notification confirming the email webhook is "
|
||||
"configured and working correctly."
|
||||
),
|
||||
full_name=current_user.full_name,
|
||||
)
|
||||
if not sent:
|
||||
raise HTTPException(status_code=502, detail="Failed to send — check the webhook URL/API key and logs.")
|
||||
return {"detail": f"Test email sent to {payload.to}"}
|
||||
|
||||
|
||||
@router.post("/jira-test")
|
||||
|
||||
Reference in New Issue
Block a user