Skip to content

fix(license): answer 503 before Keygen when SMTP config is invalid - #2047

Merged
MODSetter merged 1 commit into
MODSetter:devfrom
mehul2409:fix/1963-smtp-config-guard
Sep 29, 2026
Merged

MODSetter merged 1 commit into
MODSetter:devfrom
mehul2409:fix/1963-smtp-config-guard

Conversation

@mehul2409

@mehul2409 mehul2409 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What

_require_mailer() now builds the mailer after the SMTP_ENABLED check and answers 503 "License email delivery is not configured." when the SMTP configuration is invalid. Both POST /license/trial and POST /license/resend run it before any Keygen call. The portal doc is updated and its Known gaps line removed.

Why

A bad SMTP_* setting only raised ValueError at the first send, after /license/trial had already created and checked out the trial in Keygen. The caller got a 500 and their address was spent (a retry answers 409).

The decision the issue asks for: 503 inside _require_mailer(), not a failed startup. The same process serves the scraper API and the export window, so one mail typo shouldn't take every route down, and the license routes already turn every unavailable-mail case into a 503. The check still runs before any Keygen lookup, so the answer stays outcome-independent. One side effect: on a misconfigured deployment /license/resend used to answer 503 for customers (a send was attempted) and 200 for everyone else; it now answers 503 either way.

The two error types in protocol.py are untouched. The misconfiguration is logged with its ValueError, so the operator sees which setting is wrong.

Fixes #1963

How to test

tests/unit/license/test_mail_guard.py drives both routes through the real router, with Keygen mocked at its HTTP transport:

  • mail enabled, SMTP_HOST missing: /license/trial answers 503 and Keygen gets no request (fails on dev)
  • same for /license/resend (fails on dev)
  • a valid configuration still reaches Keygen
cd surfsense_backend
uv run pytest -m unit
uv run ruff check app/license tests/unit/license
python scripts/check_docs.py

I also ran the real backend (Postgres and Redis from docker/docker-compose.deps-only.yml, a local stand-in for Keygen, Mailpit for SMTP):

/license/trial /license/resend Keygen calls
dev, SMTP_ENABLED=TRUE, empty SMTP_HOST 500 200 4 (trial created)
this branch, same config 503 503 0
this branch, valid SMTP (Mailpit) 200, mail delivered with surfsense.lic 200 as before

Local unit run: 3399 passed. The one failure, tests/unit/knowledge_store/test_naming_byte_length.py::test_derived_name_is_writable_where_raw_title_is_not, fails on dev as well on macOS, where APFS accepts the long filename.

🤖 Generated with Claude Code

High-level PR Summary

This PR fixes a bug where invalid SMTP configuration caused /license/trial to create a trial license in Keygen before failing to send the email, resulting in a spent trial address. The fix moves SMTP configuration validation into _require_mailer() so both /license/trial and /license/resend return 503 before making any Keygen API calls when SMTP is misconfigured. This prevents licenses from being created that can never be delivered, while keeping the service operational for non-license routes.

⏱️ Estimated Review Time: 5-15 minutes

💡 Review Order Suggestion
Order File Path
1 surfsense_backend/tests/unit/license/test_mail_guard.py
2 surfsense_backend/app/license/router.py
3 docs/architecture/license/portal.md

Need help? Join our Discord

A bad SMTP_* setting only raised when the mailer was first built, at send
time. /license/trial had already created the trial in Keygen by then, so
the caller got a 500 and their address was spent.

_require_mailer() now builds the mailer as well as checking SMTP_ENABLED,
so a configuration error answers 503 before any Keygen call on both POST
routes. That also stops /license/resend answering differently for
customers and non-customers on a misconfigured deployment. A 503 was
chosen over failing at startup so a mail setting cannot take down every
other route.

Fixes MODSetter#1963

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 29, 2026

Copy link
Copy Markdown

@mehul2409 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 361d1a3c-b9b1-4f42-8cbe-75bd443bb7a8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MODSetter
MODSetter merged commit 91b33f5 into MODSetter:dev Sep 29, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants