From 0345dd07dd6a037b5ee21718282ea08313ca2b7b Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Mon, 30 Mar 2026 18:18:18 +0300 Subject: [PATCH] document authorization policies Mark the policy model as completed in the framework backlog Add English and Russian wiki pages for policies and update navigation --- Framework-Backlog-RU.md | 68 +++++++++++++++++++- Framework-Backlog.md | 68 +++++++++++++++++++- Policies-RU.md | 138 ++++++++++++++++++++++++++++++++++++++++ Policies.md | 138 ++++++++++++++++++++++++++++++++++++++++ _Sidebar.md | 2 + 5 files changed, 408 insertions(+), 6 deletions(-) create mode 100644 Policies-RU.md create mode 100644 Policies.md diff --git a/Framework-Backlog-RU.md b/Framework-Backlog-RU.md index ec3b8ee..10626d4 100644 --- a/Framework-Backlog-RU.md +++ b/Framework-Backlog-RU.md @@ -4,17 +4,79 @@ ## Приоритет 1 — Срочно -- Модель выполнения webhook: у библиотеки есть хорошая polling-модель, но нет полноценной модели выполнения webhook на уровне фреймворка. -- Модель авторизации и политик: middleware могут реализовать аутентификацию и права доступа, но нет явной модели уровня фреймворка для политик доступа, ролей или проверок возможностей. - Модель наблюдаемости: логирование уже сильное, но метрики, трассировка и структурированные хуки фреймворка пока не являются полноценной частью API. ## Приоритет 2 — Важно - Модель сервисного слоя и графа зависимостей: `DatabaseContext(T)` намеренно минималистичен, но нет более сильной концепции уровня фреймворка для сервисов приложения или зависимостей с ограниченной областью действия. -- Контракт композиции плагинов: плагины — хороший способ группировки, но нет явной модели зависимостей плагинов, общих возможностей или контрактов композиции. + +## Partial + +### Модель выполнения webhook + +Текущее состояние: + +- В репозитории уже есть низкоуровневые API для настройки webhook на уровне `tgapi`: `SetWebhook(...)`, `DeleteWebhook(...)`, `GetWebhookInfo(...)`, а также поддержка загрузки сертификата через uploader. +- Основной runtime бота по-прежнему ориентирован на polling и предоставляет только `Run()` / `RunWithContext(...)` поверх `getUpdates`. + +Что ещё отсутствует: + +- Полноценная bot-level модель выполнения webhook, например `RunWebhook(...)`, `http.Handler` или другой явный framework-owned путь приёма входящих webhook-обновлений. +- Задокументированный lifecycle contract для webhook-режима, сопоставимый с уже существующей polling-моделью. +- Единый способ пропускать webhook-обновления через те же границы runtime, не заставляя пользователя собирать внешнюю HTTP-интеграцию самостоятельно. + +Почему это только partial: + +- Поддержка webhook-транспорта для Telegram уже есть на уровне API-клиента. +- Но сам фреймворк всё ещё не даёт webhook runtime concept, сопоставимый с его polling execution model. + +### Контракт композиции плагинов + +Текущее состояние: + +- Плагины уже не являются просто "мешком команд": регистрация через `AddPlugins(...)` делает snapshot plugin state, считает регистрацию точкой фиксации и документирует post-registration mutation как неподдерживаемую. +- Это уже даёт фреймворку осмысленный базовый контракт вокруг владения конфигурацией плагина и его неизменяемости во время runtime. + +Что ещё отсутствует: + +- Явные зависимости между плагинами. +- Декларации общих возможностей или требований между плагинами. +- Модель композиции уровня фреймворка для валидации и координации отношений между плагинами. + +Почему это только partial: + +- В репозитории уже есть реальный контракт регистрации. +- Но более сильной composition model, о которой говорилось в backlog, пока всё ещё нет. ## Done +### [1.0.0-rc.13] Модель авторизации и политик + +Текущее состояние: + +- Во фреймворке теперь есть `Policy[T]` как явная переиспользуемая модель правила доступа, работающая поверх нормализованного `MsgContext` и общих данных приложения. +- Политики интегрируются в уже существующую модель выполнения через `RequirePolicy(...)`, поэтому авторизация остаётся на middleware-пути и не создаёт второй pipeline маршрутизации. +- У бота и плагинов появились явные helpers для регистрации политик на уровне конфигурации. + +Почему это важно: + +- Проверки доступа почти всегда нужны Telegram-ботам, но одних ad-hoc middleware недостаточно, чтобы это стало стабильной framework concept. +- Переиспользуемые policy helpers делают ограничения по типу чата, правам администратора и callback-контексту видимыми и компонуемыми, вместо того чтобы размазывать их по внутренностям обработчиков. +- Сохранение выполнения политик в рамках существующего middleware-пути позволяет добавить отдельный язык авторизации, не ломая текущую runtime model. + +Что теперь есть: + +- `Policy[T]` как публичная абстракция авторизации. +- `RequirePolicy(...)` для адаптации политик в блокирующие middleware. +- `Bot.UsePolicy(...)` и `Plugin.UsePolicy(...)` для удобной регистрации. +- Встроенные Telegram-aware helpers: `RequirePrivateChat(...)`, `RequireGroupChat(...)`, `RequireSupergroupChat(...)`, `RequireChatAdmin(...)`, `RequireChatCreator(...)`, `RequireBotAdmin(...)` и `RequireCallbackFromUser(...)`. +- Комбинаторы `AllPolicies(...)`, `AnyPolicy(...)` и `NotPolicy(...)`. +- Расширенная нормализация `MsgContext` для `Chat` и `ChatID`, а также регрессионные тесты на поведение политик и нормализованного контекста. + +Практическая цель: + +- Считать правила доступа полноценной концепцией фреймворка, а не оставлять контроль доступа только на уровне случайного паттерна middleware. + ### [1.0.0-rc.13] Модель пользовательских и внутренних ошибок Текущее состояние: diff --git a/Framework-Backlog.md b/Framework-Backlog.md index 2053c53..752dd7b 100644 --- a/Framework-Backlog.md +++ b/Framework-Backlog.md @@ -4,17 +4,79 @@ This page tracks framework-level backlog items that are about missing concepts i ## Priority 1 — Urgent -- Webhook runtime model: the library has a solid polling model, but no first-class webhook execution model at the framework level. -- Authorization and policy model: middleware can implement auth and permissions, but there is no explicit framework concept for access policies, roles, or capability checks. - Observability model: logging is strong, but metrics, tracing, and structured framework hooks are still missing as first-class concepts. ## Priority 2 — Important - Service layer and dependency graph model: `DatabaseContext(T)` is intentionally minimal, but there is no stronger framework concept for application services or scoped dependencies. -- Plugin composition contract: plugins are a good grouping unit, but there is no explicit model for plugin dependencies, shared capabilities, or composition contracts. + +## Partial + +### Webhook runtime model + +Current state: + +- The repository already exposes low-level Telegram webhook setup APIs through `tgapi`, including `SetWebhook(...)`, `DeleteWebhook(...)`, `GetWebhookInfo(...)`, and uploader-based certificate upload support. +- The main bot runtime is still polling-first and only exposes `Run()` / `RunWithContext(...)` around `getUpdates`. + +What is still missing: + +- A first-class bot-level webhook execution model such as `RunWebhook(...)`, an `http.Handler`, or another explicit framework-owned ingestion path for incoming webhook updates. +- A documented lifecycle contract for webhook mode that matches the existing polling runtime model. +- A unified way to feed webhook-delivered updates through the same framework runtime boundaries without requiring users to assemble their own outer HTTP integration layer. + +Why this is only partial: + +- Telegram webhook transport support exists at the API client level. +- The framework itself still does not provide a webhook runtime concept comparable to its polling execution model. + +### Plugin composition contract + +Current state: + +- Plugins are already more than a loose command bag: registration via `AddPlugins(...)` snapshots plugin state, treats registration as a commit point, and documents post-registration mutation as unsupported. +- This gives the framework a meaningful baseline contract around plugin ownership and immutability at runtime. + +What is still missing: + +- Explicit plugin dependencies. +- Shared capability declarations or requirements between plugins. +- A framework-level composition model for validating or coordinating plugin relationships. + +Why this is only partial: + +- The repository already has a real registration contract. +- It still does not have the stronger composition model originally described in the backlog item. ## Done +### [1.0.0-rc.13] Authorization and Policy Model + +Current state: + +- The framework now exposes `Policy[T]` as a first-class reusable authorization rule that runs against the normalized `MsgContext` and injected app data. +- Policies integrate with the existing execution model through `RequirePolicy(...)`, so authorization stays on the middleware path instead of introducing a second routing pipeline. +- Bot-level and plugin-level registration helpers now make policy usage explicit in configuration. + +Why this matters: + +- Access checks are a common requirement in Telegram bots, but ad-hoc middleware alone does not create a stable framework concept. +- Reusable policy helpers make chat-type restrictions, admin checks, and callback restrictions visible and composable instead of spreading them across handler internals. +- Keeping policy execution on the existing middleware path preserves the framework's current runtime model while still giving authorization its own language. + +What is now present: + +- `Policy[T]` as the public authorization abstraction. +- `RequirePolicy(...)` for adapting policies into blocking middleware. +- `Bot.UsePolicy(...)` and `Plugin.UsePolicy(...)` for registration ergonomics. +- Built-in Telegram-aware helpers such as `RequirePrivateChat(...)`, `RequireGroupChat(...)`, `RequireSupergroupChat(...)`, `RequireChatAdmin(...)`, `RequireChatCreator(...)`, `RequireBotAdmin(...)`, and `RequireCallbackFromUser(...)`. +- Composition helpers `AllPolicies(...)`, `AnyPolicy(...)`, and `NotPolicy(...)`. +- Extended `MsgContext` normalization for `Chat` and `ChatID`, plus regression coverage for policy and normalization behavior. + +Practical target: + +- Treat authorization rules as a reusable framework concept instead of leaving access control as only an ad-hoc middleware pattern. + ### [1.0.0-rc.13] User-Facing vs Internal Error Model Current state: diff --git a/Policies-RU.md b/Policies-RU.md new file mode 100644 index 0000000..8ba8361 --- /dev/null +++ b/Policies-RU.md @@ -0,0 +1,138 @@ +# Policies + +Policies — это полноценная модель правил авторизации в Laniakea. Политика работает поверх нормализованного `MsgContext` и переданных `AppData`, возвращает `nil`, если доступ разрешён, и возвращает ошибку, если действие нужно запретить или сама проверка не смогла корректно выполниться. + +Policies не вводят вторую модель выполнения. Они встраиваются в уже существующий middleware pipeline через `RequirePolicy(...)`, поэтому авторизация остаётся на том же пути маршрутизации, что и остальной фреймворк. + +## Что дают политики + +- Переиспользуемую абстракцию `Policy[T]` для проверок доступа. +- Интеграцию с middleware через `RequirePolicy(...)`. +- Helpers регистрации на `Bot` и `Plugin` через `UsePolicy(...)`. +- Встроенные Telegram-aware проверки для типичных ограничений по типу чата и правам. +- Комбинаторы для all-of, any-of и инверсии правила. + +## Основной API + +```go +type Policy[T laniakea.AppData] func(ctx *laniakea.MsgContext, data T) error + +func RequirePolicy[T laniakea.AppData](name string, p Policy[T]) Middleware[T] + +func AllPolicies[T laniakea.AppData](policies ...Policy[T]) Policy[T] +func AnyPolicy[T laniakea.AppData](policies ...Policy[T]) Policy[T] +func NotPolicy[T laniakea.AppData](policy Policy[T]) Policy[T] +``` + +Контракт политики намеренно остаётся маленьким: + +- Вернуть `nil`, чтобы разрешить выполнение. +- Вернуть пользовательскую ошибку, чтобы запретить выполнение. +- Вернуть внутреннюю ошибку, чтобы остановить выполнение без утечки текста сбоя пользователю. + +Поскольку политики используют обычную модель ошибок, они естественно сочетаются с `AsUserError(...)` и `AsInternalError(...)`. + +## Регистрация + +Политики подключаются через ту же конфигурационную поверхность бота и плагинов, что и middleware. + +```go +bot.UsePolicy("human-callbacks", laniakea.RequireCallbackFromUser()) + +plugin.UsePolicy( + "admin-only", + laniakea.AllPolicies( + laniakea.RequireGroupChat(), + laniakea.RequireChatAdmin(), + ), +) +``` + +`RequirePolicy(...)` — это нижележащий адаптер, если хочется добавить получившееся middleware вручную. + +## Встроенные политики + +Текущий набор встроенных helpers ориентирован на типичные Telegram-specific проверки доступа: + +- `RequirePrivateChat(...)` +- `RequireGroupChat(...)` +- `RequireSupergroupChat(...)` +- `RequireChatAdmin(...)` +- `RequireChatCreator(...)` +- `RequireBotAdmin(...)` +- `RequireCallbackFromUser(...)` + +Эти helpers используют нормализованные данные `MsgContext`. В частности, chat-aware политики опираются на `Chat` и `ChatID`, которые теперь заполняются не только для message-backed обновлений. + +## Композиция + +Политики задуманы как маленькие строительные блоки. + +### AllPolicies + +`AllPolicies(...)` успешен только тогда, когда успешны все вложенные политики. + +```go +plugin.UsePolicy( + "moderation", + laniakea.AllPolicies( + laniakea.RequireGroupChat(), + laniakea.RequireChatAdmin(), + laniakea.RequireBotAdmin(), + ), +) +``` + +Вычисление останавливается на первой возвращённой ошибке. + +### AnyPolicy + +`AnyPolicy(...)` успешен, когда успешна хотя бы одна вложенная политика. + +```go +policy := laniakea.AnyPolicy( + isGlobalOwner, + laniakea.RequireChatCreator(), +) +``` + +Если ни одна политика не сработала: + +- внутренняя ошибка имеет приоритет над обычным deny; +- иначе возвращается первая ошибка запрета. + +Такой подход сохраняет fail-closed поведение и не скрывает сбои, важные для операторов. + +### NotPolicy + +`NotPolicy(...)` инвертирует deny-результат. + +```go +policy := laniakea.NotPolicy(laniakea.RequirePrivateChat()) +``` + +Если вложенная политика успешна, `NotPolicy(...)` возвращает deny-ошибку. Если вложенная политика вернула внутреннюю ошибку, она сохраняется как есть и не превращается в success. + +## Модель ошибок + +Ошибки политик идут по тому же централизованному пути, что и ошибки обработчиков: + +- пользовательские ошибки могут превратиться в обычный ответ пользователю; +- внутренние ошибки логируются, но не отправляют свой сырой текст в чат автоматически. + +Это позволяет политикам безопасно работать в fail-closed режиме, не заставляя обработчики дублировать авторизационную логику. + +## Текущие ограничения + +- Политики выполняются через middleware бота и плагинов; у сцен пока нет собственного middleware layer. +- Встроенные helpers — это Telegram-aware проверки авторизации, а не полноценная ролевая или capability system. +- Во фреймворке пока нет отдельной principal model, DSL для RBAC или слоя кэширования прав. + +Эти вещи можно добавить позже, если текущей модели `Policy[T]` окажется недостаточно, но текущий дизайн позволяет сделать авторизацию явной, не превращая Laniakea в тяжёлый auth framework. + +Связанные страницы: + +- [[Middleware-RU]] +- [[MsgContext-RU]] +- [[Commands-and-Plugins-RU]] +- [[Framework-Backlog-RU]] diff --git a/Policies.md b/Policies.md new file mode 100644 index 0000000..7ae0cd6 --- /dev/null +++ b/Policies.md @@ -0,0 +1,138 @@ +# Policies + +Policies are Laniakea's first-class authorization rules. A policy runs against the normalized `MsgContext` and injected `AppData`, returns `nil` when access is allowed, and returns an error when access should be denied or when the check itself fails. + +Policies do not introduce a second execution model. They plug into the existing middleware pipeline through `RequirePolicy(...)`, so authorization stays on the same routing path as the rest of the framework. + +## What policies give you + +- A reusable `Policy[T]` abstraction for access checks. +- Middleware integration through `RequirePolicy(...)`. +- Registration helpers on `Bot` and `Plugin` through `UsePolicy(...)`. +- Built-in Telegram-aware checks for common chat and permission constraints. +- Composition helpers for all-of, any-of, and inverted rules. + +## Core API + +```go +type Policy[T laniakea.AppData] func(ctx *laniakea.MsgContext, data T) error + +func RequirePolicy[T laniakea.AppData](name string, p Policy[T]) Middleware[T] + +func AllPolicies[T laniakea.AppData](policies ...Policy[T]) Policy[T] +func AnyPolicy[T laniakea.AppData](policies ...Policy[T]) Policy[T] +func NotPolicy[T laniakea.AppData](policy Policy[T]) Policy[T] +``` + +The policy contract is intentionally small: + +- Return `nil` to allow execution. +- Return a user-visible error to deny execution. +- Return an internal error to stop execution without leaking the failure text to the user. + +Because policies follow the normal error model, they work naturally with `AsUserError(...)` and `AsInternalError(...)`. + +## Registration + +Policies are attached through the same bot and plugin configuration surface as middleware. + +```go +bot.UsePolicy("human-callbacks", laniakea.RequireCallbackFromUser()) + +plugin.UsePolicy( + "admin-only", + laniakea.AllPolicies( + laniakea.RequireGroupChat(), + laniakea.RequireChatAdmin(), + ), +) +``` + +`RequirePolicy(...)` is the lower-level adapter if you want to add the resulting middleware manually. + +## Built-in policies + +The current built-in helpers focus on common Telegram-specific access checks: + +- `RequirePrivateChat(...)` +- `RequireGroupChat(...)` +- `RequireSupergroupChat(...)` +- `RequireChatAdmin(...)` +- `RequireChatCreator(...)` +- `RequireBotAdmin(...)` +- `RequireCallbackFromUser(...)` + +These helpers use normalized `MsgContext` data. In particular, chat-aware policies rely on `Chat` and `ChatID`, which are now populated for more update kinds than only message-backed ones. + +## Composition + +Policies are intended to be small building blocks. + +### AllPolicies + +`AllPolicies(...)` succeeds only when every nested policy succeeds. + +```go +plugin.UsePolicy( + "moderation", + laniakea.AllPolicies( + laniakea.RequireGroupChat(), + laniakea.RequireChatAdmin(), + laniakea.RequireBotAdmin(), + ), +) +``` + +Evaluation stops on the first returned error. + +### AnyPolicy + +`AnyPolicy(...)` succeeds when at least one nested policy succeeds. + +```go +policy := laniakea.AnyPolicy( + isGlobalOwner, + laniakea.RequireChatCreator(), +) +``` + +If no policy succeeds: + +- an internal error wins over ordinary deny errors; +- otherwise the first deny error is returned. + +This keeps the composition fail-closed without hiding operator-relevant failures. + +### NotPolicy + +`NotPolicy(...)` inverts a deny result. + +```go +policy := laniakea.NotPolicy(laniakea.RequirePrivateChat()) +``` + +If the wrapped policy succeeds, `NotPolicy(...)` returns a deny error. If the wrapped policy returns an internal error, that internal error is preserved instead of being inverted into success. + +## Error model + +Policy errors follow the same centralized handling path as handler errors: + +- user-visible errors can produce a normal user reply; +- internal errors are logged but do not automatically leak their text to the chat. + +That means policy checks can safely fail closed without forcing handlers to duplicate authorization logic. + +## Current limits + +- Policies are executed through bot and plugin middleware; scenes do not currently expose their own middleware layer. +- The built-in helpers are Telegram-aware authorization helpers, not a full role or capability system. +- The framework does not yet provide a separate principal model, RBAC DSL, or permission cache layer. + +Those features can be added later if the existing `Policy[T]` model proves too small, but the current design keeps authorization explicit without making Laniakea into a heavy auth framework. + +Related pages: + +- [[Middleware]] +- [[MsgContext]] +- [[Commands-and-Plugins]] +- [[Framework-Backlog]] diff --git a/_Sidebar.md b/_Sidebar.md index 21f0ddb..bac1a86 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -11,8 +11,10 @@ - [[tgapi-Overview]] - [[Bot-Lifecycle]] - [[Middleware]] +- [[Policies]] - [[Scenes]] - [[Scenes-RU|Scenes (RU)]] +- [[Policies-RU|Policies (RU)]] ## Changes and troubleshooting - [[Migration]]