init: ТЗ, правила copilot, планы от моделей (DeepSeek, Claude, GPT-5.4, Gemini)
This commit is contained in:
@@ -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 без реальной необходимости
|
||||||
|
- Любое изменение кода — только после явного «делай» от пользователя
|
||||||
|
|
||||||
|
## Процесс
|
||||||
|
- Сначала уточнить непонятное → предложить простой шаг → сделать → повторить
|
||||||
|
- Не пытаться сделать всё сразу
|
||||||
Executable
BIN
Binary file not shown.
@@ -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 с описанием переменных окружения).
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user