111 lines
4.5 KiB
Python
111 lines
4.5 KiB
Python
"""管理端点鉴权(require_admin)测试。
|
||
|
||
覆盖四种情形:ADMIN_TOKEN 未配置 → 503(fail-closed,避免裸奔);已配置但缺凭证 /
|
||
凭证错误 → 401;正确凭证放行。探针端点用 ``/api/v1/admin/traces``(不依赖沙箱运行时,
|
||
测试环境可正常返回,便于判定鉴权层是否放行)。
|
||
"""
|
||
|
||
from collections.abc import AsyncGenerator
|
||
|
||
import pytest
|
||
from httpx import ASGITransport, AsyncClient
|
||
|
||
from app.main import app
|
||
|
||
_PROBE = "/api/v1/admin/traces"
|
||
|
||
|
||
@pytest.fixture
|
||
async def client() -> AsyncGenerator[AsyncClient, None]:
|
||
transport = ASGITransport(app=app)
|
||
async with AsyncClient(transport=transport, base_url="http://test") as http_client:
|
||
yield http_client
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_unconfigured_returns_503(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""ADMIN_TOKEN 与 JWT_SECRET 均未配置时管理端点 fail-closed(503)。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.admin_token", "")
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "")
|
||
resp = await client.get(_PROBE)
|
||
assert resp.status_code == 503
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_missing_credential_returns_401(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""已配置 ADMIN_TOKEN 但请求未带 Authorization 头 → 401。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.admin_token", "secret-token")
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "")
|
||
resp = await client.get(_PROBE)
|
||
assert resp.status_code == 401
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_wrong_credential_returns_401(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""Authorization 凭证错误 → 401。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.admin_token", "secret-token")
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "")
|
||
resp = await client.get(_PROBE, headers={"Authorization": "Bearer wrong"})
|
||
assert resp.status_code == 401
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_correct_credential_passes(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""正确凭证放行:鉴权层不再拦截。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.admin_token", "secret-token")
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "")
|
||
resp = await client.get(_PROBE, headers={"Authorization": "Bearer secret-token"})
|
||
assert resp.status_code != 401
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════
|
||
# JWT 模式测试
|
||
# ═══════════════════════════════════════════════════════════════
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_jwt_admin_user_passes(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""拥有 is_admin 标记的用户 JWT 放行 /admin/*。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "test-jwt-secret")
|
||
from app.db.session import AsyncSessionLocal
|
||
from app.services.auth import create_access_token, create_user
|
||
|
||
async with AsyncSessionLocal() as session:
|
||
user = await create_user(
|
||
session, username="adminuser", email="admin@test.com", password="pass", is_admin=True
|
||
)
|
||
await session.commit()
|
||
token = create_access_token(subject=user.id, extra={"owner_scope": user.id})
|
||
resp = await client.get(_PROBE, headers={"Authorization": f"Bearer {token}"})
|
||
assert resp.status_code != 401
|
||
assert resp.status_code != 403
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_admin_jwt_non_admin_returns_403(
|
||
client: AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||
) -> None:
|
||
"""普通用户 JWT 访问 /admin/* → 403。"""
|
||
monkeypatch.setattr("app.api.v1.deps.settings.jwt_secret", "test-jwt-secret")
|
||
from app.db.session import AsyncSessionLocal
|
||
from app.services.auth import create_access_token, create_user
|
||
|
||
async with AsyncSessionLocal() as session:
|
||
user = await create_user(
|
||
session, username="regular", email="regular@test.com", password="pass", is_admin=False
|
||
)
|
||
await session.commit()
|
||
token = create_access_token(subject=user.id, extra={"owner_scope": user.id})
|
||
resp = await client.get(_PROBE, headers={"Authorization": f"Bearer {token}"})
|
||
assert resp.status_code == 403
|