feat(subsonic): per-user encrypted app-password foundation
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>
This commit is contained in:
+18
-1
@@ -4,13 +4,14 @@ import datetime as dt
|
||||
import uuid
|
||||
from dataclasses import dataclass, replace
|
||||
|
||||
from app.domain.entities import Credentials, User
|
||||
from app.domain.entities import Credentials, SubsonicCredentials, User
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Stored:
|
||||
user: User
|
||||
password_hash: str
|
||||
subsonic_password_enc: str | None = None
|
||||
|
||||
|
||||
class InMemoryUserRepository:
|
||||
@@ -61,6 +62,22 @@ class InMemoryUserRepository:
|
||||
async def count(self) -> int:
|
||||
return len(self._by_id)
|
||||
|
||||
async def get_subsonic_credentials_by_username(
|
||||
self, username: str
|
||||
) -> SubsonicCredentials | None:
|
||||
for stored in self._by_id.values():
|
||||
if stored.user.username == username:
|
||||
return SubsonicCredentials(
|
||||
user=stored.user, password_enc=stored.subsonic_password_enc
|
||||
)
|
||||
return None
|
||||
|
||||
async def get_subsonic_password_enc(self, user_id: uuid.UUID) -> str | None:
|
||||
return self._by_id[user_id].subsonic_password_enc
|
||||
|
||||
async def set_subsonic_password_enc(self, user_id: uuid.UUID, password_enc: str) -> None:
|
||||
self._by_id[user_id].subsonic_password_enc = password_enc
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Token:
|
||||
|
||||
Reference in New Issue
Block a user