diff --git a/internal/api/ui/iot-console.html b/internal/api/ui/iot-console.html index dc42157..217624d 100644 --- a/internal/api/ui/iot-console.html +++ b/internal/api/ui/iot-console.html @@ -191,6 +191,7 @@ const S = { // Сохраняемые настройки token: localStorage.getItem('iot_token') || '', + // ns вычисляется из токена автоматически (SHA256(sub) → "sless-{hex}"), не вводится вручную ns: localStorage.getItem('iot_ns') || '', apiBase: localStorage.getItem('iot_api_base') || 'https://sless.kube5s.ru', mqttBase: localStorage.getItem('iot_mqtt_base')|| 'ws://iot.kube5s.ru/mqtt', @@ -213,11 +214,39 @@ const S = { // Сохраняем auth-параметры в localStorage function saveSettings() { localStorage.setItem('iot_token', S.token); - localStorage.setItem('iot_ns', S.ns); + localStorage.setItem('iot_ns', S.ns); // сохраняем для восстановления сессии localStorage.setItem('iot_api_base', S.apiBase); localStorage.setItem('iot_mqtt_base',S.mqttBase); } +// namespaceFromToken — вычисляет k8s namespace из JWT токена. +// Алгоритм ИДЕНТИЧЕН Terraform провайдеру (client.NamespaceFromSub): +// JWT payload → sub claim → SHA256(sub) → первые 8 байт → "sless-{16 hex символов}" +// Пользователь НИКОГДА не вводит namespace вручную — это гарантирует изоляцию. +async function namespaceFromToken(token) { + const parts = token.split('.'); + if (parts.length !== 3) throw new Error('Неверный формат токена (не JWT)'); + + // base64url → base64 (заменяем символы, добавляем padding) + let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/'); + while (payload.length % 4) payload += '='; + + let claims; + try { claims = JSON.parse(atob(payload)); } + catch(e) { throw new Error('Не удалось декодировать токен: ' + e.message); } + + if (!claims.sub) throw new Error('В токене нет поля sub — токен не поддерживается'); + + // SHA256(sub) через Web Crypto API (встроен в все современные браузеры) + const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(claims.sub)); + + // Берём первые 8 байт → 16 hex символов — идентично Go: hash[:8] + const hex = Array.from(new Uint8Array(buf).slice(0, 8)) + .map(b => b.toString(16).padStart(2, '0')).join(''); + + return 'sless-' + hex; +} + // ============================================================= // API // ============================================================= @@ -452,13 +481,10 @@ function renderLogin() { -
- - -
+
Данные вашего аккаунта определяются автоматически
`;