9.8 KiB
9.8 KiB
План разработки — 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 |
Открытые вопросы (нужно прояснить до кодирования)
- Чекбокс администратора — ТЗ: admin =
clientId == WZ01112+ «отдельный чек-бокс». Что это: отдельный claim в Keycloak-токене (is_admin: true)? Роль? Нужно уточнить у команды Keycloak. - Создание Company в БД — когда появляется запись: при первом входе пользователя автоматически, или администратор заводит вручную?
- Кто потребляет внешний 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
Схема БД
-- Компании (создаются автоматически при первом входе или вручную админом — уточнить)
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_IPSconfig.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-deletecrud/audit.py: append-only запись
Этап 5 — Авторизация
auth/oidc.py— валидация JWT через JWKS Keycloak, извлечениеclientID,email, определение роли- Логика роли admin:
clientID == WZ01112+ (claimis_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. Без пересборки.