Авторизация и права

Авторизация обязательна. Есть локальный backend с scrypt-хешами:

from quite_admin import PasswordAuth, hash_password
auth = PasswordAuth({"alice": hash_password(password_from_secret_store)})
# Для локального запуска: PasswordAuth.single_user("alice", password_from_secret_store)

Для пользователей из приложения реализуйте два async-метода:

from quite_admin import Identity

class ApplicationAuth:
    async def authenticate(self, request, username, password):
        user = await accounts.verify_admin(username, password)
        if user:
            return Identity(str(user.id), user.name, frozenset(user.roles))
        return None

    async def get_identity(self, request, subject):
        user = await accounts.get_active_admin(subject)
        if user:
            return Identity(str(user.id), user.name, frozenset(user.roles))
        return None

get_identity() вызывается на каждом защищённом запросе: можно сразу отзывать доступ.
Пользователь доступен в request.state.identity и в шаблонах как identity.

Права проверяются и для кнопок, и для прямых запросов:

async def can_access(request, identity, action):
    return "admin" in identity.roles or action in {"list", "view"}

admin.register(User, options=ModelOptions(permission=can_access))

Действия: list, view, create, edit, delete. Это права уровня таблицы.
Для истории действий доступен history_permission; история и поиск
пользователей учитывают текущие права list/view на таблицы.
Для tenant isolation и ограничений отдельных записей фильтруйте запросы в собственном
адаптере; одного скрытия элементов интерфейса недостаточно.

Сессии подписаны, HttpOnly, SameSite=Lax, Secure по умолчанию; secure_cookies=False
предназначен для локального HTTP. Секрет должен быть постоянным и общим для workers.
В подписанной cookie хранится subject и CSRF, а не пароль. Cookie не шифруется.
Logout удаляет cookie текущего браузера; он не отзывает скопированные cookies.
Для немедленного отзыва используйте get_identity() с серверным реестром сессий / версией доступа.

Все POST, включая login/logout, защищены CSRF. Jinja autoescape, CSP с точными хешами встроенных скриптов,
ограничение тела формы в 1 MiB, запрет duplicate form fields и Cache-Control: no-store
включены. Login limiter: 10 попыток в минуту на IP внутри процесса.
Для нескольких workers настройте общий limiter на ingress/proxy.
При нескольких админках на одном домене задайте им разные session_cookie.