docs: add AI code generation requirements and comprehensive Google-style docstrings

- Add AI code generation requirements to AGENTS.md
- Add module-level docstrings to all 46 Python modules
- Add detailed Google-style docstrings to all classes and functions
- Remove all inline comments following self-documenting code principle
- Include Args, Returns, Raises sections in function docstrings
- Add Attributes and Examples sections to class docstrings
This commit is contained in:
2026-05-02 13:15:21 +03:00
parent 6a528bcbb9
commit ca4e8877a5
52 changed files with 2043 additions and 304 deletions

View File

@@ -1,4 +1,8 @@
"""Authentication infrastructure package."""
"""Authentication infrastructure package.
This module provides Keycloak authentication client and models
for token validation and user info retrieval.
"""
from app.infrastructure.auth.client import KeycloakAuthClient
from app.infrastructure.auth.models import KeycloakUser, TokenInfo

View File

@@ -1,4 +1,8 @@
"""Keycloak authentication client."""
"""Keycloak authentication client.
This module provides a client for Keycloak authentication operations
including token introspection and user info retrieval.
"""
import time
@@ -9,10 +13,30 @@ from app.infrastructure.config.settings import Settings
class KeycloakAuthClient:
"""Client for Keycloak authentication operations."""
"""Client for Keycloak authentication operations.
Handles token validation via introspection and user info retrieval.
Implements token caching to reduce Keycloak server load.
Attributes:
_settings: Application settings with Keycloak config.
_base_url: Keycloak realm base URL.
_client_id: OAuth client identifier.
_client_secret: OAuth client secret.
_cache: Token info cache for performance.
_cache_ttl: Cache time-to-live in seconds.
Example:
>>> client = KeycloakAuthClient(settings)
>>> token_info = await client.introspect_token(token)
"""
def __init__(self, settings: Settings) -> None:
"""Initialize Keycloak client with settings."""
"""Initialize Keycloak client with settings.
Args:
settings: Application settings with Keycloak configuration.
"""
self._settings = settings
self._base_url = f"{settings.kc.server_url}/realms/{settings.kc.realm}"
self._client_id = settings.kc.client_id
@@ -21,15 +45,30 @@ class KeycloakAuthClient:
self._cache_ttl = settings.kc.token_cache_ttl
def _get_introspection_url(self) -> str:
"""Get token introspection endpoint URL."""
"""Get token introspection endpoint URL.
Returns:
Full URL for token introspection endpoint.
"""
return f"{self._base_url}/protocol/openid-connect/token/introspection"
def _get_userinfo_url(self) -> str:
"""Get userinfo endpoint URL."""
"""Get userinfo endpoint URL.
Returns:
Full URL for userinfo endpoint.
"""
return f"{self._base_url}/protocol/openid-connect/userinfo"
def _get_cached_token(self, token: str) -> TokenInfo | None:
"""Get cached token info if valid."""
"""Get cached token info if valid.
Args:
token: Access token string.
Returns:
Cached TokenInfo if valid and not expired, None otherwise.
"""
if token not in self._cache:
return None
@@ -41,9 +80,13 @@ class KeycloakAuthClient:
return token_info
def _cache_token(self, token: str, token_info: TokenInfo) -> None:
"""Cache token info."""
"""Cache token info.
Args:
token: Access token string as cache key.
token_info: TokenInfo to cache.
"""
self._cache[token] = (token_info, time.time())
# Simple cleanup of old entries
current_time = time.time()
expired_keys = [
k for k, (_, t) in self._cache.items() if current_time - t > self._cache_ttl
@@ -52,13 +95,21 @@ class KeycloakAuthClient:
del self._cache[k]
async def introspect_token(self, token: str) -> TokenInfo:
"""Introspect access token using Keycloak."""
# Check cache first
"""Introspect access token using Keycloak.
Validates token with Keycloak server and extracts user information.
Uses cache to reduce server requests for recently validated tokens.
Args:
token: Access token to validate.
Returns:
TokenInfo with validation result and user claims.
"""
cached = self._get_cached_token(token)
if cached:
return cached
# Prepare introspection request
data = {
"token": token,
"client_id": self._client_id,
@@ -81,7 +132,6 @@ class KeycloakAuthClient:
if not result.get("active", False):
return TokenInfo(active=False, raw_claims=result)
# Extract roles from realm_access or resource_access
roles: list[str] = []
realm_access = result.get("realm_access", {})
if isinstance(realm_access, dict):
@@ -96,13 +146,21 @@ class KeycloakAuthClient:
raw_claims=result,
)
# Cache valid token
self._cache_token(token, token_info)
return token_info
async def get_userinfo(self, token: str) -> KeycloakUser | None:
"""Get user information from Keycloak using access token."""
"""Get user information from Keycloak using access token.
Fetches detailed user profile from Keycloak userinfo endpoint.
Args:
token: Valid access token.
Returns:
KeycloakUser with profile data, or None on error.
"""
try:
async with httpx.AsyncClient() as client:
response = await client.get(

View File

@@ -1,4 +1,8 @@
"""Keycloak authentication models."""
"""Keycloak authentication models.
This module defines data models for Keycloak authentication data
including token info and user profiles.
"""
from dataclasses import dataclass, field
from typing import Any
@@ -6,7 +10,24 @@ from typing import Any
@dataclass(frozen=True)
class TokenInfo:
"""Information about validated token from Keycloak."""
"""Information about validated token from Keycloak.
Contains the result of token introspection including user claims
and role information.
Attributes:
active: Whether the token is active and valid.
user_id: Subject identifier from token.
username: Username from token claims.
email: Email from token claims.
roles: List of roles from token.
raw_claims: Complete raw claims from token.
Example:
>>> token_info = TokenInfo(active=True, user_id="123", roles=["user"])
>>> if token_info.is_valid:
... grant_access()
"""
active: bool
user_id: str = ""
@@ -17,13 +38,32 @@ class TokenInfo:
@property
def is_valid(self) -> bool:
"""Check if token is valid and active."""
"""Check if token is valid and active.
Returns:
True if token is active and has user_id.
"""
return self.active and bool(self.user_id)
@dataclass(frozen=True)
class KeycloakUser:
"""User information from Keycloak."""
"""User information from Keycloak.
Contains user profile data from Keycloak userinfo endpoint.
Attributes:
id: User subject identifier.
username: Username.
email: Email address.
first_name: First name.
last_name: Last name.
roles: List of user roles.
is_active: Whether user account is active.
Example:
>>> user = KeycloakUser(id="123", username="john", email="john@example.com")
"""
id: str
username: str