7a17e3babd
Subsonic auth (t=md5(password+salt), legacy p=) needs a recoverable secret, but login passwords are stored as a one-way argon2 hash. Add a separate, per-user app-password: high-entropy, random, and encrypted at rest with a Fernet key derived from SUBSONIC_SECRET_KEY (never stored in the DB). - SubsonicPasswordCipher + generate_subsonic_password in core.security - users.subsonic_password_enc column (+ Alembic migration), repo + port methods - SubsonicAuthService: verify (t+s / p / p=enc:) and rotate/reveal lifecycle - self-service GET/POST /users/me/subsonic-password + admin rotate endpoint - domain SubsonicCredentials + SubsonicCipher port; deps wiring Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
33 lines
836 B
Python
33 lines
836 B
Python
"""subsonic: per-user encrypted app-password
|
|
|
|
Revision ID: 20260608_subsonic_pw
|
|
Revises: 20260608_storage_uri
|
|
Create Date: 2026-06-08 12:00:00.000000
|
|
|
|
Adds ``users.subsonic_password_enc`` — the recoverable, Fernet-encrypted
|
|
Subsonic app-password (plan §7). NULL until the user generates one.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "20260608_subsonic_pw"
|
|
down_revision: str | None = "20260608_storage_uri"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"users",
|
|
sa.Column("subsonic_password_enc", sa.String(length=255), nullable=True),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("users", "subsonic_password_enc")
|