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