REPOSITORY / ScuroNeko/Laniakea

Wiki

KNOWLEDGE REPOSITORY

document authorization policies

Mark the policy model as completed in the framework backlog

Add English and Russian wiki pages for policies and update navigation
2026-03-30 18:18:18 +03:00
parent ced708851d
commit 0345dd07dd
5 changed files with 408 additions and 6 deletions
+65 -3
@@ -4,17 +4,79 @@
## Приоритет 1 — Срочно ## Приоритет 1 — Срочно
- Модель выполнения webhook: у библиотеки есть хорошая polling-модель, но нет полноценной модели выполнения webhook на уровне фреймворка.
- Модель авторизации и политик: middleware могут реализовать аутентификацию и права доступа, но нет явной модели уровня фреймворка для политик доступа, ролей или проверок возможностей.
- Модель наблюдаемости: логирование уже сильное, но метрики, трассировка и структурированные хуки фреймворка пока не являются полноценной частью API. - Модель наблюдаемости: логирование уже сильное, но метрики, трассировка и структурированные хуки фреймворка пока не являются полноценной частью API.
## Приоритет 2 — Важно ## Приоритет 2 — Важно
- Модель сервисного слоя и графа зависимостей: `DatabaseContext(T)` намеренно минималистичен, но нет более сильной концепции уровня фреймворка для сервисов приложения или зависимостей с ограниченной областью действия. - Модель сервисного слоя и графа зависимостей: `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 ## 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] Модель пользовательских и внутренних ошибок ### [1.0.0-rc.13] Модель пользовательских и внутренних ошибок
Текущее состояние: Текущее состояние:
+65 -3
@@ -4,17 +4,79 @@ This page tracks framework-level backlog items that are about missing concepts i
## Priority 1 — Urgent ## 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. - Observability model: logging is strong, but metrics, tracing, and structured framework hooks are still missing as first-class concepts.
## Priority 2 — Important ## 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. - 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 ## 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 ### [1.0.0-rc.13] User-Facing vs Internal Error Model
Current state: Current state:
+138
@@ -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]]
+138
@@ -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]]
+2
@@ -11,8 +11,10 @@
- [[tgapi-Overview]] - [[tgapi-Overview]]
- [[Bot-Lifecycle]] - [[Bot-Lifecycle]]
- [[Middleware]] - [[Middleware]]
- [[Policies]]
- [[Scenes]] - [[Scenes]]
- [[Scenes-RU|Scenes (RU)]] - [[Scenes-RU|Scenes (RU)]]
- [[Policies-RU|Policies (RU)]]
## Changes and troubleshooting ## Changes and troubleshooting
- [[Migration]] - [[Migration]]