Skip to main content
Glama
dsoziev1

Auth0 User Monitor

by dsoziev1
README.md
# День 18 — Планировщик и фоновые задачи (Auth0 user-monitor)

Отдельный **MCP-сервер БЕЗ LLM** (чистый код), который работает 24/7: по
расписанию (раз в 10 минут) собирает сводку по пользователям тенанта Auth0,
кладёт отчёт в SQLite и **авто-блокирует подозрительных админов**. Сводка
отдаётся по запросу мгновенно из БД. Плюс набор скриптов, моделирующих жизненный
цикл пользователей (создание → роли/доступ → активность → блокировка → удаление).

## Что внутри

| Файл | Роль |
|------|------|
| `server.py` | **MCP-сервер (без LLM)** — tools + фоновый планировщик (stdio / `--http`). |
| `scheduler.py` | Фоновый поток: раз в N сек собирает сводку, авто-блокирует подозрительных, пишет в БД. |
| `collector.py` | Сбор сводки из РЕАЛЬНОГО Auth0 (users, roles, last_login, fp-логи, доступ к приложениям). |
| `summary_store.py` | SQLite-хранилище отчётов. |
| `simulate.py` / `simlib.py` | Скрипты моделирования жизненного цикла пользователей (запускаются вручную). |
| `client.py` | Минимальный MCP-клиент для проверки. |
| `auth0_client.py` / `config.py` / `logutil.py` | Транспорт к Auth0 (с retry на 429, полные HTTP-логи, маскирование секретов). |

## MCP-инструменты (без LLM)

| Tool | Что делает |
|------|------------|
| `get_user_summary` | Последний отчёт из БД (собран по расписанию) — мгновенно, без обращения к Auth0. |
| `collect_now` | Собрать свежий отчёт сейчас, сохранить, вернуть (заодно авто-блок подозрительных). |
| `block_suspicious_users` | Найти админов с ≥ порога неудачных входов и заблокировать; вернуть id. |
| `list_reports` | История отчётов (время + итоги). |
| `set_schedule` | Сменить период сбора (сек) и/или порог подозрительности. Сохраняется, действует сразу. |

**Сводка по пользователю:** email, статус (`blocked`), `last_login`, `logins_count`,
роли, признак `admin`, доступ к приложениям (`apps`), число неудачных входов
(`failed_logins`), флаг `suspicious`.

**Подозрительный = admin-роль И ≥ `AUTH0_SUSPICIOUS_THRESHOLD` (по умолч. 5)
неудачных входов И не заблокирован.** Порог и интервал — через env.

## Как это «по-настоящему» (анализ осуществимости)

Всё, что можно — реально в тенанте; то, что Auth0 «из коробки» не даёт —
решено явно:

| Требование | Реализация |
|------------|------------|
| Создание / отключение (`blocked`) / удаление пользователей | ✅ напрямую Management API |
| Роли (выдача/отзыв) | ✅ `POST`/`DELETE /users/{id}/roles` |
| `last_login` (когда логинился) | **Реальные входы через ROPG** (password-realm grant) → Auth0 проставляет настоящий `last_login`/`logins_count` |
| «Неудачные входы с неправильным паролем» | **Реальные `fp`-логи** от ROPG с неверным паролем; коллектор считает их из `GET /logs?q=type:fp` |
| «Доступ к приложениям» | В Auth0 нет прямой связи user→app; моделируется списком в `app_metadata.sim.apps` (+ реальные роли) |
| Низкий rate-limit dev-тенанта | Retry на `429` с backoff (Retry-After / X-RateLimit-Reset) |
| Search-индекс `q=` отстаёт от создания | Списки пользователей берём через обычный `GET /users` (консистентно), не через `q=` |

Для ROPG скрипт `setup` создаёт **изолированные удаляемые ресурсы**: отдельное
regular-web приложение `day18-simulator` (с password-realm grant) и отдельную
БД-коннекшн `day18-sim-db` (включены и симулятор, и M2M-клиент). `cleanup` всё
удаляет. Секрет симулятора лежит в `.sim_state.json` (**в .gitignore**).

## Запуск

```bash
pip install -r requirements.txt
cp .env.example .env        # AUTH0_DOMAIN / CLIENT_ID / CLIENT_SECRET
```

**1. Сервер 24/7 (отдельный процесс):**
```bash
python3 server.py --http                 # http://127.0.0.1:8766/mcp, планировщик внутри
# короткий интервал для проверки:
AUTH0_SUMMARY_INTERVAL=60 python3 server.py --http
```

**2. Запросы (другой терминал):**
```bash
python3 client.py --http --call get_user_summary
python3 client.py --http --call collect_now
python3 client.py --http --call block_suspicious_users
```

**3. Смоделировать «видимость» (создаёт реальные демо-объекты в тенанте):**
```bash
python3 simulate.py scenario     # users + роли + доступ + реальные входы + блокировка
python3 simulate.py list
python3 simulate.py cleanup       # удалить всё: users + connection + app + role
```

`scenario` создаёт 6 пользователей: 2 админа (`day18-admin1/2`) и 4 обычных;
выдаёт роли/доступ; делает реальные входы; `day18-admin2` получает 6 неудачных
входов (станет подозрительным → сервер заблокирует), `day18-user3` тоже 6
неудачных, но он НЕ админ → НЕ блокируется; `day18-user4` отключается.

## Переменные окружения

| Env | По умолч. | Назначение |
|-----|-----------|------------|
| `AUTH0_SUMMARY_INTERVAL` | `600` | Период сбора (сек). |
| `AUTH0_SUSPICIOUS_THRESHOLD` | `5` | Порог неудачных входов для подозрительного админа. |
| `DAY18_DB` | `day18.db` | Файл SQLite с отчётами. |

## Агент (день 18): чат + панель инструментов

Лёгкий LLM-агент, подключённый ТОЛЬКО к монитору дня 18 (без старого Auth0
98-инструментного MCP). Интерфейс — только чат и описание инструментов сервера.

| Файл | Роль |
|------|------|
| `web.py` | Минимальный веб: чат + панель «🧰 Инструменты MCP-сервера». |
| `chat_agent.py` | Простой агент: диалог + function-calling по тулзам монитора (без этапов/инвариантов/памяти). |
| `mcp_client.py` | MCP-клиент/мост к монитору (HTTP по URL или stdio). |
| `llm_client.py` | Транспорт к DeepSeek (function-calling). |

```bash
# 1) монитор (24/7) в одном терминале:
python3 server.py --http
# 2) агент-чат в другом:
MONITOR_URL=http://127.0.0.1:8766/mcp uvicorn web:app --reload   # http://127.0.0.1:8000
```
Примеры запросов в чате: «покажи сводку», «собери сейчас», «заблокируй
подозрительных», «меняй расписание на 5 минут». Агент сам вызывает нужный
инструмент монитора; под ответом видно 🔧 какой тул был вызван.

> Сам MCP-сервер по-прежнему без LLM. LLM использует только агент.

### Авто-обновление на стороне агента (аналог «loop»)

`auto_refresh.py` — фоновый поток-таймер в агенте: пока включено, каждые N секунд
сам вызывает `collect_now` у монитора, чтобы данные обновлялись без запроса
пользователя. Это «loop», но реализованный в приложении (Claude Code `/loop` —
фича харнесса, повторно запускающая ассистента; здесь — обычный поток).

Настройка: чекбокс «Авто-обновление данных» + интервал в минутах (по умолчанию
5 мин, минимум 30 сек). Env: `AUTO_REFRESH=1`, `AUTO_REFRESH_INTERVAL=300`.
В UI — строка статуса последнего обновления; в чат добавляется `♻ авто-обновление`
при каждом новом сборе. Эндпоинты `GET/POST /auto`.

## Архитектура (24/7)

```
                       ┌─ scheduler (поток, раз в N сек) ─┐
client / агент ─MCP─▶ server.py ─▶ collector ─HTTPS─▶ Auth0 Mgmt API
                          │            ├─ users + roles + last_login
                          │            ├─ logs (type:fp) → failed_logins
                          │            └─ auto-block (admin + ≥5) → PATCH blocked
                          ▼
                    summary_store (SQLite)  ◀── get_user_summary читает отсюда
```
Сервер не использует LLM. Скрипты симуляции — отдельно, их запускают вручную.