Paper Técnico: Sistema Minimalista de Control de Acceso Granular y Multi-Base de Datos

Resumen:

Este documento describe un sistema minimalista pero potente de control de acceso granular en un entorno SaaS, donde los usuarios tienen acceso controlado a entidades y operaciones específicas, con la capacidad adicional de gestionar múltiples bases de datos. Se implementa un sistema de roles y permisos que permite definir jerarquías de acceso y control sobre entidades, operaciones y datos relacionados. Además, se incluye la capacidad de limitar el acceso a una base de datos a la vez por usuario, asegurando un entorno seguro y flexible.

1. Introducción

El control de acceso granular es un aspecto crucial en el desarrollo de aplicaciones SaaS. Este paper describe un sistema minimalista que utiliza una estructura de tipos y campos en GraphQL para gestionar los permisos y el acceso a operaciones y entidades, asegurando la flexibilidad y simplicidad en su administración. Este sistema también permite la segmentación de acceso a múltiples bases de datos específicas para los usuarios, garantizando que cada usuario solo pueda operar en una base de datos a la vez.

2. Modelo Estructural

El sistema se basa en los siguientes tipos clave:

2.1. Entidad (Entity)

Define las entidades que pueden ser gestionadas en el sistema, como Store o Product. El campo inheritsAccess indica si el acceso a esta entidad requiere un control adicional a nivel de registros específicos.

type Entity {
id: ID!
name: String!
inheritsAccess: Boolean!
}
idnameinheritsAccess
949494Storetrue
123456Productfalse

2.2. Tipo Operation

Define las operaciones que se pueden realizar sobre las entidades, como obtener o listar tiendas.

type Operation {
id: ID!
entityId: ID!
operationName: String!
}
identityIdoperationName
7373949494getStores
8383123456listProducts

2.3. Tipo EntityInherit

Este tipo se utiliza cuando una entidad requiere acceso granular. Relaciona la entidad con el tipo que define los permisos de acceso, en este caso, UserStore.

type EntityInherit {
id: ID!
entityId: ID!
inheritType: String!
}
identityIdinheritType
49949494userStore

2.4. Tipo UserStore

Define qué usuarios tienen acceso a tiendas específicas. Esto asegura que el acceso a los registros de tiendas esté restringido a nivel de usuario.

type UserStore {
id: ID!
storeId: ID!
userId: ID!
}
idstoreIduserId
8456
8586

2.5. Tipo Role

Define los diferentes roles que un usuario puede tener, incluyendo el tipo de rol (por ejemplo, guest, operator, supervisor, director), lo que simplifica la gestión del acceso y las responsabilidades.

enum RoleType {
GUEST
OPERATOR
SUPERVISOR
DIRECTOR
}

type Role {
id: ID!
name: String!
roleType: RoleType!
}
idnameroleType
5Manejo de tiendasSUPERVISOR
6Visor de productosGUEST

2.6. Tipo RoleOperation

Relaciona los roles con las operaciones que pueden ejecutar. Cada rol puede tener acceso a múltiples operaciones sobre distintas entidades.

type RoleOperation {
id: ID!
roleId: ID!
operationId: ID!
}
idroleIdoperationId
757373

2.7. Tipo UserRole

Relaciona a los usuarios con los roles que tienen asignados. Además, incluye el campo datastoreId, que indica a qué base de datos (definida en el tipo Datastore) tiene acceso el usuario.

type UserRole {
id: ID!
userId: ID!
roleId: ID!
datastoreId: ID!
}
iduserIdroleIddatastoreId
5651

2.8. Tipo Datastore

Define las diferentes bases de datos a las que los usuarios pueden tener acceso. Esto asegura que el usuario solo interactúe con los datos de una base de datos a la vez.

type Datastore {
id: ID!
name: String!
}
idname
1corpdb1
2corpdb2

3. Lógica de Acceso

El sistema sigue una lógica clara para determinar si un usuario puede realizar una operación:

Verificación del Rol:

Cuando un usuario intenta realizar una operación, se verifica si el rol asignado al usuario permite esa operación, consultando el tipo RoleOperation.

Control de Acceso Granular:

Si la entidad sobre la que se realiza la operación tiene inheritsAccess = true, se consulta el tipo EntityInherit para determinar qué registros específicos puede acceder el usuario. Por ejemplo, si la operación es getStores, se filtran las tiendas a las que el usuario tiene acceso, basado en el tipo UserStore.

Acceso a Base de Datos:

Cada operación también verifica la base de datos a la que tiene acceso el usuario mediante el campo datastoreId en UserRole. El usuario solo puede operar en una base de datos a la vez, lo que asegura segmentación y control adecuado de los datos.

4. Ejemplo Práctico de Acceso

Imaginemos que el usuario 6 quiere ejecutar la operación getStores. El flujo de acceso sería el siguiente:

  1. Operación y Rol: Se consulta en el tipo RoleOperation que el rol asignado al usuario (en este caso, SUPERVISOR) permite ejecutar la operación getStores.
  2. Control de Acceso Granular: Se verifica si la entidad Store tiene inheritsAccess = true. Al ser afirmativo, se consulta el tipo UserStore para filtrar las tiendas a las que el usuario puede acceder. El resultado sería algo como: WHERE storeId IN (5, 8).
  3. Acceso a la Base de Datos: Se verifica en el tipo UserRole que el usuario tiene acceso a la base de datos corpdb1 (mediante el valor datastoreId = 1). Solo se permite ejecutar la operación en esta base de datos.

5. Conclusión

El sistema descrito ofrece un enfoque minimalista y escalable para gestionar el acceso a entidades y operaciones en un entorno SaaS. Al combinar un control de acceso granular, roles jerárquicos y la capacidad de gestionar múltiples bases de datos, se asegura que el sistema sea flexible y seguro, sin complicar innecesariamente la lógica de administración. Esto permite que tanto administradores como usuarios interactúen con el sistema de manera intuitiva, asegurando un alto nivel de control y gobernanza sobre los datos.

6. Anexos: Esquema GraphQL

type Entity {
  id: ID!
  name: String!
  inheritsAccess: Boolean!
}

type Operation {
  id: ID!
  entityId: ID!
  operationName: String!
}

type EntityInherit {
  id: ID!
  entityId: ID!
  inheritType: String!
}

type UserStore {
  id: ID!
  storeId: ID!
  userId: ID!
}

enum RoleType {
  GUEST
  OPERATOR
  SUPERVISOR
  DIRECTOR
}

type Role {
  id: ID!
  name: String!
  roleType: RoleType!
}

type RoleOperation {
  id: ID!
  roleId: ID!
  operationId: ID!
}

type UserRole {
  id: ID!
  userId: ID!
  roleId: ID!
  datastoreId: ID!
}

type Datastore {
  id: ID!
  name: String!
}



  name: String!
}

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *