A partir de la sesión 102 del Pista móvil Couchbase LIVE Nueva Yorkcontinuamos iterando sobre la aplicación de ejemplo de Couchbase Mobile que se describía en el código de la publicación Sesión "Couchbase Mobile 101: Cómo crear tu primera aplicación móvil".
En la sesión Couchbase Mobile 102, exploramos Pasarela de sincronización en profundidad sobre sus características y sobre "Cómo añadir una sincronización segura a sus aplicaciones móviles" mediante la aplicación de ejemplo Grocery Sync, que puede encontrarse en el repositorio de Github de iOS y Android. En este blog, vamos a repasar lo que se discutió en Sync Gateway, que es el componente que une Couchbase Lite y Couchbase Server; usted puede referencia de las diapositivas sobre el tema y fragmentos de código que se describen a continuación.
Principales problemas de seguridad de los datos móviles
Un área que Sync Gateway ayuda a resolver es la replicación de datos y esto se refiere a cómo los datos se sincronizan de ida y vuelta desde la nube a su aplicación móvil en el dispositivo. La autenticación de los usuarios antes de que pueda producirse la replicación es otra área clave que Sync Gateway aborda en la que se produce la partición de datos. Una vez que la replicación está en su lugar, los datos tendrían que ser particionado en consecuencia para sus usuarios para determinar dónde se distribuirán los datos específicos. Y luego está el control de acceso a los datos en el que, dado un usuario que ha sido autenticado, Sync Gateway puede ayudar a configurar los permisos de lectura y escritura en consecuencia. Analicemos cada uno de estos aspectos con más detalle.
[1] Autenticación de usuarios
Con el modelo de autenticación pluggable que soporta Couchbase Mobile, hay una variedad de maneras de implementar la autenticación donde Sync Gateway permite configuraciones personalizadas al marco de seguridad de la arquitectura de la aplicación. Sync Gateway soporta tres proveedores públicos con modelos de autenticación 'Basic Auth', 'Facebook', y 'Persona.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "facebook" : { "registrar" : falso }, "bases de datos": { "comestibles-sync": { "servidor":"http://cbserver:8091", "cubo":"ultramarinos-sincronizar", "usuarios": {"INVITADO": {"desactivado": true}}, "sincronizar":`función(doc) {canal(doc.canales);}` } } } |
Muchas de las cuestiones de seguridad se refieren a cómo gestionar el acceso de lectura y escritura. Sync Gateway permite definir políticas de seguridad de grano fino para las políticas de lectura a nivel de documento y, a continuación, definir las políticas de escritura hasta el nivel de campo. El marco general de aplicación de políticas se basa en una función de Javascript Sync donde es flexible definir reglas de seguridad complejas para extender la aplicación móvil que ejecuta Couchbase Lite.
Función Sync: Configuración de la pasarela de sincronización
La función de sincronización es fundamental en Sync Gateway para gestionar el acceso de lectura y escritura, y es donde se definen la mayoría de las reglas de acceso a los datos. Así que es realmente el núcleo de su implementación de seguridad donde la función de sincronización es una función JavaScript que se ejecuta cada vez que cualquier documento JSON se escribe en Sync Gateway.
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "bases de datos": { "comestibles-sync": { "servidor":"http://walrus:", "cubo":"ultramarinos-sincronizar", "usuarios": {"INVITADO": {"desactivado": verdadero}}, "sincronizar":`función(doc,oldDoc) { canal(doc.canales); }` } } } |
La función se define en el archivo de configuración de Sync Gateway, donde las firmas del método toman la revisión actual del documento, doc, y la revisión anterior, oldDoc. El cuerpo del método es donde se definen las reglas de seguridad basadas en esas dos entradas. Aquí es donde una simple Función de Sincronización básica puede ser definida primero y luego lentamente reglas de seguridad más avanzadas pueden construirse para cubrir todos los casos. Con este enfoque, podemos modificar la función de sincronización a medida que se definen nuevos tipos de documentos o si hay cambios en el esquema JSON.
Función de sincronización: Permiso de escritura
- requerirUsuario(): Toma como entrada una lista de identificadores de usuario y valida si los usuarios actualmente activos forman parte de esa lista.
- requireRole(): Toma como entrada una lista de roles, donde podemos ver si al usuario requerido se le ha otorgado ese rol en particular. Si no es así, el documento será rechazado.
- requireAccess(): Toma como entrada una lista de canales donde se toma una lista de usuarios actuales y su lista de canales para ver si se les ha concedido un canal en particular. Si el usuario no forma parte de la lista, será rechazado.
- throw(): El lanzamiento le permite hacer cualquier inspección que desee en el documento entrante. Usted puede tener la validación allí y decir, por ejemplo, si un documento es de tipo 'artículo' y si el tipo no coincide, entonces usted puede rechazar el documento. Del mismo modo, se puede realizar una validación basada en que un valor se encuentre dentro de un rango determinado. Por lo tanto, la validación de tipo de nivel de campo muy granular que se puede implementar en la función de sincronización, se puede utilizar con un método throw() en los documentos que no cumplen esos criterios. Con throw() también puede proporcionar detalles de error sobre por qué se lanza y la razón por la que los documentos son rechazados.
Función de sincronización: Permiso de lectura

Un canal determinado empezará a existir en cuanto se utilice el comando canal para asignar un usuario a un canal. En ese momento, el usuario buscará documentos etiquetados con el mismo nombre de canal. ¡Existen dos canales especiales con los parámetros estrella [*] y exclamación [!]
- Estrella[*]: El canal * estrella es el canal en el que todos los documentos se añaden automáticamente a ese canal. Por ejemplo, si a un usuario invitado se le concede el canal * estrella, tendrá acceso abierto a todos los documentos. Un caso de uso claro para este atributo de canal sería similar a los privilegios de administrador donde cualquier usuario que tenga la * estrella en su canal podrá ver todo en el sistema.
- Exclamación[!]: El segundo canal especial es el atributo ! exclamation que se utiliza para describir el canal público que está enfocado desde la perspectiva del documento donde si añades un documento al canal público, entonces todos los usuarios podrán ver ese documento en particular. Un caso de uso claro para este atributo de canal sería similar a tener un documento para anuncios públicos o emitir un mensaje a todos los usuarios.
Función Sync: Por ejemplo
En el siguiente ejemplo, exploraremos utilizando las funciones descritas anteriormente en cómo asegurar el Grocery Sync Application desde la sesión Couchbase Mobile 101 donde existe como un aplicación completamente insegura y sin privacidad. Iterando con Sync Gateway, los usuarios acabarán viendo sólo sus propios elementos en lugar de ver la misma lista con todos los elementos disponibles que se añadan.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "log" : ["*"], "bases de datos": { "comestibles-sync": { "servidor":"morsa:", "cubo":"ultramarinos-sincronizar", "usuarios": { "INVITADO": { "desactivado": falso, "admin_canales" : ["*"] } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["*"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["*"] } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["*"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["*"] } }, "sincronizar" : ' función(doc, oldDoc) { /Agregar función de sincronización de marcador de posición, añadir lógica de lectura/escritura personalizada aquí }' } } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { /Agregar función de sincronización de marcador de posición, añadir lógica de lectura/escritura personalizada aquí } ' } } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { canal("artículos-"+doc.propietario); } /Agregar documento de artículo al canal de artículos del propietario ' } } } |
Cuando se escribe un documento en Sync Gateway, lo asignamos a un canal cuyo nombre tiene la forma "items"-prefijo con el valor de la propiedad owner desde dentro del documento owner que se obtiene por "doc.owner". La configuración anterior es cuando la propiedad del documento se establece en "bob" y los "items" se asignarían entonces al canal "items-bob". El usuario, "bob" tiene acceso al canal "items-bob" y básicamente ese es el canal al que se le ha dado acceso a Bob en su registro de usuario. Ahora, cualquier ítem que tenga dueño 'bob' como propiedad será visto a través de ese canal por el propio Bob pero no será visto por Alice porque no estará en el canal 'ítems-alice'. Lo que ocurrió en la configuración es que asignamos un documento a un canal apropiado y también programáticamente dimos acceso al propietario a través de su canal propietario "items-".
Así que ahora los usuarios sólo pueden ver/leer sus propios artículos y efectivamente sus propias listas de la compra. Pero todavía pueden escribir en la lista de los demás, porque Bob podría subir un artículo y establecer la propiedad "Alice", donde, en efecto, Bob podría infractar fácilmente la lista de Alice. El siguiente paso en la adición de seguridad es asegurarse de que cuando un documento entra, tendríamos la coincidencia propietario-propiedad con el usuario autenticado actual.
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { requerirUsuario(doc.propietario); //El propietario del documento de artículo debe ser el usuario autenticado canal("artículos-"+doc.propietario); } ' } } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario) } si no { requerirUsuario(doc.propietario) canal("artículos-"+doc.propietario); } } ' } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario); } si no si (doc.tipo == "artículo") { requerirUsuario(doc.propietario); canal("artículos-"+doc.propietario); } si no{ tirar({prohibido: "No válido documento tipo"}); } } }' } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario); } si no si (doc.tipo == "artículo") { requireAccess("artículos-"+doc.propietario) canal("artículos-"+doc.propietario); } si no{ tirar({prohibido: "No válido documento tipo"}); } } ' } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario); } si no si (doc.tipo == "artículo") { requireAccess("artículos-"+doc.propietario) si (oldDoc == null) { si (doc.consulte == verdadero) { tirar( {prohibido: "nuevo artículos no puede sea comprobado"}); } } canal("artículos-"+doc.propietario); } si no { tirar( {prohibido: "No válido documento tipo"}); } } ' } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario); } si no si (doc.tipo == "artículo") { requireAccess("artículos-"+doc.propietario) si (oldDoc == null) { si (doc.consulte == verdadero) { tirar( {prohibido: "nuevo artículos no puede sea comprobado"}); } si no { si (doc.consulte != oldDoc.consulte) { requerirUsuario(doc.propietario); } } } canal("artículos-"+doc.propietario); } si no { tirar( {prohibido: "No válido documento tipo"}); } } ' } } |
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 |
{ "registro" : ["*"], "bases de datos": { "ultramarinos-sincronizar": { "servidor": "morsa:", "cubo":"comestibles-sync", "usuarios": { "alice": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-alice"] }, "bob": { "desactivado" : falso, "contraseña": "contraseña", "admin_canales":["artículos-bob"] } }, "sincronizar" : ' función(doc, oldDoc) { si (doc.tipo == "amigos") { //procesar nuevo documento amigos requerirUsuario(doc.propietario); //El propietario de los amigos acceda a(doc.amigos, "artículos-"+doc.propietario); canal("privado-"+doc.propietario); acceda a(doc.propietario, "privado-"+doc.propietario); } si no si (doc.tipo == "artículo") { requireAccess("artículos-"+doc.propietario) si (oldDoc == null) { si (doc.consulte == verdadero) { tirar( {prohibido: "nuevo artículos no puede sea comprobado"}); } si no { si (doc.propietario != oldDoc.propietario) { tirar({prohibido: "Abandona Robar Artículos"}); } si (doc.consulte != oldDoc.consulte) { requerirUsuario(doc.propietario); } } } canal("artículos-"+doc.propietario); } si no { tirar( {prohibido: "No válido documento tipo"}); } } ' } } |
[3] Transporte de datos por cable
Ahora, aparte de la autenticación y la lectura/escritura de datos para el acceso de los usuarios a la seguridad, el siguiente tema es asegurarse de que sus datos están protegidos en el cable a un punto final remoto. Sync Gateway admite SSL y TLS en el transporte. Es fácil de configurar en el archivo de configuración de Sync Gateway para habilitar SSL en Sync Gateway para su aplicación móvil para tener seguridad adicional de datos.
[4] Almacenamiento de datos
Para almacenar datos en el cliente, en realidad estás encriptando el sistema de archivos del dispositivo. Hay buena información sobre lo que necesitas hacer para encriptar la base de datos local de Couchbase Lite en el portal de desarrolladores móviles. Estamos hablando de un entorno seguro en la nube y de configurarlo para el cifrado del sistema de archivos en el cliente.
Resumen
Sync Gateway es realmente la pieza que une Couchbase Lite, el framework que vive en el dispositivo integrado. Tienes tu Couchbase Server corriendo en la nube y Sync Gateway los une. Sync Gateway tiene un puñado de funciones clave que proporciona para convertir una aplicación que se ejecuta en el dispositivo local, que es una aplicación independiente desconectada en una experiencia de sincronización multiusuario con todas las funciones. Lo que se muestra arriba es cómo podemos serializar cosas en la base de datos iterando sobre el archivo de configuración de la Función Sync.
A continuación nos adentraremos en la clase del componente listener HTTP de Couchbase Lite es la sesión 103 de Couchbase Mobile sobre cómo habilitar la función Peer-to-Peer donde podrás crear experiencias sociales in-app únicas mediante "Building a Peer-to-Peer App with Couchbase Mobile".