LDAP ORM модели (orm_models.py): ================================ ✅ Применены миксины ModifyDnMixin и LdapSyncStateMixin ко всем моделям ✅ LdapUser - поддержка modify_dn + автоуправление LdapSyncState ✅ LdapGroup - поддержка modify_dn для перемещения групп ✅ LdapOrganizationalUnit - поддержка modify_dn для реорганизации Миксины (mixins.py) - НОВЫЙ ФАЙЛ: ================================= ✅ ModifyDnMixin - автоматический modify_dn при изменении base_dn • Отслеживает изменения base_dn • Выполняет LDAP modify_dn с newsuperior • Обновляет _saved_dn для корректного последующего save() • Использование: user.base_dn = new_dn; user.save() ✅ LdapSyncStateMixin - автоуправление LdapSyncState • Автоматически создает/обновляет LdapSyncState при save() • Автоматически удаляет LdapSyncState при delete() • Настройки: _sync_model_name, _sync_pk_field Django Admin для LDAP (admin.py) - НОВЫЙ ФАЙЛ: ============================================== ✅ LdapUserAdmin - полноценная админка для LDAP пользователей • List display: CN, SAM, email, Django Employee link, sync status • Search: по CN, SAM, email, имени, фамилии • Filters: по userAccountControl • Actions синхронизации: - sync_from_ldap_to_django (LDAP → Django) - sync_from_django_to_ldap (Django → LDAP) - show_sync_diff (сравнение данных) • Fieldsets: идентификация, персональные данные, контакты, UAC, группы • DN форматирование с подсветкой компонентов • Sync status indicators с иконками Django Admin для Employee (employees/admin.py): =============================================== ✅ Добавлены зеркальные actions синхронизации: • sync_from_django_to_ldap - создание/обновление в LDAP - Автоматическое создание LDAP пользователя если не существует - Генерация временного пароля - Установка is_ldap_managed = True • sync_from_ldap_to_django - обновление из LDAP • show_sync_diff - сравнение Django ↔ LDAP ✅ Импорт django.contrib.messages для уведомлений Сервисы LDAP (services/): ========================= ✅ UserService - полная миграция на ORM для UPDATE операций ✅ GroupService - использование ORM для модификаций ✅ DepartmentService - ORM для обновления OU ✅ PositionService - ORM для групп позиций Тесты (tests/unit/): =================== ✅ test_user_service.py - обновлены под ORM ✅ test_group_service.py - обновлены под ORM ✅ test_position_service.py - обновлены под ORM ✅ conftest.py - добавлены фикстуры для ORM моделей Authentication (auth_backends.py): ================================= ✅ EmailOrPhoneBackend - добавлено подробное debug логирование • Логирование email/phone lookup • Логирование is_active проверки • Логирование password check с hash prefix ✅ SuperuserOnlyBackend - добавлено debug логирование • Логирование parent auth результата • Логирование is_superuser проверки API (api/auth/serializers.py): ============================== ✅ PhoneOrEmailTokenObtainPairSerializer - исправлен KeyError • Добавлена проверка credentials перед super().validate() • Явный AuthenticationFailed при отсутствии учетных данных Middleware (middleware.py): =========================== ✅ Очистка от устаревшего кода (305 → 102 строки) • ❌ Удалена EmailVerificationMiddleware (не используется) • ❌ Удалена RegistrationIPRestrictionMiddleware (не используется) • ❌ Удалены все ссылки на namespace 'auth_front' • ✅ Возврат 403 для неавторизованных вместо редиректа Settings (settings.py): ======================= ✅ LOGIN_URL изменен с 'auth_front:login' на '/admin/login/' ✅ Добавлено DEBUG логирование для: • django.contrib.auth • eusrr_backend.auth_backends Миграции: ========= ✅ Удалены старые миграции LDAP моделей (0042, 0043) ✅ Добавлена миграция LdapSyncQueue (0042_ldapsyncqueue.py) Документация: ============= ✅ ADMIN_GUIDE.md - руководство по использованию LDAP Admin ✅ docs/guides/ldap-orm-mixins-usage.md - usage guide для миксинов ✅ docs/completed/ldap-orm-create-support.md - документация ORM миграции ✅ docs/in-progress/LDAP_ORM_MIGRATION_PLAN.md - план миграции Инфраструктура (__init__.py): ============================= ✅ Добавлен импорт admin для авторегистрации ✅ Экспорт ORM моделей (LdapUser, LdapGroup, LdapOrganizationalUnit) ИТОГО: ====== • Полная миграция с ldap3 на django-ldapdb ORM для UPDATE/MOVE/DELETE • Унификация: один подход для всех LDAP операций через модели • Автоматизация: синхронизация состояния и перемещения через миксины • UI: полноценный Django Admin для управления LDAP объектами • Отладка: расширенное логирование аутентификации • Чистота: удален устаревший код middleware и миграций • Тесты: 29 passed, 0 failed, 18 skipped Breaking Changes: НЕТ - обратная совместимость сохранена
100 lines
4.1 KiB
Python
100 lines
4.1 KiB
Python
# backend/eusrr_backend/middleware.py
|
||
"""
|
||
Кастомные middleware для проекта EUSRR.
|
||
|
||
Минимальный набор для работы Django Admin + REST API.
|
||
"""
|
||
|
||
from django.conf import settings
|
||
|
||
|
||
class AuthRequiredMiddleware:
|
||
"""
|
||
Базовая защита: неавторизованные пользователи не могут заходить куда-либо,
|
||
кроме API и админки (которые управляют аутентификацией сами).
|
||
|
||
Разрешенные зоны без аутентификации:
|
||
- /api/ - REST API с JWT/Token аутентификацией
|
||
- /admin/ - Django Admin со своей системой аутентификации
|
||
- /static/ - статические файлы
|
||
- /media/ - медиафайлы
|
||
"""
|
||
|
||
EXEMPT_PREFIXES = (
|
||
"/api/", # REST API управляет своей аутентификацией
|
||
"/admin/", # Django Admin использует встроенную аутентификацию
|
||
"/static/", # Статика
|
||
"/media/", # Медиа
|
||
)
|
||
|
||
def __init__(self, get_response):
|
||
self.get_response = get_response
|
||
self.allowed_prefixes = tuple(
|
||
getattr(settings, "AUTH_REQUIRED_EXEMPT_PREFIXES", self.EXEMPT_PREFIXES)
|
||
)
|
||
|
||
def __call__(self, request):
|
||
# Аутентифицированные пользователи проходят везде
|
||
if request.user.is_authenticated:
|
||
return self.get_response(request)
|
||
|
||
path = request.path_info
|
||
|
||
# Пропускаем разрешенные префиксы - они сами управляют аутентификацией
|
||
# Django Admin сам редиректит на /admin/login/ если нужно
|
||
if any(path.startswith(p) for p in self.allowed_prefixes):
|
||
return self.get_response(request)
|
||
|
||
# Все остальное - 403 Forbidden для неаутентифицированных
|
||
from django.http import HttpResponseForbidden, JsonResponse
|
||
|
||
# Для API запросов возвращаем JSON
|
||
if request.headers.get('Accept', '').startswith('application/json'):
|
||
return JsonResponse(
|
||
{"detail": "Authentication required"},
|
||
status=403
|
||
)
|
||
|
||
# Для браузерных запросов - HTML 403
|
||
return HttpResponseForbidden("403 Forbidden: Authentication required")
|
||
|
||
|
||
class CacheControlMiddleware:
|
||
"""
|
||
Добавляет правильные заголовки Cache-Control для HTML-страниц.
|
||
|
||
Стратегия:
|
||
- HTML страницы: private cache на 60 секунд (только для браузера)
|
||
- API: обрабатывается на уровне DRF
|
||
- Статика/медиа: обрабатывается веб-сервером
|
||
"""
|
||
|
||
NO_CACHE_PREFIXES = (
|
||
"/api/", # API управляет своим кэшем
|
||
"/admin/", # Админка всегда свежая
|
||
"/static/", # Обрабатывается веб-сервером
|
||
"/media/", # Обрабатывается веб-сервером
|
||
)
|
||
|
||
def __init__(self, get_response):
|
||
self.get_response = get_response
|
||
|
||
def __call__(self, request):
|
||
response = self.get_response(request)
|
||
|
||
# Применяем только к успешным HTML GET-запросам
|
||
if (
|
||
response.status_code == 200
|
||
and request.method == "GET"
|
||
and response.get("Content-Type", "").startswith("text/html")
|
||
):
|
||
path = request.path_info
|
||
|
||
# Проверяем исключения
|
||
if not any(path.startswith(p) for p in self.NO_CACHE_PREFIXES):
|
||
# Если заголовок уже установлен - не перезаписываем
|
||
if not response.has_header("Cache-Control"):
|
||
response["Cache-Control"] = "private, max-age=60"
|
||
|
||
return response
|