Files
app-autotest/site/api/auth.py
T

129 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Единый модуль аутентификации и создания HTTP-клиента.
Раньше _client(), _client_id(), _stand(), _token_info() были продублированы
в main.py и api_test.py с идентичным или почти идентичным кодом.
Теперь всё здесь — один источник правды для всех роутов.
Функции (все без аргументов — берут данные из Flask request/current_app):
get_token() — токен: cookie → env-переменная
get_client() — HttpClient с автоопределением стенда
get_client_id() — ClientID из JWT (base64url, без проверки подписи)
get_stand() — "dev" / "test" по токену
get_token_info() — {email, company, client_id} из JWT для UI
get_token_masked() — маскированный токен (abc...xyz) для placeholder
"""
import base64
import json
from flask import request, current_app
from api.http_client import HttpClient, detect_endpoint, stand_name, STANDS
def get_token():
"""Получить активный токен: сначала из cookie пользователя, потом из env.
Приоритет:
1. cookie "token" — пользователь ввёл свой токен в форме
2. NUBES_API_TOKEN из env — сервисный токен (для автоматических тестов)
Пользовательский токен приоритетнее — он переопределяет сервисный."""
return request.cookies.get("token") or current_app.config["NUBES_API_TOKEN"]
def get_client():
"""HttpClient с автоопределением стенда.
Если POLYGON_ENDPOINT задан → направить ВСЕ запросы в polygon.
Реальный NUBES_API_TOKEN остаётся нетронутым — polygon его не проверяет.
Иначе:
- Если NUBES_API_ENDPOINT — известный стенд (STANDS) → detect_endpoint()
- Если кастомный URL → использовать напрямую
"""
polygon = current_app.config.get("POLYGON_ENDPOINT", "")
if polygon:
return HttpClient(polygon, get_token())
token = get_token()
endpoint = current_app.config["NUBES_API_ENDPOINT"]
if endpoint in STANDS:
endpoint = detect_endpoint(token) or endpoint
return HttpClient(endpoint, token)
def get_client_id():
"""Извлечь ClientID из payload JWT-токена (base64url, без проверки подписи).
JWT состоит из трёх частей: header.payload.signature, разделённых точкой.
Нам нужен ТОЛЬКО payload — он в base64url (не base64!).
ClientID используется для:
- Изоляции данных в БД (runs.client_id)
- Изоляции трекера инстансов (/tmp/instances-{clientId}-{stand}.json)
- Отображения в UI
Безопасность: мы НЕ проверяем подпись — это не нужно.
Токен уже проверен Nubes API (detect_endpoint делает реальный запрос)."""
token = get_token()
try:
parts = token.split(".") # [header, payload, signature]
if len(parts) >= 2:
# base64url → добавляем padding ("==") на случай если длина не кратна 4
payload = base64.urlsafe_b64decode(parts[1] + "==")
return json.loads(payload).get("ClientID", "")
except Exception:
pass # битый токен — не критично, вернём пустую строку
return ""
def get_stand():
"""Определить стенд (dev/test/mock) по активному токену.
Если POLYGON_ENDPOINT задан → "polygon".
Иначе — автоопределение или stand_name."""
if current_app.config.get("POLYGON_ENDPOINT", ""):
return "polygon"
token = get_token()
endpoint = current_app.config["NUBES_API_ENDPOINT"]
if endpoint not in STANDS:
s = stand_name(endpoint)
return s if s != "?" else "mock"
endpoint = detect_endpoint(token) or endpoint
return stand_name(endpoint)
def get_token_info():
"""Извлечь {email, company, client_id} из JWT — для отображения в топбаре UI.
Возвращает dict с ключами: email, company, client_id.
Если JWT невалиден — возвращает пустой {}."""
token = get_token()
try:
parts = token.split(".")
if len(parts) >= 2:
payload = base64.urlsafe_b64decode(parts[1] + "==")
d = json.loads(payload)
return {
"email": d.get("email", ""),
"company": d.get("company_name", ""),
"client_id": d.get("ClientID", ""),
}
except Exception:
pass
return {}
def get_token_masked():
"""Маскированный env-токен для placeholder в форме: abc...xyz.
Используется ТОЛЬКО env-токен (не пользовательский!).
Если токен короче 8 символов — возвращает пустую строку."""
token = current_app.config["NUBES_API_TOKEN"]
if not token or len(token) < 8:
return ""
# Первые 4 символа + звёздочки + последние 4 символа
return token[:4] + "*" * (len(token) - 8) + token[-4:]