Definir permissões de dados

Fabric Apps usa o decorador @role para anexar regras de autorização diretamente aos seus modelos de dados. As permissões são de tipo seguro, compatíveis com refatoração e compiladas automaticamente na configuração de acesso a dados subjacente.

Antes de começar

Funções incorporadas

Fabric Apps reconhece a função predefinida authenticated. Você também pode definir funções personalizadas em suas políticas quando necessário.

Função Description Caso de uso
authenticated Requer uma sessão de usuário válida com autenticação Fabric Dados específicos do usuário, recursos protegidos

O @role decorador

Aplique @role no nível da classe para controlar quais funções podem executar quais ações em uma entidade:

@role(roleName, actions, options?)

Parâmetros

Parâmetro Tipo Description
roleName string O nome da função, como 'authenticated' ou uma função de aplicativo personalizada
actions string \| string[] Ação ou matriz única: 'create', , 'read', 'update', 'delete'ou '*' para todos
options object Objeto opcional com check, includee exclude propriedades

Exemplo básico

Restrinja os usuários 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 usuários autenticados só podem acessar itens Todo cujo userId corresponda à declaração sub do JWT.

Expressões de política de segurança de tipo

O policy callback fornece acesso tipado a campos de declarações e de entidades. O TypeScript infere o tipo de entidade da classe decorada, oferecendo-lhe segurança de preenchimento automático e refatoração:

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

Declarações com suporte

Reclamação Description Valor de exemplo
claims.sub Identificador de assunto (ID do usuário) 00000000-0000-0000-0000-000000000001
claims.email Endereço de email do usuário user@contoso.com
claims.role Função de usuário (se fornecida pelo provedor de identidade) admin

Operadores de expressão

Operador Exemplo Description
.eq() claims.sub.eq(item.userId) Verificação de igualdade

Operadores lógicos

Combinar 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 são colocados entre parênteses automaticamente para garantir o agrupamento correto.

Permissões de nível de campo

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

Incluir campos específicos

Permitir somente o title campo 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

Ocultar campos confidenciais de 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;
}

Note

Arrays de campos são tipados com base nos nomes reais das propriedades da entidade. Renomear um campo gera um erro em tempo de compilação em cada lista include ou exclude que faz referência a ele.

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: somente o criador pode criar e somente title e content campos são permitidos.
  • Ler: Somente o criador pode ler seus próprios documentos.
  • Atualização: somente o criador pode atualizar, mas não pode modificar adminNotes.
  • Excluir: somente o criador pode excluir.

Como funcionam as permissões

  • Coleção de metadados: o @role decorador coleta metadados de permissão quando a classe é definida.
  • Geração de esquema: quando você executa db apply, a CLI lê metadados e gera a configuração de permissão.
  • Compilação de política: callbacks de política em TypeScript são compilados em expressões de política de acesso a dados (por exemplo, @claims.sub eq @item.userId).
  • Imposição de tempo de execução: a camada de acesso a dados impõe permissões em cada solicitação de API.
  • Detecçã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 somente 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 completo para usuários autenticados

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

Substituiçã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 somente os administradores podem excluir.

Próximas Etapas