A medida que las aplicaciones web crecen en complejidad, es esencial garantizar que sólo los usuarios adecuados tengan acceso a los datos de Couchbase.
Sin un sistema de control de acceso bien definido, los usuarios no autorizados pueden acceder a información sensible o realizar acciones perjudiciales.
Integración de Permiso con Couchbase proporciona una solución robusta para gestionar el control de acceso en aplicaciones que utilizan Couchbase como base de datos. Permit está diseñado para agilizar el proceso de autorización, lo que permite a los desarrolladores implementar el control de acceso de grano fino aprovechando las capacidades avanzadas de gestión de datos de Couchbase.
En esta guía, veremos cómo construir un sistema de peticiones simple en el que pasamos una consulta a Couchbase y comprobamos si el usuario relevante tiene acceso para ejecutar esa consulta basándonos en comprobaciones de formato basadas en reglas. Si el usuario carece de permiso, se le denegará la ejecución de la consulta, si tiene permiso, puede continuar.
¿Qué es Couchbase?
Couchbase es una robusta base de datos NoSQL, optimizada para almacenar y gestionar documentos JSON. Su capacidad para manejar objetos JSON es lo que la diferencia de los almacenes de valores clave tradicionales. Esta flexibilidad permite a los desarrolladores ser capaces de almacenar, recuperar y gestionar datos no estructurados o semi-estructurados fácilmente, por lo que es extremadamente útil para aplicaciones con modelos de datos en evolución.
Uno de los principales puntos fuertes de Couchbase es su Capacidad de consulta SQL que proporciona una sintaxis familiar similar a SQL para documentos JSON (antes conocido como N1QL). Esto permite a los desarrolladores realizar consultas potentes en sus datos JSON, como poder añadir expresiones de tabla comunes, uniones y mucho más. Esta capa de consulta de Couchbase permite almacenar datos en formato JSON y, al mismo tiempo, realizar consultas sofisticadas.
¿Qué es RBAC?
Control de acceso basado en funciones (RBAC) es un modelo que ayuda a simplificar la gestión de permisos dentro de las aplicaciones mediante la organización de los permisos del sistema en torno a roles en lugar de individuos específicos. Proporciona un enfoque para gestionar y restringir el acceso a recursos específicos dentro de una aplicación u organización concreta.
Para implementar el control de acceso externo basado en roles con Couchbase, aprovecharemos Permit.io, una solución end-to-end para gestionar permisos y roles de usuario con una UI sencilla. Crearemos una aplicación React con el paquete Express donde el usuario pasa la consulta SQL++ con la ayuda de Couchbase. Después, veremos si el usuario tiene el permiso correspondiente. Si no lo tienen, enviaremos un mensaje. Si lo tienen, podemos modificar la petición.
Configurar un clúster Couchbase
El primer paso es crear una cuenta gratuita en Capella. Para ello, vaya a nube.couchbase.com y elija Crear una cuentaUtiliza una combinación de correo electrónico y contraseña. También puedes registrarte con tu cuenta de GitHub o Google.
Una vez que haya creado su cuenta en Capella, puede proceder a crear su cluster de base de datos. Puede elegir el Grada libre para el clúster.
En la página de inicio de su cuenta, después de iniciar sesión, haga clic en el botón + Crear base de datos en la esquina superior derecha y rellene los datos necesarios, incluido el nombre que haya elegido para la base de datos.
Cuando esté listo, siga adelante y haga clic en el botón final Crear base de datos botón.
Ya ha creado un clúster. El siguiente paso es añadir un bucket para almacenar los datos. Un bucket es similar a una tabla de base de datos, pero con diferencias significativas. Dado que estás trabajando con datos JSON no estructurados y semiestructurados, un único bucket puede contener diversos tipos de datos.
Visite la página Herramientas de datos donde encontrará un conjunto de datos previamente importado denominado viaje-muestra conjunto de datos.
Puede insertar más documentos o crear cubos completamente nuevos si es necesario. Pero para esta demostración utilizaremos el viaje-muestra conjunto de datos.
La siguiente figura ilustra la relación entre los distintos tipos de documentos del conjunto de datos de la muestra de viajes. Muestra los campos de clave primaria, ID y tipo que tiene cada documento, junto con algunos otros campos representativos de cada tipo de documento.
Utilizaremos este conjunto de datos para nuestra demostración. Para interactuar con tus datos en Capella desde tu aplicación, necesitas conocer la cadena de conexión y crear las credenciales de acceso.
Puede encontrar la cadena de conexión pulsando el botón Conectar en la barra de navegación superior del salpicadero.
Para añadir credenciales de acceso, vaya a esta página en la configuración de Capella, como se muestra a continuación, y haga clic en el botón + Crear acceso a base de datos botón . Introduzca un nombre y una contraseña y haga clic en Guardar. Asegúrese de añadir inmediatamente las credenciales a un .env ya que después no podrás recuperar la contraseña.
Después de crear sus credenciales, su configuración de Capella está completa. Pasemos a la parte de RBAC.
Configuración de permisos RBAC en Permit.io
Asegúrese de haber configurado su cuenta en Permit.io y creado un proyecto. En esta demostración, vamos a utilizar el proyecto por defecto que se creó.
Diríjase a la sección Editor de políticas y, dentro de la sección Recursos, añada los recursos pertinentes correspondientes a la directiva viaje-muestra conjunto de datos.
En este caso, añadiremos los recursos como Ruta, Aeropuerto, Hotel, y Línea aérea.
Asegúrese de que ha añadido las acciones pertinentes.
A continuación, diríjase al Funciones para añadir las funciones de usuario específicas asociadas al conjunto de datos de la muestra de viajes.
Para el conjunto de datos que estamos utilizando, tendrás que añadir las siguientes funciones: Viajero, Agente de viajes, Personal de la compañía aérea y Personal del hotel.
A continuación, dirígete a la sección de directorio para crear un nuevo tenant. Una vez creado el inquilino, añada usuarios en función de sus funciones.
Crearemos nuevos usuarios para el personal de los hoteles, las agencias de viajes y los viajeros.
Para cada usuario, añada su correo electrónico, nombre, apellidos y función específica en la sección de acceso de nivel superior.
Eso es todo lo que hizo falta para crear un modelo de control de acceso basado en funciones (RBAC) para el conjunto de datos de muestra de viajes utilizando Permit UI.
Implementación de RBAC detallado para Couchbase con Permit.io
Ahora vamos a implementar nuestro analizador de consultas SQL++ de Couchbase con Permit.
Primero, configuraremos un proyecto Node.js con Express para nuestro servidor backend. Usaremos el SDK de Couchbase para Node.js y el SDK de Permit.io.
Aquí está nuestro paquete.json con las dependencias necesarias:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "nombre": "backend", "version": "1.0.0", "principal": "index.js", "scripts": { "test": "echo "Error: no se ha especificado ninguna prueba" && exit 1" }, "palabras clave": [], "autor": "", "licencia": "ISC", "descripción": "", "dependencias": { "cors": "^2.8.5", "couchbase": "^4.4.3", "dotenv": "^16.4.5", "express": "^4.21.1", "permitio": "^2.6.1" } } |
Implantación del servidor
Desglosémoslo en componentes clave:
Inicialización
Comenzamos configurando nuestro servidor Express e inicializando el cliente Permit.io:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const express = requiere(exprés); const cors = requiere(cors); const { Permiso } = requiere(Permiso); const couchbase = requiere(couchbase); requiere(dotenv).config(); const aplicación = express(); const puerto = proceso.env.PUERTO || 3001; aplicación.utilice(cors({ origen: http://localhost:5173, métodos: [POST], allowedHeaders: [Tipo de contenido], })); // Inicializar Permitir cliente const permiso = nuevo Permiso({ ficha: proceso.env.PERMIT_SDK_TOKEN, pdp: "https://cloudpdp.api.permit.io" }); |
Análisis de consultas SQL
Para trabajar con las consultas entrantes de Couchbase, necesitamos parsearlas. Hemos implementado un simple analizador que extrae las cláusulas SELECT, FROM y WHERE de la consulta de entrada.
Veamos los componentes principales de nuestro analizador sintáctico:
Analizador de consultas
La clase QueryParser es la base de nuestra implementación de seguridad. Se encarga de dos tareas críticas:
- Análisis de consultas: Parses consultas SQL++ para determinar:
-
- Tipo de operación SELECT, UPDATE, DELETE, INSERT
- Recurso al que se accede
- Validación de la estructura de consulta
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
estático parseQuery(consulta) { // Extraer recurso de la sentencia SELECT (palabra antes de t const selectResourcePattern = /SELECCIONE\s+(\w+)\i; // Patrones regex básicos para operaciones SQL const patrones = { seleccione: /^\s*SELECCIONE\s+(?:(?!DESDE).)*\s+DESDE\s+[`]?(\ actualizar: /^\s*UPDATE\s+[`]?(\w+)[`]?(?:\.`?(\w+)`?)?( borrar: /^\s*DELETE\s+FROM\s+[`]?(\w+)[`]?(?:\.`?(\w insertar: /^\s*INSERTAR\s+EN\s+[`]?(\w+)[`]?(?:\.`?(\w }; // Determinar el tipo de operación y extraer el recurso let operation = ''; deje recurso = ''; si (patrones.seleccione.prueba(consulta)) { operación = leer; const resourceMatch = consulta.match(selectResourcePatt si (resourceMatch && resourceMatch[1]) { recurso = resourceMatch[1]; } si no { tirar nuevo Error(No se ha podido analizar el recurso de S } } else if (patterns.update.test(query)) { operation = 'actualización'; const matches = query.match(patterns.update); resource = matches[3] || matches[2] || matches[1]; } else if (patterns.delete.test(query)) { operación = 'borrar'; const matches = query.match(patterns.delete); resource = matches[3] || matches[2] || matches[1]; } else if (patterns.insert.test(query)) { operation = 'crear'; const matches = query.match(patterns.insert); resource = matches[3] || matches[2] || matches[1]; } if (!operación || !recurso) { throw new Error('Imposible a analizar consulta operación o } devolver { consulta, permiso: operación, recurso: recurso.toLowerCase() }; } |
2. Validación de seguridad: Implementa controles de seguridad para evitar la inyección SQL y otros ataques:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
estático validateQuery(consulta) { // Validación básica de seguridad const disallowedPatterns = [ /;.*;/i, // Declaraciones múltiples /--/, // Comentarios SQL /\/\*/, // Comentarios en bloque /xp_/i, // Procedimiento almacenado ampliado /EXECUTE\s+sp_/i, // Ejecución del procedimiento almacenado /EXEC\s+sp_/i, // Ejecución del procedimiento almacenado /INTO\s+OUTFILE/i, // Operaciones de archivo /CARGAR_ARCHIVO/i, // Operaciones de archivo ]; para (const patrón de disallowedPatterns) { si (patrón.prueba(consulta)) { tirar nuevo Error('Consulta contiene potencialmente daño } } devolver verdadero; } |
Gestión de permisos
En TravelQueryChecker se encarga de la lógica central de los permisos:
- Inicialización: Establece conexiones con Permit.io y Couchbase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
clase TravelQueryChecker { constructor() { este.permiso = nuevo Permiso({ ficha: proceso.env.PERMIT_SDK_TOKEN, pdp: "https://cloudpdp.api.permit.io" }); este.clusterConnStr = couchbases://cb.6gj2r4ygxyjrfcgf this.username = 'shivay1'; this.password = 'Shivay1234!'; this.bucketName = 'viaje-muestra'; } async init() { intentar { this.cluster = await couchbase.connect(this.clusterC nombre_usuario: this.nombre_usuario, contraseña: esta.contraseña, configProfile: 'wanDesarrollo', }); this.bucket = this.cluster.bucket(this.bucketName); this.collection = this.bucket.defaultCollection(); console.log('Conectado a Couchbase Capella'); } catch (error) { console.error('Couchbase conexión error:', error); tirar error; } } |
- Comprobación de permisos: Verifica los permisos de los usuarios a través de Permit.io:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
async checkQueryPermission(userId, queryConfig) { si (!queryConfig) { devolver { permitido: falso, error: Consulta no válida } intentar { const permitted = await this.permit.check( String(userId), queryConfig.permission, { tipo: queryConfig.resource, arrendatario: "por defecto", recurso: queryConfig.resource } ); devolver { permitido, consulta: consultaConfig.consulta, permiso: queryConfig.permission, recurso: queryConfig.resource }; } catch (error) { console.error('Permiso consulte error:', error); devolver { permitido: falso, error: error.mensaje }; } } |
Esta función utiliza el Permit.io SDK para comprobar si un usuario tiene permiso para realizar una acción específica en un recurso.
3. Ejecución de consultas: Gestiona el flujo completo desde la comprobación de permisos hasta la ejecución de la consulta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
async executeQuery(userId, rawQuery, parámetros = []) { pruebe { //Validar consulta por seguridad QueryParser.validateQuery(rawQuery); /Parar la consulta para obtener el permiso y el recurso const queryConfig = QueryParser.parseQuery(rawQuery) consola.registro('Parsed query config:', queryConfig); const permissionCheck = await este.checkQueryPermiss si (!permissionCheck.permitido) { devolver { estado: no permitido, error: `Usuario ${userId} es no permitido a e }; } const opciones = { parámetros: parámetros }; const resultado = await este.grupo.consulta(rawQuery, op devolver { estado: permitido, éxito: verdadero, resultados: resultado.filas, metadatos: { métricas: resultado.métricas, perfil: resultado.perfil } }; } captura (error) { consola.error('Error de ejecución de consulta:', error); devolver { estado: error, error: error.mensaje }; } } |
Punto final de la API
Por último, exponemos un punto final de la API para gestionar las consultas entrantes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Ruta para gestionar consultas SQL aplicación.Correo electrónico:("/consulta, async (consulte, res) => { const { userId, consulta, parámetros } = consulte.cuerpo; si (!userId || !consulta) { devolver res.estado(400).json({ error: 'userId y consulta a } pruebe { const resultado = await queryChecker.executeQuery(userId, q res.json(resultado); } captura (error) { res.estado(500).json({ error: error.mensaje }); } }); |
Conexión Couchbase
Ahora definiremos el código de conexión a Couchbase en nuestro servidor backend Express, que nos ayudará a conectarnos al Cluster de Couchbase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Conectar con el cluster Couchbase async init() { pruebe { este.grupo = await couchbase.conecte(este.clusterC nombre de usuario: este.nombre de usuario, contraseña: este.contraseña, configProfile: wanDesarrollo, }); este.cubo = este.grupo.cubo(este.bucketName); este.colección = este.cubo.defaultCollection(); consola.registro(Conectado a Couchbase Capella); } captura (error) { consola.error('Error de conexión Couchbase:', error); tirar error; } } |
Código Frontend
Para demostrar la funcionalidad de la integración, hemos creado un sencillo frontend React que permite a los usuarios introducir consultas y ver los resultados.
Para la configuración, puede comprobar este código para los componentes de interfaz de usuario existentes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
importar Reaccione, { useState } de reaccionar; importar { Botón } de "@/components/ui/button" importar { Entrada } de "@/components/ui/input" importar { Área de texto } de "@/components/ui/textarea" importar { Tarjeta, Contenido de la tarjeta, Descripción de la tarjeta, Pie de tarjeta, Encabezamiento de tarjeta, Título de la tarjeta } de "@/components/ui/card" exportar por defecto función Aplicación() { const [consulta, setQuery] = useState(''); const [usuario, setUser] = useState(''); const [resultado, setResult] = useState<null | { éxito: booleano; resultados?: cualquier[]; error?: cadena }>(null); const handleSubmit = async (e: Reaccione.Evento de formulario) => { e.preventDefault(); pruebe { const respuesta = await buscar(http://localhost:3001/query, { método: POST, cabeceras: { Tipo de contenido: aplicación/json, }, cuerpo: JSON.stringify({ consulta, usuario }), }); const datos = await respuesta.json(); setResult(datos); } captura (error) { consola.error("Error:, error); setResult({ éxito: falso, error: Se ha producido un error al procesar su solicitud". }); } }; devolver ( <div className="contenedor mx-auto p-4"> <Tarjeta className="w-full max-w-2xl mx-auto"> <Encabezamiento de tarjeta> <Título de la tarjeta>Permiso Checker</Título de la tarjeta> <Descripción de la tarjeta>Entre en a consulta y usuario a consulte permisos</Descripción de la tarjeta> </Encabezamiento de tarjeta> <Contenido de la tarjeta> <formulario onSubmit={handleSubmit} className="espacio-y-4"> <div> <etiqueta htmlPara="consulta" className="block text-sm font-medium text-gray-700">Consulta</etiqueta> <Área de texto id="consulta" valor={consulta} onChange={(e) => setQuery(e.objetivo.valor)} marcador de posición="Introduzca aquí su consulta SQL++" className="mt-1" filas={4} /> </div> <div> <etiqueta htmlPara="usuario" className="block text-sm font-medium text-gray-700">Usuario</etiqueta> <Entrada id="usuario" tipo="texto" valor={usuario} onChange={(e) => setUser(e.objetivo.valor)} marcador de posición="Introduzca el correo electrónico del usuario" className="mt-1" /> </div> <Botón tipo="enviar" className="w-full">Consulte Permisos</Botón> </formulario> </Contenido de la tarjeta> <Pie de tarjeta> {resultado && ( <div className={`mt-4 p-4 redondeado ${result.success ? 'bg-verde-100' : 'bg-rojo-100'}`}> {resultado.éxito ? ( <div> <h3 className="font-bold text-green-800">Permiso Concedido</h3> <pre className="mt-2 whitespace-pre-wrap">{JSON.stringify(resultado.resultados, null, 2)}</pre> </div> ) : ( <div> <h3 className="font-bold text-red-800">Permiso Denegado</h3> <p>{resultado.error}</p> </div> )} </div> )} </Pie de tarjeta> </Tarjeta> </div> ); } |
Este componente React dado permite a los usuarios introducir una consulta y su identificador de usuario, y luego muestra los resultados o cualquier error relacionado con los permisos.
Para obtener el código completo, consulte la página Permiso-Demo Repositorio GitHub.
Demo
Con todo en su lugar, puede ejecutar el servidor Express backend y la aplicación React por separado. Una vez que ambos se estén ejecutando, verás una interfaz de usuario como la siguiente. Añade la consulta y el usuario correspondiente, luego haz clic en el botón Comprobar permisos botón.
En el ejemplo anterior, puede ver el usuario que tiene permiso y el usuario que no tiene permiso para acceder al recurso.
Conclusión
En este tutorial, exploramos cómo instalar y configurar Permit para añadir ajustes RBAC para el conjunto de datos de muestra de viajes de Couchbase y cómo comprobar los permisos para una consulta SQL++ dada.
¿Y si desea un nivel de control más granular que las funciones de usuario, centrándose en identidades específicas de usuario? Para eso y mucho más, le recomendamos que siga explorando nuestros materiales de aprendizaje, como diferencia entre RBAC y ABAC y añadir ABAC a su aplicación con Permit.
¿Quiere saber más sobre la autorización? ¿Tiene alguna pregunta? Póngase en contacto a nosotros en nuestro Comunidad Slack.