Files
Aurora-Core/app/models/__init__.py
T
NixReaperandClaude Sonnet 5 7bee596a0e
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Add user accounts: registration, login, and JWT auth
The first authenticated-user concept in Aurora Core -- everything else in
this codebase is public, read-only catalog data. Foundation for ratings
and a watchlist (not yet built): POST /auth/register, POST /auth/login,
GET /auth/me.

Security specifics worth knowing:
- Passwords hashed with argon2 (via argon2-cffi), never stored plain or
  logged.
- Login returns an identical 401 for a nonexistent email and a wrong
  password on a real one, including matching response timing
  (burn_time_like_a_password_check) -- otherwise either the status code
  or the timing alone lets a client enumerate registered emails.
- AURORA_SECRET_KEY now actually signs JWTs (previously defined but
  unused anywhere) -- .env.example's existing token_urlsafe(48) guidance
  already produces a long enough key, just flagged its new stakes.
- Email matched case-insensitively via explicit normalization in the
  service layer, not relied on from Pydantic's EmailStr (which does not
  lowercase the local part).

12 new integration tests (register/login/me over real HTTP via the
`client` fixture) plus 10 unit tests for the hashing/JWT logic itself,
following the same-day lesson that only unit tests actually execute
locally without Postgres -- the auth core is tested for real here rather
than only through integration tests that might not run before a deploy.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 06:15:39 -05:00

83 lines
2.1 KiB
Python

"""SQLAlchemy models.
Import every model module here so `Base.metadata` is fully populated for
Alembic autogeneration and `Base.metadata.create_all()` (used only in
tests) regardless of which module happens to trigger the import first.
"""
from app.models.artwork import Artwork
from app.models.base import Base
from app.models.collection import Collection, CollectionMovie, CollectionRelation, CollectionSeries
from app.models.company import Company, MovieCompany, SeriesCompany
from app.models.credit import Credit
from app.models.external_id import ExternalID
from app.models.genre import Genre, MovieGenre, SeriesGenre
from app.models.keyword import Keyword, MovieKeyword, SeriesKeyword
from app.models.language_country import Certification, Country, Language
from app.models.movie import (
Movie,
MovieAlternateTitle,
MovieProductionCountry,
MovieRecommendation,
MovieSpokenLanguage,
ReleaseDate,
)
from app.models.person import Person
from app.models.provider import IngestionJob, Provider, ProviderRecord
from app.models.trending import TrendingEntry
from app.models.tv import (
Episode,
Season,
Series,
SeriesAlternateTitle,
SeriesProductionCountry,
SeriesSpokenLanguage,
)
from app.models.user import User
from app.models.video import Video
from app.models.watch_provider import WatchProvider, WatchProviderOffering
__all__ = [
"Base",
"Artwork",
"Collection",
"CollectionMovie",
"CollectionSeries",
"CollectionRelation",
"Company",
"MovieCompany",
"SeriesCompany",
"Credit",
"ExternalID",
"Genre",
"MovieGenre",
"SeriesGenre",
"Keyword",
"MovieKeyword",
"SeriesKeyword",
"Language",
"Country",
"Certification",
"Movie",
"MovieAlternateTitle",
"MovieSpokenLanguage",
"MovieProductionCountry",
"ReleaseDate",
"MovieRecommendation",
"Person",
"Provider",
"ProviderRecord",
"IngestionJob",
"Series",
"SeriesAlternateTitle",
"SeriesSpokenLanguage",
"SeriesProductionCountry",
"Season",
"Episode",
"Video",
"WatchProvider",
"WatchProviderOffering",
"TrendingEntry",
"User",
]