4 changed files with 49 additions and 21 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ipwhitelist", "name": "ipwhitelist",
"version": "0.5.147", "version": "0.5.150",
"description": "IP WhiteList microservice for cloud provider", "description": "IP WhiteList microservice for cloud provider",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
+1 -1
View File
@@ -13,7 +13,7 @@
// ── IAM API ─────────────────────────────────────────────────────────────────── // ── IAM API ───────────────────────────────────────────────────────────────────
// URL IAM-сервиса для получения профилей пользователя (GET /api/v1/auth/user) // URL IAM-сервиса для получения профилей пользователя (GET /api/v1/auth/user)
// и переключения активной компании (POST /api/v1/user/switch-profile). // и переключения активной компании (POST /api/v1/user/switch-profile).
const IAM_API_URL = (process.env.IAM_API_URL || 'https://auth-api-dev.ngcloud.ru').replace(/\/$/, ''); const IAM_API_URL = (process.env.IAM_API_URL || 'https://auth-api.ngcloud.ru').replace(/\/$/, '');
// ── Мок-пользователи (только dev/staging) ───────────────────────────────────── // ── Мок-пользователи (только dev/staging) ─────────────────────────────────────
// В продакшене этот список не используется — вход через Keycloak. // В продакшене этот список не используется — вход через Keycloak.
+43 -15
View File
@@ -69,7 +69,7 @@ function createV2Router() {
const token = req.session && req.session.token; const token = req.session && req.session.token;
if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>'); if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>');
try { try {
const raw = await iamFetch(token, 'https://auth-api-test.ngcloud.ru/api/v1/auth/user'); const raw = await iamFetch(token, config.iamApiBase + '/auth/user');
res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Content-Type', 'text/html; charset=utf-8');
res.send(rawPage('IAM TEST', raw)); res.send(rawPage('IAM TEST', raw));
} catch (e) { } catch (e) {
@@ -82,7 +82,7 @@ function createV2Router() {
const token = req.session && req.session.token; const token = req.session && req.session.token;
if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>'); if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>');
try { try {
const raw = await iamFetch(token, 'https://auth-api-dev.ngcloud.ru/api/v1/auth/user'); const raw = await iamFetch(token, config.iamApiBase + '/auth/user');
res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Content-Type', 'text/html; charset=utf-8');
res.send(rawPage('IAM DEV', raw)); res.send(rawPage('IAM DEV', raw));
} catch (e) { } catch (e) {
@@ -90,29 +90,39 @@ function createV2Router() {
} }
}); });
// ── GET /v2/impersonation-check — IAM impersonation: 3 GET эндпоинта ── // ── GET /v2/iam-gateway-test — сравнение auth-api vs lk-api-gateway ──
router.get('/impersonation-check', async (req, res) => { router.get('/iam-gateway-test', async (req, res) => {
const token = req.session && req.session.token; const token = req.session && req.session.token;
if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>'); if (!token) return res.send('<h2>Нет токена</h2><p><a href="/v2/login">Войти</a></p>');
const endpoints = [ const base = config.iamApiBase;
{ name: 'status', url: 'https://auth-api.ngcloud.ru/api/v1/impersonation/status' }, const tests = [
{ name: 'history', url: 'https://auth-api.ngcloud.ru/api/v1/impersonation/history' }, {
{ name: 'history/all', url: 'https://auth-api.ngcloud.ru/api/v1/impersonation/history/all' }, name: 'auth-api /auth/user',
url: base + '/auth/user',
},
{
name: 'lk-api-gateway /iam/auth/user',
url: 'https://lk-api-gateway.ngcloud.ru/api/v1/iam/auth/user',
},
{
name: 'auth-api /impersonation/status',
url: base + '/impersonation/status',
},
]; ];
const results = {}; const results = {};
for (const ep of endpoints) { for (const t of tests) {
try { try {
const data = await iamFetch(token, ep.url); const data = await iamFetch(token, t.url);
results[ep.name] = { ok: true, data }; results[t.name] = { ok: true, data };
} catch (e) { } catch (e) {
results[ep.name] = { ok: false, error: e.message }; results[t.name] = { ok: false, error: e.message };
} }
} }
res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Content-Type', 'text/html; charset=utf-8');
res.send(iamStatPage(results)); res.send(iamComparePage(results));
}); });
// ── /v2/app — пользовательский CRUD (только с сессией) ────────────── // ── /v2/app — пользовательский CRUD (только с сессией) ──────────────
@@ -136,7 +146,25 @@ function createV2Router() {
router.use('/export', createExportRouter()); router.use('/export', createExportRouter());
// ── GET /v2/logout — редирект на основной выход ───────────────────── // ── GET /v2/logout — редирект на основной выход ─────────────────────
router.get('/logout', (req, res) => res.redirect('/logout')); 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('<!DOCTYPE html><html><head><meta charset="utf-8"><title>Выход</title>'
+ '<style>body{font-family:sans-serif;background:#111;color:#eee;padding:40px;text-align:center}'
+ 'h2{color:#4fc3f7}p{font-size:16px;margin:20px 0}'
+ '.btn{display:inline-block;padding:12px 24px;margin:0 10px;border-radius:6px;text-decoration:none;font-size:16px}'
+ '.btn-yes{background:#ef5350;color:#fff}.btn-no{background:#333;color:#eee}'
+ '</style></head><body>'
+ '<h2>Выход из приложения</h2>'
+ '<p>Выход завершит сессию во всех сервисах Nubes (Keycloak SSO).<br>Вы уверены?</p>'
+ '<a href="/logout" class="btn btn-yes">Да, выйти</a>'
+ '<a href="/v2/app" class="btn btn-no">Отмена</a>'
+ '</body></html>');
}
res.redirect('/logout');
});
return router; return router;
} }
@@ -187,7 +215,7 @@ function debugPage(u, version) {
</body></html>`; </body></html>`;
} }
function iamStatPage(results) { function iamComparePage(results) {
function fmt(data, depth) { function fmt(data, depth) {
if (data === null || data === undefined) return '<span style="color:#888">null</span>'; if (data === null || data === undefined) return '<span style="color:#888">null</span>';
if (typeof data === 'string') return '<span style="color:#ce93d8">"' + data.replace(/</g, '&lt;') + '"</span>'; if (typeof data === 'string') return '<span style="color:#ce93d8">"' + data.replace(/</g, '&lt;') + '"</span>';
+4 -4
View File
@@ -4,18 +4,18 @@
// ЭТО: все настройки в одном месте. // ЭТО: все настройки в одном месте.
// ЗАЧЕМ: всё что может поменяться между средами — здесь, не размазано по коду. // ЗАЧЕМ: всё что может поменяться между средами — здесь, не размазано по коду.
// //
// ПРИОРИТЕТ: process.env (V2_*) → хардкод-умолчания. // ПРИОРИТЕТ: process.env → хардкод-умолчания.
// Умолчания — для тестовой среды (VM KC на italo.kube5s.ru:8080).
// ═══════════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════════
module.exports = { module.exports = {
version: '0.5.146', version: '0.5.149',
// ── IAM ────────────────────────────────────────────────────────────────── // ── IAM ──────────────────────────────────────────────────────────────────
iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user', iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user',
iamApiBase: process.env.IAM_API_BASE || 'https://auth-api.ngcloud.ru/api/v1',
// ── OIDC (Keycloak) ────────────────────────────────────────────────────── // ── OIDC (Keycloak) ──────────────────────────────────────────────────────
kcBaseUrl: process.env.V2_KC_BASE_URL || 'https://italo.kube5s.ru:8080/realms/ipwhitelist', kcBaseUrl: process.env.V2_KC_BASE_URL || 'https://login.ngcloud.ru/realms/ipwhitelist',
kcClientId: process.env.V2_KC_CLIENT_ID || 'ipwhitelist-app', kcClientId: process.env.V2_KC_CLIENT_ID || 'ipwhitelist-app',
kcClientSecret: process.env.V2_KC_CLIENT_SECRET || '', kcClientSecret: process.env.V2_KC_CLIENT_SECRET || '',
appUrl: process.env.V2_APP_URL || 'https://whitelist.nodejsk8s.services.ngcloud.ru', appUrl: process.env.V2_APP_URL || 'https://whitelist.nodejsk8s.services.ngcloud.ru',