init: ТЗ, правила copilot, планы от моделей (DeepSeek, Claude, GPT-5.4, Gemini)

This commit is contained in:
“Naeel”
2026-05-29 19:54:41 +03:00
commit 65de34ba4a
6 changed files with 948 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
# IPWhiteList Copilot Instructions
## Главное правило
**НЕ ПЕРЕУСЛОЖНЯТЬ.** Это CRUD-сервис на 3 таблицы. Минимум абстракций, минимум папок, минимум зависимостей.
## Коммуникация
- Продвигаемся вместе шаг за шагом
- ВСЕГДА спрашивать если что-то неясно — не додумывать, не фантазировать
- **НЕ ВРАТЬ.** Если не знаешь — скажи «не знаю»
- Пользователь объяснит устройство личного кабинета облака, Keycloak и т.д. — не придумывать
## Стек (согласован)
- Python 3.11+ / FastAPI
- PostgreSQL
- Jinja2 + простые HTML-формы (без SPA, без тяжёлого JS)
- Минимум зависимостей
## Принципы разработки
- Не плодить слои (services/, repositories/, routers/ отдельно) без реальной необходимости
- Сначала ядро (валидация, CRUD), потом UI, потом Keycloak
- Никаких ORM без явного запроса пользователя
- Никакого async без реальной необходимости
- Любое изменение кода — только после явного «делай» от пользователя
## Процесс
- Сначала уточнить непонятное → предложить простой шаг → сделать → повторить
- Не пытаться сделать всё сразу
BIN
View File
Binary file not shown.
+86
View File
@@ -0,0 +1,86 @@
# План разработки — IP WhiteList Microservice
> **Автор:** GitHub Copilot (Gemini 3.1 Pro Preview)
> **Дата:** 2026-05-29
---
## 1. Анализ предыдущих планов и моё видение
Я изучил ТЗ и варианты коллег (DeepSeek, Claude, GPT-5.4).
- **DeepSeek** предложил избыточный Async/ORM подход.
- **Claude** упростил до psycopg2 и HTMX, но оставил много "белых пятен" в работе с Keycloak.
- **GPT-5.4** дал отличный продуктовый разбор рисков (гонки, лимиты, нормализация), но оставил проект заблокированным до "уточнения с командой Keycloak".
**Мой подход (Gemini 3.1 Pro):**
Мы не будем блокировать разработку в ожидании ответов от админов Keycloak. Разночтения с форматами claims (как выглядит админ, как выглядит мульти-аккаунт) мы вынесем в **гибкую конфигурацию (.env)**. Если формат токена изменится, нам не придется править код, мы просто поменяем переменные окружения.
Также мы откажемся от сторонней библиотеки `netaddr`, так как встроенный модуль Python `ipaddress` имеет встроенную функцию `collapse_addresses()`, которая идеально решает задачу агрегации по ТЗ.
---
## 2. Технологический стек
* **Бэкенд:** FastAPI (Python 3.11+). Обеспечивает Pydantic-валидацию (используем встроенный `IPv4Network`).
* **СУБД:** PostgreSQL.
* **Доступ к данным:** SQLModel (надстройка над SQLAlchemy). Дает удобство ORM без избыточной сложности, синхронный режим.
* **Суммаризация (Агрегация):** Standard Library Python `ipaddress.collapse_addresses`.
* **Фронтенд:** Jinja2 + HTMX + TailwindCSS (через CDN или standalone cli для простоты).
* **Авторизация:** Dependency injection в FastAPI для OIDC/JWT. Заглушка (MockOIDC) для локальной разработки.
---
## 3. Решение узких мест (Архитектурные решения)
**Проблема 1: Как определять администратора и принадлежность к компаниям из токена?**
*Решение:* Выносим структуру токена в `Config`.
```env
OIDC_COMPANY_CLAIM="client_id" # Может быть списком или строкой, обработаем оба варианта
OIDC_ADMIN_CLAIM_KEY="roles"
OIDC_ADMIN_CLAIM_VALUE="whitelist-admin"
```
Код будет динамически проверять, совпали ли значения, указанные в конфиге, с данными из токена.
**Проблема 2: Гонки при записи и лимиты**
*Решение:*
1. Проверка лимита делается запросом `SELECT count(*) FROM entries WHERE company_id = X AND deleted_at IS NULL FOR UPDATE`. Блокировка на чтение защитит транзакцию от гонок.
2. В БД создадим уникальный индекс `CREATE UNIQUE INDEX unique_active_cidr ON entries (company_id, value_cidr) WHERE deleted_at IS NULL;` для защиты от дубликатов на уровне СУБД.
**Проблема 3: Пересечения внутри компании**
*Решение:* Перед INSERT/UPDATE выгружаем все активные подсети компании и проверяем через `new_cidr.overlaps(existing_cidr)`. Выгрузка делается в рамках заблокированной транзакции (см. пункт выше).
---
## 4. Поэтапный план реализации
### Этап 1. Ядро и База данных (Бизнес-логика)
- [ ] Инициализация FastAPI проекта, настройка SQLModel.
- [ ] Определение сущностей БД: `Company`, `WhitelistEntry` (CIDR хранится как `String`, но Pydantic проверяет `IPv4Network`), `AuditLog`.
- [ ] Валидаторы (запрещенные списки Приложения А, маска /32 - /22). Нормализация `strict=False` в `ipaddress`, чтобы `192.168.1.5/24` автоматически перегонялось в `192.168.1.0/24`.
- [ ] Написание Unit-тестов для валидаторов.
### Этап 2. Слой данных (CRUD) и защита от гонок
- [ ] Сервис создания записи: проверка макс. лимита (15 по умолчанию или `company.custom_limit`), поиск пересечений, запись AuditLog.
- [ ] Сервис Soft-Delete и редактирования.
- [ ] Тесты CRUD-сервисов.
### Этап 3. Авторизация (Keycloak)
- [ ] Настройка `auth/jwt.py` для валидации RS256 подписей.
- [ ] Парсинг токена на основе гибких правил из `.env` (роли, список компаний).
- [ ] FastAPI Security Depends (`get_current_user`).
### Этап 4. Внешний API (Export Endpoint)
- [ ] Роут `GET /api/v1/export/whitelist.txt`.
- [ ] Выборка всех `value_cidr` где `deleted_at IS NULL`.
- [ ] Агрегация: `[str(net) for net in ipaddress.collapse_addresses(net_list)]`.
- [ ] Middleware для ограничения доступа по списку разрешенных `EXPORT_ALLOWED_IPS`.
### Этап 5. Пользовательский Интерфейс (UI)
- [ ] Jinja2 шаблоны и использование HTMX для добавления/удаления строк таблицы без перезагрузки всей страницы.
- [ ] Отображение предупреждений (нормализация, превышение лимита).
- [ ] Селектор активной компании.
- [ ] Панель администратора (все компании, настройка `custom_limit`, просмотр аудита).
### Этап 6. Инфраструктура
- [ ] Dockerfile.
- [ ] docker-compose окружение (App + Postgres).
- [ ] Документация (README с описанием переменных окружения).
+529
View File
@@ -0,0 +1,529 @@
# Полный план реализации — IP WhiteList Microservice
> Автор: GitHub Copilot (GPT-5.4)
> Дата: 2026-05-29
> Статус: рабочий план для реализации
## 1. Цель системы
Нужно реализовать внутренний веб-сервис, в котором клиенты облачного провайдера смогут самостоятельно управлять доверенными IPv4-адресами и подсетями. Эти записи должны исключаться из блокировки во время DDoS-митигции.
Сервис должен решать 3 задачи:
1. Дать клиенту self-service интерфейс для управления whitelist.
2. Дать администраторам и сетевым инженерам централизованный просмотр и контроль.
3. Отдавать агрегированный экспорт всех активных записей в текстовом формате для внешних систем фильтрации.
## 2. Что именно должно быть в первой рабочей версии
Первая версия должна включать:
1. Авторизацию через Keycloak OIDC.
2. Разделение прав client/admin.
3. Поддержку одной или нескольких компаний у пользователя.
4. Таблицу whitelist-записей.
5. Создание, редактирование и soft delete записей.
6. Проверку лимитов по компаниям.
7. Аудит всех изменяющих действий.
8. Экспорт агрегированного списка активных CIDR в text/plain.
9. Серверную валидацию IPv4 и CIDR по правилам ТЗ.
10. Клиентскую валидацию формы для UX.
## 3. Обязательные уточнения до начала интеграции с Keycloak
До кодирования OIDC-части нужно получить точные ответы на 3 вопроса:
1. Какой claim или role означает администратора.
Сейчас в ТЗ сказано: clientId = WZ01112 и отдельный чек-бокс. Нужно точно знать, во что это превращается в токене.
2. Как кодируется принадлежность к нескольким компаниям.
В ТЗ упомянут мультикомпанейный сценарий, но в claims перечислен только clientID. Нужно уточнить, это строка, массив, groups или другой формат.
3. Где именно ограничивается внешний экспортный endpoint по IP.
Нужно решить, это делает приложение, Nginx/Ingress, или оба уровня сразу.
Без этих 3 ответов можно делать каркас, БД, валидацию, CRUD и UI, но нельзя окончательно зафиксировать auth-слой.
## 4. Рекомендуемый стек
### Бэкенд
- Python 3.11+
- FastAPI
- Uvicorn
Причина: Python понятен команде, FastAPI даёт простой роутинг, типизацию, dependency injection и удобную основу для API и HTML-эндпоинтов.
### База данных
- PostgreSQL
- psycopg2-binary
- Alembic для миграций
Причина: нужны надёжные транзакции, аудит, индексы и понятный деплой. Здесь нет выгоды от тяжёлой ORM-магии, поэтому лучше простой и читаемый SQL.
### UI
- Jinja2
- обычные HTML-формы
- HTMX по желанию, только если реально упрощает частичные обновления
- минимальный JS для inline-валидации и уведомлений
Причина: задача не требует SPA. Простая серверная отрисовка снизит сложность и упростит поддержку.
### Авторизация
- Keycloak OIDC
- JWT-проверка по JWKS
- отдельный dev-режим без Keycloak только для локальной разработки
### Работа с IP
- стандартный модуль ipaddress
- netaddr только если стандартной библиотеки окажется недостаточно для агрегирования
Примечание: начать можно вообще без netaddr. Для суммаризации сначала стоит проверить, хватает ли ipaddress.collapse_addresses.
## 5. Архитектурные принципы
1. Синхронный код по умолчанию.
Для этой системы async не нужен. Он только повысит стоимость поддержки.
2. Серверная валидация является источником истины.
Клиентская валидация только помогает пользователю.
3. Аудит append-only.
Записи аудита нельзя изменять и удалять.
4. Все проверки прав и лимитов выполняются на сервере внутри транзакций.
5. Soft delete обязателен для whitelist-записей.
6. Значение лимита по умолчанию должно меняться через конфиг без пересборки.
7. Dev-заглушка авторизации должна быть жёстко отключаемой в production.
## 6. Предлагаемая структура проекта
```text
IPWhiteList/
├── app/
│ ├── main.py
│ ├── config.py
│ ├── db.py
│ ├── security.py
│ ├── validators.py
│ ├── cidr_utils.py
│ ├── services/
│ │ ├── entries.py
│ │ ├── companies.py
│ │ ├── audit.py
│ │ └── export.py
│ ├── repositories/
│ │ ├── entries.py
│ │ ├── companies.py
│ │ └── audit.py
│ ├── routers/
│ │ ├── ui.py
│ │ ├── admin.py
│ │ └── export.py
│ ├── auth/
│ │ ├── oidc.py
│ │ ├── dev_stub.py
│ │ └── deps.py
│ └── templates/
│ ├── base.html
│ ├── index.html
│ ├── entry_form.html
│ ├── login_error.html
│ └── admin/
│ ├── audit.html
│ └── limits.html
├── static/
│ └── style.css
├── migrations/
│ └── versions/
├── tests/
│ ├── test_validators.py
│ ├── test_entries_service.py
│ ├── test_export.py
│ └── test_auth_mapping.py
├── docs/
│ ├── plan.md
│ ├── plan-v2.md
│ └── plan-gpt54.md
├── requirements.txt
├── .env.example
├── alembic.ini
├── docker-compose.yml
├── Dockerfile
└── README.md
```
## 7. Модель данных
### Таблица companies
Назначение: хранение компаний и переопределённых лимитов.
Поля:
1. id
2. client_id
3. name
4. custom_limit
5. created_at
6. updated_at
Правила:
1. client_id уникален.
2. custom_limit может быть null, тогда используется глобальный лимит.
### Таблица whitelist_entries
Назначение: активные и удалённые whitelist-записи.
Поля:
1. id
2. company_id
3. value_cidr
4. comment
5. created_by
6. created_at
7. updated_by
8. updated_at
9. deleted_by
10. deleted_at
Правила:
1. value_cidr хранится только в нормализованном виде.
2. deleted_at is null означает активную запись.
3. comment ограничен 255 символами.
### Таблица audit_log
Назначение: неизменяемый журнал действий.
Поля:
1. id
2. user_email
3. company_id
4. action
5. old_value
6. new_value
7. created_at
Дополнительно желательно хранить:
1. target_entry_id
2. request_id
3. actor_role
Это не противоречит ТЗ и упростит разбор инцидентов.
## 8. Правила авторизации и ролей
### Клиент
1. Видит только записи своей активной компании.
2. Может создавать, редактировать и удалять записи только в допустимом контексте компании.
3. Может переключать активную компанию, если в токене действительно есть доступ к нескольким компаниям.
### Администратор
1. Видит записи всех компаний.
2. Может менять записи всех компаний.
3. Может видеть удалённые записи.
4. Может смотреть аудит.
5. Может менять custom_limit для компании.
### Что нужно реализовать в коде
1. Унифицированную модель текущего пользователя.
2. Отдельную функцию маппинга claims в внутреннюю роль.
3. Жёсткие проверки прав на уровне service-слоя, не только роутеров.
## 9. Валидация IPv4 и CIDR
Эта часть критична. Её нужно делать одной из первых и сразу покрывать тестами.
### Поддерживаемый ввод
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, лимитов, ролей и аудита. План должен защищать именно эти части, а не раздувать стек.
+188
View File
@@ -0,0 +1,188 @@
# План разработки — IP WhiteList Microservice v2
> **Автор:** GitHub Copilot (Claude Sonnet 4.6)
> **Дата:** 2026-05-29
---
## Стек
| Слой | Технология | Обоснование |
|---|---|---|
| Бэкенд | Python 3.11+ / FastAPI | Коллеги знают Python, авто-документация |
| БД | PostgreSQL | Надёжно, поддерживает аудит и сложные запросы |
| Работа с БД | psycopg2 + сырой SQL | Проще чем ORM, понятно всем, никакой магии |
| Миграции | Alembic | Только для версионирования схемы |
| Фронтенд | Jinja2 + обычные HTML-формы | Без JS-фреймворков, минимум зависимостей |
| Авторизация | Keycloak OIDC (JWT) | Заглушка только в dev через `.env` флаг |
| IP-логика | stdlib `ipaddress` + `netaddr` | Суммаризация CIDR через `netaddr` |
---
## Открытые вопросы (нужно прояснить до кодирования)
1. **Чекбокс администратора** — ТЗ: admin = `clientId == WZ01112` + «отдельный чек-бокс». Что это: отдельный claim в Keycloak-токене (`is_admin: true`)? Роль? Нужно уточнить у команды Keycloak.
2. **Создание Company в БД** — когда появляется запись: при первом входе пользователя автоматически, или администратор заводит вручную?
3. **Кто потребляет внешний endpoint** — endpoint без авторизации, доступ по IP. Список доверенных IP задаётся конфигом? Nginx ACL?
---
## Файловая структура
```
IPWhiteList/
├── app/
│ ├── main.py # FastAPI app, роутеры, startup
│ ├── config.py # Настройки из .env (DEFAULT_LIMIT, DEV_MODE, DB_DSN и др.)
│ ├── db.py # psycopg2 connection pool
│ ├── models/
│ │ └── sql.py # DDL-схема (только для документации, не ORM)
│ ├── validators.py # IPv4/CIDR: формат, маска, серые адреса, нормализация
│ ├── crud/
│ │ ├── entries.py # CRUD whitelist_entries
│ │ ├── companies.py # Компании и лимиты
│ │ └── audit.py # Запись в audit_log
│ ├── auth/
│ │ ├── oidc.py # Валидация JWT Keycloak
│ │ ├── stub.py # Dev-заглушка (только при DEV_MODE=true)
│ │ └── deps.py # FastAPI Depends: current_user
│ ├── routers/
│ │ ├── entries.py # CRUD UI-роуты + HTMX-фрагменты
│ │ ├── admin.py # Аудит, лимиты (только admin)
│ │ └── external.py # GET /api/v1/export — txt-файл
│ └── cidr_utils.py # Суммаризация через netaddr
├── templates/
│ ├── base.html
│ ├── index.html # Таблица записей + индикатор лимита
│ ├── partials/
│ │ ├── table.html # HTMX-фрагмент таблицы
│ │ └── form.html # Форма создания/редактирования
│ └── admin/
│ ├── audit.html
│ └── limits.html
├── static/
│ └── style.css
├── migrations/
│ ├── env.py
│ └── versions/
├── tests/
│ ├── test_validators.py # Юниты для IPv4-валидации (критично!)
│ └── test_crud.py
├── docs/
│ ├── plan.md # LEGACY
│ ├── plan-v2.md # Этот файл
│ └── WhiteIPlist.docx # Исходное ТЗ
├── .env.example
├── alembic.ini
├── docker-compose.yml # PostgreSQL для dev
├── requirements.txt
└── README.md
```
---
## Схема БД
```sql
-- Компании (создаются автоматически при первом входе или вручную админом — уточнить)
CREATE TABLE companies (
id SERIAL PRIMARY KEY,
client_id VARCHAR(64) UNIQUE NOT NULL, -- из Keycloak claim
name VARCHAR(255),
custom_limit INTEGER DEFAULT NULL -- NULL = использовать глобальный DEFAULT_LIMIT
);
-- Whitelist-записи
CREATE TABLE whitelist_entries (
id SERIAL PRIMARY KEY,
company_id INTEGER NOT NULL REFERENCES companies(id),
value CIDR NOT NULL, -- нормализованный CIDR
comment VARCHAR(255),
created_by VARCHAR(255) NOT NULL, -- email из токена
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_by VARCHAR(255),
updated_at TIMESTAMPTZ,
deleted_by VARCHAR(255),
deleted_at TIMESTAMPTZ -- NULL = активная запись
);
-- Аудит (только append, без UPDATE/DELETE)
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
user_email VARCHAR(255) NOT NULL,
company_id INTEGER NOT NULL,
action VARCHAR(32) NOT NULL, -- CREATE | UPDATE | DELETE
old_value TEXT,
new_value TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Индексы
CREATE INDEX ON whitelist_entries(company_id) WHERE deleted_at IS NULL;
CREATE INDEX ON audit_log(company_id);
```
---
## Этапы
### Этап 1 — Каркас + конфиг
- [ ] Структура папок
- [ ] `requirements.txt`: fastapi, uvicorn, psycopg2-binary, alembic, jinja2, python-jose, netaddr
- [ ] `.env.example` со всеми переменными: `DB_DSN`, `DEFAULT_LIMIT=15`, `DEV_MODE=false`, `KEYCLOAK_URL`, `KEYCLOAK_REALM`, `KEYCLOAK_CLIENT_ID`, `ALLOWED_EXPORT_IPS`
- [ ] `config.py` — читает `.env`, все параметры типизированы
- [ ] `db.py` — psycopg2 connection pool (SimpleConnectionPool)
- [ ] `docker-compose.yml` с PostgreSQL
### Этап 2 — Миграции (схема БД)
- [ ] Alembic init
- [ ] Initial migration: `companies`, `whitelist_entries`, `audit_log` + индексы
- [ ] Проверка `alembic upgrade head`
### Этап 3 — Валидатор IPv4 (с тестами)
- [ ] `validators.py`: принимает строку → возвращает нормализованный CIDR или ошибку
- [ ] Проверка формата: одиночный IP или CIDR
- [ ] Проверка маски: /22 – /32 (шире /21 — `ValidationError`)
- [ ] Нормализация host-битов: `192.168.1.5/24``192.168.1.0/24` + флаг `was_normalized=True`
- [ ] Запрет серых диапазонов (все из Приложения А ТЗ)
- [ ] `tests/test_validators.py` — покрыть все граничные случаи
### Этап 4 — CRUD-логика
- [ ] `crud/companies.py`: get_or_create по client_id, get_limit (custom_limit ?? DEFAULT_LIMIT)
- [ ] `crud/entries.py`: список активных, создание (лимит + дубликаты + пересечения), редактирование, soft-delete
- [ ] `crud/audit.py`: append-only запись
### Этап 5 — Авторизация
- [ ] `auth/oidc.py` — валидация JWT через JWKS Keycloak, извлечение `clientID`, `email`, определение роли
- [ ] Логика роли admin: `clientID == WZ01112` + (claim `is_admin == true`**уточнить**)
- [ ] `auth/stub.py` — только при `DEV_MODE=true`: читает `X-Dev-User` из заголовка
- [ ] `auth/deps.py``Depends(current_user)` для роутеров
### Этап 6 — Роутеры + UI
- [ ] `routers/entries.py`: список, форма создания, форма редактирования, удаление, переключатель компании
- [ ] `routers/admin.py`: журнал аудита, управление лимитами
- [ ] Шаблоны Jinja2: base.html, index.html, form.html, admin/audit.html, admin/limits.html
- [ ] Индикатор лимита «X из N» на странице
- [ ] Уведомление о нормализации адреса пользователю
### Этап 7 — Внешний endpoint
- [ ] `GET /api/v1/export` — только активные записи всех компаний
- [ ] Суммаризация через `netaddr.cidr_merge()`
- [ ] Ответ: `text/plain`, одна строка — один CIDR
- [ ] IP-фильтр из `ALLOWED_EXPORT_IPS` (middleware или Depends)
### Этап 8 — Деплой
- [ ] `Dockerfile` (python:3.11-slim, uvicorn)
- [ ] Systemd unit как альтернатива
- [ ] Nginx конфиг: reverse proxy + location для static
- [ ] README: как поднять с нуля
---
## Ключевые принципы
- **Синхронный код везде** — никакого async/await. FastAPI поддерживает синхронные роутеры.
- **Серверная валидация — авторитетная**. Клиентская — только UX.
- **`DEV_MODE=true`** — единственный способ обойти Keycloak. В prod недоступен.
- **Audit log — append only**. Никаких UPDATE/DELETE в `audit_log`.
- **Лимит `DEFAULT_LIMIT`** — всегда из `config.py`, который читает `.env`. Без пересборки.
+118
View File
@@ -0,0 +1,118 @@
# ~~План разработки — IP WhiteList Microservice~~ [LEGACY]
> ⚠️ **УСТАРЕЛО.** Этот план содержит ошибки (async ORM, неполные требования, отсутствие тестов).
> Актуальный план: `plan-v2.md`
> **Автор:** GitHub Copilot (DeepSeek V4 Flash)
> **Дата:** 2026-05-29
---
## Стек
| Слой | Технология |
|---|---|
| Бэкенд | Python 3.11+ / FastAPI |
| БД | PostgreSQL |
| ORM | SQLAlchemy (async) + Alembic (миграции) |
| Фронтенд | Jinja2 + HTMX + минимальный CSS |
| Авторизация | Keycloak OIDC (на старте — заглушка/мок) |
| Валидация | Pydantic + встроенный `ipaddress` |
---
## Этапы
### Этап 1 — Каркас проекта
- [ ] Структура проекта: `app/`, `templates/`, `static/`, `migrations/`
- [ ] `requirements.txt` (FastAPI, SQLAlchemy, asyncpg, Alembic, Jinja2, python-keycloak)
- [ ] Конфигурация (`.env`, `config.py`)
- [ ] `docker-compose.yml` с PostgreSQL
### Этап 2 — Модели БД и миграции
- [ ] Модель `Company` (id, clientId, name, individual_limit)
- [ ] Модель `WhitelistEntry` (id, company_id, value, comment, created_by, created_at, updated_at, deleted_at, deleted_by)
- [ ] Модель `AuditLog` (id, user_email, company_id, action, old_value, new_value, timestamp)
- [ ] Alembic initial migration
### Этап 3 — Валидация IPv4
- [ ] Валидатор: одиночный IPv4 / CIDR
- [ ] Проверка маски: /32 – /22 (шире /21 — отказ)
- [ ] Нормализация host-битов в 0
- [ ] Запрет серых/приватных диапазонов (Приложение А из ТЗ)
- [ ] Проверка дубликатов и пересечений в пределах компании
### Этап 4 — CRUD + Бизнес-логика
- [ ] Создание записи (с проверкой лимита)
- [ ] Просмотр таблицы записей (для клиента — свои компании, для админа — все)
- [ ] Редактирование (с повторной валидацией)
- [ ] Soft delete (deleted_at, deleted_by)
- [ ] Лимиты: глобальный default 15, индивидуальный per-company
### Этап 5 — Аудит
- [ ] Запись всех изменяющих операций в `AuditLog`
- [ ] Просмотр журнала (только админ)
### Этап 6 — Внешний endpoint
- [ ] `GET /api/v1/whitelist/aggregated` — txt-файл
- [ ] Суммаризация (агрегация) CIDR всех компаний
- [ ] Только активные (не soft-deleted) записи
### Этап 7 — Авторизация (заглушка → Keycloak)
- [ ] Заглушка: header `X-Client-ID`, `X-User-Email`, `X-Role`
- [ ] Роли: client / admin
- [ ] Переключатель компаний (для пользователей в нескольких компаниях)
- [ ] Позже: полноценный OIDC через Keycloak
### Этап 8 — UI (Jinja2 + HTMX)
- [ ] Страница входа / редирект на Keycloak
- [ ] Таблица записей с фильтрами
- [ ] Форма создания/редактирования (с клиентской валидацией)
- [ ] Индикатор лимита: «использовано X из N»
- [ ] Админка: фильтр по компаниям, просмотр удалённых, журнал аудита
### Этап 9 — Деплой
- [ ] Systemd unit / Dockerfile
- [ ] Nginx reverse proxy (если нужно)
- [ ] CI/CD или ручная инструкция
---
## Файловая структура (план)
```
IPWhiteList/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI app
│ ├── config.py # Настройки из .env
│ ├── models.py # SQLAlchemy модели
│ ├── schemas.py # Pydantic схемы
│ ├── validators.py # IPv4/CIDR валидация
│ ├── crud.py # CRUD-операции
│ ├── auth.py # Авторизация (заглушка → Keycloak)
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── entries.py # CRUD whitelist
│ │ ├── admin.py # Админка
│ │ └── external.py # Внешний endpoint
│ └── utils.py # Суммаризация CIDR, лимиты
├── templates/
│ ├── base.html
│ ├── index.html # Таблица записей
│ ├── entry_form.html # Форма создания/редактирования
│ └── admin/
│ ├── audit.html # Журнал аудита
│ └── limits.html # Управление лимитами
├── static/
│ └── style.css
├── migrations/
│ └── alembic/
├── docs/
│ ├── plan.md # Этот файл
│ └── WhiteIPlist.docx # Исходное ТЗ
├── .env.example
├── docker-compose.yml
├── requirements.txt
└── README.md
```