Безопасность и аудит
Аутентификация
Token Authentication
Система использует DRF TokenAuthentication — простой и надёжный механизм для REST API:
# Получить токен
POST /api/v2/api-token-auth/
{"username": "user@example.com", "password": "password"}
# Ответ
{"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}
# Использование в запросах
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4bТокены хранятся в таблице authtoken_token. Каждый пользователь имеет один токен.
Инвалидация токена
При выходе из системы токен удаляется:
DELETE /api/v2/logout/
# Удаляет Token для текущего пользователяАвторизация (Права доступа)
Глобальная политика
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}Все endpoints требуют аутентификации. Исключения задаются явно.
Кастомные права
Frontend проверяет права через PermissionService (libs/core/services/permission.service.ts):
- Директива
HasPermissionDirectiveскрывает элементы UI - Guard
auth.guard.tsзащищает маршруты
Роли (Guards на фронтенде)
| Guard | Доступ |
|---|---|
AuthGuard | Любой авторизованный пользователь |
AccountantGuard | Бухгалтер (ограниченный доступ) |
WarehouseWorkerGuard | Складской работник |
DriverWorkerGuard | Водитель |
SuperUserGuard | Администратор системы |
Система аудита
django-simple-history
Модуль simple_history автоматически сохраняет историю изменений моделей:
from simple_history.models import HistoricalRecords
class Documents(MPTTModel):
# ...
history = HistoricalRecords()Для каждой модели с историей создаётся таблица historical_*. Хранит:
- Дата и время изменения
- Кто изменил (
history_user) - Тип операции (создание / обновление / удаление)
- Снимок объекта в момент изменения
system.auditlog
Дополнительный аудит-лог (system.auditlog) — кастомный модуль:
class LogEntry(Model):
user = FK(User)
action = CharField() # 'create', 'update', 'delete'
content_type = FK(ContentType)
object_id = IntegerField()
object_repr = CharField() # строковое представление
changes = JSONField() # JSON с изменёнными полями
timestamp = DateTimeField()
ip_address = GenericIPAddressField()Просмотр лога: Django Admin → System → Audit Log Entries
CORS
# settings.py
CORS_ORIGIN_ALLOW_ALL = True # в dev-режиме
CORS_ORIGIN_WHITELIST = [
"http://localhost:4200",
"https://office.narmak.ru",
]В production CORS_ORIGIN_ALLOW_ALL = False и используется whitelist.
Хранение файлов (Selectel)
Файлы загружаются в облачное хранилище Selectel Object Storage:
SELECTEL_STORAGE = {
'container_name': 'narmak-files',
'user': '...',
'key': '...',
}
DEFAULT_FILE_STORAGE = 'django_selectel.storage.SelectelStorage'Прямые URL файлов недоступны без токена (приватный контейнер).
Защита API от перебора
- Rate limiting через Django middleware (настраивается в
settings.py) - При 5 неудачных попытках входа — временная блокировка IP
Рекомендации по безопасности
| Аспект | Статус | Рекомендация |
|---|---|---|
| HTTPS | Требует настройки nginx | Использовать Let's Encrypt |
| SECRET_KEY | Из settings.ini | Хранить в env-переменных |
| DEBUG=False | В production | Проверить перед деплоем |
| CORS | Whitelist в prod | Убрать ALLOW_ALL |
| Токены | Не истекают | Рассмотреть JWT с expiry |
| Пароли | bcrypt (Django default) | Минимальная длина 8 символов |