Definire le autorizzazioni per i dati

Fabric Apps usa il decoratore @role per associare direttamente le regole di autorizzazione ai modelli di dati. Le autorizzazioni sono sicure rispetto ai tipi, compatibili con il refactoring e vengono compilate automaticamente nella configurazione sottostante per l'accesso ai dati.

Prima di iniziare

  • Comprendere la differenza tra l'autenticazione (chi si è) e l'autorizzazione (cosa è possibile fare)
  • Consulta Configurare l'autenticazione per configurare la verifica dell'identità
  • Panoramica dei modelli di dati per informazioni di base sulle entità

Ruoli predefiniti

Fabric App riconosce il ruolo predefinito authenticated. È anche possibile definire ruoli personalizzati nei criteri quando necessario.

Ruolo Description Caso di utilizzo
authenticated Richiede una sessione utente valida con autenticazione Fabric Dati specifici dell'utente, risorse protette

Il decorator @role

Applicare @role a livello di classe per controllare quali ruoli possono eseguire le azioni su un'entità:

@role(roleName, actions, options?)

Parameters

Parametro Tipo Description
roleName string Nome del ruolo, ad esempio 'authenticated' o ruolo applicazione personalizzato
actions string \| string[] Singola azione o matrice: 'create', 'read', 'update', 'delete'o '*' per tutti
options object Oggetto facoltativo con proprietà check, include e exclude

Esempio di base

Limitare gli utenti autenticati ai propri dati:

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

In questo esempio:

  • Gli utenti autenticati possono accedere solo agli elementi Todo in cui userId corrisponde all'attestazione JWT sub .

Espressioni di criteri con controllo di tipo

Il callback policy consente l'accesso tipizzato sia alle dichiarazioni sia ai campi delle entità. TypeScript deduce il tipo dell'entità dalla classe decorata, offrendoti il completamento automatico e un refactoring sicuro:

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

Attestazioni supportate

Richiesta di rimborso Description Valore di esempio
claims.sub Identificatore del soggetto (ID utente) 00000000-0000-0000-0000-000000000001
claims.email Indirizzo e-mail dell'utente user@contoso.com
claims.role Ruolo utente (se fornito dal provider di identità) admin

Operatori di espressione

Operatore Example Description
.eq() claims.sub.eq(item.userId) Controllo di uguaglianza

Operatori logici

Combinare espressioni con .and() e .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))
})

Entrambi i lati sono racchiusi automaticamente tra parentesi per il raggruppamento corretto.

Autorizzazioni a livello di campo

Specificare i campi a cui un ruolo può accedere usando include o exclude nelle opzioni del ruolo.

Includi campi specifici

Consentire solo il campo title durante le operazioni di creazione:

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

Escludere campi specifici

Nascondere i campi sensibili dalle operazioni di lettura:

@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

Le matrici di campi vengono tipate in base ai nomi effettivi delle proprietà dell'entità. La ridenominazione di un campo genera un errore in fase di compilazione in ogni include elenco o exclude che vi fa riferimento.

Autorizzazioni specifiche dell'azione

Applica regole diverse per ogni azione usando più decoratori @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;
}

Questa configurazione:

  • Crea: solo il creatore può creare e sono consentiti solo i campi title e content.
  • Lettura: solo l'autore può leggere i propri documenti.
  • Aggiornamento: solo l'autore può aggiornare, ma non può modificare adminNotes.
  • Elimina: solo l'autore può eliminare.

Funzionamento delle autorizzazioni

  • Raccolta di metadati: l'elemento @role Decorator raccoglie i metadati delle autorizzazioni quando viene definita la classe .
  • Generazione dello schema: quando si esegue db apply, l'interfaccia della riga di comando legge i metadati e genera la configurazione delle autorizzazioni.
  • Compilazione dei criteri: i callback dei criteri TypeScript vengono compilati in espressioni dei criteri di accesso ai dati (ad esempio, @claims.sub eq @item.userId).
  • Imposizione del runtime: il livello di accesso ai dati applica le autorizzazioni per ogni richiesta API.
  • Rilevamento dei conflitti: più @role decoratori sulla stessa classe vengono aggregati in base al ruolo, con avvisi per dichiarazioni conflittuali.

Modelli comuni

Accesso solo proprietario

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

Accesso completo per gli utenti autenticati

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

Override per gli amministratori

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

Gli amministratori possono modificare qualsiasi risorsa, ma solo gli amministratori possono eliminare.

Passaggi successivi