feat: IAM API интеграция + обновление ТЗ + тесты
- src/auth.js: fetchIamUser(), switchProfile(), userFromPayload(iamData) - src/config.js: IAM_API_URL из env - src/routes/oidc.js: обогащение сессии через IAM (с fallback на JWT) - ui/routes/auth.js: обогащение при токен-логине - ui/routes/entries.js: switchTo через POST /switch-profile - src/api/routes/entries.js: resolveCompany через profiles[] - views/index.ejs: переключатель компаний с названиями из profiles - .env.example: IAM_API_URL - docs: обновлены ТЗ-реализация.md, ТЗ-плюс.md, добавлен iam-integration.md - tests: api-crud.sh (14 тестов CRUD + валидации) - .gitignore: исключены .env.test, DEPLOY-TESTING.md
This commit is contained in:
+159
-115
@@ -1,160 +1,204 @@
|
||||
# ТЗ-плюс — уточнения и дополнения от заказчика
|
||||
|
||||
> Основа: `docs/ТЗ.md`
|
||||
> Файл для фиксации уточнений, дополнений и решений по мере обсуждения с заказчиком/DevOps.
|
||||
> Каждая запись = дата + источник + формулировка + статус.
|
||||
> Дата последнего обновления: 2026-06-04
|
||||
> Файл для фиксации уточнений, дополнений и решений.
|
||||
|
||||
---
|
||||
|
||||
## 1. Аутентификация и Keycloak
|
||||
## 1. Аутентификация — интеграция с IAM API
|
||||
|
||||
### 1.1. client_id приложения в Keycloak
|
||||
- **Дата**: 2026-06-02
|
||||
- **Источник**: HAR-файл
|
||||
- **Факт**: `client_id` присутствует в URL auth-запроса
|
||||
- **Статус**: 🔴 требует подтверждения — какой client_id будет для ipwhitelist?
|
||||
### 1.1. IAM API — единый сервис авторизации (УТОЧНЕНО 04.06.2026)
|
||||
|
||||
### 1.2. ClientID в JWT — УТОЧНЕНО
|
||||
- **ТЗ**: claim `ClientID` — идентификатор компании
|
||||
- **Факт (HAR)**: `ClientID: "WZ01325"`
|
||||
- **Факт (другой источник)**: `ClientID` может отсутствовать
|
||||
- **Уточнение заказчика (01.06.2026)**: в claims приходит `clientid`. Формат может быть разный.
|
||||
- Одно значение: `WZ04228`, `1700`, `asokolov-test`
|
||||
- Несколько через запятую: `WZ11125, WZ03816`, `WZ52235, WZ62587, WZ02315`
|
||||
- **Логика**: первое «слово» до запятой определяет общий ЛК.
|
||||
Пользователи с одинаковым первым словом → общий whitelist.
|
||||
- **Что делать в коде**: брать первый `clientid` до запятой как активную компанию.
|
||||
Остальные (если есть) — дополнительные компании пользователя для переключателя.
|
||||
- **Статус**: 🟡 уточнено, требует реализации в коде
|
||||
IAM (Identity & Access Management) — сервис авторизации экосистемы Nubes.
|
||||
Все смежные сервисы ходят в него для проверки компании пользователя.
|
||||
|
||||
### 1.3. Несколько компаний на пользователя — УТОЧНЕНО
|
||||
- **ТЗ, п.3.1**: «поддерживается сценарий, когда пользователь принадлежит нескольким компаниям»
|
||||
- **Уточнение заказчика (01.06.2026)**: поле `clientid` в claims содержит список через запятую.
|
||||
Примеры: `WZ11125, WZ03816`, `WZ52235, WZ62587, WZ02315`.
|
||||
- **Логика ЛК**: первое значение до запятой — активная компания.
|
||||
Пользователи с одинаковым первым `clientid` видят общий whitelist.
|
||||
Переключатель между компаниями — все значения из списка.
|
||||
- **Открытый вопрос**: какое именно поле в Keycloak за это отвечает? Заказчик уточнит.
|
||||
- **Статус**: 🟡 уточнено, требует реализации в коде
|
||||
**Три стенда:**
|
||||
|
||||
### 1.4. Admin-роль в Keycloak
|
||||
- **ТЗ**: admin = `ClientID = WZ01112` + отдельный чек-бокс
|
||||
- **Вопрос**: чек-бокс — это claim? Какой? `is_admin`, `realm_access.roles`, `groups`?
|
||||
- **Статус**: 🔴
|
||||
| Стенд | URL |
|
||||
|---|---|
|
||||
| Dev | `https://auth-api-dev.ngcloud.ru` |
|
||||
| Test | `https://auth-api-test.ngcloud.ru` |
|
||||
| Prod | `https://auth-api.ngcloud.ru` |
|
||||
|
||||
### 1.5. JWKS URL
|
||||
- **Вариант A**: `https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs`
|
||||
- **Вариант B**: `https://auth-api...` (через API Gateway)
|
||||
- **Статус**: 🔴
|
||||
**Swagger:** `https://auth-api-dev.ngcloud.ru/api/v1/documentation/`
|
||||
|
||||
### 1.6. Redirect URI
|
||||
- **Предложение**: `https://<домен>/callback`
|
||||
- **Статус**: 🔴 требует подтверждения DevOps
|
||||
**Исходный источник:** CRM Elma (контакт, компания, отношение N:M).
|
||||
|
||||
**Статус:** 🟢 подтверждено
|
||||
|
||||
### 1.2. Поток аутентификации (УТОЧНЕНО 04.06.2026)
|
||||
|
||||
```
|
||||
Пользователь → Keycloak (SSO) → IAM (обмен code) → access_token + refresh_token
|
||||
│
|
||||
┌───────────────────────────────┘
|
||||
▼
|
||||
GET /api/v1/auth/user (Bearer access_token)
|
||||
│
|
||||
▼
|
||||
{ profiles[], userInfo }
|
||||
```
|
||||
|
||||
`access_token` от IAM (token_type: "access", TTL 12 часов) сразу годится для
|
||||
вызова `GET /api/v1/auth/user`. Никакого дополнительного обмена не требуется.
|
||||
|
||||
**Статус:** 🟢 подтверждено (Node.js HTTP 200, получен реальный ответ)
|
||||
|
||||
### 1.3. client_id приложения в Keycloak
|
||||
- **Источник:** HAR-файл
|
||||
- **Факт:** `client_id` в URL auth-запроса
|
||||
- **Статус:** 🔴 требует подтверждения
|
||||
|
||||
### 1.4. JWKS URL и Redirect URI
|
||||
- **Статус:** 🔴 требует подтверждения DevOps
|
||||
|
||||
---
|
||||
|
||||
## 2. Инфраструктура и деплой
|
||||
## 2. Компании пользователя — IAM /auth/user
|
||||
|
||||
### 2.1. Платформа
|
||||
- **Статус**: 🔴 уточнить — Node.js на k8s? Какой кластер?
|
||||
### 2.1. `GET /api/v1/auth/user` (УТОЧНЕНО 04.06.2026)
|
||||
|
||||
### 2.2. Деплой
|
||||
- **Статус**: 🔴 уточнить — CI/CD? Как поставлять `jsonEnv`?
|
||||
**Реальный ответ** (пользователь `tazetdinovn@gmail.com`, prod):
|
||||
|
||||
### 2.3. Сетевое ограничение для /export
|
||||
- **Вопрос**: какие IP/подсети будут потребителями внешней выдачи?
|
||||
- **Статус**: 🔴
|
||||
```json
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"id": 4357,
|
||||
"client_id": "WZ03709",
|
||||
"company_id": "019cc24a-727e-740f-b407-bec79dab4162",
|
||||
"company_name": "naeel_test",
|
||||
"company_numeric_id": 2645,
|
||||
"is_active_profile": true
|
||||
}
|
||||
],
|
||||
"userInfo": {
|
||||
"clientID": "WZ03709",
|
||||
"company": "naeel_test",
|
||||
"companyId": "019cc24a-727e-740f-b407-bec79dab4162",
|
||||
"companyNumericId": 2645,
|
||||
"email": "tazetdinovn@gmail.com",
|
||||
"isAdmin": false,
|
||||
"fio": {"fullName": "Тазетдинов Наиль Фаритович", "name": "Наиль", "secondName": "Фаритович", "surname": "Тазетдинов"},
|
||||
"contactId": "019cc268-6c6a-781e-8613-4bed4ec7cd20",
|
||||
"elmaUserId": 39715
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Маппинг полей для использования в коде:**
|
||||
|
||||
| Назначение | Откуда |
|
||||
|---|---|
|
||||
| Список WZ-кодов компаний | `profiles[].client_id` |
|
||||
| Активная компания | `profiles[].is_active_profile === true` |
|
||||
| ID для switch-profile | `profiles[].id` |
|
||||
| WZ активной компании | `userInfo.clientID` |
|
||||
| Название компании | `userInfo.company` |
|
||||
| UUID компании | `userInfo.companyId` |
|
||||
| Флаг админа | `userInfo.isAdmin` |
|
||||
| Email | `userInfo.email` |
|
||||
|
||||
**Статус:** 🟢 подтверждено
|
||||
|
||||
### 2.2. `POST /api/v1/user/switch-profile` (УТОЧНЕНО 04.06.2026)
|
||||
|
||||
Тело: `{"profile_id": 3120}` или `{"company_id": "uuid"}`.
|
||||
|
||||
**Статус:** 🟡 уточнено по Swagger, не тестировалось на живом multi-company юзере
|
||||
|
||||
### 2.3. ⚫ ClientID через запятую в JWT — УСТАРЕЛО
|
||||
|
||||
- **Было (01.06):** `ClientID: "WZ11125, WZ03816"` в claims JWT
|
||||
- **Стало (04.06):** JWT содержит только ОДНУ компанию (активную).
|
||||
Правильный источник списка — `profiles[]` из IAM API.
|
||||
- **Статус:** ⚫ отменено
|
||||
|
||||
### 2.4. ⚫ Admin = WZ01112 — УСТАРЕЛО
|
||||
|
||||
- **Было:** `clientId === 'WZ01112'` → админ
|
||||
- **Стало:** `userInfo.isAdmin` из IAM API
|
||||
- **Статус:** ⚫ отменено
|
||||
|
||||
---
|
||||
|
||||
## 3. Функциональные уточнения
|
||||
## 3. Сетевая доступность
|
||||
|
||||
### 3.1. Мульти-компания в UI
|
||||
- **ТЗ, п.3.1**: «переключатель активной компании»
|
||||
- **Вопрос**: дизайн переключателя? Dropdown в шапке? Отдельная страница?
|
||||
- **Статус**: 🔴
|
||||
### 3.1. ddos-guard (УТОЧНЕНО 04.06.2026)
|
||||
|
||||
### 3.2. Soft-delete и фильтр «показать удалённые»
|
||||
- **ТЗ, п.4.1**: «для администратора предусмотрен фильтр»
|
||||
- **Код**: `includeDeleted` всегда `false`
|
||||
- **Статус**: 🟡 не реализовано в коде
|
||||
Все `*.ngcloud.ru` за ddos-guard. IP `81.200.23.210` не блокируется —
|
||||
проблема в HTTP-клиенте:
|
||||
|
||||
### 3.3. Индивидуальные лимиты компаний
|
||||
- **ТЗ, п.4.5**: админ может задать индивидуальный лимит
|
||||
- **Код**: ?
|
||||
- **Статус**: 🔴 проверить реализацию
|
||||
| Клиент | auth-api | deck-api-test |
|
||||
|---|---|---|
|
||||
| `curl` (без --http2) | ❌ 403 | ❌ 403 |
|
||||
| `curl --http2` | ✅ 200 | ❌ 403 |
|
||||
| `Node.js https` | ✅ 200 | ✅ 200 |
|
||||
| Браузер | ✅ 200 | ✅ 200 |
|
||||
|
||||
### 3.4. Уведомление о нормализации
|
||||
- **ТЗ, п.5**: «пользователь должен быть уведомлен, что ввел адрес из хостовой части»
|
||||
- **Статус**: 🔴 проверить реализацию в UI
|
||||
**Правило для curl:** `--http2` + `Accept: application/json`.
|
||||
|
||||
**Node.js (приложение):** работает без проблем.
|
||||
|
||||
**Статус:** 🟢 подтверждено
|
||||
|
||||
---
|
||||
|
||||
## 4. Тестовые данные
|
||||
## 4. Инфраструктура и деплой
|
||||
|
||||
### 4.1. Тестовые пользователи
|
||||
- **Платформа:** 🔴 уточнить
|
||||
- **CI/CD:** 🔴 уточнить
|
||||
- **Сетевое ограничение /export:** 🔴 уточнить
|
||||
|
||||
---
|
||||
|
||||
## 5. Функциональные уточнения
|
||||
|
||||
### 5.1. Мульти-компания в UI
|
||||
- Список компаний из `profiles[]`, активная по `is_active_profile`
|
||||
- Переключение: UI → `POST /switch-profile` на IAM → обновить сессию
|
||||
- **Статус:** 🟡 требует реализации
|
||||
|
||||
### 5.2. Soft-delete фильтр
|
||||
- `includeDeleted` всегда `false`
|
||||
- **Статус:** 🟡 не реализовано
|
||||
|
||||
### 5.3. Индивидуальные лимиты
|
||||
- **Статус:** 🔴 проверить
|
||||
|
||||
### 5.4. Уведомление о нормализации
|
||||
- **Статус:** 🔴 проверить
|
||||
|
||||
---
|
||||
|
||||
## 6. Тестовые данные
|
||||
|
||||
| Email | ClientID | Компания | Роль |
|
||||
|---|---|---|---|
|
||||
| `client@example.com` | `WZ01325` | Тест | Клиент |
|
||||
| `admin@nubes.ru` | `WZ01112` | Нубес | Админ |
|
||||
| `tazetdinovn@gmail.com` | ? | ? | ? (из token.txt) |
|
||||
| `tazetdinovn@gmail.com` | WZ03709 | naeel_test | Пользователь |
|
||||
| `client@example.com` | WZ01325 | Тест | Клиент |
|
||||
| `admin@nubes.ru` | WZ01112 | Нубес | Админ |
|
||||
|
||||
### 4.2. HAR-сессия
|
||||
- Файл: `Files/nubes_login.har`
|
||||
- Пользователь: `tazet@narod.ru`
|
||||
- Дата: 2026-05-30
|
||||
- Окружение: deck-test
|
||||
### HAR-сессия
|
||||
- Файл: `Files/nubes_login.har`, пользователь `tazet@narod.ru`, deck-test
|
||||
|
||||
---
|
||||
|
||||
## 6. Уточнения от заказчика (01.06.2026)
|
||||
## 7. Принятые решения
|
||||
|
||||
### 6.1. Формат clientid в токене
|
||||
- Поле: `clientid` в claims JWT
|
||||
- Формат: строка, значения через запятую если несколько
|
||||
- Примеры:
|
||||
- `"WZ04228"` — одна компания
|
||||
- `"1700"` — числовой ID
|
||||
- `"asokolov-test"` — текстовый ID
|
||||
- `"WZ11125, WZ03816"` — две компании
|
||||
- `"WZ52235, WZ62587, WZ02315"` — три компании
|
||||
- **Правило**: первое «слово» до запятой = активная компания (определяет ЛК)
|
||||
- **Общий доступ**: пользователи с одинаковым первым словом имеют общий whitelist
|
||||
|
||||
### 6.2. Экспорт — подтверждение формата
|
||||
- GET-endpoint отдаёт txt файл
|
||||
- Одна строка = один объект (адрес/подсеть)
|
||||
- Без разделителей, без заголовков
|
||||
|
||||
### 6.3. Текущее демо — одобрено
|
||||
- `https://white.nodejsk8s.dev.nubes.ru/login` — ✓
|
||||
- `https://white.nodejsk8s.dev.nubes.ru/exp` — ✓ (вывод без токена для тестирования)
|
||||
|
||||
### 6.4. Что ещё уточняется
|
||||
- Какое именно поле Keycloak маппится в `clientid` claim? (Заказчик уточнит)
|
||||
- Как определяется admin-роль? (отдельный чек-бокс в Keycloak?)
|
||||
- `client_id` и `client_secret` для регистрации приложения
|
||||
|
||||
### 6.5. Права внутри компании — уточнено (02.06.2026)
|
||||
- **Вопрос**: может ли любой юзер компании редактировать whitelist всей компании?
|
||||
- **Ответ заказчика**: «Да, любой юзер, который может войти может редактировать»
|
||||
- **Вывод**: внутри компании роли не разграничиваются. Любой сотрудник компании имеет полный доступ к whitelist своей компании (создание, редактирование, удаление). Аудит фиксирует кто именно сделал изменение.
|
||||
|
||||
---
|
||||
|
||||
## 5. Решения и договорённости
|
||||
|
||||
> Сюда записывать принятые решения с датой и контекстом.
|
||||
|
||||
(пока пусто)
|
||||
| Дата | Решение |
|
||||
|---|---|
|
||||
| 01.06.2026 | Экспорт: txt, одна строка = один CIDR |
|
||||
| 01.06.2026 | Демо одобрено |
|
||||
| 02.06.2026 | Любой юзер компании редактирует whitelist всей компании |
|
||||
| 04.06.2026 | **IAM API — источник компаний.** `GET /auth/user` → `profiles[]`. `isAdmin` из `userInfo.isAdmin`. Переключение через `POST /switch-profile`. |
|
||||
| 04.06.2026 | ddos-guard: curl требует `--http2`, Node.js работает |
|
||||
|
||||
---
|
||||
|
||||
## Легенда статусов
|
||||
|
||||
| Статус | Значение |
|
||||
|---|---|
|
||||
| 🔴 | Требует уточнения |
|
||||
| 🟡 | Уточнено, не реализовано |
|
||||
| 🟢 | Реализовано / подтверждено |
|
||||
|
||||
Reference in New Issue
Block a user