À medida que os aplicativos da Web aumentam em complexidade, é essencial garantir que somente os usuários certos tenham acesso aos dados do Couchbase.
Sem um sistema de controle de acesso bem definido, usuários não autorizados podem acessar informações confidenciais ou realizar ações prejudiciais.
Integração Permissão com o Couchbase oferece uma solução robusta para gerenciar o controle de acesso em aplicativos que utilizam o Couchbase como banco de dados. O Permit foi projetado para simplificar o processo de autorização, permitindo que os desenvolvedores implementem um controle de acesso refinado e, ao mesmo tempo, aproveitem os recursos avançados de gerenciamento de dados do Couchbase.
Neste guia, vamos criar um sistema de solicitação simples em que passamos uma consulta do Couchbase e verificamos se o usuário relevante tem acesso para executar essa consulta com base em verificações de formato baseadas em regras. Se o usuário não tiver permissão, ele será impedido de executar a consulta; se tiver permissão, poderá prosseguir.
O que é o Couchbase?
O Couchbase é um banco de dados NoSQL robusto, otimizado para armazenar e gerenciar documentos JSON. Sua capacidade de lidar com objetos JSON é o que o diferencia dos armazenamentos tradicionais de valores-chave. Essa flexibilidade permite que os desenvolvedores armazenem, recuperem e gerenciem facilmente dados não estruturados ou semiestruturados, o que o torna extremamente útil para aplicativos com modelos de dados em evolução.
Um dos principais pontos fortes do Couchbase é sua Capacidade de consulta SQL++ que fornece uma sintaxe familiar semelhante ao SQL para documentos JSON (anteriormente conhecido como N1QL). Isso permite que os desenvolvedores realizem consultas avançadas em seus dados JSON, como a possibilidade de adicionar expressões de tabela comuns, uniões e muito mais. Essa camada de consulta do Couchbase permite que os dados sejam armazenados no formato JSON e, ao mesmo tempo, oferece suporte a consultas sofisticadas.
O que é RBAC?
Controle de acesso baseado em função (RBAC) é um modelo que ajuda a simplificar o gerenciamento de permissões dentro dos aplicativos, organizando as permissões do sistema em torno de funções e não de indivíduos específicos. Ele oferece uma abordagem para gerenciar e restringir o acesso a recursos específicos em um determinado aplicativo ou organização.
Para implementar o controle de acesso externo baseado em função com o Couchbase, utilizaremos Permissão.iouma solução completa para gerenciar permissões e funções de usuários com uma interface de usuário simples. Criaremos um aplicativo React com o pacote Express em que o usuário passa a consulta SQL++ com a ajuda do Couchbase. Em seguida, veremos se o usuário tem a permissão relevante. Se não tiver, enviaremos uma mensagem. Se tiver, poderemos modificar a solicitação.
Configurar um cluster do Couchbase
A primeira etapa é criar uma conta Capella gratuita. Para isso, navegue até cloud.couchbase.com e escolha Criar contaUse uma combinação de e-mail e senha. Você também pode se inscrever com sua conta do GitHub ou do Google.
Depois de criar sua conta no Capella, você pode prosseguir com a criação do cluster do banco de dados. Você pode escolher o Nível gratuito para o cluster.
Na página inicial de sua conta, depois de fazer login, clique no botão + Criar banco de dados no canto superior direito e preencha os detalhes necessários, inclusive o nome que você escolheu para o banco de dados.
Quando estiver pronto, clique no botão final Criar banco de dados botão.
Agora você criou um cluster. A próxima etapa é adicionar um bucket para armazenar seus dados. Um bucket é semelhante a uma tabela de banco de dados, mas com diferenças significativas. Como você está trabalhando com dados JSON não estruturados e semiestruturados, um único bucket pode conter diversos tipos de dados.
Vá até o site Ferramentas de dados onde você encontrará um conjunto de dados pré-importado conhecido como amostra de viagem conjunto de dados.
Você pode inserir mais documentos ou criar baldes completamente novos, se necessário. Mas, para esta demonstração, usaremos o amostra de viagem conjunto de dados.
A figura a seguir ilustra a relação entre os diferentes tipos de documentos no conjunto de dados de amostra de viagem. Ela mostra os campos de chave primária, ID e tipo que cada documento possui, juntamente com alguns outros campos representativos em cada tipo de documento.
Usaremos esse conjunto de dados em nossa demonstração. Para interagir com os dados no Capella a partir do seu aplicativo, você precisa conhecer a string de conexão e criar credenciais de acesso.
Você pode encontrar a string de conexão clicando no ícone Conectar na barra de navegação superior do painel.
Para adicionar credenciais de acesso, navegue até essa página em suas configurações do Capella, conforme mostrado abaixo, e clique no botão + Criar acesso ao banco de dados botão. Forneça um nome e uma senha e clique em Salvar. Certifique-se de adicionar imediatamente as credenciais a um .env pois você não conseguirá recuperar a senha novamente depois disso.
Depois de criar suas credenciais, a configuração do Capella estará concluída. Vamos passar para a parte do RBAC!
Configurar permissões RBAC no Permit.io
Certifique-se de ter configurado sua conta em Permissão.io e criou um projeto. Nesta demonstração, usaremos o projeto padrão que foi criado.
Vá para a seção Policy Editor e, na seção Resources (Recursos), adicione os recursos relevantes correspondentes ao amostra de viagem conjunto de dados.
Nesse caso, adicionaremos os recursos como Rota, Aeroporto, Hotel, e Companhia aérea.
Certifique-se de que você adicionou as ações relevantes.
Em seguida, vá até o Funções para adicionar as funções de usuário específicas associadas ao conjunto de dados de amostra de viagem.
Para o conjunto de dados que estamos usando, você precisará adicionar as seguintes funções: Viajante, Agente de viagens, Equipe da companhia aérea e Equipe do hotel.
Em seguida, vá para a seção de diretório para criar um novo locatário. Depois de criar o locatário, adicione usuários com base em suas funções.
Criaremos novos usuários para funcionários de hotéis, agentes de viagens e viajantes.
Para cada usuário, adicione seu e-mail, nome, sobrenome e função específica na seção de acesso de nível superior.
Isso é tudo o que é necessário para criar um modelo de controle de acesso baseado em função (RBAC) para o conjunto de dados de amostra de viagem usando a interface do usuário do Permit.
Implementação de RBAC refinado para Couchbase com Permit.io
Agora vamos implementar nosso analisador de consultas SQL++ do Couchbase com o Permit.
Primeiro, configuraremos um projeto Node.js com o Express para nosso servidor de backend. Usaremos o SDK do Couchbase para Node.js e o SDK do Permit.io.
Aqui está o nosso package.json com as dependências necessárias:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "name" (nome): "backend", "versão": "1.0.0", "principal": "index.js", "scripts": { "teste": "echo \"Error: no test specified\" && exit 1" }, "palavras-chave": [], "autor": "", "licença": "ISC", "description" (descrição): "", "dependencies" (dependências): { "cors": "^2.8.5", "couchbase": "^4.4.3", "dotenv": "^16.4.5", "expresso": "^4.21.1", "permitio": "^2.6.1" } } |
Implementação do servidor
Vamos dividi-lo em componentes-chave:
Inicialização
Começamos configurando nosso servidor Express e inicializando o cliente Permit.io:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const expresso = exigir("expresso); const cors = exigir('cors'); const { Permissão } = exigir('permitio'); const couchbase = exigir('couchbase'); exigir('dotenv').configuração(); const aplicativo = expresso(); const porto = processo.env.PORTO || 3001; aplicativo.uso(cors({ origem: 'http://localhost:5173', métodos: ['POST'], allowedHeaders: ['Content-Type'], })); // Inicializar o cliente Permit const permissão = novo Permissão({ token: processo.env.PERMISSÕES_SDK_TOKEN, pdp: "https://cloudpdp.api.permit.io" }); |
Análise de consultas SQL++
Para trabalhar com as consultas de entrada do Couchbase, precisamos analisá-las. Implementamos um analisador simples que extrai as cláusulas SELECT, FROM e WHERE da consulta de entrada.
Vamos dar uma olhada nos componentes principais do nosso analisador:
Analisador de consultas
A classe QueryParser é a base da nossa implementação de segurança. Ela lida com duas tarefas essenciais:
- Análise de consulta: Putiliza consultas SQL++ para determinar:
-
- Tipo de operação SELECT, UPDATE, DELETE, INSERT
- Recurso que está sendo acessado
- Validação da estrutura da 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) { // Extrair recurso da instrução SELECT (palavra antes de t const selectResourcePattern = /SELECT\s+(\w+)\./i; // Padrões básicos de regex para operações SQL const padrões = { selecionar: /^\s*SELECIONAR\s+(?:(?!DE).)*\s+DE\s+[`]?(\ update: /^\s*UPDATE\s+[`]?(\w+)[`]?(?:\.`?(\w+)`?)?( excluir: /^\s*DELETE\s+FROM\s+[`]?(\w+)[`]?(?:\.`?(\w inserir: /^\s*INSERIR\s+PARA\s+[`]?(\w+)[`]?(?:\.`?(\w }; // Determinar o tipo de operação e extrair o recurso let operation = ''; deixar recurso = ''; se (padrões.selecionar.teste(consulta)) { operação = 'ler'; const resourceMatch = consulta.partida(selectResourcePatt se (resourceMatch && resourceMatch[1]) { recurso = resourceMatch[1]; } mais { lançar novo Erro('Não foi possível analisar o recurso de S } } else if (patterns.update.test(query)) { operation = 'atualização'; const matches = query.match(patterns.update); resource = matches[3] || matches[2] || matches[1]; } else if (patterns.delete.test(query)) { operação = 'excluir'; const matches = query.match(patterns.delete); resource = matches[3] || matches[2] || matches[1]; } else if (patterns.insert.test(query)) { operation = 'criar'; const matches = query.match(patterns.insert); resource = matches[3] || matches[2] || matches[1]; } Se (!operation || !resource) { lançar um novo erro('Não é possível para analisar consulta operação ou } retorno { consulta, permissão: operação, recurso: recurso.toLowerCase() }; } |
2. Validação de segurança: Implementa verificações de segurança para evitar injeção de SQL e outros 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) { // Validação de segurança básica const padrões não permitidos = [ /;.*;/i, // Múltiplas declarações /--/, // Comentários SQL /\/\*/, // Bloquear comentários /xp_/i, // Procedimento armazenado estendido /EXECUTE\s+sp_/i, // Execução de procedimento armazenado /EXEC\s+sp_/i, // Execução de procedimento armazenado /INTO\s+OUTFILE/i, // Operações de arquivo /LOAD_FILE/i, // Operações de arquivo ]; para (const padrão de padrões não permitidos) { se (padrão.teste(consulta)) { lançar novo Erro('Consulta contém potencialmente dano } } retorno verdadeiro; } |
Gerenciamento de permissões
O TravelQueryChecker lida com a lógica de permissão principal:
- Inicialização: Configura as conexões com o Permit.io e o 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 |
classe TravelQueryChecker { construtor() { este.permissão = novo Permissão({ token: processo.env.PERMISSÕES_SDK_TOKEN, pdp: "https://cloudpdp.api.permit.io" }); este.clusterConnStr = 'couchbases://cb.6gj2r4ygxyjrfcgf this.username = 'shivay1'; this.password = 'Shivay1234!'; this.bucketName = 'viagens-amostra'; } async init() { tente { this.cluster = await couchbase.connect(this.clusterC username: this.username, senha: this.password, configProfile: 'wanDesenvolvimento', }); this.bucket = this.cluster.bucket(this.bucketName); this.collection = this.bucket.defaultCollection(); console.log('Conectado para Couchbase Capela'); } catch (error) { console.error('Couchbase conexão erro:', erro); lançar erro; } } |
- Verificação de permissão: Verifica as permissões do usuário por meio do 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 |
assíncrono checkQueryPermission(userId, queryConfig) { se (!queryConfig) { retorno { permitido: falso, erro: 'Condição de consulta inválida } tente { const permitted = await this.permit.check( String(userId), queryConfig.permission, { tipo: queryConfig.resource, locatário: "default", recurso: queryConfig.resource } ); retornar { permitido, consulta: queryConfig.query, permissão: queryConfig.permission, recurso: queryConfig.resource }; } catch (error) { console.error('Permissão verificar erro:', erro); retorno { permitido: falso, erro: erro.mensagem }; } } |
Essa função usa o Permissão.io SDK para verificar se um usuário tem permissão para executar uma ação específica em um recurso.
3. Execução de consultas: Lida com o fluxo completo, desde a verificação de permissão até a execução da 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 |
assíncrono executeQuery(userId, rawQuery, parâmetros = []) { tentar { //Validar consulta para segurança QueryParser.validateQuery(rawQuery); /Parse a consulta para obter permissão e recurso const queryConfig = QueryParser.parseQuery(rawQuery) console.registro('Configuração de consulta analisada:', queryConfig); const permissionCheck = aguardar este.checkQueryPermiss se (!permissionCheck.permitido) { retorno { status: 'not-permitted' (não permitido), erro: `Usuário ${userId} é não permitido para e }; } const opções = { parâmetros: parâmetros }; const resultado = aguardar este.agrupamento.consulta(rawQuery, op retorno { status: "permitido, sucesso: verdadeiro, resultados: resultado.linhas, metadados: { métricas: resultado.métricas, perfil: resultado.perfil } }; } captura (erro) { console.erro('Erro de execução de consulta:', erro); retorno { status: 'erro', erro: erro.mensagem }; } } |
Ponto de extremidade da API
Por fim, expomos um endpoint de API para lidar com as consultas recebidas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Rota para lidar com consultas SQL++ aplicativo.postagem('/query', assíncrono (req, res) => { const { userId, consulta, parâmetros } = req.corpo; se (!userId || !consulta) { retorno res.status(400).json({ erro: 'userId e consulta a } tentar { const resultado = aguardar verificador de consultas.executeQuery(userId, q res.json(resultado); } captura (erro) { res.status(500).json({ erro: erro.mensagem }); } }); |
Conexão com o Couchbase
Agora, definiremos o código de conexão do Couchbase em nosso servidor Express de backend, que nos ajuda a nos conectar ao cluster do Couchbase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Conectar-se ao cluster do Couchbase assíncrono inicial() { tentar { este.agrupamento = aguardar couchbase.conectar(este.clusterC nome de usuário: este.nome de usuário, senha: este.senha, configProfile: 'wanDevelopment', }); este.balde = este.agrupamento.balde(este.bucketName); este.coleção = este.balde.defaultCollection(); console.registro('Conectado ao Couchbase Capella'); } captura (erro) { console.erro('Erro de conexão do Couchbase:', erro); lançar erro; } } |
Código de front-end
Para demonstrar a funcionalidade da integração, criamos um frontend React simples que permite que os usuários insiram consultas e vejam os resultados.
Para configuração, você pode verificar esse código para os componentes da interface do usuário 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 |
importação Reagir, { useState } de 'react'; importação { Botão } de "@/components/ui/button" importação { Entrada } de "@/components/ui/input" importação { Área de texto } de "@/components/ui/textarea" importação { Cartão, Conteúdo do cartão, CardDescription (Descrição do cartão), CardFooter, CardHeader, Título do cartão } de "@/components/ui/card" exportação padrão função Aplicativo() { const [consulta, setQuery] = useState(''); const [usuário, setUser] = useState(''); const [resultado, setResult] = useState<nulo | { sucesso: booleano; resultados?: qualquer[]; erro?: string }>(nulo); const handleSubmit = assíncrono (e: Reagir.FormEvent) => { e.preventDefault(); tentar { const resposta = aguardar buscar('http://localhost:3001/query', { método: 'POST', cabeçalhos: { 'Content-Type': 'application/json', }, corpo: JSON.stringify({ consulta, usuário }), }); const dados = aguardar resposta.json(); setResult(dados); } captura (erro) { console.erro('Erro:', erro); setResult({ sucesso: falso, erro: 'Ocorreu um erro ao processar sua solicitação'. }); } }; retorno ( <div className="contêiner mx-auto p-4"> <Cartão className="w-full max-w-2xl mx-auto"> <CardHeader> <Título do cartão>Permissão Verificador</Título do cartão> <CardDescription (Descrição do cartão)>Entrar a consulta e usuário para verificar permissões</CardDescription (Descrição do cartão)> </CardHeader> <Conteúdo do cartão> <formulário onSubmit={handleSubmit} className="space-y-4"> <div> <rótulo htmlPara="query" (consulta) className="block text-sm font-medium text-gray-700">Consulta</rótulo> <Área de texto id="query" (consulta) valor={consulta} onChange={(e) => setQuery(e.alvo.valor)} espaço reservado="Digite sua consulta SQL++ aqui" className="mt-1" linhas={4} /> </div> <div> <rótulo htmlPara="usuário" className="block text-sm font-medium text-gray-700">Usuário</rótulo> <Entrada id="usuário" tipo="texto" valor={usuário} onChange={(e) => setUser(e.alvo.valor)} espaço reservado="Digite o e-mail do usuário" className="mt-1" /> </div> <Botão tipo="submit" (enviar) className="w-full">Verificar Permissões</Botão> </formulário> </Conteúdo do cartão> <CardFooter> {resultado && ( <div className={`mt-4 p-4 rounded ${result.success ? 'bg-green-100' : 'bg-red-100'}``}> {resultado.sucesso ? ( <div> <h3 className="font-bold text-green-800">Permissão Concedido</h3> <pré className="mt-2 whitespace-pre-wrap">{JSON.stringify(resultado.resultados, nulo, 2)}</pré> </div> ) : ( <div> <h3 className="font-bold text-red-800">Permissão Negado</h3> <p>{resultado.erro}</p> </div> )} </div> )} </CardFooter> </Cartão> </div> ); } |
Esse componente React permite que os usuários insiram uma consulta e seu identificador de usuário e, em seguida, exibe os resultados ou quaisquer erros relacionados à permissão.
Para obter o código completo, consulte o Permissão-Demo Repositório do GitHub.
Demonstração
Com tudo pronto, você pode executar o servidor Express de backend e o aplicativo React separadamente. Quando ambos estiverem em execução, você verá uma interface do usuário como a mostrada abaixo. Adicione a consulta e o usuário correspondente e, em seguida, clique no botão Verificar permissões botão.
No exemplo acima, você pode ver o usuário que tem permissão e o usuário que não tem permissão para acessar o recurso.
Conclusão
Neste tutorial, exploramos como definir e configurar o Permit para adicionar configurações de RBAC ao conjunto de dados de amostra de viagens do Couchbase e como verificar as permissões de uma determinada consulta SQL++.
E se você quiser um nível de controle mais granular do que as funções de usuário, concentrando-se em identidades específicas do usuário? Para isso e muito mais, recomendamos que continue explorando nossos materiais de aprendizagem, como A diferença entre RBAC e ABAC e adicionar o ABAC ao seu aplicativo com o Permit.
Deseja saber mais sobre a implementação da autorização? Tem dúvidas? Entre em contato para nós em nossa Comunidade do Slack.