Files
IPWhiteList/docs/[DEPRECATED]-plan-v2.md
T

9.8 KiB
Raw Blame History

План разработки — 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

Схема БД

-- Компании (создаются автоматически при первом входе или вручную админом — уточнить)
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/24192.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.pyDepends(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. Без пересборки.