feat: test auth mode — accept any plain token without JWT validation
authTestMode=true in middleware/auth.go: - any non-whitespace, non-JWT string is accepted as Bearer token - string is used as sub for namespace derivation (SHA256) - JWT validation still runs for actual JWT strings (xxx.yyy.zzz) - revert to strict mode: authTestMode = false iot-console.html: - namespaceFromToken: plain tokens use the string itself as sub - login form: updated placeholder + hint explaining test mode Image: v0.1.60
This commit is contained in:
@@ -285,26 +285,42 @@ function saveSettings() {
|
||||
localStorage.setItem('iot_mqtt_base',S.mqttBase);
|
||||
}
|
||||
|
||||
// namespaceFromToken — вычисляет k8s namespace из JWT токена.
|
||||
// namespaceFromToken — вычисляет k8s namespace из токена.
|
||||
// Алгоритм ИДЕНТИЧЕН Terraform провайдеру (client.NamespaceFromSub):
|
||||
// JWT payload → sub claim → SHA256(sub) → первые 8 байт → "sless-{16 hex символов}"
|
||||
// Пользователь НИКОГДА не вводит namespace вручную — это гарантирует изоляцию.
|
||||
// SHA256(sub) → первые 8 байт → "sless-{16 hex символов}"
|
||||
//
|
||||
// Поддерживает два режима (соответствует authTestMode на сервере):
|
||||
// - Режим JWT (прод): токен вида xxx.yyy.zzz → извлекаем sub из payload → SHA256(sub)
|
||||
// - Тестовый режим: любая строка без пробелов → используем её саму как sub → SHA256(token)
|
||||
//
|
||||
// Чтобы вернуться только к JWT: убрать ветку isPlainToken ниже.
|
||||
async function namespaceFromToken(token) {
|
||||
let sub;
|
||||
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) throw new Error('Неверный формат токена (не JWT)');
|
||||
if (parts.length === 3) {
|
||||
// ── JWT путь (боевой режим) ──────────────────────────────────────────────
|
||||
// base64url → base64 (заменяем символы, добавляем padding)
|
||||
let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (payload.length % 4) payload += '=';
|
||||
|
||||
// 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); }
|
||||
|
||||
let claims;
|
||||
try { claims = JSON.parse(atob(payload)); }
|
||||
catch(e) { throw new Error('Не удалось декодировать токен: ' + e.message); }
|
||||
if (!claims.sub) throw new Error('В токене нет поля sub — токен не поддерживается');
|
||||
sub = claims.sub;
|
||||
} else {
|
||||
// ── Тестовый режим: plain token ─────────────────────────────────────────
|
||||
// Строка без пробелов используется напрямую как идентификатор (sub).
|
||||
// Сервер (authTestMode=true) тоже принимает такой токен без JWT-проверки.
|
||||
// ВАЖНО: каждая уникальная строка → отдельный изолированный namespace.
|
||||
if (/\s/.test(token)) throw new Error('Токен не должен содержать пробелы');
|
||||
sub = token;
|
||||
}
|
||||
|
||||
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));
|
||||
// SHA256(sub) через Web Crypto API (встроен во все современные браузеры)
|
||||
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(sub));
|
||||
|
||||
// Берём первые 8 байт → 16 hex символов — идентично Go: hash[:8]
|
||||
const hex = Array.from(new Uint8Array(buf).slice(0, 8))
|
||||
@@ -541,9 +557,13 @@ function renderLogin() {
|
||||
</div>
|
||||
<div id="login-err"></div>
|
||||
<div class="form-group">
|
||||
<label>Bearer токен</label>
|
||||
<input id="f-tok" type="password" value="${h(S.token)}" placeholder="eyJ...">
|
||||
<div class="hint" style="margin-top:6px;">Данные вашего аккаунта определяются автоматически</div>
|
||||
<label>Bearer токен <span style="color:#e08020;font-size:10px;font-weight:600;">· тестовый режим</span></label>
|
||||
<input id="f-tok" type="password" value="${h(S.token)}" placeholder="любая-уникальная-строка или eyJ...JWT">
|
||||
<div class="hint" style="margin-top:6px;">
|
||||
<b>Тест:</b> введите любую строку без пробелов — например <code>alice</code> или <code>my-device-test</code>.<br>
|
||||
Каждая строка → отдельный изолированный namespace.<br>
|
||||
Реальный JWT nubes тоже принимается без изменений.
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary mt8" style="width:100%;" id="login-btn" onclick="doLogin()">
|
||||
Войти
|
||||
|
||||
Reference in New Issue
Block a user