Definición de permisos de datos

Fabric Apps usa el decorador de @role para adjuntar reglas de autorización directamente a los modelos de datos. Los permisos tienen seguridad de tipos, son fáciles de refactorizar y se compilan automáticamente en la configuración subyacente de acceso a datos.

Antes de empezar

Roles integrados

Fabric Apps reconoce el rol integrado authenticated. También puede definir roles personalizados en las directivas cuando sea necesario.

Función Descripción Caso de uso
authenticated Requiere una sesión de usuario válida con autenticación de Fabric Datos específicos del usuario, recursos protegidos

El @role decorador

Aplique @role a nivel de clase para controlar qué roles pueden realizar qué acciones sobre una entidad:

@role(roleName, actions, options?)

Parámetros

Parámetro Tipo Descripción
roleName string El nombre del rol, como 'authenticated' o un rol de aplicación personalizado
actions string \| string[] Acción única o matriz: 'create', 'read', 'update', 'delete'o '*' para todos
options object Objeto opcional con propiedades check, include y exclude

Ejemplo básico

Restringir los usuarios autenticados a sus propios datos:

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

En este ejemplo:

  • Los usuarios autenticados solo pueden acceder a los elementos Todo cuyo userId coincide con su declaración sub del JWT.

Expresiones de directiva con tipado seguro

La policy función de devolución de llamada proporciona acceso tipado tanto a las declaraciones como a los campos de entidad. TypeScript infiere el tipo de entidad a partir de la clase decorada, lo que te ofrece autocompletado y seguridad al refactorizar:

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

Declaraciones compatibles

Reclamación Descripción Ejemplo de valor
claims.sub Identificador del firmante (identificador de usuario) 00000000-0000-0000-0000-000000000001
claims.email Dirección de correo electrónico del usuario user@contoso.com
claims.role Rol de usuario (si lo proporciona el proveedor de identidades) admin

Operadores de expresión

Operador Ejemplo Descripción
.eq() claims.sub.eq(item.userId) Comprobación de igualdad

Operadores logicos

Combinar expresiones con .and() y .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 lados se ponen automáticamente entre paréntesis para una agrupación correcta.

Permisos de nivel de campo

Especifique los campos a los que puede acceder un rol mediante include o exclude en las opciones de rol.

Incluir campos específicos

Permitir solo el title campo durante las operaciones de creación:

@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 confidenciales de las operaciones de lectura:

@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

Los arrays de campos están tipados con los nombres de propiedad reales de la entidad. Cambiar el nombre de un campo produce un error en tiempo de compilación en cada lista include o exclude que hace referencia a él.

Permisos específicos por acción

Aplique reglas diferentes por acción mediante varios @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 configuración:

  • Crear: solo el creador puede crear y solo se permiten los campos title y content.
  • Lectura: Solo el creador puede leer sus propios documentos.
  • Actualización: solo el creador puede actualizar, pero no puede modificar adminNotes.
  • Eliminar: solo el creador puede eliminar.

Funcionamiento de los permisos

  • Colección de metadatos: el @role decorador recopila metadatos de permisos cuando se define la clase .
  • Generación de esquemas: al ejecutar db apply, la CLI lee los metadatos y genera la configuración de permisos.
  • Compilación de políticas: las funciones callback de políticas de TypeScript se compilan en expresiones de política de acceso a datos (por ejemplo, @claims.sub eq @item.userId).
  • Cumplimiento en tiempo de ejecución: la capa de acceso a datos exige permisos en cada solicitud de API.
  • Detección de conflictos: Los varios decoradores @role de una misma clase se agrupan según el rol, con advertencias sobre declaraciones conflictivas.

Patrones comunes

Acceso solo para el propietario

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

Acceso total para usuarios autenticados

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

Anulación del 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;
}

Los administradores pueden modificar cualquier recurso, pero solo los administradores pueden eliminar.

Pasos siguientes