docs: актуализация (2026-05-30)
Deprecated (описывали состояние до рефакторинга): [DEPRECATED]-plan.md [DEPRECATED]-analysis-2026-05-30.md [DEPRECATED]-auth-architecture.md Новые/обновлённые: architecture.md — текущее устройство: стек, модули, auth-схема, env, маршруты plan.md — только pending задачи (блокеры KK, деплой, Redis, пагинация) questions.md — закрытые вопросы отмечены, открытые: KK creds + admin claim + /export IP
This commit is contained in:
+67
-78
@@ -1,107 +1,96 @@
|
||||
# IP WhiteList — План разработки
|
||||
# IP WhiteList — План (pending задачи, 2026-05-30)
|
||||
|
||||
> **Дата:** 2026-05-30
|
||||
> **Основание:** [WhiteIPlist.txt](../WhiteIPlist.txt) (ТЗ) + фактический код в [ipwhitelist-app](../../ipwhitelist-app)
|
||||
> **Статус:** каноничный — единственный актуальный план
|
||||
> **Ветка:** `sonnet`
|
||||
> Всё что было в старых планах — реализовано. Здесь только то, чего ещё нет.
|
||||
|
||||
---
|
||||
|
||||
## 1. Текущее состояние
|
||||
## Блокер 1 — Keycloak OIDC (нужны данные от DevOps)
|
||||
|
||||
### Готово ✅
|
||||
Код OIDC Authorization Code Flow написан и готов (`src/auth.js`).
|
||||
Не активируется пока не получены:
|
||||
|
||||
| Слой | Файлы | Что есть |
|
||||
| Что нужно | Env-переменная | Статус |
|
||||
|---|---|---|
|
||||
| БД | `sql/schema.sql` | `companies`, `whitelist_entries` (soft delete), `audit_log` + индексы |
|
||||
| Валидатор | `src/validators.js` | Все 14 запрещённых диапазонов Приложения А, /22–/32, нормализация host-битов, проверка пересечений |
|
||||
| CRUD | `src/queries.js` | `createEntry`, `updateEntry`, `deleteEntry` (soft), `listEntries`, `getExportCIDRs`, `getAudit`, `getLimit` |
|
||||
| UI | `views/index.ejs` | Список, форма добавления, кнопка удаления, стиль Nubes |
|
||||
| Роуты | `server.js` | `GET /`, `POST /add`, `POST /delete/:id`, `GET /export`, `GET /healthz` |
|
||||
| client_id в realm cloud | `KC_CLIENT_ID` | ❌ ждём DevOps |
|
||||
| client_secret | `KC_CLIENT_SECRET` | ❌ ждём DevOps |
|
||||
| redirect_uri зарегистрирован в KK | (в самом KK) | ❌ ждём DevOps |
|
||||
| SESSION_SECRET для продакшена | `SESSION_SECRET` | ❌ нужно сгенерировать |
|
||||
|
||||
### Написано в queries.js, но не подключено к роутам
|
||||
|
||||
- `updateEntry` — нет `POST /update/:id`, нет UI редактирования
|
||||
- `getAudit` — нет страницы аудита
|
||||
- `listEntries(companyId, includeDeleted=true)` — флаг есть, не используется
|
||||
Без этого сервис работает в mock-режиме (`/dev-login` как тестовый backdoor).
|
||||
|
||||
---
|
||||
|
||||
## 2. Что требует ТЗ, но отсутствует
|
||||
## Блокер 2 — Admin-роль из Keycloak
|
||||
|
||||
| # | Требование | Готовность |
|
||||
|---|---|---|
|
||||
| 1 | Редактирование записи из UI | ❌ queries есть, роута нет |
|
||||
| 2 | Админ-панель (все компании, лимиты, аудит, фильтры) | ❌ |
|
||||
| 3 | OIDC Keycloak вместо base64-заглушки | ❌ |
|
||||
| 4 | Суммаризация CIDR в `/export` | ❌ отдаёт сырой список |
|
||||
| 5 | Клиентская валидация (JS в форме) | ❌ |
|
||||
| 6 | Переключатель компаний (multi-company) | ❌ |
|
||||
| 7 | Просмотр soft-deleted записей админом | ❌ |
|
||||
| 8 | Изменение `custom_limit` для компании | ❌ |
|
||||
Сейчас admin = `clientId === WZ01112`.
|
||||
Как реально приходит `isAdmin` из KK — не ясно (см. [questions.md](questions.md)).
|
||||
После получения ответа — 1–2 строки в `userFromPayload()` в `src/auth.js`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Порядок реализации
|
||||
## Задача 1 — Деплой ветки sonnet
|
||||
|
||||
### Этап 1 — Пользовательский сценарий (client-flow)
|
||||
|
||||
- [x] Валидатор IPv4/CIDR — полный
|
||||
- [x] Создание / удаление / экспорт
|
||||
- [ ] **Добавить `POST /update/:id`** в `server.js`
|
||||
- [ ] **Добавить inline-форму редактирования** в `views/index.ejs`
|
||||
- [ ] **Клиентская валидация** (JS: формат, маска, длина комментария)
|
||||
|
||||
### Этап 2 — Административная панель
|
||||
|
||||
- [ ] Определение admin-роли (`clientId === 'WZ01112'` + чекбокс)
|
||||
- [ ] `GET /admin` — страница со всеми компаниями
|
||||
- [ ] `POST /admin/limit/:companyId` — изменение custom_limit
|
||||
- [ ] `GET /admin/audit` — журнал аудита
|
||||
- [ ] Фильтр по компании + показ удалённых записей
|
||||
|
||||
### Этап 3 — Авторизация Keycloak OIDC
|
||||
|
||||
- [ ] `npm install openid-client`
|
||||
- [ ] Замена base64-decode на проверку подписи JWT
|
||||
- [ ] Маппинг claims → `req.user` (clientId, email, role)
|
||||
- [ ] Оставить `DEV_MODE` только для локальной разработки
|
||||
|
||||
### Этап 4 — Экспорт и Multi-company
|
||||
|
||||
- [ ] `npm install cidr-tools` — суммаризация в `GET /export`
|
||||
- [ ] Переключатель активной компании (если несколько `clientId` в claims)
|
||||
|
||||
### Этап 5 — Завершение
|
||||
|
||||
- [ ] Автотесты (`jest` + `supertest`)
|
||||
- [ ] Пагинация (если лимит > 50)
|
||||
- [ ] Сверка всех пунктов ТЗ
|
||||
- [ ] Передеплоить на `white.nodejsk8s.dev.nubes.ru` (сейчас там master)
|
||||
- [ ] Добавить env-секреты в k8s: `SESSION_SECRET`, `CSRF_SECRET`
|
||||
- [ ] Проверить smoke-тест: `/healthz`, `/login`, `/export`
|
||||
|
||||
---
|
||||
|
||||
## 4. Что НЕ делать
|
||||
## Задача 2 — Сессии при multi-pod
|
||||
|
||||
- ❌ Не переписывать на Python/FastAPI
|
||||
- ❌ Не менять схему БД
|
||||
- ❌ Не переписывать `validators.js` (он полный)
|
||||
- ❌ Не переписывать `queries.js`
|
||||
- ❌ Не делать SPA
|
||||
Сейчас: MemoryStore (in-process, не масштабируется).
|
||||
При нескольких репликах k8s сессии будут теряться.
|
||||
|
||||
- [ ] `npm install connect-redis ioredis`
|
||||
- [ ] Подключить Redis в `server.js` (3 строки конфига)
|
||||
- [ ] Добавить `REDIS_URL` в env
|
||||
|
||||
**Блокер:** нужен Redis в k8s (или принять ограничение на 1 реплику пока).
|
||||
|
||||
---
|
||||
|
||||
## 5. Зависимости для установки
|
||||
## Задача 3 — IP-ограничение /export
|
||||
|
||||
```
|
||||
npm install cidr-tools openid-client
|
||||
npm install --save-dev jest supertest
|
||||
```
|
||||
ТЗ: endpoint доступен без авторизации, ограничен по IP на старте.
|
||||
Текущее решение: открытый endpoint с rate-limit (20 req/мин).
|
||||
|
||||
Всё остальное — в рамках Express + EJS + pg.
|
||||
- [ ] Уточнить: ограничение в приложении или ingress? (см. [questions.md](questions.md))
|
||||
- [ ] Если в приложении: whitelist IP через `EXPORT_ALLOWED_IPS` env var
|
||||
|
||||
---
|
||||
|
||||
## 6. Риски
|
||||
## Задача 4 — Пагинация
|
||||
|
||||
- **Admin-роль:** неясно как «отдельный чек-бокс» из ТЗ попадает в токен — требует уточнения с командой Keycloak
|
||||
- **Multi-company claims:** ТЗ говорит о нескольких компаниях, но в claims только `clientID` — нужен реальный формат
|
||||
- **IP-ограничение `/export`:** делать в приложении или на уровне ingress — решить при деплое
|
||||
При `custom_limit` > 50 таблица станет неудобной.
|
||||
Сейчас все записи выводятся без пагинации.
|
||||
|
||||
- [ ] `GET /?page=N` + `LIMIT/OFFSET` в `queries.listEntries()`
|
||||
- [ ] Кнопки Назад/Вперёд в `views/index.ejs`
|
||||
|
||||
Низкий приоритет — текущий дефолтный лимит 15 записей на компанию.
|
||||
|
||||
---
|
||||
|
||||
## Задача 5 — Auto-dismiss alert
|
||||
|
||||
Flash-алерты (ошибка/успех) сейчас исчезают только при перезагрузке.
|
||||
|
||||
- [ ] 10 строк JS в `views/index.ejs`: `setTimeout(() => alert.remove(), 5000)`
|
||||
|
||||
---
|
||||
|
||||
## Не делать (закрыто)
|
||||
|
||||
| Что | Почему |
|
||||
|---|---|
|
||||
| ~~CSRF-защита~~ | ✅ csrf-csrf (double-submit cookie) |
|
||||
| ~~Рефакторинг server.js~~ | ✅ 131 строка, роутеры вынесены |
|
||||
| ~~Автотесты~~ | ✅ 50 unit-тестов (`node tests/run-tests.js`) |
|
||||
| ~~aggregateCIDRs в /export~~ | ✅ в `src/validators.js` |
|
||||
| ~~Admin-панель~~ | ✅ `src/routes/admin.js`, `views/admin.ejs` |
|
||||
| ~~Редактирование записи~~ | ✅ `POST /edit/:id` |
|
||||
| ~~Аудит~~ | ✅ `GET /audit` |
|
||||
| ~~OIDC Authorization Code Flow~~ | ✅ `src/auth.js` + `src/routes/auth.js` |
|
||||
| ~~Session middleware~~ | ✅ `express-session` в `server.js` |
|
||||
| ~~Dev-login backdoor~~ | ✅ `/dev-login` с пресетами + кастомные поля |
|
||||
|
||||
Reference in New Issue
Block a user