From 2d164fb9ee0a56993bb34bd65ba4f018eaee6428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 17 Jun 2026 20:51:12 +0400 Subject: [PATCH] =?UTF-8?q?v0.5.158:=20logout=20=D0=B1=D0=B5=D0=B7=20KC,?= =?UTF-8?q?=20401=3Ddestroy+redirect=20SSO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- v2/server.js | 47 ++++++++++++++++++++++++++++-------------- v2/src/router/index.js | 24 ++++++++++++++------- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 149e113..3b4a606 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipwhitelist", - "version": "0.5.157", + "version": "0.5.158", "description": "IP WhiteList microservice for cloud provider", "main": "server.js", "scripts": { diff --git a/v2/server.js b/v2/server.js index c261f12..3ff792f 100644 --- a/v2/server.js +++ b/v2/server.js @@ -145,25 +145,42 @@ function createV2Router() { const { createExportRouter } = require('./src/export'); router.use('/export', createExportRouter()); - // ── GET /v2/logout — редирект на основной выход ───────────────────── + // ── GET /v2/logout — выход только из whitelist (без KC SSO) ───────── router.get('/logout', (req, res) => { const ua = (req.headers['user-agent'] || '').toLowerCase(); const isBrowser = ua.includes('mozilla') || ua.includes('chrome') || ua.includes('safari'); - if (isBrowser) { - res.set('Content-Type', 'text/html; charset=utf-8'); - return res.send('Выход' - + '' - + '

Выход из приложения

' - + '

Выход завершит сессию во всех сервисах Nubes (Keycloak SSO).
Вы уверены?

' - + 'Да, выйти' - + 'Отмена' - + ''); + if (!isBrowser) { + req.session.destroy(() => res.redirect('/v2/login')); + return; } - res.redirect('/logout'); + + if (req.query.confirm === 'yes') { + req.session.destroy(() => { + res.set('Content-Type', 'text/html; charset=utf-8'); + res.send('Выход' + + '' + + '

Вы вышли из сервиса

' + + '

Сессия whitelist завершена.
Сессия в других сервисах Nubes сохранена.

' + + 'Войти снова' + + ''); + }); + return; + } + + res.set('Content-Type', 'text/html; charset=utf-8'); + res.send('Выход' + + '' + + '

Выход из сервиса

' + + '

Вы будете выведены только из whitelist.
Сессия в других сервисах Nubes сохранится.

' + + 'Выйти' + + 'Отмена' + + ''); }); return router; diff --git a/v2/src/router/index.js b/v2/src/router/index.js index 8201ea2..6df03ba 100644 --- a/v2/src/router/index.js +++ b/v2/src/router/index.js @@ -1,18 +1,27 @@ const https = require('https'); const config = require('../config'); -function resolveContext(req, res, next) { +/** + * Express middleware — извлекает контекст из сессии. + * При 401 от IAM — чистит сессию и редиректит на логин (SSO). + */ +async function resolveContext(req, res, next) { const u = req.session && req.session.user; - - // Нет сессии — нет доступа if (!u) return res.redirect('/v2/login'); - // ── Проверка имперсонации через IAM ────────────────────────────── - // Если есть токен — запрашиваем свежие данные, чтобы обнаружить имперсонацию - // которая могла начаться после логина (ЛК сессия отдельно от whitelist). const token = req.session && req.session.token; if (token) { - checkImpersonation(token, req, u).catch(() => { /* fallback — тихо */ }); + try { + await checkImpersonation(token, req, u); + } catch (e) { + const msg = (e.message || ''); + // 401 — токен протух, чистим сессию → SSO даст свежий + if (msg.startsWith('401')) { + req.session.destroy(() => res.redirect('/v2/login')); + return; + } + // Другие ошибки IAM — игнорируем, используем старые данные + } } applyContext(req, u); @@ -21,6 +30,7 @@ function resolveContext(req, res, next) { /** * Запрашивает IAM и обновляет сессию если имперсонация изменилась. + * Бросает ошибку при 401 — вызывающий обработает. */ async function checkImpersonation(token, req, u) { const url = config.iamApiBase + '/auth/user';