552 lines
27 KiB
Markdown
552 lines
27 KiB
Markdown
# Актуальный план реализации — IP WhiteList
|
||
|
||
> Автор: GitHub Copilot (GPT-5.4)
|
||
> Дата: 2026-05-30
|
||
> Основание: ТЗ из WhiteIPlist.txt + фактический код в ipwhitelist-app
|
||
> Статус: заменяет предыдущую версию плана
|
||
|
||
## 1. Вывод по текущему состоянию
|
||
|
||
Предыдущий план был неполным, потому что строился не от ТЗ, а от видимого MVP. После сверки с WhiteIPlist.txt картина такая:
|
||
|
||
1. Основа сервиса уже написана лучше, чем казалось: схема БД, soft delete, аудит, custom_limit, updateEntry и полный список запрещённых диапазонов уже есть в коде.
|
||
2. Основной разрыв находится не в модели данных, а между слоями: часть функций реализована в src/queries.js, но не выведена в server.js и views.
|
||
3. Главные недостающие вещи для соответствия ТЗ: нормальная OIDC-авторизация, admin-сценарии, редактирование из UI, клиентская валидация, агрегирующий экспорт.
|
||
4. Переписывать стек или БД не нужно. Нужно довести существующую реализацию до полноты требований.
|
||
|
||
## 2. Что требует ТЗ
|
||
|
||
Сервис по ТЗ обязан поддерживать:
|
||
|
||
1. Self-service страницу управления whitelist-записями.
|
||
2. Авторизацию через Keycloak OIDC.
|
||
3. Роли client и admin.
|
||
4. Возможность работы пользователя с одной или несколькими компаниями.
|
||
5. Создание, редактирование и soft delete записей.
|
||
6. Серверную и клиентскую валидацию IPv4/CIDR.
|
||
7. Глобальный лимит и индивидуальные лимиты по компаниям.
|
||
8. Журнал аудита.
|
||
9. Внешний endpoint выдачи агрегированного списка активных CIDR.
|
||
10. Admin-функции: просмотр всех компаний, фильтрация, просмотр удалённых записей, изменение лимитов.
|
||
|
||
## 3. Что уже реализовано сейчас
|
||
|
||
### 3.1 База данных
|
||
|
||
В sql/schema.sql уже есть всё базовое, что нужно для ТЗ:
|
||
|
||
1. Таблица companies с client_id и custom_limit.
|
||
2. Таблица whitelist_entries с updated_by, updated_at, deleted_by, deleted_at.
|
||
3. Таблица audit_log.
|
||
4. Индекс активных записей и индекс по audit_log(company_id).
|
||
|
||
Вывод: схему БД переписывать не нужно.
|
||
|
||
### 3.2 Серверная логика
|
||
|
||
В src/queries.js уже реализованы:
|
||
|
||
1. getOrCreateCompany
|
||
2. getLimit
|
||
3. listEntries с includeDeleted
|
||
4. createEntry
|
||
5. updateEntry
|
||
6. deleteEntry как soft delete
|
||
7. getExportCIDRs
|
||
8. getAudit
|
||
|
||
Вывод: слой работы с БД уже покрывает значительную часть ТЗ.
|
||
|
||
### 3.3 Валидация
|
||
|
||
В src/validators.js уже есть:
|
||
|
||
1. Только IPv4.
|
||
2. Маски только /22–/32.
|
||
3. Нормализация host-битов.
|
||
4. Запрет всех диапазонов из Приложения А ТЗ, включая CGNAT, Benchmarking, Multicast, Reserved и Limited broadcast.
|
||
5. Проверка пересечений.
|
||
|
||
Вывод: серверная валидация по составу требований почти полная.
|
||
|
||
### 3.4 UI и маршруты
|
||
|
||
В server.js и views/index.ejs уже есть:
|
||
|
||
1. Главная страница со списком записей.
|
||
2. Форма добавления.
|
||
3. Soft delete через POST /delete/:id.
|
||
4. Экспорт через GET /export.
|
||
5. Вывод текущего лимита и числа использованных записей.
|
||
6. UI, близкий к стилю Nubes.
|
||
|
||
Вывод: клиентский сценарий создания и удаления уже работает как MVP.
|
||
|
||
## 4. Что не соответствует ТЗ или не доведено до конца
|
||
|
||
### 4.1 Авторизация
|
||
|
||
Сейчас в server.js не OIDC, а упрощённый decode токена через base64 без проверки подписи. Это подходит только как временная заглушка, но не соответствует ТЗ.
|
||
|
||
Нужно:
|
||
|
||
1. Реальная проверка JWT через Keycloak/JWKS или openid-client.
|
||
2. Нормальный маппинг claims в user-модель.
|
||
3. Явное определение роли admin.
|
||
4. Поддержка сценария с несколькими компаниями.
|
||
|
||
Это главный блокер продакшна.
|
||
|
||
### 4.2 Редактирование записи
|
||
|
||
updateEntry уже написана, но:
|
||
|
||
1. Нет роута POST /update/:id.
|
||
2. Нет формы редактирования в index.ejs.
|
||
3. Нет пользовательского сценария изменения записи.
|
||
|
||
То есть требование ТЗ формально не закрыто, хотя код на уровне queries уже есть.
|
||
|
||
### 4.3 Admin-функции
|
||
|
||
По ТЗ администратор должен:
|
||
|
||
1. Видеть записи всех компаний.
|
||
2. Фильтровать по компании.
|
||
3. Видеть soft-deleted записи.
|
||
4. Смотреть аудит.
|
||
5. Менять лимиты компаний.
|
||
|
||
Сейчас ничего из этого не выведено в server.js и views.
|
||
|
||
### 4.4 Экспорт
|
||
|
||
GET /export уже есть, но он отдаёт просто список value_cidr без агрегации. ТЗ требует суммаризацию в минимальный набор CIDR по всем активным записям.
|
||
|
||
Это функциональный разрыв, а не косметика.
|
||
|
||
### 4.5 Клиентская валидация
|
||
|
||
ТЗ требует валидацию на клиенте и сервере. Сейчас есть только серверная.
|
||
|
||
Нужно добавить в форму как минимум:
|
||
|
||
1. Проверку формата IPv4/CIDR.
|
||
2. Проверку диапазона маски.
|
||
3. Ограничение длины комментария.
|
||
4. Сообщение о возможной нормализации.
|
||
|
||
### 4.6 Multi-company сценарий
|
||
|
||
ТЗ явно говорит, что пользователь может принадлежать нескольким компаниям. Сейчас всё построено вокруг одного clientId в req.user.
|
||
|
||
Нужно:
|
||
|
||
1. Понять реальный формат claims.
|
||
2. Ввести activeCompany в контекст пользователя.
|
||
3. Добавить переключатель активной компании в UI.
|
||
|
||
## 5. Что не надо перепридумывать
|
||
|
||
1. Не надо переписывать проект на другой язык или другой фреймворк.
|
||
2. Не надо менять схему БД ради самой схемы.
|
||
3. Не надо переписывать validators.js целиком.
|
||
4. Не надо переписывать queries.js целиком.
|
||
5. Не надо делать SPA.
|
||
|
||
Правильный путь: минимально нарастить уже существующую Node.js/Express/EJS реализацию.
|
||
|
||
## 6. Риски и спорные места
|
||
|
||
### 6.1 overlaps в validators.js
|
||
|
||
Условие в overlaps написано нестандартно и плохо читается. Прямого доказанного бага по одной только формуле сейчас нет, но это место требует отдельного теста на:
|
||
|
||
1. полное совпадение,
|
||
2. вложенность,
|
||
3. непересекающиеся диапазоны,
|
||
4. соседние диапазоны,
|
||
5. одиночный IP против подсети.
|
||
|
||
Решение: сначала добавить точечные тесты, и только потом менять формулу, если тест покажет дефект.
|
||
|
||
### 6.2 Admin-роль
|
||
|
||
ТЗ говорит: admin это clientId = WZ01112 и отдельный чек-бокс. Сейчас неясно, как этот чек-бокс попадает в токен. Без этого нельзя финально закрыть auth-модель.
|
||
|
||
### 6.3 Multi-company claims
|
||
|
||
ТЗ требует сценарий нескольких компаний, но в списке claims указан только clientID. Здесь нужна конкретика от команды Keycloak.
|
||
|
||
## 7. Новый план реализации
|
||
|
||
### Этап 1. Довести до полноты пользовательский сценарий
|
||
|
||
Цель: закрыть основной client-flow без смены архитектуры.
|
||
|
||
1. Добавить роут POST /update/:id в server.js.
|
||
2. Добавить UI редактирования в views/index.ejs.
|
||
3. Показать updated_at и updated_by, если запись менялась.
|
||
4. Добавить клиентскую валидацию формы добавления и редактирования.
|
||
5. Добавить точечные тесты на overlaps и нормализацию.
|
||
|
||
Результат этапа: client сможет не только добавлять и удалять, но и редактировать записи, как требует ТЗ.
|
||
|
||
### Этап 2. Закрыть admin-функциональность
|
||
|
||
Цель: реализовать недостающую управленческую часть ТЗ.
|
||
|
||
1. Ввести определение роли admin в auth-слое.
|
||
2. Добавить GET /admin.
|
||
3. Добавить фильтр по компании.
|
||
4. Добавить показ удалённых записей.
|
||
5. Добавить GET /admin/audit.
|
||
6. Добавить POST /admin/limit/:companyId.
|
||
7. Добавить отдельный admin view.
|
||
|
||
Результат этапа: появляется реальная административная панель, а не только клиентский экран.
|
||
|
||
### Этап 3. Привести авторизацию к ТЗ
|
||
|
||
Цель: убрать временную заглушку и сделать реальную интеграцию с Keycloak.
|
||
|
||
1. Заменить наивный decode токена на верификацию подписи.
|
||
2. Добавить конфигурацию issuer, audience, jwks/oidc.
|
||
3. Нормализовать claims в req.user.
|
||
4. Поддержать admin-claim.
|
||
5. Поддержать multi-company claims.
|
||
6. Оставить DEV_MODE только для локальной разработки.
|
||
|
||
Результат этапа: сервис можно выводить из чисто dev-сценария.
|
||
|
||
### Этап 4. Довести экспорт до требований ТЗ
|
||
|
||
Цель: сделать экспорт пригодным для систем фильтрации.
|
||
|
||
1. Добавить суммаризацию активных записей в минимальный набор CIDR.
|
||
2. Суммаризировать по всем компаниям совместно.
|
||
3. Исключать soft-deleted записи.
|
||
4. Оставить выдачу text/plain, одна строка на объект.
|
||
5. Отдельно решить, где ограничивается доступ к export endpoint: ingress, app или оба уровня.
|
||
|
||
Результат этапа: экспорт соответствует ТЗ, а не является просто дампом таблицы.
|
||
|
||
### Этап 5. Завершение и проверка полноты
|
||
|
||
1. Сверить все пункты ТЗ с реализованным поведением.
|
||
2. Обновить тестовый сценарий.
|
||
3. Проверить UX ошибок и предупреждений.
|
||
4. Проверить поведение при снижении custom_limit ниже текущего числа активных записей.
|
||
5. Проверить сценарии admin/client отдельно.
|
||
|
||
## 8. Практический приоритет
|
||
|
||
Если делать не всё сразу, а по реальной важности, порядок такой:
|
||
|
||
1. Редактирование записи из UI.
|
||
2. Клиентская валидация.
|
||
3. Admin-панель и лимиты.
|
||
4. Реальный OIDC.
|
||
5. Multi-company.
|
||
6. Суммаризация export.
|
||
|
||
Почему именно так:
|
||
|
||
1. Редактирование уже почти готово и закрывает явный пробел ТЗ.
|
||
2. Admin-функции сейчас отсутствуют полностью.
|
||
3. OIDC блокирует продакшн, но не мешает локально добить функциональность.
|
||
4. Экспорт уже работает как черновой endpoint, но должен быть доведён до суммаризации до релиза.
|
||
|
||
## 9. Итог
|
||
|
||
Правильный план для этого проекта не “переписать всё правильно”, а “довести уже написанное до требований ТЗ”.
|
||
|
||
Текущее состояние проекта:
|
||
|
||
1. Data-layer в основном готов.
|
||
2. Server-layer частично готов.
|
||
3. UI-layer закрывает только часть client-сценария.
|
||
4. Auth-layer пока временный.
|
||
5. Admin-layer почти отсутствует.
|
||
6. Export-layer не завершён по требованиям агрегации.
|
||
|
||
Главный вывод: проект ближе к рабочему состоянию, чем казалось по старым планам, но прошлый план был методологически неверен, потому что не опирался на ТЗ и не различал “не написано” и “написано, но не подключено”.
|
||
|
||
Эта часть критична. Её нужно делать одной из первых и сразу покрывать тестами.
|
||
|
||
### Поддерживаемый ввод
|
||
|
||
1. Одиночный IPv4 адрес, который трактуется как /32.
|
||
2. IPv4 подсеть в CIDR нотации.
|
||
|
||
### Запрещённый ввод
|
||
|
||
1. IPv6.
|
||
2. Доменное имя.
|
||
3. Маска шире допустимой.
|
||
4. Любые private или special ranges из приложения А.
|
||
|
||
### Правила маски
|
||
|
||
Допустимы только /22 ... /32.
|
||
|
||
### Нормализация
|
||
|
||
Если пользователь ввёл адрес с host-битами, сервис должен:
|
||
|
||
1. Нормализовать значение до адреса сети.
|
||
2. Сохранить нормализованное значение.
|
||
3. Вернуть пользователю явное сообщение, что адрес был нормализован.
|
||
|
||
### Проверки в пределах компании
|
||
|
||
1. Запрет полного дубликата активной записи.
|
||
2. Запрет любого пересечения активной записи с существующими активными записями той же компании.
|
||
3. Между разными компаниями пересечения допускаются.
|
||
|
||
### Список запрещённых диапазонов
|
||
|
||
Нужно захардкодить как конфигурацию приложения и покрыть тестами:
|
||
|
||
1. 10.0.0.0/8
|
||
2. 172.16.0.0/12
|
||
3. 192.168.0.0/16
|
||
4. 100.64.0.0/10
|
||
5. 127.0.0.0/8
|
||
6. 169.254.0.0/16
|
||
7. 192.0.0.0/24
|
||
8. 192.0.2.0/24
|
||
9. 198.51.100.0/24
|
||
10. 203.0.113.0/24
|
||
11. 198.18.0.0/15
|
||
12. 224.0.0.0/4
|
||
13. 240.0.0.0/4
|
||
14. 255.255.255.255/32
|
||
|
||
## 10. Лимиты и конкурентность
|
||
|
||
Это важное место, которого обычно недооценивают.
|
||
|
||
### Правила лимитов
|
||
|
||
1. Есть глобальный DEFAULT_LIMIT, по умолчанию 15.
|
||
2. Для компании может быть custom_limit.
|
||
3. При снижении лимита ниже текущего количества записей существующие записи не удаляются.
|
||
4. Пока число активных записей больше лимита, новые записи создавать нельзя.
|
||
|
||
### Риск гонок
|
||
|
||
Если два запроса одновременно создают записи в одной компании, возможны:
|
||
|
||
1. Пробитие лимита.
|
||
2. Пропуск пересечения.
|
||
3. Пропуск дубликата.
|
||
|
||
### Что делать
|
||
|
||
Операцию создания и обновления записи нужно делать в транзакции с сериализацией логики на уровне компании. Практически это можно решить так:
|
||
|
||
1. Брать advisory lock по company_id перед проверками и записью.
|
||
2. Либо делать SELECT ... FOR UPDATE по строке компании, если этого достаточно для вашей схемы доступа.
|
||
|
||
Для первой версии я бы выбрал advisory lock по company_id. Это проще и надёжнее для бизнес-ограничений, которые нельзя полностью выразить обычным unique index.
|
||
|
||
## 11. Экспорт агрегированного списка
|
||
|
||
### Требования
|
||
|
||
1. В экспорт попадают только активные записи.
|
||
2. Данные берутся по всем компаниям.
|
||
3. Пересечения между компаниями допустимы на уровне хранения, но в export должны агрегироваться в минимальный набор CIDR.
|
||
4. Формат ответа: text/plain.
|
||
5. Одна строка = один CIDR.
|
||
|
||
### Что нужно зафиксировать реализационно
|
||
|
||
1. Результат должен быть отсортирован для стабильности.
|
||
2. В ответе должен быть завершающий перевод строки.
|
||
3. Content-Type должен быть text/plain; charset=utf-8.
|
||
4. Желательно отдавать Content-Disposition с понятным именем файла.
|
||
|
||
### Защита endpoint
|
||
|
||
Если endpoint на старте работает без auth, то доступ надо ограничить минимум одним из способов:
|
||
|
||
1. Проверка client IP в приложении.
|
||
2. Ограничение на reverse proxy.
|
||
3. Оба сразу.
|
||
|
||
## 12. UI-потоки
|
||
|
||
### Экран клиента
|
||
|
||
Должны быть:
|
||
|
||
1. Селектор активной компании, если компаний несколько.
|
||
2. Таблица записей.
|
||
3. Индикатор использовано X из N.
|
||
4. Форма создания записи.
|
||
5. Возможность редактирования.
|
||
6. Возможность soft delete.
|
||
|
||
### Экран администратора
|
||
|
||
Должны быть:
|
||
|
||
1. Таблица по всем компаниям.
|
||
2. Фильтр по компании.
|
||
3. Фильтр показа удалённых записей.
|
||
4. Просмотр журнала аудита.
|
||
5. Управление лимитами компании.
|
||
|
||
### UX-детали, которые обязательно сделать
|
||
|
||
1. Понятные сообщения об ошибках валидации.
|
||
2. Явное сообщение о нормализации адреса.
|
||
3. Явное сообщение о превышении лимита.
|
||
4. Явное сообщение о пересечении с существующей записью.
|
||
|
||
## 13. Пошаговый план реализации
|
||
|
||
### Этап 1. Каркас проекта
|
||
|
||
1. Создать структуру каталогов.
|
||
2. Подготовить requirements.txt.
|
||
3. Подготовить .env.example.
|
||
4. Подключить FastAPI, Jinja2, static.
|
||
5. Подготовить docker-compose.yml с PostgreSQL.
|
||
|
||
Результат этапа: приложение стартует, открывается базовая страница, есть подключение к БД.
|
||
|
||
### Этап 2. Схема БД и миграции
|
||
|
||
1. Настроить Alembic.
|
||
2. Создать initial migration.
|
||
3. Поднять таблицы companies, whitelist_entries, audit_log.
|
||
4. Добавить нужные индексы.
|
||
|
||
Результат этапа: схема БД фиксирована и воспроизводима.
|
||
|
||
### Этап 3. Валидатор CIDR
|
||
|
||
1. Реализовать разбор IPv4 и CIDR.
|
||
2. Реализовать проверку маски.
|
||
3. Реализовать нормализацию.
|
||
4. Реализовать проверку запрещённых диапазонов.
|
||
5. Написать тесты на валидатор.
|
||
|
||
Результат этапа: независимый, протестированный модуль бизнес-валидации.
|
||
|
||
### Этап 4. Сервисный слой для записей
|
||
|
||
1. Реализовать list.
|
||
2. Реализовать create.
|
||
3. Реализовать update.
|
||
4. Реализовать soft delete.
|
||
5. Реализовать проверки лимитов, дубликатов и пересечений.
|
||
6. Добавить транзакционную защиту от гонок.
|
||
|
||
Результат этапа: бизнес-операции работают без UI.
|
||
|
||
### Этап 5. Аудит
|
||
|
||
1. Добавить запись CREATE.
|
||
2. Добавить запись UPDATE со старым и новым состоянием.
|
||
3. Добавить запись DELETE.
|
||
4. Добавить интерфейс чтения для admin.
|
||
|
||
Результат этапа: все изменяющие действия фиксируются.
|
||
|
||
### Этап 6. Авторизация
|
||
|
||
1. Реализовать dev-заглушку.
|
||
2. Реализовать чтение и валидацию JWT из Keycloak.
|
||
3. Реализовать преобразование claims в current user.
|
||
4. Реализовать проверки client/admin.
|
||
5. Реализовать переключение компании.
|
||
|
||
Результат этапа: права и контекст пользователя работают сквозным образом.
|
||
|
||
### Этап 7. HTML-интерфейс
|
||
|
||
1. Реализовать страницу списка.
|
||
2. Реализовать формы создания и редактирования.
|
||
3. Реализовать soft delete из UI.
|
||
4. Реализовать админские экраны.
|
||
|
||
Результат этапа: сервис пригоден для ручной эксплуатации.
|
||
|
||
### Этап 8. Экспорт
|
||
|
||
1. Реализовать сбор всех активных CIDR.
|
||
2. Реализовать агрегацию.
|
||
3. Реализовать endpoint export.
|
||
4. Реализовать сетевое ограничение.
|
||
|
||
Результат этапа: внешняя система может забирать текстовый агрегированный whitelist.
|
||
|
||
### Этап 9. Финализация
|
||
|
||
1. Написать README.
|
||
2. Подготовить Dockerfile.
|
||
3. Подготовить пример systemd unit при необходимости.
|
||
4. Прогнать ручной smoke-test.
|
||
|
||
Результат этапа: сервис можно разворачивать и передавать коллегам.
|
||
|
||
## 14. Тестовая стратегия
|
||
|
||
Минимально обязательные тесты:
|
||
|
||
1. Валидный одиночный IPv4 превращается в /32.
|
||
2. Валидная подсеть принимается.
|
||
3. Host-биты нормализуются.
|
||
4. Маски шире допустимой границы отклоняются.
|
||
5. IPv6 отклоняется.
|
||
6. Все запрещённые диапазоны отклоняются.
|
||
7. Дубликат в одной компании запрещён.
|
||
8. Пересечение в одной компании запрещено.
|
||
9. Тот же CIDR в другой компании разрешён.
|
||
10. Soft delete освобождает лимит.
|
||
11. Export не включает soft-deleted записи.
|
||
12. Export агрегирует CIDR корректно.
|
||
13. Client не видит чужие компании.
|
||
14. Admin видит все компании.
|
||
15. Аудит создаётся для create, update, delete.
|
||
|
||
## 15. Что можно отложить после первой версии
|
||
|
||
Это не нужно тащить в MVP:
|
||
|
||
1. Полноценный SPA.
|
||
2. Сложная ORM.
|
||
3. WebSocket.
|
||
4. Фоновая очередь.
|
||
5. Исторические версии записей кроме audit log.
|
||
6. Автоматическое уведомление по email.
|
||
|
||
## 16. Главные риски проекта
|
||
|
||
1. Неясный формат claims из Keycloak.
|
||
2. Гонки при одновременном создании записей.
|
||
3. Ошибки в трактовке пересечений CIDR.
|
||
4. Неправильная нормализация адресов без понятного сообщения пользователю.
|
||
5. Слишком раннее усложнение фронтенда.
|
||
|
||
## 17. Что я бы делал первым
|
||
|
||
Если начинать реализацию прямо сейчас, порядок такой:
|
||
|
||
1. Каркас проекта.
|
||
2. Схема БД.
|
||
3. Валидатор и тесты.
|
||
4. Сервис create/update/delete с транзакциями.
|
||
5. Только потом UI и Keycloak.
|
||
|
||
Это самый безопасный путь: сначала фиксируется ядро бизнес-логики, потом уже внешний слой.
|
||
|
||
## 18. Итоговое решение
|
||
|
||
За основу реализации стоит брать простой Python/FastAPI сервис с PostgreSQL, синхронной серверной логикой, жёсткой серверной валидацией, транзакционной защитой от гонок и минималистичным HTML UI.
|
||
|
||
Главная мысль: сложность здесь не во фронтенде и не в фреймворке, а в корректной реализации правил CIDR, лимитов, ролей и аудита. План должен защищать именно эти части, а не раздувать стек. |