Определение разрешений данных

Fabric Apps использует декоратор @role для привязки правил авторизации непосредственно к вашим моделям данных. Разрешения являются типобезопасными, рефакторными и автоматически компилируются в базовую конфигурацию доступа к данным.

Перед тем как начать

  • Общие сведения о разнице между проверкой подлинности (кто вы являетесь) и авторизацией (что можно сделать).
  • Ознакомьтесь с Настройка аутентификации, чтобы настроить проверку личности
  • Ознакомьтесь с обзором моделей данных для понимания основ сущностей

Встроенные роли

Приложения Fabric распознает встроенную роль authenticated. При необходимости вы также можете задать пользовательские роли в своих политиках.

Роль Description Сценарий использования
authenticated Требуется действительный пользовательский сеанс с аутентификацией Fabric Данные конкретного пользователя, защищённые ресурсы

Декоратор @role

Примените @role на уровне класса, чтобы управлять тем, какие роли могут выполнять те или иные действия для сущности:

@role(roleName, actions, options?)

Parameters

Parameter Тип Description
roleName string Имя роли, например 'authenticated' или пользовательская роль приложения
actions string \| string[] Одно действие или массив: 'create', , 'read''update''delete'или '*' для всех
options object Необязательный объект со свойствами check, include и exclude

Базовый пример

Ограничьте прошедших проверку подлинности пользователей собственными данными:

import { entity, role, uuid, text } from '@microsoft/rayfin-core';

@entity()
@role('authenticated', ['create', 'read', 'update', 'delete'], {
  policy: (claims, item) => claims.sub.eq(item.userId),
})
export class Todo {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) description?: string;
  @text() userId!: string;
}

В этом примере:

  • Прошедшие проверку подлинности пользователи могут получить доступ только к элементам Todo, где userId соответствует их утверждению JWT sub .

Типобезопасные выражения политик

Функция обратного вызова policy предоставляет типизированный доступ как к полям утверждений, так и к полям сущностей. TypeScript выводит тип сущности на основе декорированного класса, обеспечивая автодополнение и безопасность рефакторинга:

policy: (claims, item) => claims.sub.eq(item.userId)

Поддерживаемые утверждения

Утверждение Description Пример значения
claims.sub Идентификатор субъекта (идентификатор пользователя) 00000000-0000-0000-0000-000000000001
claims.email Адрес электронной почты пользователя user@contoso.com
claims.role Роль пользователя (если она предоставлена поставщиком удостоверений) admin

Операторы выражений

Operator Example Description
.eq() claims.sub.eq(item.userId) Проверка равенства

Логические операторы

Объедините выражения с .and() и .or():

// User must own the item AND item must be active
@role('authenticated', 'read', {
  policy: (claims, item) =>
    claims.sub.eq(item.userId).and(item.isActive.eq(true))
})

// User is admin OR user owns the item
@role('authenticated', ['update', 'delete'], {
  policy: (claims, item) =>
    claims.role.eq('admin').or(claims.sub.eq(item.ownerId))
})

Обе стороны автоматически скобляются для правильной группировки.

Разрешения на уровне поля

Укажите поля, к которым роль может получить доступ с помощью include или exclude в настройках роли.

Включение определенных полей

Разрешить поле title только при создании:

@entity()
@role('authenticated', 'create', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  include: ['title'],
})
export class Document {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) content?: string;
  @text() createdBy!: string;
}

Исключение определенных полей

Скрыть конфиденциальные поля при операциях чтения:

@entity()
@role('authenticated', 'read', {
  exclude: ['lastLogin', 'passwordHash'],
})
export class User {
  @uuid() id!: string;
  @text() email!: string;
  @date({ optional: true }) lastLogin?: Date;
  @text() passwordHash!: string;
}

Замечание

Массивы полей типизированы фактическими именами свойств сущности. Переименование поля приводит к ошибке на этапе компиляции в каждом списке include или exclude, который ссылается на него.

Разрешения для конкретных действий

Применяйте разные правила к каждому действию с помощью нескольких декораторов @role:

@entity()
@role('authenticated', 'create', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  include: ['title', 'content'],
})
@role('authenticated', 'read', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
})
@role('authenticated', 'update', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  exclude: ['adminNotes'],
})
@role('authenticated', 'delete', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
})
export class SecureDocument {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) content?: string;
  @text({ optional: true }) adminNotes?: string;
  @text() createdBy!: string;
}

Эта конфигурация:

  • Создать: только создатель может создавать, и разрешены только поля title и content.
  • Читать: Только создатель может читать собственные документы.
  • Обновление: только создатель может обновить, но они не могут изменить adminNotes.
  • Удаление: только создатель может удалить.

Как работают разрешения

  • Коллекция метаданных: @role декоратор собирает метаданные разрешений при определении класса.
  • Создание схемы: при запуске db applyCLI считывает метаданные и создает конфигурацию разрешений.
  • Компиляция политик: обратные вызовы политики TypeScript компилируются в выражения политики доступа к данным (например, @claims.sub eq @item.userId).
  • Контроль доступа во время выполнения: уровень доступа к данным обеспечивает соблюдение разрешений для каждого API-запроса.
  • Обнаружение конфликтов: несколько @role декораторов одного класса агрегируются на одну роль с предупреждениями для конфликтующих объявлений.

Распространенные шаблоны

Доступ только для владельца

@entity()
@role('authenticated', '*', {
  policy: (claims, item) => claims.sub.eq(item.ownerId)
})
export class PrivateNote {
  @uuid() id!: string;
  @text() ownerId!: string;
  @text() content!: string;
}

Полный доступ для прошедших проверку подлинности пользователей

@entity()
@role('authenticated', '*')
export class BlogPost {
  @uuid() id!: string;
  @text() title!: string;
  @text() content!: string;
}

Переопределение администратором

@entity()
@role('authenticated', ['create', 'read', 'update'], {
  policy: (claims, item) =>
    claims.role.eq('admin').or(claims.sub.eq(item.ownerId))
})
@role('authenticated', 'delete', {
  policy: (claims, _item) => claims.role.eq('admin')
})
export class ManagedResource {
  @uuid() id!: string;
  @text() ownerId!: string;
  @text() name!: string;
}

Администраторы могут изменять любой ресурс, но только администраторы могут удалять их.

Дальнейшие действия