データのアクセス許可を定義する

Fabric Apps では、@role デコレーターを使用して、承認規則をデータ モデルに直接アタッチします。 アクセス許可は、型セーフでリファクタリングしやすいものであり、基になるデータ アクセス構成に自動的にコンパイルされます。

始める前の準備

  • 認証 (ユーザー) と承認 (実行できる操作) の違いを理解する
  • [ 認証の構成] を確認して ID 検証を設定する
  • エンティティの基本に関する データ モデルの概要 を理解する

組み込みロール

Fabric Apps は、組み込みの authenticated ロールを認識します。 必要に応じて、ポリシーでカスタム ロールを定義することもできます。

役割 説明 利用シーン
authenticated Fabric認証を使用する有効なユーザー セッションが必要です ユーザー固有のデータ、保護されたリソース

@role デコレーター

クラス レベルで @role を適用して、エンティティに対してどのアクションを実行できるかを制御します。

@role(roleName, actions, options?)

パラメーター

パラメーター タイプ 説明
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;
}

この例は次のとおりです。

  • 認証されたユーザーは、userId が自身の JWT sub クレームと一致する Todo 項目にのみアクセスできます。

型安全なポリシー式

policy コールバックは、要求フィールドとエンティティ フィールドの両方に型指定されたアクセスを提供します。 TypeScript は、修飾されたクラスからエンティティ型を推論し、オートコンプリートとリファクタリングの安全性を提供します。

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

サポートされているクレーム

請求 説明 値の例
claims.sub サブジェクト識別子 (ユーザー ID) 00000000-0000-0000-0000-000000000001
claims.email ユーザーのメール アドレス user@contoso.com
claims.role ユーザー ロール (ID プロバイダーによって提供される場合) admin

式の演算子

Operator 説明
.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;
}

Note

フィールド配列は、エンティティの実際のプロパティ名に型指定されます。 フィールドの名前を変更すると、フィールドを参照するすべての 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 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;
}

管理者は任意のリソースを変更できますが、削除できるのは管理者だけです。

次のステップ