Saltar al contenido principal

Permisos

Ver en Git


Sistema de Permisos - Agregar un feature a la DB

El sistema funciona con intersección de permisos. Un usuario puede hacer algo solo si TANTO su tipo de usuario COMO su rol específico lo permiten.

Tablas principales​

  • funcionalidades_plataforma - Define los features de la plataforma (actividades, ensayos, stocks, etc.)
  • funcionalidades - Define todos los permisos de los features (leer, escribir, borrar, etc.)
  • tipo_usuario - Define límites máximos de permisos por tipo de usuario (Free, Pro, etc.)
  • roles - Define conjuntos de permisos asignables (Admin, Editor, Solo lectura, etc.)
  • permisos - Asigna roles a usuarios en lotes específicos
  • permisos_estancias - Asigna roles a usuarios en campos específicos
  • funcionalidades_plataforma_espacios - Define qué funcionalidades están habilitadas por espacio

Agregar nuevo feature​

Paso 1: Crear el feature

Agregar el feature en la tabla funcionalidades_plataforma. El parámetro "base" indica si se agrega automáticamente a nuevos espacios.

INSERT INTO funcionalidades_plataforma (funcionalidad, base) 
VALUES ('trials', true);
-- Supongamos que se creó con ID 10

Paso 2: Habilitarlo en espacios

Agregar el feature a los espacios donde debe estar disponible.

UPDATE funcionalidades_plataforma_espacios 
SET funcionalidades = funcionalidades || ARRAY[10]
WHERE workspace_id = 1;

Paso 3: Agregarlo al tipo de usuario

El tipo de usuario debe tener el feature habilitado en funcionalidades_plataforma.

UPDATE tipo_usuario 
SET funcionalidades_plataforma = funcionalidades_plataforma || ARRAY[10]
WHERE id = 204; -- Usuario Auravant Pro

Agregar permisos del feature​

Definir los permisos específicos (crear, leer, editar, eliminar).

Paso 1: Definir los permisos

Insertar las funcionalidades básicas. Los campos son: id, nombre, tipo de permiso (A=Agregar, L=Leer, M=Editar, B=Borrar), código del permiso, habilitado para aurapps, y entity.

INSERT INTO funcionalidades VALUES
(370, 'Crear ensayos', 'A', 'TA', false, 'Trials'),
(371, 'Leer ensayos', 'L', 'TL', false, 'Trials'),
(372, 'Modificar ensayos', 'M', 'TM', false, 'Trials'),
(373, 'Eliminar ensayos', 'B', 'TB', false, 'Trials');

Paso 2: Agregarlos al tipo de usuario

El tipo de usuario debe tener estos permisos en permisos_default:

UPDATE tipo_usuario 
SET permisos_default = permisos_default || ARRAY[370,371,372,373]
WHERE id = 204; -- Usuario Pro

Paso 3: Agregarlos a los roles

Asignar los permisos a cada rol según corresponda:

-- Rol Admin: todos los permisos
UPDATE roles
SET permisos_default = permisos_default || ARRAY[370,371,372,373]
WHERE id = 1;

-- Rol Editor: crear, leer, editar (sin eliminar)
UPDATE roles
SET permisos_default = permisos_default || ARRAY[370,371,372]
WHERE id = 3;

-- Rol Solo lectura: solo leer
UPDATE roles
SET permisos_default = permisos_default || ARRAY[371]
WHERE id = 2;

-- Rol Agregado: crear y leer (sin editar ni eliminar)
UPDATE roles
SET permisos_default = permisos_default || ARRAY[370,371]
WHERE id = 4;

Cómo llegan los permisos al frontend​

Los permisos llegan al front a través de dos endpoints principales:

getUserData - Permisos generales

Devuelve los permisos generales del usuario calculados como intersección entre su tipo de usuario y roles. Tambien devuelve los features habilitados haciendo una intersección entre los de su tipo de usuario y los del espacio:

{
"funcionalidades": [1, 2, 3, 45, 370, 371, 372],
"funcionalidades_plataforma": [10, 15, 20],
"uid": 141555,
}

getFields - Permisos específicos por campo y lote

Devuelve los permisos específicos que tiene el usuario sobre cada campo y lote, considerando su rol en cada lugar:

{
"user": {
"farms": {
"226055": {
"name": "Campo 1",
"permissions": [1, 2, 3, 370, 371],
"fields": {
"777169": {
"name": "Lote 1",
"permissions": [1, 2, 3, 370, 371, 372, 373]
}
}
}
}
}
}

Diferencias importantes:

  • getUserData: permisos generales del usuario
  • getFields: permisos específicos por campo/lote
  • Los permisos pueden variar según el rol del usuario en cada ubicación

Validar permisos en el backend​

Para validar permisos en el backend se usa la clase Permissions que busca automáticamente los permisos del usuario.

Validar permiso en lote específico:

from commons.permisos import Permissions

# Verificar si el usuario puede crear ensayos en un lote
user_permissions = Permissions.fields(user_id=141555, fields=777169)
if 370 in user_permissions.get(777169, []):
# Usuario puede crear ensayos en este lote
pass
else:
raise ApiErr(2, "Sin permisos para crear ensayos")

Validar permiso en múltiples lotes:

# Verificar permisos en varios lotes
field_ids = [777169, 777170, 777171]
user_permissions = Permissions.fields(user_id=141555, fields=field_ids)

for field_id in field_ids:
if 370 not in user_permissions.get(field_id, []):
raise ApiErr(2, "Sin permisos en lote {}".format(field_id))

Validar permiso usando helper:

from commons.permisos import validar_permisos

# Validar usando código del permiso
if not validar_permisos(user_id=141555, codigo="TA", lote=777169):
raise ApiErr(2, "Sin permisos para crear ensayos")

Servicio interno de permisos (/services/permissions)​

Además de la validación en backend con la clase Permissions, existe un servicio interno pensado para que otros servicios (notificaciones, tareas, etc.) consulten permisos vía HTTP. Todos los endpoints:

  • Cuelgan del blueprint blueprint_internal (aurapi_src/apis/permissions.py), con prefijo /services/permissions.
  • Usan ResourceAura + @ApiAura (autenticación/validaciones estándar).
  • Validan el body con JSON Schema (aurapi_src/schemas/permissions.py).
  • Reutilizan el mismo motor de cálculo (commons/permisos.py), por lo que respetan grupos de supervisión/colaboración y herencia campo → lote.

Los permisos que devuelven estos endpoints son ids de la tabla funcionalidades (FX). La letra del código de cada FX indica el tipo de acción: A=Agregar/crear, L=Leer, M=Modificar, B=Borrar. No hay un único nivel "lectura/escritura/admin"; el acceso se infiere del conjunto de FX.

Endpoints existentes​

EndpointDirecciónRecibeDevuelve
POST /verify/usersusuario → FX de usuariouser_idfxs (lista de FX a nivel usuario)
POST /verify/fieldsusuario → lotesuser_id, fields (ids)fxs {field_id: [fx...]}
POST /verify/farmsusuario → camposuser_id, farms (ids)fxs {farm_id: [fx...]}
POST /verify/users/v2usuario → FX de usuariouser_idigual que v1
POST /verify/fields/v2usuario → lotesuser_id, fields (uuid)fields {field_id: {...}}
POST /verify/farms/v2usuario → camposuser_id, farms (uuid)farms {farm_id: {...}}
POST /verify/field-userslotes → usuariosuser_id (array), fields_id (array){field_id: [{user_id, fxs, codes}]} (o {field_id: [user_id]} con lite)

Parámetros opcionales comunes: ver_id / dev_id (para intersecar con el claimset de una extensión aurapp).

POST /verify/field-users (lote → usuarios)​

Responde la pregunta inversa a verify/fields: dado un conjunto de lotes y un conjunto de usuarios candidatos, qué usuarios tienen acceso a cada lote y con qué FX. Pensado para casos como notificaciones ("¿a qué usuarios les notifico sobre este lote?").

Request:

{
"user_id": [15, 16, 17, 18],
"fields_id": [1, 2, 3],
"fx": 30,
"lite": false,
"ver_id": null,
"dev_id": null
}
  • user_id (obligatorio): array de ids de usuario candidatos.
  • fields_id (obligatorio): array de ids de lote.
  • fx (opcional): id de una funcionalidad. Si se envía, un usuario solo se incluye si tiene esa FX puntual sobre el lote (igual se devuelve el set completo de sus FX).
  • lite (opcional, default false): si es true, la respuesta lista solo los ids de usuario por lote, sin el detalle de FX.
  • ver_id / dev_id (opcionales): contexto de extensión.

Response (por defecto, lite=false):

{
"2": [
{"user_id": 16, "fxs": [30, 32], "codes": ["LOA", "LOM"]},
{"user_id": 17, "fxs": [30], "codes": ["LOA"]}
],
"3": [
{"user_id": 15, "fxs": [30, 31, 32], "codes": ["LOA", "LOB", "LOM"]}
]
}
  • La clave es el field_id (string en JSON). Los lotes sin usuarios con acceso se omiten.
  • Por cada usuario con acceso: fxs (ids de funcionalidades) y codes (códigos legibles equivalentes, resueltos con bidirectional_fx_map). Si una FX no está mapeada en CODE_ID_MAP, aparece en fxs pero no en codes.

Response (lite=true):

{
"2": [16, 17],
"3": [15]
}
  • Mismo criterio de acceso y de omisión de lotes vacíos, pero el valor por lote es solo la lista de ids de usuario.

Implementación: itera Permissions.fields(uid, fields_id) por cada usuario e invierte el resultado a {field_id: [usuarios]}. Hace N consultas (una por usuario), adecuado para volúmenes típicos de notificaciones; si se necesita escalar a cientos de usuarios por request habría que reescribirlo como una única query multi-usuario.