658 lines
16 KiB
Markdown
658 lines
16 KiB
Markdown
|
|
# 🔧 Auth API Reference
|
|||
|
|
|
|||
|
|
## 🎯 Обзор
|
|||
|
|
|
|||
|
|
Полный справочник по API системы аутентификации с примерами кода и использования.
|
|||
|
|
|
|||
|
|
## 📚 Token Managers
|
|||
|
|
|
|||
|
|
### SessionTokenManager
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.tokens.sessions import SessionTokenManager
|
|||
|
|
|
|||
|
|
sessions = SessionTokenManager()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Методы
|
|||
|
|
|
|||
|
|
##### `create_session(user_id, auth_data=None, username=None, device_info=None)`
|
|||
|
|
Создает новую сессию для пользователя.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
- `auth_data` (dict, optional): Данные аутентификации
|
|||
|
|
- `username` (str, optional): Имя пользователя
|
|||
|
|
- `device_info` (dict, optional): Информация об устройстве
|
|||
|
|
|
|||
|
|
**Возвращает:** `str` - JWT токен
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
token = await sessions.create_session(
|
|||
|
|
user_id="123",
|
|||
|
|
username="john_doe",
|
|||
|
|
device_info={"ip": "192.168.1.1", "user_agent": "Mozilla/5.0"}
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `verify_session(token)`
|
|||
|
|
Проверяет валидность JWT токена и Redis сессии.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict | None` - Payload токена или None
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
payload = await sessions.verify_session(token)
|
|||
|
|
if payload:
|
|||
|
|
user_id = payload.get("user_id")
|
|||
|
|
username = payload.get("username")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `validate_session_token(token)`
|
|||
|
|
Валидирует токен сессии с дополнительными проверками.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
|
|||
|
|
**Возвращает:** `tuple[bool, dict]` - (валидность, данные)
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
valid, data = await sessions.validate_session_token(token)
|
|||
|
|
if valid:
|
|||
|
|
print(f"Session valid for user: {data.get('user_id')}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `get_session_data(token, user_id)`
|
|||
|
|
Получает данные сессии из Redis.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict | None` - Данные сессии
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
session_data = await sessions.get_session_data(token, user_id)
|
|||
|
|
if session_data:
|
|||
|
|
last_activity = session_data.get("last_activity")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `refresh_session(user_id, old_token, device_info=None)`
|
|||
|
|
Обновляет сессию пользователя.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
- `old_token` (str): Старый JWT токен
|
|||
|
|
- `device_info` (dict, optional): Информация об устройстве
|
|||
|
|
|
|||
|
|
**Возвращает:** `str` - Новый JWT токен
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
new_token = await sessions.refresh_session(
|
|||
|
|
user_id="123",
|
|||
|
|
old_token=old_token,
|
|||
|
|
device_info={"ip": "192.168.1.1"}
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `revoke_session_token(token)`
|
|||
|
|
Отзывает конкретный токен сессии.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
|
|||
|
|
**Возвращает:** `bool` - Успешность операции
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
revoked = await sessions.revoke_session_token(token)
|
|||
|
|
if revoked:
|
|||
|
|
print("Session revoked successfully")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `get_user_sessions(user_id)`
|
|||
|
|
Получает все активные сессии пользователя.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
|
|||
|
|
**Возвращает:** `list[dict]` - Список сессий
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
user_sessions = await sessions.get_user_sessions("123")
|
|||
|
|
for session in user_sessions:
|
|||
|
|
print(f"Token: {session['token'][:20]}...")
|
|||
|
|
print(f"Last activity: {session['last_activity']}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `revoke_user_sessions(user_id)`
|
|||
|
|
Отзывает все сессии пользователя.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
|
|||
|
|
**Возвращает:** `int` - Количество отозванных сессий
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
revoked_count = await sessions.revoke_user_sessions("123")
|
|||
|
|
print(f"Revoked {revoked_count} sessions")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### OAuthTokenManager
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.tokens.oauth import OAuthTokenManager
|
|||
|
|
|
|||
|
|
oauth = OAuthTokenManager()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Методы
|
|||
|
|
|
|||
|
|
##### `store_oauth_tokens(user_id, provider, access_token, refresh_token=None, expires_in=3600, additional_data=None)`
|
|||
|
|
Сохраняет OAuth токены в Redis.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
- `provider` (str): OAuth провайдер (google, github, etc.)
|
|||
|
|
- `access_token` (str): Access токен
|
|||
|
|
- `refresh_token` (str, optional): Refresh токен
|
|||
|
|
- `expires_in` (int): Время жизни в секундах
|
|||
|
|
- `additional_data` (dict, optional): Дополнительные данные
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
await oauth.store_oauth_tokens(
|
|||
|
|
user_id="123",
|
|||
|
|
provider="google",
|
|||
|
|
access_token="ya29.a0AfH6SM...",
|
|||
|
|
refresh_token="1//04...",
|
|||
|
|
expires_in=3600,
|
|||
|
|
additional_data={"scope": "read write"}
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `get_token(user_id, provider, token_type)`
|
|||
|
|
Получает OAuth токен.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
- `provider` (str): OAuth провайдер
|
|||
|
|
- `token_type` (str): Тип токена ("oauth_access" или "oauth_refresh")
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict | None` - Данные токена
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
access_data = await oauth.get_token("123", "google", "oauth_access")
|
|||
|
|
if access_data:
|
|||
|
|
token = access_data["token"]
|
|||
|
|
expires_in = access_data.get("expires_in")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `revoke_oauth_tokens(user_id, provider)`
|
|||
|
|
Отзывает OAuth токены провайдера.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `user_id` (str): ID пользователя
|
|||
|
|
- `provider` (str): OAuth провайдер
|
|||
|
|
|
|||
|
|
**Возвращает:** `bool` - Успешность операции
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
revoked = await oauth.revoke_oauth_tokens("123", "google")
|
|||
|
|
if revoked:
|
|||
|
|
print("OAuth tokens revoked")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### BatchTokenOperations
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.tokens.batch import BatchTokenOperations
|
|||
|
|
|
|||
|
|
batch = BatchTokenOperations()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Методы
|
|||
|
|
|
|||
|
|
##### `batch_validate_tokens(tokens)`
|
|||
|
|
Массовая валидация токенов.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `tokens` (list[str]): Список JWT токенов
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict[str, bool]` - Результаты валидации
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
tokens = ["token1", "token2", "token3"]
|
|||
|
|
results = await batch.batch_validate_tokens(tokens)
|
|||
|
|
# {"token1": True, "token2": False, "token3": True}
|
|||
|
|
|
|||
|
|
for token, is_valid in results.items():
|
|||
|
|
print(f"Token {token[:10]}... is {'valid' if is_valid else 'invalid'}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `batch_revoke_tokens(tokens)`
|
|||
|
|
Массовый отзыв токенов.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `tokens` (list[str]): Список JWT токенов
|
|||
|
|
|
|||
|
|
**Возвращает:** `int` - Количество отозванных токенов
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
revoked_count = await batch.batch_revoke_tokens(tokens)
|
|||
|
|
print(f"Revoked {revoked_count} tokens")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `cleanup_expired_tokens()`
|
|||
|
|
Очистка истекших токенов.
|
|||
|
|
|
|||
|
|
**Возвращает:** `int` - Количество очищенных токенов
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
cleaned_count = await batch.cleanup_expired_tokens()
|
|||
|
|
print(f"Cleaned {cleaned_count} expired tokens")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### TokenMonitoring
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.tokens.monitoring import TokenMonitoring
|
|||
|
|
|
|||
|
|
monitoring = TokenMonitoring()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Методы
|
|||
|
|
|
|||
|
|
##### `get_token_statistics()`
|
|||
|
|
Получает статистику токенов.
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict` - Статистика системы
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
stats = await monitoring.get_token_statistics()
|
|||
|
|
print(f"Active sessions: {stats['session_tokens']}")
|
|||
|
|
print(f"OAuth tokens: {stats['oauth_access_tokens']}")
|
|||
|
|
print(f"Memory usage: {stats['memory_usage'] / 1024 / 1024:.2f} MB")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `health_check()`
|
|||
|
|
Проверка здоровья системы токенов.
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict` - Статус системы
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
health = await monitoring.health_check()
|
|||
|
|
if health["status"] == "healthy":
|
|||
|
|
print("Token system is healthy")
|
|||
|
|
print(f"Redis connected: {health['redis_connected']}")
|
|||
|
|
else:
|
|||
|
|
print(f"System unhealthy: {health.get('error')}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `optimize_memory_usage()`
|
|||
|
|
Оптимизация использования памяти.
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict` - Результаты оптимизации
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
results = await monitoring.optimize_memory_usage()
|
|||
|
|
print(f"Cleaned expired: {results['cleaned_expired']}")
|
|||
|
|
print(f"Memory freed: {results['memory_freed']} bytes")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🛠️ Utility Functions
|
|||
|
|
|
|||
|
|
### Auth Utils
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.utils import (
|
|||
|
|
extract_token_from_request,
|
|||
|
|
get_auth_token,
|
|||
|
|
get_auth_token_from_context,
|
|||
|
|
get_safe_headers,
|
|||
|
|
get_user_data_by_token
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `extract_token_from_request(request)`
|
|||
|
|
Извлекает токен из HTTP запроса.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `request`: HTTP запрос (FastAPI, Starlette, etc.)
|
|||
|
|
|
|||
|
|
**Возвращает:** `str | None` - JWT токен или None
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
token = await extract_token_from_request(request)
|
|||
|
|
if token:
|
|||
|
|
print(f"Found token: {token[:20]}...")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `get_auth_token(request)`
|
|||
|
|
Расширенное извлечение токена с логированием.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `request`: HTTP запрос
|
|||
|
|
|
|||
|
|
**Возвращает:** `str | None` - JWT токен или None
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
token = await get_auth_token(request)
|
|||
|
|
if token:
|
|||
|
|
# Токен найден и залогирован
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `get_auth_token_from_context(info)`
|
|||
|
|
Извлечение токена из GraphQL контекста.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `info`: GraphQL Info объект
|
|||
|
|
|
|||
|
|
**Возвращает:** `str | None` - JWT токен или None
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
@auth_required
|
|||
|
|
async def protected_resolver(info, **kwargs):
|
|||
|
|
token = await get_auth_token_from_context(info)
|
|||
|
|
# Используем токен для дополнительных проверок
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `get_safe_headers(request)`
|
|||
|
|
Безопасное получение заголовков запроса.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `request`: HTTP запрос
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict[str, str]` - Словарь заголовков
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
headers = get_safe_headers(request)
|
|||
|
|
auth_header = headers.get("authorization", "")
|
|||
|
|
user_agent = headers.get("user-agent", "")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `get_user_data_by_token(token)`
|
|||
|
|
Получение данных пользователя по токену.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict | None` - Данные пользователя
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
user_data = await get_user_data_by_token(token)
|
|||
|
|
if user_data:
|
|||
|
|
print(f"User: {user_data['username']}")
|
|||
|
|
print(f"ID: {user_data['user_id']}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🎭 Decorators
|
|||
|
|
|
|||
|
|
### GraphQL Decorators
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.decorators import auth_required, permission_required
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `@auth_required`
|
|||
|
|
Требует авторизации для выполнения resolver'а.
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
@auth_required
|
|||
|
|
async def get_user_profile(info, **kwargs):
|
|||
|
|
"""Получение профиля пользователя"""
|
|||
|
|
user = info.context.get('user')
|
|||
|
|
return {
|
|||
|
|
"id": user.id,
|
|||
|
|
"username": user.username,
|
|||
|
|
"email": user.email
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `@permission_required(permission)`
|
|||
|
|
Требует конкретного разрешения.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `permission` (str): Название разрешения
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
@auth_required
|
|||
|
|
@permission_required("shout:create")
|
|||
|
|
async def create_shout(info, input_data):
|
|||
|
|
"""Создание публикации"""
|
|||
|
|
user = info.context.get('user')
|
|||
|
|
|
|||
|
|
shout = Shout(
|
|||
|
|
title=input_data['title'],
|
|||
|
|
content=input_data['content'],
|
|||
|
|
author_id=user.id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return shout
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔧 Middleware
|
|||
|
|
|
|||
|
|
### AuthMiddleware
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.middleware import AuthMiddleware
|
|||
|
|
|
|||
|
|
middleware = AuthMiddleware()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Методы
|
|||
|
|
|
|||
|
|
##### `authenticate_user(request)`
|
|||
|
|
Аутентификация пользователя из запроса.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `request`: HTTP запрос
|
|||
|
|
|
|||
|
|
**Возвращает:** `dict | None` - Данные пользователя
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
user_data = await middleware.authenticate_user(request)
|
|||
|
|
if user_data:
|
|||
|
|
request.user = user_data
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `set_cookie(response, token)`
|
|||
|
|
Установка httpOnly cookie с токеном.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `response`: HTTP ответ
|
|||
|
|
- `token` (str): JWT токен
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
await middleware.set_cookie(response, token)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### `delete_cookie(response)`
|
|||
|
|
Удаление cookie с токеном.
|
|||
|
|
|
|||
|
|
**Параметры:**
|
|||
|
|
- `response`: HTTP ответ
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
await middleware.delete_cookie(response)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔒 Error Handling
|
|||
|
|
|
|||
|
|
### Исключения
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from auth.exceptions import (
|
|||
|
|
AuthenticationError,
|
|||
|
|
InvalidTokenError,
|
|||
|
|
TokenExpiredError,
|
|||
|
|
OAuthError
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `AuthenticationError`
|
|||
|
|
Базовое исключение аутентификации.
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
try:
|
|||
|
|
payload = await sessions.verify_session(token)
|
|||
|
|
if not payload:
|
|||
|
|
raise AuthenticationError("Invalid session token")
|
|||
|
|
except AuthenticationError as e:
|
|||
|
|
return {"error": str(e), "status": 401}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `InvalidTokenError`
|
|||
|
|
Невалидный токен.
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
try:
|
|||
|
|
valid, data = await sessions.validate_session_token(token)
|
|||
|
|
if not valid:
|
|||
|
|
raise InvalidTokenError("Token validation failed")
|
|||
|
|
except InvalidTokenError as e:
|
|||
|
|
return {"error": str(e), "status": 401}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### `TokenExpiredError`
|
|||
|
|
Истекший токен.
|
|||
|
|
|
|||
|
|
**Пример:**
|
|||
|
|
```python
|
|||
|
|
try:
|
|||
|
|
# Проверка токена
|
|||
|
|
pass
|
|||
|
|
except TokenExpiredError as e:
|
|||
|
|
return {"error": "Token expired", "status": 401}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📊 Response Formats
|
|||
|
|
|
|||
|
|
### Успешные ответы
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Успешная аутентификация
|
|||
|
|
{
|
|||
|
|
"authenticated": True,
|
|||
|
|
"user_id": "123",
|
|||
|
|
"username": "john_doe",
|
|||
|
|
"expires_at": 1640995200
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Статистика токенов
|
|||
|
|
{
|
|||
|
|
"session_tokens": 150,
|
|||
|
|
"oauth_access_tokens": 25,
|
|||
|
|
"oauth_refresh_tokens": 25,
|
|||
|
|
"verification_tokens": 5,
|
|||
|
|
"memory_usage": 1048576
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Health check
|
|||
|
|
{
|
|||
|
|
"status": "healthy",
|
|||
|
|
"redis_connected": True,
|
|||
|
|
"token_count": 205,
|
|||
|
|
"timestamp": 1640995200
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Ошибки
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Ошибка аутентификации
|
|||
|
|
{
|
|||
|
|
"authenticated": False,
|
|||
|
|
"error": "Invalid or expired token",
|
|||
|
|
"status": 401
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Ошибка системы
|
|||
|
|
{
|
|||
|
|
"status": "error",
|
|||
|
|
"error": "Redis connection failed",
|
|||
|
|
"timestamp": 1640995200
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🧪 Testing Helpers
|
|||
|
|
|
|||
|
|
### Mock Utilities
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from unittest.mock import AsyncMock, patch
|
|||
|
|
|
|||
|
|
# Mock SessionTokenManager
|
|||
|
|
@patch('auth.tokens.sessions.SessionTokenManager')
|
|||
|
|
async def test_auth(mock_sessions):
|
|||
|
|
mock_sessions.return_value.verify_session.return_value = {
|
|||
|
|
"user_id": "123",
|
|||
|
|
"username": "testuser"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Ваш тест здесь
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# Mock Redis
|
|||
|
|
@patch('storage.redis.redis')
|
|||
|
|
async def test_redis_operations(mock_redis):
|
|||
|
|
mock_redis.get.return_value = b'{"user_id": "123"}'
|
|||
|
|
mock_redis.exists.return_value = True
|
|||
|
|
|
|||
|
|
# Ваш тест здесь
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Test Fixtures
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import pytest
|
|||
|
|
|
|||
|
|
@pytest.fixture
|
|||
|
|
async def auth_token():
|
|||
|
|
"""Фикстура для создания тестового токена"""
|
|||
|
|
sessions = SessionTokenManager()
|
|||
|
|
return await sessions.create_session(
|
|||
|
|
user_id="test_user",
|
|||
|
|
username="testuser"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@pytest.fixture
|
|||
|
|
async def authenticated_request(auth_token):
|
|||
|
|
"""Фикстура для аутентифицированного запроса"""
|
|||
|
|
mock_request = AsyncMock()
|
|||
|
|
mock_request.headers = {"authorization": f"Bearer {auth_token}"}
|
|||
|
|
return mock_request
|
|||
|
|
```
|