49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
"""outgoing system mail (password reset) configured in the admin panel
|
|||
|
|
|
||
|
|
The server sends its own mail — today only password-reset links — through an
|
||
|
|
SMTP account the administrator sets up in the admin panel. The password is
|
||
|
|
envelope-encrypted with MASTER_KEY like every mail-account secret. ``public_url``
|
||
|
|
is where users reach the app; reset links are built from it, never from the
|
||
|
|
request's Host header, which an attacker controls.
|
||
|
|
|
||
|
|
Revision ID: 0020
|
||
|
|
Revises: 0019
|
||
|
|
Create Date: 2026-09-30
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from collections.abc import Sequence
|
||
|
|
|
||
|
|
from alembic import op
|
||
|
|
|
||
|
|
revision: str = "0020"
|
||
|
|
down_revision: str | None = "0019"
|
||
|
|
branch_labels: str | Sequence[str] | None = None
|
||
|
|
depends_on: str | Sequence[str] | None = None
|
||
|
|
|
||
|
|
_COLUMNS = (
|
||
|
|
("smtp_host", "text NOT NULL DEFAULT ''"),
|
||
|
|
("smtp_port", "integer NOT NULL DEFAULT 587"),
|
||
|
|
("smtp_security", "text NOT NULL DEFAULT 'starttls'"),
|
||
|
|
("smtp_username", "text NOT NULL DEFAULT ''"),
|
||
|
|
("smtp_secret_ct", "bytea"),
|
||
|
|
("smtp_from", "text NOT NULL DEFAULT ''"),
|
||
|
|
("public_url", "text NOT NULL DEFAULT ''"),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def upgrade() -> None:
|
||
|
|
for name, ddl in _COLUMNS:
|
||
|
|
op.execute(f"ALTER TABLE app_settings ADD COLUMN {name} {ddl}")
|
||
|
|
op.execute(
|
||
|
|
"ALTER TABLE app_settings ADD CONSTRAINT ck_app_settings_smtp_security "
|
||
|
|
"CHECK (smtp_security IN ('starttls', 'ssl', 'none'))"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def downgrade() -> None:
|
||
|
|
op.execute("ALTER TABLE app_settings DROP CONSTRAINT IF EXISTS ck_app_settings_smtp_security")
|
||
|
|
for name, _ in reversed(_COLUMNS):
|
||
|
|
op.execute(f"ALTER TABLE app_settings DROP COLUMN IF EXISTS {name}")
|