# 🔧 Delete Connection Fix - 500 Error ## Проблема При попытке удалить активное подключение возникала ошибка: ``` DELETE /connections/38 500 (Internal Server Error) [guacamole-service] Failed to delete connection | context: {"connectionId":"38","error":{}} ``` --- ## Причина После исправления middleware (для решения проблемы с восстановлением сессий), возникла новая проблема: ### Что происходило: 1. **Пользователь создает подключение:** - Guacamole выдает `auth_token_A` - Сохраняется в Redis: `conn_data['auth_token'] = auth_token_A` 2. **Пользователь делает logout/login:** - Guacamole выдает **НОВЫЙ** `auth_token_B` - Старый `auth_token_A` становится **невалидным** - Но в Redis для подключения все еще хранится `auth_token_A` ❌ 3. **Пользователь пытается удалить подключение:** - Код пытается удалить используя `conn_data['auth_token']` (старый `auth_token_A`) - Guacamole отклоняет запрос: **токен невалиден** - Результат: **500 Internal Server Error** ❌ --- ## Решение ### 1. Для ручного удаления (`DELETE /connections/{id}`) **Используем ТЕКУЩИЙ токен пользователя** из активной сессии: **ДО (❌ НЕПРАВИЛЬНО):** ```python @app.delete("/connections/{connection_id}") async def delete_connection(connection_id: str, request: Request, ...): user_info = get_current_user(request) conn_data = redis_connection_storage.get_connection(connection_id) # ❌ Использует СТАРЫЙ токен из Redis if guacamole_client.delete_connection_with_user_token( connection_id, conn_data['auth_token'] # ← Старый, невалидный токен! ): # ... ``` **ПОСЛЕ (✅ ПРАВИЛЬНО):** ```python @app.delete("/connections/{connection_id}") async def delete_connection(connection_id: str, request: Request, ...): user_info = get_current_user(request) # ✅ Получаем ТЕКУЩИЙ токен из активной сессии пользователя current_user_token = get_current_user_token(request) if not current_user_token: raise HTTPException(status_code=401, detail="Authentication token not available") conn_data = redis_connection_storage.get_connection(connection_id) # ✅ Используем ТЕКУЩИЙ токен пользователя if guacamole_client.delete_connection_with_user_token( connection_id, current_user_token # ← Актуальный токен! ): # ... ``` --- ### 2. Для автоматического cleanup (фоновая задача) **Используем СИСТЕМНЫЙ токен** (не пользовательский): **ДО (❌ НЕПРАВИЛЬНО):** ```python def cleanup_expired_or_orphaned_connections(log_action: str = "expired"): for conn_id in expired_connections: conn_data = redis_connection_storage.get_connection(conn_id) # ❌ Использует токен пользователя (может быть невалидным) if guacamole_client.delete_connection_with_user_token( conn_id, conn_data['auth_token'] # ← Старый токен пользователя ): # ... ``` **ПОСЛЕ (✅ ПРАВИЛЬНО):** ```python def cleanup_expired_or_orphaned_connections(log_action: str = "expired"): for conn_id in expired_connections: conn_data = redis_connection_storage.get_connection(conn_id) # ✅ Cleanup - системная операция, используем системный токен if guacamole_client.delete_connection_with_system_token(conn_id): # ... ``` **Почему системный токен:** - Cleanup - это фоновая системная задача - Не привязана к конкретному пользователю - Системный токен всегда валиден - Правильнее с точки зрения архитектуры --- ## Архитектура токенов ``` ┌─────────────────────────────────────────────────────────────┐ │ Пользователь логинится │ └────────────┬────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Guacamole выдает auth_token_NEW │ └────────────┬────────────────────────────────────────────────┘ │ ├─► Сохраняется в Redis Session (для middleware) │ └─► СТАРЫЕ подключения все еще хранят auth_token_OLD в Redis (невалидный!) ``` ### Решение проблемы: **Для операций от лица пользователя:** ``` Пользователь → JWT → Middleware → Redis Session → ТЕКУЩИЙ токен │ ▼ Guacamole API ✅ ``` **Для системных операций:** ``` Система (Cleanup) → Системный токен → Guacamole API ✅ ``` --- ## Изменённые файлы ### 1. `GuacamoleRemoteAccess/api/main.py` **Строки 3111-3120** - Добавлено получение текущего токена: ```python # ✅ КРИТИЧНО: Получаем ТЕКУЩИЙ токен пользователя для удаления current_user_token = get_current_user_token(request) if not current_user_token: logger.error("No Guacamole token available for user", username=user_info["username"], connection_id=connection_id) raise HTTPException( status_code=401, detail="Authentication token not available" ) ``` **Строки 3156-3158** - Используется текущий токен: ```python # ✅ КРИТИЧНО: Удаляем из Guacamole используя ТЕКУЩИЙ токен пользователя # Не используем conn_data['auth_token'] так как он может быть невалидным после logout/login if guacamole_client.delete_connection_with_user_token(connection_id, current_user_token): ``` **Строки 1295-1297** - Cleanup использует системный токен: ```python # ✅ КРИТИЧНО: Удаляем из Guacamole используя СИСТЕМНЫЙ токен # Cleanup - это системная операция, не используем auth_token пользователя if guacamole_client.delete_connection_with_system_token(conn_id): ``` --- ## Как это работает ### Сценарий 1: Пользователь удаляет подключение ``` Client API Redis Session Guacamole │ │ │ │ │───DELETE /conn/38─>│ │ │ │ JWT Token │ │ │ │ │──get_current_user_token>│ │ │ │<─current_token──────────│ │ │ │ │ │ │ │────DELETE connection────────────────────>│ │ │ (current_token) │ │ │ │<───200 OK───────────────────────────────│ │<──200 OK───────────│ │ │ │ Success │ │ │ ``` **Ключевой момент:** Используется `current_token` из активной сессии, а НЕ `auth_token` из Redis подключения! --- ### Сценарий 2: Cleanup удаляет истекшие подключения ``` Background Task API Guacamole │ │ │ │─cleanup()────>│ │ │ │──get_system_token()──>│ │ │<─system_token─────────│ │ │ │ │ │──DELETE connections──>│ │ │ (system_token) │ │ │<─200 OK──────────────│ │<─complete─────│ │ ``` **Ключевой момент:** Используется `system_token`, не токен какого-либо пользователя! --- ## Связь с предыдущими исправлениями ### 1. Middleware Fix (исправление восстановления сессий) **Проблема:** JWT содержал `session_id`, но не `guac_token` **Решение:** Middleware загружает токен из Redis сессии **Побочный эффект:** Токены в старых подключениях становятся невалидными после logout/login --- ### 2. Delete Connection Fix (это исправление) **Проблема:** Удаление использовало старый токен из `conn_data['auth_token']` **Решение:** Удаление использует **текущий токен** из активной сессии --- ## Проверка исправления ### 1. Перезапустить API ```bash cd GuacamoleRemoteAccess docker-compose restart api ``` ### 2. Протестировать удаление подключения ```bash # 1. Залогиниться curl -X POST https://mc.exbytestudios.com/auth/login-ecdh \ -H "Content-Type: application/json" \ -d '{"username":"user","password":"pass","session_id":"..."}' \ > login_response.json # Извлечь JWT JWT=$(jq -r '.access_token' login_response.json) # 2. Создать подключение curl -X POST https://mc.exbytestudios.com/connect \ -H "Authorization: Bearer $JWT" \ -H "Content-Type: application/json" \ -d '{"hostname":"test","protocol":"ssh"}' \ > connection.json # Извлечь connection_id CONN_ID=$(echo $connection | jq -r '.connection_id') # 3. Удалить подключение curl -X DELETE https://mc.exbytestudios.com/connections/$CONN_ID \ -H "Authorization: Bearer $JWT" # Должен вернуть: # HTTP/1.1 200 OK # {"message":"Connection deleted successfully"} ``` ### 3. Проверить логи ```bash docker-compose logs api | grep -i "delete" # Должно быть: # [info] Connection deleted successfully # НЕ должно быть: # [error] Failed to delete connection ``` --- ## ✅ Результат После исправления: - ✅ Удаление подключения работает даже после logout/login - ✅ Используется актуальный токен из активной сессии - ✅ Cleanup использует системный токен (более правильно) - ✅ Нет 500 ошибок при удалении - ✅ Логика токенов консистентна --- ## 📝 Checklist - [x] Добавлено получение `current_user_token` в `delete_connection` - [x] `delete_connection` использует `current_user_token` вместо `auth_token` - [x] Cleanup использует `delete_connection_with_system_token` - [ ] Перезапущен API: `docker-compose restart api` - [ ] Протестировано удаление подключения - [ ] Проверены логи (нет ошибок) --- **Дата исправления:** 2025-11-04 **Связанные исправления:** Middleware Fix, CORS Duplicate Fix **Статус:** 🟢 **ИСПРАВЛЕНО**