定義資料權限

Fabric Apps 使用 @role 裝飾器,直接將授權規則附加到你的資料模型上。 權限具備型別安全特性、便於重構,並會自動編譯為底層的資料存取組態。

開始之前

  • 了解認證(你是誰)和授權(你能做什麼)之間的差別
  • 檢視 配置認證 以設定身份驗證
  • 了解 資料模型的概覽 以了解實體基礎

內建角色

Fabric Apps 會辨識內建的 authenticated 角色。 必要時,你也可以在政策中定義自訂角色。

角色 Description 應用案例
authenticated 需要有效的使用者會話與 Fabric 認證 使用者專屬資料,受保護資源

裝飾師@role

在類別層級應用 @role 以控制哪些角色可以對實體執行哪些動作:

@role(roleName, actions, options?)

參數

參數 類型 Description
roleName string 角色名稱,例如 'authenticated' 或自訂應用程式角色
actions string \| string[] 單動作或陣列:'create''read''update''delete''*'對所有
options object 可選物件,具有 checkinclude,且具有 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;
}

在此範例中:

  • 已認證的使用者只能存取與其 JWT userId 主張相符的sub待辦事項。

型別安全政策表達式

policy 回呼函式提供對宣告與實體欄位的型別化存取。 TypeScript 會從經過裝飾的類別中推斷實體類型,讓你享有自動補全與重構安全性:

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

支援的主張

索賠 Description 範例值
claims.sub 主旨識別碼(使用者ID) 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))
})

兩邊會自動用括號標示,以便正確分組。

現場層級權限

使用角色選項中的 includeexclude,指定角色可存取的欄位。

請包含特定欄位

僅允許在建立操作期間使用此 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;
}

備註

欄位陣列會被類型化為實體的實際屬性名稱。 重新命名欄位會在每個參考該欄位的 includeexclude 清單中產生編譯時錯誤。

動作專屬權限

使用多個 @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;
}

此配置:

  • 創建:只有創作者能創建,且只有 titlecontent 欄位被允許。
  • 閱讀:只有創作者能閱讀自己的文件。
  • 更新:只有創作者可以更新,但他們無法修改 adminNotes
  • 刪除:只有創作者能刪除。

權限運作方式

  • 元資料收集@role 當類別定義時,裝飾者會收集權限元資料。
  • 架構產生:當你執行 db apply時,CLI 會讀取元資料並產生權限設定。
  • 政策編譯: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;
}

管理員可以修改任何資源,但只有管理員能刪除。

下一步