init_guac
This commit is contained in:
328
guacamole_test_11_26/docs/DELETE_CONNECTION_FIX.md
Executable file
328
guacamole_test_11_26/docs/DELETE_CONNECTION_FIX.md
Executable file
@ -0,0 +1,328 @@
|
||||
# 🔧 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
|
||||
**Статус:** 🟢 **ИСПРАВЛЕНО**
|
||||
|
||||
Reference in New Issue
Block a user