Перейти к содержанию

Система подтверждения опасных операций синхронизации

Обзор

Система подтверждения опасных операций синхронизации обеспечивает защиту от случайной потери данных при изменении структуры параметров в админке Django. При попытке выполнения операций, которые могут привести к потере данных (например, удаление колонок с данными), система запрашивает подтверждение пользователя.

Архитектура решения

Основные компоненты

  1. Savepoints - точки сохранения в транзакции для возможности отката
  2. SyncConfirmationRequired - исключение для опасных операций
  3. Диалог подтверждения - интерфейс для принятия решения пользователем
  4. Асинхронная синхронизация - принудительная синхронизация в отдельном процессе

Поток выполнения

graph TD
    A[Пользователь сохраняет параметр] --> B[Создание savepoint]
    B --> C[Сохранение изменений в БД]
    C --> D[Попытка синхронизации force=False]
    D --> E{SyncConfirmationRequired?}
    E -->|Да| F[Показать диалог подтверждения]
    E -->|Нет| G[Коммит savepoint]
    G --> H[Успешное завершение]
    F --> I{Пользователь подтвердил?}
    I -->|Да| J[Запуск async синхронизации force=True]
    I -->|Нет| K[Откат к savepoint]
    J --> L[Коммит savepoint]
    K --> M[Отмена изменений]

Реализация

1. Модификация ParameterAdmin

Создание savepoint в save_model:

def save_model(self, request, obj, form, change):
    with transaction.atomic():
        savepoint_id = transaction.savepoint()
        request.session['parameter_savepoint_id'] = savepoint_id
        super().save_model(request, obj, form, change)
        self._pending_sync = True

Обработка синхронизации в response_change:

def response_change(self, request, obj):
    if hasattr(self, '_pending_sync') and self._pending_sync:
        try:
            self._sync_parameter_after_save(request, obj)
            transaction.savepoint_commit(savepoint_id)
        except SyncConfirmationRequired as e:
            # Сохранение данных в сессии и редирект на диалог
            request.session['sync_confirmation_data'] = {
                'parameter_id': obj.id,
                'message': str(e),
                'details': e.details,
                'return_url': request.get_full_path()
            }
            return redirect('admin:parameters_confirm_sync_operations')

2. Диалог подтверждения

View confirm_sync_operations: - GET: Отображение информации об опасных операциях - POST с confirm=1: Запуск асинхронной синхронизации с force=True - POST с cancel=1: Откат к savepoint

Шаблон confirm_sync_operations.html: - Детальная информация о планируемых изменениях - Список опасных операций с описанием рисков - Кнопки подтверждения и отмены

3. Обработка MeasureAdmin

Аналогичная логика реализована для изменений мер: - Создание savepoint при сохранении - Обработка SyncConfirmationRequired - Перенаправление на тот же диалог подтверждения

Типы опасных операций

Система определяет следующие типы опасных операций:

delete_id_column

  • Описание: Удаление колонки id
  • Риск: Потеря первичных ключей и связей
  • Действие: Всегда требует подтверждения

delete_dimension

  • Описание: Удаление колонок измерений
  • Риск: Потеря связей с иерархиями
  • Действие: Требует подтверждения при наличии записей

delete_measure_with_data

  • Описание: Удаление колонок мер с данными
  • Риск: Потеря бизнес-данных
  • Действие: Требует подтверждения при наличии не-NULL значений

alter_column_with_data

  • Описание: Изменение типа колонки с данными
  • Риск: Потеря данных при несовместимых типах
  • Действие: Требует подтверждения при наличии данных

Безопасность данных

Механизмы защиты

  1. Savepoints: Возможность полного отката всех изменений
  2. Анализ данных: Проверка наличия данных перед опасными операциями
  3. Подробная информация: Детальное описание планируемых изменений
  4. Асинхронное выполнение: Принудительная синхронизация в отдельном процессе

Обработка ошибок

  • Автоматический откат при любых ошибках в savepoint
  • Логирование всех операций
  • Информативные сообщения пользователю
  • Ссылки на задачи Celery для отслеживания прогресса

Использование

Сценарии использования

  1. Добавление новой меры - безопасная операция, выполняется автоматически
  2. Удаление меры с данными - показывается диалог подтверждения
  3. Изменение типа меры - анализируется совместимость типов
  4. Изменение структуры измерений - проверяется влияние на данные

Действия пользователя

При появлении диалога подтверждения: 1. Внимательно изучить список опасных операций 2. Убедиться в наличии резервных копий данных 3. Выбрать одно из действий: - Подтвердить - запустить принудительную синхронизацию - Отменить - откатить все изменения

После подтверждения: 1. Отслеживать выполнение задачи по ссылке на TaskResult 2. Проверить результаты синхронизации 3. Убедиться в корректности данных

Тестирование

Основные тесты системы безопасности

Создан набор тестов test_admin_sync_confirmation.py:

  • test_parameter_save_creates_savepoint - создание savepoint
  • test_sync_confirmation_required_handling - обработка исключений
  • test_confirm_sync_operations_view_get - отображение диалога
  • test_confirm_sync_operations_confirm - подтверждение операций
  • test_confirm_sync_operations_cancel - отмена с откатом

Новые тесты в PLQM-1161

test_admin_unsafe_operations.py

Расположение: /src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py

Назначение: Тестирование интеграции системы безопасности с Django админкой.

Ключевые тесты: - test_admin_detects_unsafe_measure_deletion — обнаружение небезопасных операций удаления мер в админке - test_admin_shows_confirmation_dialog — отображение диалога подтверждения в админке - test_admin_executes_confirmed_operations — выполнение подтвержденных операций через админку - test_admin_cancels_unsafe_operations — отмена небезопасных операций в админке - test_admin_handles_measure_changes — обработка изменений мер в inline формах админки - test_admin_handles_dimensions_changes — обработка изменений измерений в админке

Особенности тестов: - Тестируют полный flow от обнаружения до выполнения небезопасных операций - Проверяют интеграцию с Django админкой - Валидируют сериализацию данных в сессии - Проверяют обработку различных типов небезопасных операций - Тестируют работу с inline формами (меры, измерения)

Запуск тестов:

# Основные тесты системы безопасности
pytest src/planiqum/core/parameters/tests/test_admin_sync_confirmation.py -v

# Тесты интеграции с админкой
pytest src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py -v

# Конкретный тест с отладочным выводом
pytest src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py::test_admin_detects_unsafe_measure_deletion -v -s

# Все тесты системы безопасности
pytest src/planiqum/core/parameters/tests/test_admin_sync_confirmation.py src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py -v

Конфигурация

Настройки в settings.py

Система использует существующие настройки: - USE_CELERY - для асинхронного выполнения - CELERY_* - настройки Celery для фоновых задач

Зависимости

  • Django transactions и savepoints
  • Celery для асинхронных задач
  • django-celery-results для отслеживания задач
  • Существующая система синхронизации параметров

Исправленные проблемы

Проблема с inline формами (исправлено)

Описание проблемы: При удалении мер через inline формы в админке параметра система не запускала синхронизацию и не показывала диалог подтверждения.

Причины: 1. В save_formset не устанавливался флаг _pending_sync 2. В модели Measure отсутствовал метод delete() 3. Функция on_measure_delete использовала устаревший метод syncdb()

Решение: 1. Обновлен save_formset - добавлена проверка изменений в inline формах:

def save_formset(self, request, form, formset, change):
    has_changes = False

    # Проверяем удаленные объекты
    if hasattr(formset, 'deleted_objects') and formset.deleted_objects:
        has_changes = True

    # Проверяем измененные формы
    for inline_form in formset.forms:
        if inline_form.has_changed():
            has_changes = True
            break

    super().save_formset(request, form, formset, change)

    # Устанавливаем флаг синхронизации при наличии изменений
    if has_changes:
        self._pending_sync = True

  1. Добавлен метод delete() в модель Measure:

    def delete(self, *args, **kwargs):
        # Удаляем таблицу ревизий, если используется
        if hasattr(self, 'use_revision') and self.use_revision:
            remove_measure_revision_table(self)
    
        super().delete(*args, **kwargs)
    

  2. Обновлена функция on_measure_delete - использует новый метод sync() вместо syncdb()

Ограничения и известные проблемы

  1. Совместимость с транзакциями: Savepoints работают только внутри транзакций
  2. Сессии: Данные подтверждения хранятся в сессии пользователя
  3. Время жизни: Данные подтверждения не имеют автоматического срока истечения
  4. Concurrent access: Не защищено от одновременного изменения параметра

Дальнейшее развитие

Планируемые улучшения

  1. Модальные окна: Реализация диалога подтверждения в виде модального окна
  2. Предпросмотр изменений: Визуализация планируемых изменений структуры
  3. Уведомления: Email-уведомления о критических изменениях
  4. Аудит: Журнал всех подтвержденных опасных операций

Интеграция с другими компонентами

  • Система прав доступа для ограничения опасных операций
  • Интеграция с системой бэкапов для автоматического создания копий
  • Webhook для уведомления внешних систем о структурных изменениях