REPOSITORY / ScuroNeko/Laniakea
Wiki
document authorization policies
Mark the policy model as completed in the framework backlog Add English and Russian wiki pages for policies and update navigation
+65
-3
@@ -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] Модель пользовательских и внутренних ошибок
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
+65
-3
@@ -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:
|
||||
|
||||
+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]]
|
||||
- [[Bot-Lifecycle]]
|
||||
- [[Middleware]]
|
||||
- [[Policies]]
|
||||
- [[Scenes]]
|
||||
- [[Scenes-RU|Scenes (RU)]]
|
||||
- [[Policies-RU|Policies (RU)]]
|
||||
|
||||
## Changes and troubleshooting
|
||||
- [[Migration]]
|
||||
|
||||
Reference in New Issue
Block a user