Files
ERP/backend/app/utils/security.py
T
MMD e1495afee9 fix(auth): auth deps use their own DB session; smoke suites made dataset-independent
RUNTIME BUG (caught by live smoke tests): the global auth dependency
queried the request-scoped session, auto-beginning its transaction — any
endpoint opening 'async with db.begin()' afterwards crashed with
'transaction already begun' (POST /materials 500). get_current_user /
get_actor now resolve in a short-lived session (expire_on_commit=False
keeps the context usable detached); inventory/pm scopes keep passing the
request session explicitly.

Smoke suites rewritten against CURRENT contracts:
- assertions are behavior-based (shapes, envelopes, filters), no magic
  seed counts; dynamic lookups instead of hard-coded names/ids
- rerun-tolerant: tolerate 409-on-create by reusing prior artifacts
- aligned with current rules: revert blocked after inventory receipts
  (v2.6), attachments allowed in every status, termsheets optional at
  quote level
- SMOKE_USER/SMOKE_PASSWORD env for login (default tester/123456)

Verified on a fresh volume: migration + audit_log bootstrap, Casdoor
seed (bcrypt), demo data, and 29+76+61 smoke checks all green.
2026-09-19 00:49:15 +03:30

34 lines
1.2 KiB
Python

"""Attribution for quote_events.actor — the VERIFIED user, not the raw token.
Historically this stored the bearer token string verbatim (unverifiable and
a token leak into the database). All routes that use it now sit behind the
global JWT dependency, so the token is resolved to the authenticated user
and the display name is recorded instead.
"""
from fastapi import Request
from app.auth import get_optional_user
async def get_actor(request: Request) -> str | None:
"""Resolve the caller's display name from the Casdoor Bearer token.
Uses its own session (get_optional_user default) — same reason as
get_current_user: quote routers open explicit db.begin() blocks.
"""
ctx = await get_optional_user(request)
if ctx is not None:
return ctx.display_name
# Dev-mode header fallback (only active when DEV_LOGIN_ENABLED) carries
# the name in X-User-Name / X-User-Name-B64 — keep attribution useful.
name = request.headers.get("x-user-name")
import base64
b64 = request.headers.get("x-user-name-b64")
if b64:
try:
name = base64.b64decode(b64.encode()).decode("utf-8")
except Exception:
pass
return name