Files
blog.pyaqa.ru/tests/api/conftest.py
Sergey Vanyushkin 184b95969c
Some checks failed
ci/woodpecker/pr/lint Pipeline failed
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/type Pipeline was successful
feat(auth): implement Keycloak authentication with RBAC and pagination
Major changes:
- Add Keycloak integration via token introspection endpoint
- Implement RBAC system with roles: admin, user, guest
- Add role-based permissions for post operations
- Add pagination support (default limit: 10) to list endpoints
- Add published_only filter with admin-only override for unpublished posts

Security improvements:
- Remove hardcoded default secrets (SECRET_KEY, KEYCLOAK_CLIENT_SECRET)
- Update .env.example with proper security placeholders
- Add comprehensive RBAC unit tests

Infrastructure:
- Add httpx dependency for HTTP client
- Add KeycloakAuthClient with token caching (TTL: 60s)
- Add role-based dependencies (RequireAdmin, RequireUser, etc.)
- Update DI container with Keycloak provider

Endpoints updated:
- GET /posts: filter by published status (admin can see all)
- Add pagination params (limit, offset) to list endpoints
- Enforce RBAC on post operations

Tests:
- Add 16 auth infrastructure tests
- Add 13 RBAC role tests
- Update existing tests for new required settings

Breaking changes:
- SECRET_KEY and KEYCLOAK_CLIENT_SECRET now required (no defaults)
2026-05-02 11:21:45 +03:00

58 lines
1.5 KiB
Python

"""API test fixtures."""
from typing import AsyncGenerator
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from httpx import ASGITransport, AsyncClient
from app.infrastructure.auth.models import TokenInfo
from app.main import app_factory
@pytest.fixture
def mock_keycloak_client() -> MagicMock:
"""Create mock Keycloak client for testing."""
mock_client = AsyncMock()
mock_client.introspect_token.return_value = TokenInfo(
active=True,
user_id="test-user-id",
username="testuser",
email="test@example.com",
roles=["user"],
)
return mock_client
@pytest.fixture
async def client(mock_keycloak_client: MagicMock) -> AsyncGenerator[AsyncClient, None]:
"""Create async HTTP client for API testing."""
with patch(
"app.presentation.api.deps.KeycloakAuthClient",
return_value=mock_keycloak_client,
):
app = app_factory()
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.fixture
def auth_headers() -> dict[str, str]:
"""Return mock authentication headers."""
return {"Authorization": "Bearer test_token"}
@pytest.fixture
def unauthorized_keycloak_client() -> MagicMock:
"""Create mock Keycloak client that returns invalid token."""
mock_client = AsyncMock()
mock_client.introspect_token.return_value = TokenInfo(
active=False,
user_id="",
username="",
email="",
roles=[],
)
return mock_client