The session held a wallet of up to six accounts, and a request picked one by a header or an ?account= parameter. It now holds one: user_id and its epoch. A sign-in replaces whoever was signed in, sign-out and account deletion end the session, and an account refused mid-session (revoked, suspended, deactivated) clears it. /api/me/accounts and /api/me/switch are gone, and with them their refusal codes and the UI copy for them. A cookie the multi-account build wrote carries no single epoch and is refused: its holder signs in once more. A Google link finishing in a browser now signed in as someone else is refused, as before for an account outside the wallet. A test holds that no app code reads the wallet keys or the account header again.
46 lines
1.7 KiB
Python
46 lines
1.7 KiB
Python
"""The subtitle routes must refuse a session the rest of the app refuses.
|
|
|
|
They used to sit behind `_require_session`, the cookie-only gate the poster proxies use — a signed
|
|
cookie was enough, with none of `current_user`'s checks (is_active, is_suspended, the session epoch
|
|
that "log out everywhere" bumps). Fine for a poster; not for a route that starts reads of the media
|
|
library. Found by a 2026-09 code review; the gate itself pre-dates that review.
|
|
"""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
import app.auth as auth
|
|
import app.state as app_state
|
|
from app.db import get_db
|
|
from app.main import app
|
|
from app.plex import subtitles
|
|
from app.routes.plex import playback
|
|
|
|
|
|
class _NoPool:
|
|
def __init__(self) -> None:
|
|
self.jobs: list = []
|
|
|
|
def submit(self, fn, *args) -> None:
|
|
self.jobs.append((fn, args))
|
|
|
|
|
|
def test_a_suspended_account_cannot_drive_the_subtitle_extractor(
|
|
db, make_user, override_dep, monkeypatch, tmp_path
|
|
):
|
|
user = make_user(is_suspended=True)
|
|
override_dep(get_db, db.info["request"])
|
|
monkeypatch.setattr(app_state, "_configured_cache", True)
|
|
# A validly SIGNED cookie for that account — which is all the old gate looked at.
|
|
for mod in (auth, playback):
|
|
monkeypatch.setattr(mod, "session_user_id", lambda request: user.id)
|
|
monkeypatch.setattr(subtitles, "CACHE_DIR", tmp_path)
|
|
pool = _NoPool()
|
|
monkeypatch.setattr(subtitles, "_WIN_POOL", pool)
|
|
monkeypatch.setattr(subtitles, "_FULL_POOL", pool)
|
|
subtitles.reset_state()
|
|
|
|
client = TestClient(app)
|
|
assert client.get("/api/plex/subtitle/98614/0/status?at=600").status_code == 401
|
|
assert client.get("/api/plex/subtitle/98614/0").status_code == 401
|
|
assert pool.jobs == [], "a refused session still scheduled an extraction"
|