Définir des autorisations de données

Fabric Apps utilise le décorateur @role pour attacher des règles d’autorisation directement à vos modèles de données. Les autorisations sont typées de façon sûre, faciles à refactoriser et compilées automatiquement en configuration sous-jacente d’accès aux données.

Avant de commencer

Rôles intégrés

Fabric Apps reconnaît le rôle authenticated intégré. Vous pouvez également définir des rôles personnalisés dans vos stratégies si nécessaire.

Rôle Description Cas d’utilisation
authenticated Nécessite une session utilisateur valide avec l’authentification Fabric Données spécifiques à l’utilisateur, ressources protégées

Décorateur @role

Appliquez @role au niveau de la classe pour contrôler quels rôles peuvent effectuer les actions sur une entité :

@role(roleName, actions, options?)

Paramètres

Paramètre Type Description
roleName string Le nom du rôle, tel que 'authenticated' ou un rôle d’application personnalisé
actions string \| string[] Action ou tableau unique : 'create', , 'read''update', 'delete'ou '*' pour tous
options object Objet facultatif avec check, includeet exclude propriétés

Exemple de base

Limitez les utilisateurs authentifiés à leurs propres données :

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;
}

Dans cet exemple :

  • Les utilisateurs authentifiés peuvent accéder uniquement aux éléments Todo dont userId correspond à leur revendication JWT sub.

Expressions de stratégie à typage sûr

La policy fonction de rappel fournit un accès typé à la fois aux champs de déclarations et aux champs d’entité. TypeScript déduit le type d’entité à partir de la classe décorée, ce qui vous offre l’autocomplétion et une sécurité lors du refactoring :

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

Revendications prises en charge

Réclamation Description Exemple de valeur
claims.sub Identificateur d’objet (ID d’utilisateur) 00000000-0000-0000-0000-000000000001
claims.email Adresse e-mail de l’utilisateur user@contoso.com
claims.role Rôle d’utilisateur (s’il est fourni par le fournisseur d’identité) admin

Opérateurs d’expression

Operator Example Description
.eq() claims.sub.eq(item.userId) Vérification de l’égalité

Opérateurs logiques

Combiner des expressions avec .and() et .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))
})

Les deux côtés sont mis entre parenthèses automatiquement afin de garantir un regroupement correct.

Autorisations au niveau du champ

Spécifiez les champs qu’un rôle peut accéder à l’aide include ou exclude dans les options de rôle.

Inclure des champs spécifiques

Autoriser uniquement le champ title lors des opérations de création :

@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;
}

Exclure des champs spécifiques

Masquer les champs sensibles des opérations de lecture :

@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

Les tableaux de champs sont typés selon les noms de propriétés réels de l’entité. Le changement de nom d’un champ génère une erreur au moment de la compilation dans chaque include ou exclude liste qui la référence.

Autorisations spécifiques à l’action

Appliquez différentes règles par action à l’aide de plusieurs @role décorateurs :

@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;
}

Cette configuration :

  • Créer : seul le créateur peut créer, et seuls title les content champs sont autorisés.
  • Lecture : seul le créateur peut lire ses propres documents.
  • Mise à jour : seul le créateur peut mettre à jour, mais il ne peut pas le modifier adminNotes.
  • Supprimer : seul le créateur peut supprimer.

Fonctionnement des autorisations

  • Collection de métadonnées : le @role décorateur collecte les métadonnées d’autorisation lorsque la classe est définie.
  • Génération de schéma : lorsque vous exécutez db apply, l’interface CLI lit les métadonnées et génère la configuration des autorisations.
  • Compilation des stratégies : les fonctions de rappel de stratégie TypeScript sont compilées en expressions de stratégie d’accès aux données (par exemple, @claims.sub eq @item.userId).
  • Application du runtime : la couche d’accès aux données applique des autorisations à chaque demande d’API.
  • Détection des conflits : plusieurs @role décorateurs de la même classe sont agrégés par rôle, avec des avertissements pour les déclarations en conflit.

Modèles courants

Accès propriétaire uniquement

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

Accès complet pour les utilisateurs authentifiés

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

Forçage administrateur

@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;
}

Les administrateurs peuvent modifier n’importe quelle ressource, mais seuls les administrateurs peuvent supprimer.

Étapes suivantes