Gateway de sincronização

Couchbase LIVE Nova York: Couchbase Mobile 102 - Como adicionar sincronização segura aos seus aplicativos móveis

Da sessão 102 no Couchbase LIVE New York pista móvelEm seguida, continuamos a iterar no aplicativo de amostra do Couchbase Mobile que foi descrito no código do Sessão "Couchbase Mobile 101: Como criar seu primeiro aplicativo móvel".

Na sessão Couchbase Mobile 102, exploramos Gateway de sincronização em detalhes sobre seus recursos e sobre "Como adicionar sincronização segura a seus aplicativos móveis", protegendo o aplicativo de amostra Grocery Sync, que pode ser encontrado no repositório do Github para iOS e Android. Neste blog, abordaremos o que foi discutido sobre o Sync Gateway, que é o componente que une o Couchbase Lite e o Couchbase Server; você pode referência aos slides sobre o tópico e os trechos de código descritos a seguir.

Principais preocupações com a segurança de dados móveis

Uma área que o Sync Gateway ajuda a resolver é a replicação de dados, que diz respeito à forma como os dados são sincronizados entre a nuvem e o aplicativo móvel no dispositivo. A autenticação dos seus usuários antes que a replicação possa ocorrer é outra área importante que o Sync Gateway aborda e sobre a qual ocorre o particionamento de dados. Quando a replicação estiver em vigor, os dados precisarão ser particionados de acordo com os usuários para determinar onde os dados específicos serão distribuídos. Além disso, há o controle de acesso aos dados, no qual, se um usuário tiver sido autenticado, o Sync Gateway poderá ajudar a configurar as permissões de leitura e gravação adequadamente. Vamos explorar cada um desses aspectos em mais detalhes.

[1] Autenticação do usuário

Com o modelo de autenticação conectável suportado pelo Couchbase Mobile, há uma variedade de maneiras de implementar a autenticação em que o Sync Gateway permite configurações personalizadas para a estrutura de segurança da arquitetura do aplicativo. O Sync Gateway oferece suporte a três provedores públicos com modelos de autenticação "Basic Auth", "Facebook" e "Persona.

Autenticação do Facebook: Configuração do gateway de sincronização
O trecho de código abaixo ilustra como configurar o Facebook para o Sync Gateway.

No arquivo de configuração, definimos que queremos usar o Facebook como provedor de autenticação. Além desse ponto, o Sync Gateway fará o trabalho de comunicação com o Facebook para autenticar o token para os clientes.
Autenticação de provedores personalizados: Caminho de comunicação   
Para implementar um provedor personalizado em que existe uma base de usuários em uma configuração de servidor LDAP, o aplicativo móvel aponta primeiro para o aplicativo ou servidor de autenticação. O servidor de autenticação é, então, responsável pela autenticação dos usuários e, depois de autenticar um usuário com êxito, o servidor de autenticação faria uma chamada de API para a API de administração do Sync Gateway na segunda etapa para obter uma sessão válida para esse usuário. Esse token de sessão é então roteado de volta para o servidor de autenticação e, em seguida, devolvido ao cliente móvel. A terceira e última etapa, depois que o aplicativo móvel obtém o token, é a comunicação direta com o Sync Gateway usando o token de sessão para obter todos os dados necessários para o usuário no dispositivo.
[2] Acesso de leitura e gravação de dados

Muitas das questões de segurança estão na forma de como gerenciamos o acesso de leitura e gravação. O Sync Gateway permite definir políticas de segurança de granulação fina para políticas de leitura no nível do documento e, em seguida, definir as políticas de gravação até o nível do campo. A estrutura geral de aplicação de políticas baseia-se em uma função Javascript Sync, na qual é flexível definir regras de segurança complexas para estender o aplicativo móvel que executa o Couchbase Lite.

Função de sincronização: Configuração do gateway de sincronização

A função Sync é o núcleo do Sync Gateway para gerenciar o acesso de leitura e gravação e é onde a maioria das regras de acesso aos dados é definida. Portanto, é realmente o núcleo de sua implementação de segurança, em que a função Sync é uma função JavaScript que é executada sempre que qualquer documento JSON é gravado no Sync Gateway.

A função é definida no arquivo de configuração do Sync Gateway, no qual as assinaturas do método recebem a revisão atual do documento, doc, e a revisão anterior, oldDoc. O corpo do método é onde as regras de segurança são definidas com base nessas duas entradas. É aqui que uma função de sincronização básica simples pode ser definida primeiro e, em seguida, regras de segurança lentamente mais avançadas podem ser criadas para cobrir todos os casos. Com essa abordagem, podemos modificar a função de sincronização à medida que novos tipos de documentos forem definidos ou se houver alterações no esquema JSON.

Função de sincronização: Permissão de gravação

Com a segurança do lado da gravação, podemos determinar se um determinado documento proveniente de um usuário conhecido pode ou não ser gravado no armazenamento de back-end. Para o gateway de sincronização, há quatro métodos principais na função de sincronização a serem usados para permissões de gravação:
  1. requireUser(): Recebe como entrada uma lista de IDs de usuário e valida se os usuários ativos no momento fazem parte dessa lista
  2. requireRole(): Recebe como entrada uma lista de funções, onde podemos ver se o requireUser recebeu essa função específica. Se não tiver, o documento será rejeitado.
  3. requireAccess(): Recebe como entrada uma lista de canais, na qual é obtida uma lista de usuários atuais e sua lista de canais para verificar se lhes foi concedido um determinado canal. Se o usuário não fizer parte da lista, ele será rejeitado.
  4. lançar(): O lançamento permite que você faça qualquer inspeção que desejar no documento de entrada. Você pode ter uma validação e dizer, por exemplo, se um documento é do tipo "item" e se o tipo não corresponder, você pode rejeitar o documento. Da mesma forma, você pode ter uma validação baseada em um valor que esteja dentro de um determinado intervalo. Portanto, a validação de tipo em nível de campo muito granular que pode ser implementada na Sync Function pode ser usada com um método throw() nos documentos que não atendem a esses critérios. Com throw(), você também pode fornecer detalhes do erro sobre o motivo pelo qual ele é lançado e o motivo pelo qual os documentos são rejeitados.

Função de sincronização: Permissão de leitura

Com a segurança do lado da leitura, podemos usar "Canais" para o particionamento de dados por meio do uso da primitiva "channel()" para documentos, enquanto os usuários são atribuídos a diferentes canais usando a primitiva "access()". O conceito aqui é que cada documento é associado a um conjunto de um ou mais canais e que cada função de usuário tem um conjunto de canais que pode ler.

Um canal específico passará a existir assim que o comando channel for usado para atribuir um usuário a um canal. Nesse momento, o usuário procurará documentos marcados com o mesmo nome de canal. Existem dois canais especiais com os parâmetros estrela [*] e exclamação [!].

  1. Estrela[*]: O canal * Star é o canal em que todos os documentos são adicionados automaticamente a esse canal. Um exemplo é quando um usuário convidado recebe o canal *estrela, ele terá acesso ilimitado a todos os documentos. Um caso de uso claro para esse atributo de canal seria semelhante aos privilégios de administrador, em que qualquer usuário que tenha a * estrela em seu canal poderá ver tudo no sistema.
  2. Exclamação[!]: O segundo canal especial é o atributo ! exclamation, que é usado para descrever o canal público que é focado na perspectiva do documento, em que, se você adicionar um documento ao canal público, todos os usuários poderão ver esse documento específico. Um caso de uso claro para esse atributo de canal seria semelhante a ter um documento para anúncios públicos ou transmitir uma mensagem para todos os usuários.

Função de sincronização: Por exemplo

No exemplo a seguir, exploraremos usando os recursos descritos acima em como proteger o Grocery Sync Application da sessão Couchbase Mobile 101, onde ele existe como um aplicativo totalmente inseguro e sem privacidade. Ao interagir com o Sync Gateway, os usuários acabarão vendo apenas seus próprios itens, em vez de ver a mesma lista com todos os itens disponíveis que são adicionados.

O arquivo de configuração inicial do Sync Gateway é onde nada está ativado e é a configuração padrão que vem com o aplicativo Grocery Sync. Também ativamos o usuário convidado para que ele tenha acesso a todos os canais, o que significa que veremos todos os documentos e novos itens que forem adicionados. O primeiro passo para adicionar segurança é ter usuários para autenticar, que é o que implementaremos a seguir.

A ideia é identificar usuários individuais que fazem parte do aplicativo e usar isso como uma medida de segurança dentro do aplicativo. A atualização do arquivo de configuração está criando os usuários Alice e Bob. Removemos a conta de Convidado e usamos a Autenticação básica pré-definindo dois usuários, sendo que inicialmente os usuários têm acesso a todos os canais. Agora, com os usuários criados, queremos criar uma função de sincronização personalizada para permitir a definição da lógica de segurança de leitura e gravação para os usuários no aplicativo.

Agora adicionamos uma função de sincronização de espaço reservado e é aqui que nos concentraremos e continuaremos a iterar na lógica para fornecer controle de acesso aos usuários sobre os dados no sistema. Em seguida, protegeremos ainda mais os canais fornecendo nomes de canais específicos. 

Agora, daremos aos usuários acesso ao seu próprio canal de itens na configuração do sync-gateway, removendo a estrela ["*"] do canal de administração e adicionando um novo canal privado específico para o usuário, como no caso aqui, "items-alice". Anteriormente, com o canal ["*"], ele permitia que os usuários vissem todos os documentos no sistema e não queremos isso como medida de segurança. Fazemos o mesmo para "Bob", configurando o canal "admin_channels" para ser específico para "items-bob". Ao fazer isso, teremos itens acessíveis somente aos seus proprietários, portanto, precisamos vincular o campo do proprietário em um documento de item de mercearia ao canal de itens pessoais do proprietário. Em seguida, modificaremos ainda mais a função de sincronização para que isso ocorra.

Quando um documento está sendo gravado no Sync Gateway, nós o atribuímos a um canal em que o nome do canal tem o formato de "itens" - prefixado com o valor da propriedade do proprietário de dentro do documento do proprietário que é obtido por "doc.owner". A configuração anterior é quando a propriedade do documento é definida como "bob" e os "itens" são atribuídos ao canal "items-bob". O usuário "bob" tem acesso ao canal "items-bob" e, basicamente, esse é o canal ao qual Bob recebeu acesso em seu registro de usuário. Agora, qualquer item que tenha o proprietário "bob" como propriedade será visto por esse canal pelo próprio Bob, mas não será visto por Alice porque não estará no canal "items-alice". O que ocorreu na configuração foi que atribuímos um documento a um canal apropriado e também concedemos acesso ao proprietário de forma programática por meio do canal "items-" do proprietário.

Portanto, agora os usuários só podem ver/ler seus próprios itens e, efetivamente, suas próprias listas de compras. Mas eles ainda podem escrever na lista um do outro, pois Bob pode carregar um item e definir a propriedade do proprietário como "Alice", o que, de fato, permite que Bob infrinja facilmente a lista de compras de Alice. A próxima etapa para adicionar segurança é garantir que, quando um documento for recebido, a propriedade do proprietário corresponda ao usuário autenticado atual.

A configuração atualizada agora contém a função 'requireUser' no início da função de sincronização com um parâmetro de entrada sendo a propriedade do proprietário que é obtida por meio de 'doc.owner' de dentro do documento. Basicamente, o usuário autenticado atual que está tentando carregar esse documento deve ter um ID de usuário que corresponda à propriedade do proprietário no documento do item de mercearia que ele está carregando.  E, se não corresponderem, o sistema emitirá um erro para o novo item de mercearia, pois não será aceito. Agora os usuários têm sua própria lista de compras e ela é privada, e ninguém pode escrever para eles.
Seria fantástico se os usuários pudessem adicionar amigos para adicionar itens a uma determinada lista. O que atualizaremos a seguir na configuração é a capacidade de adicionar um novo tipo de "documento" com um "tipo de documento de amigo", e esse documento será usado para conceder aos amigos acesso à lista de compras de um determinado usuário.

Como agora há uma chave de "tipo" nos dois documentos que pode ser diferenciada pelos tipos de documento "itens" ou "amigos", podemos adicionar uma opção na função de sincronização para permitir que processemos os diferentes documentos de maneiras diferentes na função de sincronização
Primeiro, verificamos o tipo de documento para ver se ele é do tipo "friends" e, em seguida, verificamos se a propriedade do proprietário do documento corresponde ao ID do usuário autenticado atual por meio do método "requireUser()" e, se não corresponder, nós o rejeitaremos, pois somente o proprietário pode convidar amigos para atualizar sua lista de compras. Se a propriedade do proprietário corresponder ao usuário autenticado atual, daremos acesso a todos os amigos por meio do canal "items-doc.owner".
Assim, por exemplo, quando Bob coloca seu documento de amigos para convidar Alice, basicamente damos a Alice acesso ao canal items-bob.
Em seguida, vamos mapear o documento que é do tipo "amigos" para um canal chamado 'private-doc.owner' que, nesse caso, será 'private-bob' e observe que, dessa vez, esse é um canal criado dinamicamente. Ele não é criado nos canais de administração do registro do usuário, e você vê que esses canais são criados em tempo real quando o usuário envia um convite pela primeira vez. Isso significa basicamente que ninguém mais verá a lista de amigos que foi convidada pelo usuário, pois ela é privada apenas para o próprio usuário. Em seguida, daremos ao "doc.owner" ou ao(s) usuário(s) autenticado(s) atual(is) acesso a esse novo canal privado que foi criado para ele.
A cláusula "else" é onde, se esse não for o documento de um amigo, então esse será um documento de item de sincronização de compras e, novamente, validamos que a propriedade owner corresponde ao ID de usuário autenticado atual. Como antes, adicionamos os itens ao canal 'items-' para a propriedade do proprietário do item e damos a esse usuário acesso dinâmico a esse canal.
Enquanto atualizamos a função Sync com a lógica de troca acima, podemos adicionar um teste adicional para rejeitar qualquer tipo de documento desconhecido; portanto, se não for um documento do tipo "friends" ou "item", rejeitaremos o documento chamando o método "throw()".

Você pode ver que adicionamos outra cláusula "else" para capturar todos os outros tipos de documentos e simplesmente lançaremos uma mensagem de "tipo de documento inválido", que será enviada de volta ao cliente. Essa é uma maneira de impedir que as pessoas insiram coisas que não queremos em nosso banco de dados.
Portanto, agora que convidamos amigos, queremos realmente dar a eles a capacidade de escrever novos itens na lista do proprietário. O que temos de fazer é alterar o método anterior "requireUser() para 'requireAccess()' para que não apenas o proprietário possa gravar itens no canal de itens, mas qualquer pessoa que tenha recebido acesso a esse canal específico também possa adicionar/gravar novos itens. É aqui que os amigos só recebem esse acesso ao canal de itens de um usuário por meio de um convite de um documento de um amigo.

Em vez do método 'requireUser()', vamos substituí-lo pelo método O método 'requireAccess("items-"+doc.owner)' agora, basicamente, se você quiser escrever um novo item em uma lista, terá de ter acesso ao canal de itens do usuário específico para o qual deseja adicionar o item. Alguns recursos adicionais do aplicativo são que, se um amigo escrever um novo item em nossa lista, queremos ter certeza de que ele não poderá marcar o item. Portanto, o que queremos é que ele adicione apenas novos itens que não estejam marcados e que somente o proprietário da lista de compras possa marcá-los adequadamente. Faremos uma verificação adicional quando um amigo escrever um documento, para que o documento não tenha sua propriedade definida como true e, assim, testar o valor booleano da propriedade doc.checked.

Primeiro, verificamos se 'oldDoc' é nulo, o que significa que se trata de um item recém-criado. Portanto, validaremos a propriedade 'check' no documento para verificar se ela é verdadeira e, se for verdadeira, então lançaremos um erro em que os "novos itens não podem ser verificados" quando forem criados. Só queremos que os proprietários verifiquem ou desmarquem os itens quando eles forem criados e validaremos isso certificando-nos de que há um (doc e oldDoc), o que significa que há uma atualização para o item e, em seguida, veremos se a propriedade 'check' foi alterada entre a versão anterior do item e a nova versão. Podemos fazer uma verificação simples para garantir que o 'oldDoc' não seja NULL e, em seguida, requireUser(owner), mas também queremos que os amigos possam corrigir erros de digitação no texto do item, mesmo que não possam marcar um item, os amigos podem querer alterar o título ou o rótulo do item. Em seguida, faremos a validação em nível de campo para verificar se o oldDoc anterior existe ou não. Portanto, compararemos "doc.Checked" com "oldDoc.checked" para ver se houve alteração. Então, se tiver sido alterado, faremos um 'requireUser(owner)' no proprietário do documento, pois ele é o único que pode alterar a caixa de seleção.

Observando a "cláusula else há um código adicional para verificar se o valor "check" foi alterado entre a versão anterior do item e a nova versão que está sendo enviada. Se tiver sido alterado, exigiremos que o usuário autenticado atual seja o proprietário do documento.
Assim, somente o proprietário pode alterar a caixa de seleção. Mas outros amigos do proprietário podem alterar coisas como o título do item.
Temos um último problema que queremos analisar e resolver. Queremos ter certeza de que ninguém poderá alterar o "proprietário" de um item depois que ele for criado. Isso significa que os amigos podem fazer modificações nos rótulos, mas não podem alterar a caixa de seleção em um item ou alterar o proprietário real de um item. Vamos ver como podemos resolver esse problema na configuração final.

Portanto, faremos uma verificação adicional e, se essa for uma atualização em que 'oldDoc != nil' e o proprietário da nova versão do documento não corresponder ao proprietário da versão anterior (doc.owner != oldDoc.owner), lançaremos um erro. Agora, você pode ver que estamos fazendo um teste adicional para que, se o proprietário tiver mudado entre a versão anterior e a versão atual, lançaremos um erro basicamente de volta ao cliente, pois ele não pode alterar o proprietário do documento.

[3] Transporte de dados no fio

Agora, além das questões de autenticação e leitura/gravação de dados para acesso do usuário com relação à segurança, o próximo tópico é garantir que os dados estejam protegidos ao serem transmitidos para um ponto de extremidade remoto. O Sync Gateway é compatível com SSL e TLS no transporte. É fácil configurar no arquivo de configuração do Sync Gateway para ativar o SSL no Sync Gateway para que seu aplicativo móvel tenha mais segurança de dados.

[4] Armazenamento de dados

Para armazenamento de dados no cliente, você está realmente fazendo a criptografia do sistema de arquivos no dispositivo. Há boas informações sobre o que você precisa fazer para criptografar o banco de dados local do Couchbase Lite no portal do desenvolvedor móvel. Estamos falando de um ambiente de nuvem seguro e de sua configuração para a criptografia do sistema de arquivos no cliente.

Resumo

O Sync Gateway é realmente a peça que une o Couchbase Lite, a estrutura que está no dispositivo incorporado. Você tem o Couchbase Server em execução na nuvem, e o Sync Gateway une tudo isso. O Sync Gateway tem um punhado de funções-chave que ele fornece para transformar um aplicativo em execução no dispositivo local, que é um aplicativo autônomo desconectado, em uma experiência de sincronização com vários usuários e com todos os recursos. O que é mostrado acima é como podemos serializar as coisas no banco de dados por meio da iteração no arquivo de configuração Sync Function.

Em seguida, veremos a classe de componente de ouvinte HTTP do Couchbase Lite e a sessão do Couchbase Mobile 103 sobre como ativar o recurso Peer-to-Peer, em que você pode criar experiências sociais exclusivas no aplicativo com "Building a Peer-to-Peer App with Couchbase Mobile".

Compartilhe este artigo
Receba atualizações do blog do Couchbase em sua caixa de entrada
Esse campo é obrigatório.

Autor

Postado por William Hoang, defensor do desenvolvedor móvel, Couchbase

William foi um defensor do desenvolvedor na equipe de engenharia móvel/experiência do desenvolvedor na Couchbase. Seu amor por café e código o levou ao mundo dos dispositivos móveis, ao mesmo tempo em que apreciava as experiências presenciais off-line. Antes disso, William trabalhou na equipe de relações com desenvolvedores do Twitter, BlackBerry e Microsoft, além de ter sido engenheiro de GPS incorporado a software na Research In Motion. William se formou na McGill University em Engenharia Elétrica de Software

Deixe um comentário

Pronto para começar a usar o Couchbase Capella?

Iniciar a construção

Confira nosso portal do desenvolvedor para explorar o NoSQL, procurar recursos e começar a usar os tutoriais.

Use o Capella gratuitamente

Comece a trabalhar com o Couchbase em apenas alguns cliques. O Capella DBaaS é a maneira mais fácil e rápida de começar.

Entre em contato

Deseja saber mais sobre as ofertas do Couchbase? Deixe-nos ajudar.