документация: сессия 2026-06-22 — export view=2, баг имперсонации, TZ аудит
This commit is contained in:
@@ -0,0 +1,135 @@
|
|||||||
|
# Сессия 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.
|
||||||
Reference in New Issue
Block a user