136 lines
5.1 KiB
Markdown
136 lines
5.1 KiB
Markdown
# Сессия 2026-06-22 — export view=2, фикс имперсонации, TZ аудит
|
||
|
||
## Версии
|
||
- **0.1.1** — сброс версии с 0.5.180
|
||
- **0.1.2** — `white-space: pre` на `<code>` (пробелы схлопывались)
|
||
- **0.1.3** — таблица без точек, октеты в своих `<td>`
|
||
- **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
|
||
<table>
|
||
<tr>
|
||
<td> 62</td><td> 76</td><td> 39</td><td> 5</td><td> / 32</td>
|
||
</tr>
|
||
</table>
|
||
```
|
||
|
||
- Каждый октет в своей `<td>`, без точек
|
||
- `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.
|