shinkan-jinkendo/backend/tests/test_exercise_media_download.py
Lars b752883392
All checks were successful
Deploy Development / deploy (push) Successful in 41s
Test Suite / pytest-backend (push) Successful in 23s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 25s
feat: enhance media access and security for exercises
- Updated PostgreSQL binding in docker-compose to restrict access to localhost only.
- Implemented a new API endpoint for secure media file delivery, requiring authentication via token.
- Enhanced governance checks for exercise media access, ensuring only authorized users can retrieve files.
- Updated frontend components to utilize the new media file access method, improving user experience while maintaining security.
- Documented changes in production readiness audit and access layer endpoint audit for clarity on security enhancements.
2026-05-07 10:52:14 +02:00

76 lines
2.2 KiB
Python

"""
Geschützte Übungs-Mediendatei: Auth + Governance (kein anonymes /media/).
"""
from __future__ import annotations
import os
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
os.environ.setdefault("SKIP_DB_MIGRATE", "1")
from auth import require_auth_flexible
from main import app
from tenant_context import TenantContext, get_tenant_context_flexible
@pytest.fixture
def client() -> TestClient:
return TestClient(app)
@pytest.fixture(autouse=True)
def _clear_overrides():
yield
app.dependency_overrides.pop(require_auth_flexible, None)
app.dependency_overrides.pop(get_tenant_context_flexible, None)
def test_exercise_media_file_unauthenticated_401(client: TestClient) -> None:
r = client.get("/api/exercises/1/media/2/file")
assert r.status_code == 401
def test_exercise_media_file_forbidden_when_not_visible(client: TestClient) -> None:
app.dependency_overrides[get_tenant_context_flexible] = lambda: TenantContext(
profile_id=22,
global_role="trainer",
effective_club_id=None,
club_ids=frozenset(),
memberships=[],
)
mock_cm = MagicMock()
mock_conn = MagicMock()
mock_cm.__enter__.return_value = mock_conn
mock_cm.__exit__.return_value = False
mock_cur = MagicMock()
mock_cur.fetchone.return_value = {
"id": 3,
"visibility": "private",
"club_id": None,
"created_by": 99,
}
with patch("routers.exercises.get_db", return_value=mock_cm), patch(
"routers.exercises.get_cursor", return_value=mock_cur
), patch("routers.exercises.library_content_visible_to_profile", return_value=False):
r = client.get("/api/exercises/3/media/2/file", headers={"X-Auth-Token": "t"})
assert r.status_code == 403
def test_get_maturity_model_requires_admin(client: TestClient) -> None:
from auth import require_auth
def _user() -> dict:
return {"profile_id": 1, "role": "trainer"}
app.dependency_overrides[require_auth] = _user
try:
r = client.get("/api/maturity-models/1", headers={"X-Auth-Token": "x"})
assert r.status_code == 403
finally:
app.dependency_overrides.pop(require_auth, None)