v0.6.9: защита от пустого clientId (null client_id)

This commit is contained in:
2026-06-15 10:35:03 +04:00
parent 3d3354c49e
commit 00d3928a50
5 changed files with 84 additions and 2 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ipwhitelist",
"version": "0.6.8",
"version": "0.6.9",
"description": "IP WhiteList microservice for cloud provider",
"main": "server.js",
"scripts": {
+80
View File
@@ -832,3 +832,83 @@ express HTTP
| 1 | Интеграция в основной код | ✅ 0.5.98 |
| 2 | Auth (KC callback) | ⏸ заблокирован облаком |
| 3 | Убрать старые v2_* таблицы из БД | ⏸ после деплоя |
---
## Интеграция v2 в основной проект (0.6.00.6.8, 2026-06-15)
### Что сделано
| Версия | Что |
|--------|-----|
| 0.5.98 | Таблицы `v2_*``companies`, `whitelist_entries`, `audit_log` |
| 0.5.98 | `impersonation/` модуль — добавление компаний через ENV |
| 0.5.99 | V2 как основной UI: `app.use('/', createV2Router())`, старый UI закомментирован |
| 0.6.0 | Дизайн Nubes: лого, header, стили из `views/index.ejs` |
| 0.6.1 | Фикс: email в header |
| 0.6.2 | `displayEmail` справа от статистики |
| 0.6.3 | Жёлтый баннер имперсонации вместо метки `(имперсонация)` |
| 0.6.4 | Авто-имперсонация: `IMPERSONATION_ORIGINAL → TARGET` |
| 0.6.4 | `ADMIN_EMAIL` в `resolveContext` |
| 0.6.5 | `loginEmail` из `originalUserEmail` |
| 0.6.6 | `req.displayEmail` — разделение экран/аудит |
| 0.6.7 | **Перепутаны местами**: `req.email` и `req.impersonatedBy` |
| 0.6.8 | «Выйти» — только реальный KC-юзер |
### Ключевая ошибка: перепутаны `req.email` и `req.impersonatedBy`
**Что было неправильно (0.6.6 и ранее):**
```js
req.email = реальный created_by = ntazetdinov@nubes.ru
req.impersonatedBy = имперсонированный impersonated_by = tazet@narod.ru
```
**Почему ошибался:** AI думал что `req.email` должно хранить реального юзера «для аудита»,
а имперсонированного — для показа. Это неверно. Везде должен использоваться ТЕКУЩИЙ
юзер (имперсонированный если есть имперсонация), а реальный — ТОЛЬКО в колонке
`impersonated_by` таблицы аудита.
**Как правильно (0.6.7+):**
```js
req.email = ТЕКУЩИЙ created_by = tazet@narod.ru
req.impersonatedBy = РЕАЛЬНЫЙ impersonated_by = ntazetdinov@...
```
**Урок:** `req.email` — это ВСЕГДА текущий юзер. Имперсонация не меняет его смысл —
она меняет значение `session.user.email`, и `req.email` просто берёт его.
### Правильная логика (0.6.8)
```
KC логин: ntazetdinov@nubes.ru
session.user.email = ntazetdinov@nubes.ru
enhanceImpersonation: ORIGINAL совпал → авто-имперсонация
session.user.originalUserEmail = ntazetdinov@nubes.ru (реальный)
session.user.email = tazet@narod.ru (текущий)
resolveContext:
req.email = u.email = tazet@narod.ru (текущий — created_by, экран)
req.impersonatedBy = u.originalUserEmail (реальный — только аудит)
req.isImpersonated = true
user/index.js → шаблон:
loginEmail = ntazetdinov@nubes.ru → «Выйти»
email = tazet@narod.ru → контент, статистика
```
### Переменные ENV для авто-имперсонации
| Переменная | Значение | Роль |
|-----------|---------|------|
| `IMPERSONATION_ORIGINAL` | `ntazetdinov@nubes.ru` | Условие: чей email сравнить |
| `IMPERSONATION_TARGET` | `tazet@narod.ru` | Цель: в кого имперсонировать |
| `IMPERSONATION_COMPANY` | `WZ01325` | Компания: какая компания |
| `IMPERSONATION_EXTRA_COMPANIES` | `WZ01112` | Доп. компании |
| `ADMIN_EMAIL` | `tazet@narod.ru` | Псевдо-админ |
Все три (`ORIGINAL`, `TARGET`, `COMPANY`) должны быть заданы — иначе авто-имперсонация не включается.
+1 -1
View File
@@ -9,7 +9,7 @@
// ═══════════════════════════════════════════════════════════════════════════════
module.exports = {
version: '0.6.8',
version: '0.6.9',
// ── IAM ──────────────────────────────────────────────────────────────────
iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user',
+1
View File
@@ -31,6 +31,7 @@ const { validate } = require('../validators');
// Возвращает { id, client_id, name, custom_limit }.
// Вызывается из list/add/edit/remove перед каждой операцией.
async function resolve(clientId) {
if (!clientId || !clientId.trim()) throw new Error('Ошибка: не определён clientId (W-номер компании). Обратитесь к администратору.');
return q.getOrCreateCompany(clientId, clientId);
}
+1
View File
@@ -42,6 +42,7 @@ function resolveContext(req, res, next) {
// ВЕЗДЕ: created_by, экран, формы
req.email = u.email || '';
req.clientId = isImpersonated ? u.impersonatedCompanyId : (u.activeClientId || u.clientId || '');
if (!req.clientId) return res.status(400).send('<h2>Ошибка</h2><p>Не удалось определить W-номер компании. Проверьте настройки IAM.</p><p><a href="/logout">Выйти</a></p>');
req.companyName = u.companyName || req.clientId;
req.isAdmin = !!(u.isAdmin && u.adminMode) || (ADMIN_EMAIL && u.email === ADMIN_EMAIL);
req.isImpersonated = isImpersonated;