diff --git a/docs/history/2026-06-11.md b/docs/history/2026-06-11.md new file mode 100644 index 0000000..e23696f --- /dev/null +++ b/docs/history/2026-06-11.md @@ -0,0 +1,128 @@ +# Сессия 2026-06-11 — Имперсонация, toggle, аудит + +## Хронология + +### 1. Починка toggle «Администратор» +**Проблема:** чекбокс не показывался. +**Причина:** `canAdminMode` вычислялся в `resolveUser`, но не передавался в `res.render`. +**Исправление:** добавлен `canAdminMode: req.user.canAdminMode` в `entries.js`. + +### 2. Дубликат catch → крэш +При правке `ui/index.js` остался дубликат `} catch {`. Приложение падало при старте. +**Исправление:** удалён лишний catch. + +### 3. JS onclick → form POST +Toggle не реагировал — сложный inline JS с `getComputedStyle` и `fetch`. +**Исправление:** заменён на `
`. Ноль JS. + +### 4. Чужой email в header +Админ видел `nstasyuk@nubes.ru` вместо своего — шаблон показывал `selectedCompany.owner_email`. +**Исправление:** всегда показывать `user.email`. + +### 5. Хардкод isNail +IAM не давал `isAdmin: true` для `ntazetdinov@nubes.ru`. +**Исправление:** временный хардкод `isNail` в `ui/index.js`. + +### 6. canWrite из IAM +IAM возвращает `can_write: false` для всех — это IAM-шная RBAC, не наш флаг. +**Исправление:** `canWrite` убран полностью. + +### 7. Имперсонация: флаги из IAM +Добавлены поля из IAM: `isImpersonated`, `originalUserEmail`, `originalUserFullName`, `impersonationType`. +**Файлы:** `src/auth.js:fetchIamUser`, `src/routes/oidc.js`, `ui/index.js:resolveUser`. + +### 8. Баннер имперсонации +В `views/index.ejs` добавлен жёлтый баннер при `isImpersonated`. + +### 9. Двойной аудит +Колонка `impersonated_by` в `audit_log`. +**Файлы:** `src/db.js` (миграция), `src/queries.js:logAudit`, `src/api/routes/entries.js`. + +### 10. X-Imp-* заголовки — мост UI→API +UI-слой знал про имперсонацию, API-слой — нет. +**Исправление:** `api-client.js:impHeaders()`, `bearerMiddleware` читает `X-Imp-Email`, `X-Imp-OriginalEmail`, `X-Imp-ClientId`. + +### 11. Безопасность: OIDC state check +`|| true` в `/callback` отключал проверку state во всех окружениях. +**Исправление:** убрано `|| true`. + +### 12. Безопасность: open redirect +`POST /login-token` редиректил без `safeReturn`. +**Исправление:** добавлен `safeReturn(returnTo)`. + +### 13. Безопасность: client_id без проверки +При пустом `allowedIds` можно было подставить любой `?client_id=`. +**Исправление:** `requestedId` принимается только если `allowedIds.length > 0`. + +### 14. Тестовая имперсонация +Три env-переменные: `IMPERSONATION_ORIGINAL`, `IMPERSONATION_TARGET`, `IMPERSONATION_COMPANY`. +**Файл:** `ui/index.js` (блок `TEST IMPERSONATION`). + +### 15. Часовой пояс GMT+4 +`Europe/Moscow` → `Etc/GMT-4` в `views/index.ejs` и `views/audit.ejs`. + +### 16. Аудит: колонка «Имперсонировал» +В `views/audit.ejs` добавлена колонка `impersonated_by`. Фильтр по компании — кнопка вместо JS. + +### 17. Удалённые записи: визуал +Зачёркивание + полупрозрачность. Кнопки скрыты для удалённых. +**Файл:** `views/index.ejs`. + +### 18. Чекбокс «Показать удалённые» +Возвращён в админ-режим. +**Файл:** `views/index.ejs`. + +### 19. ТЗ-факт +Составлен документ `TZs/ТЗ-факт.md` — техзадание по фактически реализованному функционалу. + +### 20. Сверка с требованиями Сергея +Прочитан `Files/wsm.txt`. Подтверждено соответствие. Не сделано: ссылка на запрещённые префиксы, текст-мануал от Стасюка. + +## Основные файлы, изменённые в сессии + +| Файл | Что | +|---|---| +| `ui/index.js` | canAdminMode, isNail, TEST IMPERSONATION, canWrite | +| `ui/routes/entries.js` | canAdminMode в render, impHeaders, упрощение ownCo | +| `ui/routes/auth.js` | toggle-admin redirect, open redirect fix | +| `ui/api-client.js` | impHeaders(), X-Imp-* заголовки | +| `src/auth.js` | fetchIamUser (все поля), bearerMiddleware (X-Imp-*), safeReturn | +| `src/routes/oidc.js` | isImpersonated в сессию, fix || true | +| `src/api/routes/entries.js` | impersonatedBy в queries, client_id fix, export JSON | +| `src/db.js` | ALTER TABLE impersonated_by | +| `src/queries.js` | logAudit с impersonatedBy | +| `views/index.ejs` | toggle (form POST), баннеры, удалённые записи, includeDeleted | +| `views/audit.ejs` | колонка impersonated_by, кнопка фильтра, GMT+4 | +| `server.js` | debug-session с lastAudit | + +## Ветки +- `save-2026-06-11` — снапшот перед критичными фиксами +- `save-2026-06-12` — финальное состояние + +--- + +## Что осталось (для следующего чата) + +### Баги +1. **Toggle виден при имперсонации** — по требованию Сергея: «Когда я имперсонирую — вижу то же что пользователь». При имперсонации `canAdminMode` должен быть false (сейчас true, потому что вычисляется из оригинального JWT, а не из `activeClientId`). `ui/index.js` строка 65-67. +2. **Email в header берётся из `payload.email`**, а не из `req.session?.user?.email`. Иногда показывает чужой email. `ui/index.js` строка 75. + +### Не сделано по ТЗ/требованиям Сергея +3. Ссылка на список зарезервированных префиксов (14 диапазонов) +4. Краткий мануал на главной для пользователей (ждём от Стасюка) + +### Временные решения (убрать при настройке IAM) +5. `isNail` — хардкод `ntazetdinov@nubes.ru` как админ. Заменить на `isAdmin` из IAM. `ui/index.js` строка 63. +6. `TEST IMPERSONATION` — env-переменные. Убрать для прода. `ui/index.js` строки 70-78. + +### Архитектурный долг +7. Дублирование `buildSessionUser` в `oidc.js` и `auth.js` +8. Флаги `isAdmin`/`adminMode`/`canAdminMode`/`isNail` — упростить до двух +9. Циклическая зависимость `ui → src/auth` через `require('../../src/auth')` + +## Как работать дальше + +1. Прочитать ТЗ: `TZs/ТЗ-факт.md` (фактическое), `docs/ТЗ.md` (оригинал) +2. Прочитать требования Сергея: `Files/wsm.txt` (особенно строки 378-470) +3. Начать с бага №1 (toggle + имперсонация) — это противоречит требованию заказчика +