Files
ipwhitelist-app/v2/history/2026-06-12.md
T

372 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# V2 — история 2026-06-12 (финал)
## V2 — это тестовый стенд для модулей
Код в `v2/` — НЕ production. Это полигон для отладки модулей.
Каждый модуль тестируется изолированно, затем будет интегрирован в основной код.
## Текущее состояние (0.5.73)
```
v2/
├── server.js # createV2Router() → /v2/login, /v2/iam, /v2/logout
├── src/
│ ├── auth/index.js # fetchIamUser(), login(), exchangeCode(), buildAuthUrl()
│ ├── config/index.js # iamUrl, appUrl, version (без KC — пока не нужен)
│ ├── crud/index.js # createCrudRouter() — НЕ монтирован (будет позже)
│ ├── db/
│ │ ├── index.js # pg pool
│ │ ├── queries.js # CRUD SQL
│ │ └── schema.js # ensureSchema(pool) — автосоздание v2_* таблиц
│ └── validators/index.js # CIDR validate, overlaps, BLOCKED_RANGES
└── history/
└── 2026-06-12.md
```
## Как работает сейчас
1. `/v2/login` → редирект на `/login?returnTo=/v2/iam`
2. Основной OIDC: production-KC → `/callback` → сессия → редирект на `/v2/iam`
3. `/v2/iam` → показывает IAM-данные из `req.session.user`
V2 НЕ делает свой OIDC. Использует готовую сессию основного приложения.
Никаких своих KC-настроек. Никакого `/v2/callback`.
## Модуль auth/index.js — готов, но не используется
Функции готовы к production:
- `buildAuthUrl(config)` — URL редиректа на KC
- `exchangeCode(code, config)` — обмен code → токен
- `fetchIamUser(token, iamUrl)` — IAM → профиль
- `generateState()` — CSRF
- `login(code, oidcConfig, iamUrl)` — оркестратор
Не тестировались: нужен `/v2/callback` в redirect URIs production-KC.
## Модуль crud/ — готов, не монтирован
Функции:
- `createCrudRouter()` — роутер с выбором компании (всегда список, даже если одна)
- CRUD через `db/queries.js`
Таблицы: v2_companies, v2_entries, v2_audit (автосоздание через schema.js).
## Модуль db/ — готов
- `db/index.js` — pg pool
- `db/queries.js` — getOrCreateCompany, listEntries, createEntry, updateEntry, deleteEntry, getAudit, ...
- `db/schema.js` — ensureSchema(pool) — CREATE TABLE IF NOT EXISTS
## Модуль validators/ — готов
CIDR validate, overlaps, cidrToRange, aggregateCIDRs, BLOCKED_RANGES.
## Что дальше
1. fio в fetchIamUser — возвращать строку (сейчас объект)
2. Добавить `/v2/callback` в redirect URIs production-KC
3. Протестировать auth/login() с production-KC
4. Подключить crud модуль
5. Админка, экспорт
## Ключевые решения
- V2 НЕ трогает основной код (server.js — только app.use('/v2', ...))
- Каждый модуль — отдельная папка в src/
- env-переменные — через process.env, без .env/dotenv
- Умолчания — хардкод, переопределяются через V2_* env
---
## Модуль router/ (0.5.75)
`resolveContext` middleware — определяет контекст из `req.session.v2_user`:
```
нет сессии / нет v2_user → 302 /v2/login
обычный юзер → req.v2_email, req.v2_clientId (activeClientId)
юзер с несколькими компаниями → activeClientId из сессии
админ + adminMode → req.v2_isAdmin = true
админ без adminMode → req.v2_isAdmin = false (видит как юзер)
имперсонация → email подменён на originalUserEmail
clientId подменён на impersonatedCompanyId
req.v2_impersonatedBy = реальный админ
```
### Тесты (v2/src/router/test.js)
9 тестов, все пройдены ✅:
1. нет сессии → /v2/login
2. нет v2_user → /v2/login
3. обычный юзер 1 компания
4. юзер 2 компании → activeClientId
5. админ adminMode=true
6. админ adminMode=false
7. имперсонация с originalUserEmail
8. имперсонация без originalUserEmail
9. без activeClientId → fallback на clientId
### Ошибки при тестировании
- Причина: спешка. null вместо undefined, двойной префикс v2_v2_ в ключах.
- Урок: сначала думать, потом писать.
## Текущая структура (0.5.75)
```
v2/src/
├── auth/index.js # fetchIamUser(), login(), exchangeCode()
├── config/index.js # iamUrl, appUrl, version
├── router/
│ ├── index.js # resolveContext middleware
│ └── test.js # 9 тестов
├── user/index.js # createUserRouter (пустышка)
├── admin/index.js # createAdminRouter (пустышка)
├── crud/index.js # createCrudRouter (не монтирован)
├── db/ # pool, queries, schema
└── validators/index.js # CIDR validate
```
---
## ⚠️ ПРИ ИНТЕГРАЦИИ — что переименовать
### Таблицы (префикс `v2_` → убрать)
```
v2_companies → companies
v2_entries → whitelist_entries
v2_audit → audit_log
```
Причина: PostgreSQL один на всё приложение.
### Сессия — НЕ трогать (уже без префикса)
```
req.session.user ← одинаково в v2 и основном коде
req.session.token
req.session.adminMode
```
### req.v2_* контекст — убрать префикс
```
req.v2_email → req.email
req.v2_clientId → req.clientId
req.v2_isAdmin → req.isAdmin
req.v2_isImpersonated → req.isImpersonated
req.v2_impersonatedBy → req.impersonatedBy
req.v2_allClientIds → req.allClientIds
req.v2_profiles → req.profiles
req.v2_companyName → req.companyName
```
Причина: в основном коде нет префиксов.
---
## Текущее состояние (0.5.89)
### Структура v2/
```
v2/
├── server.js # createV2Router() — монтируется в server.js как /v2
├── history/
│ └── 2026-06-12.md # этот файл
└── src/
├── auth/index.js # fetchIamUser(), login(), exchangeCode(), buildAuthUrl()
├── config/index.js # V2_* env, version, умолчания
├── router/index.js # resolveContext middleware (email, clientId, isAdmin, impersonation)
├── user/index.js # createUserRouter() — UI CRUD с выбором компании
├── admin/index.js # createAdminRouter() — ПУСТЫШКА (email, clientId)
├── crud/index.js # createCrudRouter() — старый, НЕ монтирован
├── db/
│ ├── index.js # pg pool
│ ├── queries.js # CRUD: getOrCreateCompany, listEntries, createEntry, updateEntry, deleteEntry, getAudit, getLimit, setLimit, getExportCIDRs, getAllCompanies
│ └── schema.js # ensureSchema(pool) — автосоздание v2_companies, v2_entries, v2_audit
├── test/
│ ├── index.js # createTestRouter() — /v2/test?action=... и GET /chaos
│ └── test.sh # 54 curl-теста
└── validators/index.js # validate(), overlaps(), cidrToRange(), aggregateCIDRs(), BLOCKED_RANGES
```
### Маршруты /v2
| Маршрут | Что | Авторизация |
|---------|-----|------------|
| `/v2/login` | Редирект на `/login?returnTo=/v2/app` | Нет |
| `/v2/iam` | IAM-данные из сессии | Да (сессия) |
| `/v2/app` | UI CRUD (createUserRouter) | resolveContext |
| `/v2/admin` | Админка (пустышка) | resolveContext |
| `/v2/test` | Тестовый API (`?action=...`) | Нет |
| `/v2/test/chaos` | Параллельный хаос-тест | Нет |
| `/v2/logout` | Редирект на `/logout` | Нет |
### Деплой
git push → Gitea → Nubes UI redeploy → `whitelist.nodejsk8s.services.ngcloud.ru`
VM (italo.kube5s.ru) — НЕ используется для деплоя v2.
---
## Тесты — полный список
### 1. test.sh — 54 curl-теста (✅ все пройдены)
**Запрещённые диапазоны (14 тестов):**
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, 127.0.0.0/8,
169.254.0.0/16, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24,
198.18.0.0/15, 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32
**Нормализация и маски (5 тестов):**
- /22 работает, /21 отклонена, /33 отклонена, IPv6 отклонён, домен отклонён
- Нормализация хостовой части (13.0.0.5/24 → 13.0.0.0/24)
- wasNormalized=true
**Лимиты (2 теста):**
- 16-я запись отклонена (лимит 15)
- Сообщение о лимите
**CRUD полный цикл (10 тестов):**
- Добавление, добавление 2, добавление /32
- Список — 3 записи
- Изменение CIDR, проверка в БД
- Soft delete, список после удаления, с удалёнными
**Изоляция компаний (3 теста):**
- Добавление в компанию A/B, пересечение разрешено
**Переключение компаний (5 тестов):**
- Добавление в активную, переключение, список, добавление в другую, обратно
**Имперсонация — двойной аудит (6 тестов):**
- created_by = originalUserEmail
- audit: impersonated_by, user_email
- UPDATE: old→new записаны
- DELETE записан
**Нагрузка (2 теста):**
- 50 добавлений (первые 15 ок, остальные лимит)
- БД: ровно 15 записей
**Экспорт (1 тест):**
- Эндпоинт доступен
### 2. Chaos-тест — параллельные пользователи (✅)
`GET /v2/test/chaos` — 4 компании × 10 записей = 40 параллельных операций.
Проверки:
- ok: 40, errors: 0
- totalEntries: 40, totalAudit: 40
- 4 компании по 10 записей, без дубликатов, в лимите
Префиксы CIDR: 11, 12, 13, 14 (10.x заблокирован 10.0.0.0/8).
### 3. Router — resolveContext (✅ 9 тестов)
Файл: `v2/src/router/test.js` (или внутренние).
1. нет сессии → /v2/login
2. нет user → /v2/login
3. обычный юзер 1 компания
4. юзер 2 компании → activeClientId
5. админ adminMode=true
6. админ adminMode=false
7. имперсонация с originalUserEmail
8. имперсонация без originalUserEmail
9. без activeClientId → fallback на clientId
### Сводка
| Группа | Кол-во | Статус |
|--------|--------|--------|
| test.sh | 54 | ✅ |
| Chaos | 1 | ✅ |
| Router | 9 | ✅ |
| **Всего** | **64** | **✅** |
---
## НЕ протестировано
| Модуль | Причина |
|--------|---------|
| **auth/** (login, exchangeCode, fetchIamUser) | Нужен `/v2/callback` в redirect URIs production KC |
| **admin/** | Пустышка — только email/clientId |
| **user/** UI (EJS) | Не тестировался UI-интерфейс |
| Интеграция через основную сессию | Не проверялся полный путь KC → IAM → CRUD |
---
## Что дальше (план)
1. Реализовать **admin/** — аудит, лимиты, все компании
2. Реализовать **export/** — выгрузка CIDR
3. Добавить `/v2/callback` в production KC → протестировать auth/
4. EJS-шаблоны вместо inline HTML
5. Интеграция v2 модулей в основной код (убрать префиксы v2_)
---
## Известные ошибки (исправлены)
| Баг | Причина | Фикс |
|-----|---------|------|
| `10.x` CIDR → 45 ошибок в chaos | `10.0.0.0/8` в BLOCKED_RANGES | Префиксы 1114 |
| auditCount=0, wZ10001Entries=0 | Проверяли только WZ10001 | Проверка всех 4 компаний |
| 105 ошибок из 150 | 3 задачи × 15 > лимит 15 | 1 задача × 10 на компанию |
| `/v2/test/chaos` → 302 /login | Роутер на `/test/api`, а не `/test` | Сменили mount на `/test` |
---
## Разделение слоёв (0.5.90+) — ПЛАН
### Проблема
Сейчас `user/index.js` знает про `db/queries.js` и `getOrCreateCompany`/`companyId`:
```
user/index.js → db/queries.js → SQL
```
Фронтенд смешан с логикой БД.
### Решение: три слоя
```
user/index.js (фронтенд, Express) → crud/index.js (API БД) → db/queries.js (SQL)
```
**crud/index.js** — чистые функции-агностики:
- Не знает про Express, req, res, сессии
- Принимает `clientId` (W-номер), сам резолвит `companyId` через `getOrCreateCompany`
- Вход: plain values, Выход: plain object или throw
```js
list(clientId) { entries, used, limit }
add(clientId, cidr, comment, email, impBy) { entry, wasNormalized }
edit(entryId, clientId, cidr, comment, email, impBy) { entry, wasNormalized }
remove(entryId, clientId, email, impBy) void
```
**user/index.js** — только Express:
- Дёргает crud.*, рендерит HTML
- Не знает про companyId, getOrCreateCompany, SQL
**db/queries.js** — без изменений (чистый SQL)
### Что изменится
| Файл | Было | Стало |
|------|------|-------|
| crud/index.js | старый, не рабочий | новый API-слой |
| user/index.js | require('../db/queries') | require('../crud') |
| test/index.js | require('../db/queries') | require('../crud') |
### Преимущества
1. **Тестирование**: crud тестируется без Express, user тестируется с моком crud
2. **Переиспользование**: admin, test, export — все через crud
3. **Независимость**: фронтенд не знает про deviceId, БД-схему, транзакции