Definir permissões de dados

Fabric Apps usa o decorador @role para anexar regras de autorização diretamente aos teus modelos de dados. As permissões são seguras em termos de tipos, propícias à refatorização e compiladas automaticamente na configuração subjacente de acesso a dados.

Antes de começar

Funções incorporadas

Fabric Apps reconhece a função integrada authenticated. Também pode definir papéis personalizados nas suas políticas quando necessário.

Função Descrição Caso de utilização
authenticated Requer uma sessão de utilizador válida com autenticação Fabric Dados específicos do utilizador, recursos protegidos

O @role decorador

Aplique @role ao nível da classe para controlar que funções podem executar que ações sobre uma entidade:

@role(roleName, actions, options?)

Parameters

Parâmetro Tipo Descrição
roleName string O nome da função, como 'authenticated' ou uma função de aplicação personalizada
actions string \| string[] Ação simples ou matriz: 'create', 'read', 'update', 'delete', ou '*' para todo
options object Objeto opcional com check, include, e exclude propriedades

Exemplo básico

Restringa os utilizadores autenticados aos seus próprios dados:

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

Neste exemplo:

  • Os utilizadores autenticados só podem aceder a itens Todo em que userId corresponde à declaração sub do respetivo JWT.

Expressões de políticas seguras por tipos

O policy callback fornece acesso digitado tanto aos campos de reivindicações como de entidade. O TypeScript infere o tipo de entidade a partir da classe decorada, dando-te autocompletamento e segurança de refatoração:

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

Reivindicações suportadas

Afirmação Descrição Valor de exemplo
claims.sub Identificador do assunto (ID de utilizador) 00000000-0000-0000-0000-000000000001
claims.email Endereço de e-mail do usuário user@contoso.com
claims.role Papel do utilizador (se fornecido pelo fornecedor de identidade) admin

Operadores de expressão

Operador Example Descrição
.eq() claims.sub.eq(item.userId) Verificação de igualdade

Operadores lógicos

Combine expressões com .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))
})

Ambos os lados estão automaticamente entre parênteses para agrupamento correto.

Permissões ao nível de campo

Especifique os campos a que uma função pode aceder usando include ou exclude nas opções de função.

Incluir campos específicos

Permitir o title campo apenas durante as operações de criação:

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

Excluir campos específicos

Oculte campos sensíveis das operações de leitura:

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

Observação

As matrizes de campos são tipadas de acordo com os nomes reais das propriedades da entidade. Renomear um campo origina um erro em tempo de compilação em todas as listas include ou exclude que lhe fazem referência.

Permissões específicas de ação

Aplique regras diferentes por ação usando vários @role decoradores:

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

Esta configuração:

  • Criar: Apenas o criador pode criar, e apenas os campos title e content são permitidos.
  • Leitura: Só o criador pode ler os seus próprios documentos.
  • Atualização: Só o criador pode atualizar, mas não pode modificar adminNotes.
  • Eliminar: Só o criador pode apagar.

Como funcionam as permissões

  • Recolha de metadados: O @role decorador recolhe metadados de permissão quando a classe é definida.
  • Geração de esquemas: Quando executa db apply, a CLI lê metadados e gera a configuração de permissões.
  • Compilação de políticas: Os callbacks de políticas TypeScript são compilados em expressões de políticas de acesso a dados (por exemplo, @claims.sub eq @item.userId).
  • Imposição em tempo de execução: A camada de acesso a dados impõe permissões a cada pedido à API.
  • Deteção de conflitos: Vários @role decoradores na mesma classe são agregados por função, com avisos para declarações conflitantes.

Padrões comuns

Acesso exclusivo ao proprietário

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

Acesso total para utilizadores autenticados

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

anulação pelo administrador

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

Os administradores podem modificar qualquer recurso, mas só os administradores podem apagar.

Passos seguintes