1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-baserow

Клонировать/Скачать
permissions-guide.md 28 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 26.06.2025 16:02 92285f5

🔐 Система разрешений

Система разрешений используется для контроля доступа к ресурсам или функциональности в Baserow. Она определяет, кто имеет право выполнять определенные операции или получать доступ к определенным данным.

Система разрешений модульна и позволяет легко добавлять или заменять различные компоненты, отвечающие за авторизацию пользовательских операций. Это обеспечивает гибкость и модульность в контроле доступа к ресурсам или функциональности в Baserow.

Это позволяет легко реализовывать и заменять различные стратегии авторизации по мере необходимости без значительных изменений в общем коде.

📖 Словарь

Прежде чем продолжать, нам нужно договориться о определении некоторых терминов:

Объект: представляет собой элемент данных в Baserow. Один из Field, Row, Table, Database, Workspace, User, Team, Role, Webhook, …

Иерархические объекты: в Baserow Объекты связаны друг с другом, и между двумя Объектами может быть зависимость родитель-потомок. Можно создать полное иерархическое дерево со всеми объектами Baserow. Например, Объект Table является потомком Объекта Database.

Актор: общий термин, объединяющий все, что может выполнять Операции над Объектом в Baserow. Может быть User, но также и Personal API Token или AnonymousUser

Операция: действие, которое Актор выполняет над Объектом. Некоторые примеры:

  • database.list_tables: операция для получения списка Tables, связанных с Database.
  • database_table.create_row: действие для создания Row в Table.
  • database_table.update: действие для обновления Объекта Table.

Контекст: Объект, на котором применяется Операция. Например:

  • для Операции database.list_tables контекстный объект — это Database, для которого мы хотим получить список Table.
  • для Операции database_table.create_row контекстный объект — это Table, для которой мы хотим создать Row.
  • для Операции database_table.update контекстный объект — это также Table, которую мы хотим обновить.

Запрос разрешения: Запрос разрешения представляется тройкой, состоящей из Актора, Операции и Контекста, которая используется для определения, предоставляется ли доступ к конкретному ресурсу или функциональности системой разрешений. Контекст может быть опущен, если Операция не требует его.

Система разрешений: весь механизм в Baserow, который решает, разрешен ли Запрос разрешения или нет. Система разрешений полагается на Менеджеры разрешений для принятия решения по конкретному Запросу разрешения в определенном Рабочем пространстве.

Менеджер разрешений: Менеджер разрешений — это модульная часть Системы разрешений, которая может решать, разрешен ли Запрос разрешения или нет, при выполнении определенных критериев. Каждый Менеджер разрешений отвечает за принятие решения в определенных ситуациях. Например, StaffOnlyPermissionManager может решить запретить Операцию, если данный Актор не является частью персонала.

Рабочее пространство: Операция может выполняться в конкретном Рабочем пространстве (ранее называлось Group).

Субъект: включает всех Акторов, а также группы Акторов, такие как Teams.

Персональный API токен: токен аутентификации, который пользователи могут создать в своих настройках в Baserow. Он принадлежит User, для Рабочего пространства, позволяя доступ к некоторым из наших API-эндпоинтов.

📃 Основные принципы

Для каждой Операции, которую Актор хочет выполнить на Контексте, система разрешений проверяет Запрос разрешения. Внутри системы каждый Менеджер разрешений проверяет Запрос разрешения по одному и тому же порядку. Каждый Менеджер разрешений может:

  • Разрешить запрос разрешения. Тогда операция может быть выполнена.
  • Запретить запрос разрешения. Операция отменяется и возникает ошибка.
  • Пропустить запрос разрешения. Запрос разрешения затем проверяется следующим Менеджером разрешений.

Если ни один из менеджеров разрешений не разрешил или не запретил запрос, то по умолчанию доступ запрещен.

Диаграмма разрешений

source

Пример:

  1. Если непривилегированный пользователь хочет создать Table в Database своего Workspace, проверяется следующее разрешение: (user, "database.create_table", database) системой разрешений.
  2. Запрос разрешения сначала передается CorePermissionManager, который не может принять решение, так как это не ядро операции. Затем он передается StaffOnlyPermissionManager, который также не может принять решение, так как это не операция только для персонала.
  3. Последний менеджер разрешений, который проверяет запрос, — это BasicPermissionManager, который позволит запрос разрешения, так как это не операция только для администраторов, поэтому пользователь может выполнить его.

⚙️ Бэкенд

Большая часть системы разрешений управляется бэкендом. Основные компоненты системы разрешений следующие:

  • OperationType: для каждой Операции, которую вы хотите проверить, вам нужен OperationType.
  • PermissionManagerType: Менеджеры разрешений отвечают за то, чтобы позволить или запретить запросы разрешений.
  • SubjectType: каждый Актор, который вы хотите использовать в системе разрешений, должен принадлежать к SubjectType.
  • ObjectScopeType: каждый контекстный объект должен быть частью иерархии объектов Baserow. На данный момент это реализовано путем наличия связанного ObjectScopeType для каждого типа объекта.

Все они являются объектами, которые можно зарегистрировать в соответствующем реестре для расширения основных функций системы разрешений Baserow.

Проверка разрешения на бэкенде

Когда вы хотите проверить разрешение на бэкенде, вам нужно использовать метод CoreHandler.check_permission.

CoreHandler().check_permission(
    # Актор может быть пользователем, который выполнил запрос: actor = request.user.
    actor, 
    # CreateRowDatabaseTable — это класс `OperationType`, а `.type` — его имя.
    CreateRowDatabaseTable.type,  
    context=table, 
    workspace=workspace
)

Если запрос разрешения разрешен, этот метод вернет True. В противном случае он вызовет исключение PermissionException.

Рабочее пространство (ранее известное как группа) является необязательным параметром, если операция является ядерной операцией и не относится к какому-либо конкретному рабочему пространству.

Фильтрация Django Queryset

Другой распространенный случай использования, связанный с разрешениями, — это фильтрация Django Queryset на основе прав пользователя. Вы можете достичь фильтрации Queryset с помощью этого метода.

CoreHandler().filter_queryset(
    # Актор может быть пользователем, который выполнил запрос: actor = request.user.
    actor, 
    # ListTablesDatabaseTableOperationType — это класс `OperationType`, а `.type` — его имя.
    ListTablesDatabaseTableOperationType.type,  
    queryset,
    workspace=workspace
)

Здесь контекст — это база данных, так как мы выводим таблицы этой базы данных, но Queryset — это Queryset таблиц. Это согласуется с свойством object_scope класса ListTablesDatabaseTableOperationType, которое равно TableObjectScope. Цель свойства object_scope заключается в том, чтобы помочь определить, какие объекты являются целью операции.

Объявление новой операции

Для каждой операции, которую вы хотите проверить, должен быть зарегистрирован экземпляр OperationType. Его можно объявить следующим образом:

from baserow.core.registries import OperationType

class ListTablesDatabaseTableOperationType(OperationType):
    type = "database.list_tables" # Тип
    context_scope_name = "database" # Имя типа контекста, необходимого для проверки прав
    object_scope_name = "database_table" # Имя типа объектов, обрабатываемых операцией

Для большинства операций context_scope_name и object_scope_name совпадают, поэтому последнее можно опустить. Однако для всех операций "list" object_scope_name обычно является одним из потомков контекстного объекта в иерархии объектов. Когда вы хотите вывести все Tables базы данных Database, контекст — это база данных Database, а объекты — это таблицы Tables. Когда вы выводите все базы данных приложения Application, контекст — это приложение Application, а объекты — связанные базы данных.

Этот класс должен быть зарегистрирован в реестре operation_type_registry, чтобы его можно было использовать.

from baserow.core.registries import operation_type_registry
operation_type_registry.register(ListTablesDatabaseTableOperationType())

Для каждой зарегистрированной операции создается экземпляр Operation, который сохраняется в базе данных. Вы можете использовать их позже в коде вашего менеджера разрешений при необходимости.

Создание менеджера разрешений бэкенда

Менеджер разрешений отвечает за то, чтобы решать, разрешен ли запрос разрешения для определенной области приложения. Для обеспечения правильного разделения ответственности хороший менеджер разрешений должен обрабатывать только один случай проверки прав. Менеджеры разрешений затем накладываются друг на друга для создания сложного и мощного алгоритма проверки прав. Вы можете думать о них как о Django middleware, но вместо этого для запроса разрешения.

Для создания нового менеджера разрешений вам нужно создать новый класс PermissionManagerType и реализовать необходимые методы.

from baserow.core.registries import PermissionManagerType

class OwnedTablePermissionManagerType(PermissionManagerType):
    type = "owned_table"

    def check_multiple_permissions(self, check, workspace=None, include_trash=False):
        ...

    def get_permissions_object(self, actor, workspace=None):
        ...

    def filter_queryset(self, actor, operation_name, queryset, workspace=None):
        ...

Краткий обзор этих методов:

  • .check_multiple_permissions — это метод проверки прав сам по себе. Он принимает несколько проверок сразу для лучшей производительности. Для каждой проверки результат должен содержать значение True, если менеджер прав может принять запрос на право доступа, или экземпляр PermissionException, если нет или он вообще не должен включать эту проверку.
  • get_permissions_object должен возвращать любое значение, которое будет полезно фронтенд-менеджеру прав для проверки фронтенд-права доступа. Данные должны быть достаточными для того, чтобы фронтенд мог принять решение без необходимости запрашивать дополнительные данные с бэкенда.
  • filter_queryset используется для фильтрации Queryset на основе прав актора на объекты, возвращаемые Queryset. Метод должен исключать те же объекты, которые были бы исключены методом .check_permission, если бы он был вызван для каждого объекта Queryset.

Вы можете прочитать связанные строки документации для получения более подробной информации о этих методах.

Затем вы можете зарегистрировать его в реестре permission_manager_type_registry.

from baserow.core.registries import permission_manager_type_registry
permission_manager_type_registry.register(OwnedTablePermissionManagerType())

Вы должны добавить ваш менеджер прав в список активных менеджеров прав в настройках Django:

PERMISSION_MANAGERS = [
'core',
'staff_only',
...
'owned_table', # <- здесь
...
'basic'
]

Позиция менеджера прав в списке зависит от его приоритета над другими менеджерами прав. В нашем случае мы хотим, чтобы менеджер прав ответил до того, как базовый менеджер прав откажется от него.

Теперь вы можете проверить право доступа, обрабатываемое вашим менеджером прав 🎯.

Помните, что вам вероятно потребуется фронтенд-менеджер прав для каждого бэкенд-менеджера прав. См. раздел фронтенд для получения дополнительной информации.

📺 Фронтенд

Как проверить право доступа

На фронтенде вы можете проверить право доступа с помощью метода $hasPermission, доступного на экземпляре Vue:

// Внутри компонента Vue
// this.$hasPermission(<operationName>, <contextObject>, <currentWorkspaceId>)
this.$hasPermission("database.create_table", database, workspace.id);

Этот вызов вернет true, если операция допускается, иначе — false.

Объект прав доступа

Фронтенд права доступа рассчитываются с помощью объекта прав доступа, отправляемого бэкендом при входе пользователя в систему для каждого рабочего пространства, к которому у пользователя есть доступ. Проверьте метод .get_permissions_object каждого бэкенд-менеджера прав.

Объект прав доступа выглядит следующим образом:

[
  {
    "name": "core",
    "permissions": [
      "list_workspaces"
    ]
  },
  {
    "name": "staff",
    "permissions": {
      "staff_only_operations": [
        "settings.update"
      ],
      "is_staff": true
    }
  },
  {
    "name": "basic",
    "permissions": {
      "admin_only_operations": [
        "workspace.list_invitations",
        "...",
        "workspace.delete"
      ],
      "is_admin": true
    }
  }
]

Каждый элемент списка был сгенерирован менеджером прав на бэкенде. Свойство name — это .type самого менеджера прав, а свойство permissions может содержать любое значение, которое помогает фронтенду решить, можно ли предоставить право доступа. Для каждого бэкенд-менеджера прав должен быть зарегистрирован фронтенд-менеджер прав для обработки его значения.

Для проверки прав доступа плагин фронтенд $hasPermission спрашивает у каждого менеджера прав по порядку списка (в порядке списка), предоставлен ли право доступа или нет на основе данных из свойства permissions.

Например, метод BasicPermissionManagerType.hasPermission(permissions, operation, context, workspaceId) будет вызван с следующим объектом:

{
    "admin_only_operations": [
    "workspace.list_invitations",
    "...",
    "workspace_user.delete"
    ],
    "is_admin": true
}

См. следующий раздел для получения информации о том, как создать фронтенд-менеджер прав.

Создание менеджера прав

Для каждого бэкенд-менеджера прав вам вероятно потребуется фронтенд-менеджер прав (некоторые менеджеры прав не требуют этого).

Вы можете создать фронтенд-менеджер прав следующим образом:

import { PermissionManagerType } from '@baserow/modules/core/permissionManagerTypes'


export class OwnedTablePermissionManagerType extends PermissionManagerType {
  static getType() {
    return 'owned_table'
  }

  hasPermission(permissions, operation, context) {
    // ...
  }
}

Проверьте документацию методов класса PermissionManagerType, чтобы понять, как реализовать метод hasPermission для вашего менеджера прав.

Затем вам нужно зарегистрировать его во время инициализации плагина Vue в файле фронтенда plugin.js вашего проекта.

app.$registry.register('permissionManager', new OwnedTablePermissionManagerType(context))

И вот оно что — у вас есть полностью функциональный фронтенд-менеджер прав.

📝 Заключение

Если вы хотите создать новый способ проверки прав доступа, вам нужно:

  • Создать бэкенд-менеджер прав
  • Реализовать его методы
  • Зарегистрировать менеджер прав
  • Добавить недостающие операции при необходимости
  • Создать фронтенд-менеджер прав
  • Реализовать его методы
  • Зарегистрировать фронтенд-менеджер прав
  • Проверить все

🤔 Несколько соображений

Система прав доступа была спроектирована с учетом следующих ограничений:

  • Должна быть расширяемой (для поддержки RBAC из корпоративной папки)
  • Должна быть максимально совместимой с предыдущей системой (метод .has_user)
  • Должна хорошо работать с реальным временем
  • Должна уметь работать как с объектами, так и с коллекциями объектов
  • Должна быть производительной
  • Должна избегать дублирования кода между бэкендом и фронтендом

Это может объяснить некоторые из принятых решений.

Более технически:

  • Родителем базы данных (Database) не является рабочее пространство (Workspace), как можно было бы подумать сначала, а более общий тип — приложение (Application). Это решает множество проблем (хотя и создает некоторые при недостаточном внимании).
  • Для актора типа пользователь (User) базовый менеджер прав имеет две роли: ADMIN и MEMBER, что совместимо с предыдущей системой прав доступа. Имя роли хранится в поле WorkspaceUser.permissions. Идея заключается в том, чтобы другие системы ролей использовали это поле для совместимости и избегания дублирования данных или синхронизации при переходе от одной системы к другой. Для типа менеджера прав BasicPermissionManagerType значение ADMIN в этом свойстве означает, что пользователь является администратором (ADMIN). Для любых других значений пользователь рассматривается как обычный участник (MEMBER) рабочего пространства.
  • Текущий персональный API токен частично мигрирован в текущую систему прав доступа.
  • Анонимный пользователь (AnonymousUser) является типом субъекта (SubjectType), который может обрабатываться некоторыми менеджерами прав.

Планируется:

  • Добавить новый токен аутентификации для замены старого токена, который действительно использует систему прав доступа.
  • Переименовать поле .permissions в более понятное.
  • Обработать публичные представления с помощью нового менеджера прав.
  • Создать несколько новых ролей.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-baserow.git
git@api.gitlife.ru:oschina-mirror/mirrors-baserow.git
oschina-mirror
mirrors-baserow
mirrors-baserow
develop