# Сессия 2026-06-22 — export view=2, фикс имперсонации, TZ аудит ## Версии - **0.1.1** — сброс версии с 0.5.180 - **0.1.2** — `white-space: pre` на `` (пробелы схлопывались) - **0.1.3** — таблица без точек, октеты в своих `` - **0.1.4** — `text-align: right` в ячейках - **0.1.5** — `padStart(2)` плотнее (один пробел, не два) - **0.1.6** — фикс детекции имперсонации - **0.1.7** — аудит: время Москвы вместо UTC --- ## 1. Export view=2 — HTML-таблица с выровненными IP ### Устройство Файл: `v2/src/export/index.js` Три режима: | Параметр | Content-Type | Поведение | |---|---|---| | (нет) | `text/plain` | Скачивание `.txt` | | `?view=1` | `text/plain` | Inline в браузере | | `?view=2` | `text/html` | Таблица: 4 ячейки октетов + маска | ### Структура view=2 ```html
62 76 39 5 / 32
``` - Каждый октет в своей ``, без точек - `padStart(2, ' ')` — минимум 2 символа на октет - `text-align: right` — выравнивание по правой границе - `white-space: pre` — пробелы не схлопываются - Границы не видны (`border-collapse: collapse`, без `border` на td) --- ## 2. Фикс имперсонации — БАГ ### Симптом На странице `/v2/app` не показывался баннер «Режим имперсонации», хотя IAM API возвращал `is_impersonated: true`. ### Причина В двух местах `v2/src/router/index.js` проверка имперсонации использовала `u.impersonatedCompanyId`: ```js // checkImpersonation — строка 78 (было) const oldIsImp = !!(u.originalUserEmail && u.impersonatedCompanyId); // applyContext — строка 133 (было) const isImpersonated = !!(u.originalUserEmail && u.impersonatedCompanyId); ``` **Поле `impersonatedCompanyId` отсутствует в ответе IAM.** IAM `/auth/user` возвращает: ```json { "impersonation": { "is_impersonated": true, "impersonatedUserId": "...", "originalUserEmail": "ntazetdinov@nubes.ru", "originalUserFullName": "...", "originalUserCompany": "...", "sessionId": 613, ... // ⚠️ impersonatedCompanyId — НЕТ } } ``` Из-за этого: - `u.impersonatedCompanyId` всегда `''` (пустая строка = falsy) - `!!(truthy && falsy)` → всегда `false` - Баннер не показывался - `checkImpersonation` на каждый запрос заново дёргал IAM (думал что статус изменился) ### Исправление Заменить проверку на `u.isImpersonated` (которое корректно выставляется в `checkImpersonation`): ```js // checkImpersonation (исправлено) const oldIsImp = !!u.isImpersonated; // applyContext (исправлено) const isImpersonated = !!(u.isImpersonated || u.originalUserEmail); ``` --- ## 3. Время в аудите — UTC → MSK Файл: `views/v2/admin-audit.ejs`, строка 100. ```js // Было (возвращает серверное время = UTC): new Date(a.created_at).toLocaleString('ru') // Стало (Москва, UTC+3): new Date(a.created_at).toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }) ``` --- ## 4. Проверка IAM API Три эндпоинта, проверены через Node.js с IAM-токеном (`iss: auth-api`): | Эндпоинт | Статус | Результат | |---|---|---| | `auth-api /auth/user` | 200 | Полные данные + имперсонация | | `lk-api-gateway /iam/auth/user` | 200 | **Идентично** auth-api | | `auth-api /impersonation/status` | 200 | `is_impersonating`, `session_id`, etc. | Данные между `auth-api` и `lk-api-gateway` совпадают поле-в-поле. ### Важно: токены - **Keycloak-токен** (iss: `auth.k8s.ngcloud.ru`) → IAM возвращает 401 (не может проверить подпись) - **IAM-токен** (iss: `auth-api`) → работает - **Tech-токен** (8541 символ) → nginx 400 (превышен лимит заголовка 8KB) - Для проверки API нужен IAM-токен из активной сессии --- ## 5. Админ при имперсонации `tazet@narod.ru` показывается как админ, потому что: 1. IAM честно говорит `isAdmin: false` для `tazet@narod.ru` 2. Но `applyContext` проверяет `email === ADMIN_EMAIL` → `true` 3. `ADMIN_EMAIL=tazet@narod.ru` в ENV Это ожидаемое поведение — `ADMIN_EMAIL` даёт админские права независимо от IAM.