Include Loyalty/Communication/Sports Center backends and registry updates alongside production nginx and compose wiring. Co-authored-by: Cursor <cursoragent@cursor.com>
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""Security tests — auth required and error hygiene."""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from httpx import ASGITransport, AsyncClient
|
|
|
|
from app.core.config import settings
|
|
from app.core.security import get_current_user
|
|
from app.main import app
|
|
from app.tests.conftest import TENANT_A, tenant_headers
|
|
from shared.exceptions import UnauthorizedError
|
|
from shared.security import CurrentUser
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unauthenticated_denied_when_auth_required(db_setup, monkeypatch):
|
|
monkeypatch.setattr(settings, "auth_required", True)
|
|
|
|
async def _deny():
|
|
raise UnauthorizedError("توکن احراز هویت ارائه نشده است")
|
|
|
|
app.dependency_overrides[get_current_user] = _deny
|
|
try:
|
|
transport = ASGITransport(app=app)
|
|
async with AsyncClient(transport=transport, base_url="http://testserver") as ac:
|
|
resp = await ac.get(
|
|
"/api/v1/programs", headers=tenant_headers(TENANT_A)
|
|
)
|
|
assert resp.status_code == 401
|
|
body = resp.json()
|
|
assert "error" in body
|
|
assert "traceback" not in str(body).lower()
|
|
assert "superapp_password" not in str(body)
|
|
finally:
|
|
app.dependency_overrides.pop(get_current_user, None)
|
|
monkeypatch.setattr(settings, "auth_required", False)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_permission_denied_for_viewer_role(db_setup, monkeypatch):
|
|
monkeypatch.setattr(settings, "auth_required", True)
|
|
|
|
async def _viewer():
|
|
return CurrentUser(user_id="viewer", roles=["tenant_viewer"])
|
|
|
|
app.dependency_overrides[get_current_user] = _viewer
|
|
try:
|
|
transport = ASGITransport(app=app)
|
|
async with AsyncClient(transport=transport, base_url="http://testserver") as ac:
|
|
resp = await ac.post(
|
|
"/api/v1/programs",
|
|
json={"code": "SEC", "name": "Secured"},
|
|
headers=tenant_headers(TENANT_A),
|
|
)
|
|
assert resp.status_code == 403
|
|
assert resp.json()["error"]["code"] in {"forbidden", "permission_denied"}
|
|
finally:
|
|
app.dependency_overrides.pop(get_current_user, None)
|
|
monkeypatch.setattr(settings, "auth_required", False)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_tenant_header(client):
|
|
resp = await client.get(
|
|
"/api/v1/programs", headers={"X-Tenant-ID": "not-a-uuid"}
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.json()["error"]["code"] == "invalid_tenant_id"
|