Implementa la seguridad a nivel de fila para el contenido incorporado de Looker

Escrito por Christopher Seymour, analista de datos sénior, y Dean Hicks, ingeniero de Relaciones con Desarrolladores

Introducción

La función de incorporación de Looker es una de las funciones más potentes y valiosas del producto Looker. Si estás leyendo esta guía, es probable que ya estés incorporando contenido de Looker en tu aplicación o que quieras hacerlo próximamente.

El objetivo de esta guía es ayudarte a comprender mejor el diseño de la función de incorporación de Looker para que puedas compilar una aplicación potente y segura con el objetivo de entregar datos a tus usuarios. Como una profundización del tema, se trata de una lectura breve, así que ten en cuenta que esta guía no está diseñada como una solución rápida para un problema sencillo, sino como un componente básico que te ayudará a administrar mejor todo tu caso de uso de incorporaciones de Looker.

Descripción general de los casos de uso

En esta guía, se describe un caso de uso común en el que tu empresa incorpora contenido de Looker a tu producto.

Para este caso de uso de incorporación firmada, supongamos que eres el administrador de tu instancia de Looker. Trabajas con dos tipos de usuarios incorporados: clientes (o “usuarios de marca”), que solo deberían poder acceder a los datos que pertenecen a su empresa, y propietarios de cuenta, que podrán acceder a los datos de varios clientes específicos. Tiene un panel con algunos mosaicos que muestra a cada cliente que usa su producto, pero necesita que el panel se filtre automáticamente para cada cliente, de modo que los paneles muestren solo los datos específicos de ese cliente. En los ejemplos de este documento, se usan dos empresas ficticias: Hooli y Pied Piper.

Tienes una tabla llamada productos, que muestra algunas métricas de productos para diferentes marcas. Cada marca corresponde a un usuario de incorporación diferente (con un external_user_id diferente) en la aplicación de incorporación firmada. Como cada usuario incorporado debería poder ver solo los datos de su propia marca, existe una exploración simple que usa un filtro de acceso en un atributo de usuario de marca:

explore: products {
  access_filter: {
    field: products.brand
    user_attribute: brand
  }
}

Tienes un panel simple que se basa en esta exploración y tiene dos mosaicos: uno muestra el nombre de la marca y el otro la cantidad de productos para ella.

Usa el extremo create_sso_embed_url a fin de generar URLs de incorporación de este panel para cada usuario de incorporación. En este ejemplo, se usan dos marcas: Pied Piper y Hooli. Este es el cuerpo de la solicitud que usas en la llamada create_sso_embed_url para Pied Piper, con external_user_id pied_piper:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "pied_piper",
  "first_name": "PiedPiper",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper"}
}

La URL que generaste para Pied Piper muestra el panel de la siguiente manera:

Este es el cuerpo de la solicitud que se usa en la llamada create_sso_embed_url para Hooli, con external_user_id hooli:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "hooli",
  "first_name": "Hooli",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Hooli"}
}

La URL que se generó para Hooli muestra el panel de la siguiente manera:

¡Voilà! El panel se filtra según el valor de cada usuario de incorporación para el atributo de usuario brand.

Sigue explorando

¡Genial! Pero ¿qué sucede si quiero otorgarle a un propietario de cuenta acceso a varias marcas? ¿Cómo puedo asegurarme de que solo los usuarios relevantes vean mis datos?

¡Buenas noticias! La función de incorporación firmada de Looker se diseñó con el objetivo de permitir que los desarrolladores creen experiencias de datos potentes y personalizadas para los usuarios, a la vez que garantizan que se mantenga la administración de datos definida por tu modelo de datos y políticas de acceso al contenido.

Asegurarse de que la administración de datos sea hermética es fundamental para ofrecer una experiencia de datos potente. Sigue leyendo para explorar algunos conceptos y prácticas recomendadas que puedes usar para diseñar la experiencia que mejor se adapte a tus necesidades. En primer lugar, veremos una breve descripción general de cómo funciona todo esto.

Conceptos básicos de las incorporaciones firmadas de Looker

Es importante tener en cuenta que la autenticación y administración de usuarios de Looker en el contexto de incorporación funciona fundamentalmente de la misma manera que en el contexto no incorporado y, en esencia, de la misma manera que la mayoría de las otras aplicaciones web.

Esto puede ser confuso en el contexto de la incorporación firmada de Looker, porque el paso de autenticación firmada, la configuración del usuario y el panel en sí se combinan en una URL larga y compleja. Sin embargo, esa URL se usa para establecer la sesión, que sigue siendo válida incluso después de acortar la URL. Tener en cuenta este concepto contribuirá mucho a que tengas éxito en la creación de excelentes experiencias de datos.

Estructura de URL de incorporación firmada

Esta es una de las URLs de autenticación de incorporación firmadas que genera la llamada create_sso_embed_url con el cuerpo de la solicitud para Pied Piper:

https://mylookerinstance.cloud.looker.com/login/embed/%2Fembed%2Fdashboards%2F17?permissions=%5B%22access_data%22%2C%22see_user_dashboards%22%5D&models=%5B%22thelook%22%5D&signature=iG6vcKBgnA50jaL2iShFeQHwFPN7wvTx7Rz6r%2FtFuvE%3D&nonce=%22967729518a7dbb8a178f1c03a3511dd1%22&time=1696013242&session_length=300&external_user_id=%22pied_piper%22&access_filters=%7B%7D&first_name=%22Pied%22&last_name=%22Piper%22&user_attributes=%7B%22brand%22%3A%22Pied+Piper%22%7D&force_logout_login=true

Esta es la misma URL decodificada y dividida en líneas individuales:

https://mylookerinstance.cloud.looker.com/login/embed/
/embed/dashboards/17
?permissions=["access_data","see_user_dashboards"]
&models=["thelook"]
&signature=iG6vcKBgnA50jaL2iShFeQHwFPN7wvTx7Rz6r/tFuvE=
&nonce="967729518a7dbb8a178f1c03a3511dd1"
&time=1696013242
&session_length=300
&external_user_id="pied_piper"
&access_filters={}
&first_name="PiedPiper"
&last_name="User"
&user_attributes={"brand":"Pied Piper"}
&force_logout_login=true

Cuando accedes a esta URL, suceden algunas cosas:

  1. Looker busca una cuenta de usuario existente con external_user_id = pied_piper. Si no existe ninguna, Looker crea una cuenta de usuario nueva con ese external_user_id.

  2. Los detalles de la cuenta del usuario existente, incluidos los permisos, los modelos, los grupos (si se especifican) y los valores de los atributos de usuario (si se especifican), se reemplazan por los detalles de la cuenta que se especifican en la URL.

  3. Looker autentica al usuario y establece una sesión para él almacenando una cookie de sesión en el navegador.

  4. Luego, Looker redirecciona a la URL de destino, o URL de redireccionamiento, que se especifica en la llamada create_sso_embed_url:

    https://mylookerinstance.cloud.looker.com/embed/dashboards/17.

    Puedes ver esta URL de redireccionamiento como una URL relativa codificada en la URL incorporada original firmada:

    %2Fembed%2Fdashboards%2F17

Aunque los pasos del 1 al 3 se realizan en segundo plano automáticamente, y todo lo que ve el usuario final es el resultado final (el panel en sí), estos pasos son, básicamente, los mismos que los de un usuario normal de Looker que no está incorporado. Supongamos que deseas que un usuario acceda con credenciales de usuario y contraseña. El proceso debería ser similar al siguiente:

  1. Tú (como administrador de Looker) navegas al panel Administrador: Usuarios y usas la barra de búsqueda para verificar si ya existe una cuenta de usuario para este usuario. De lo contrario, debes crear una cuenta de usuario nueva.

  2. Tú (el administrador de Looker) presionas Editar junto al usuario en el panel Administrador - Usuarios y aprovisiona al usuario permisos, modelos, grupos, valores de atributos del usuario y otros valores.

  3. El usuario se dirige a la página de acceso en https://mylookerinstance.cloud.looker.com/login y, luego, ingresa su nombre de usuario y contraseña. Looker autentica al usuario y establece una sesión para él almacenando una cookie de sesión en el navegador.

  4. Luego, Looker redirecciona a la página de destino (por lo general, https://mylookerinstance.cloud.looker.com/browse).

Ten en cuenta que la cookie de sesión se aplicará a todas las pestañas de la ventana del navegador. Si el usuario comienza en https://mylookerinstance.cloud.looker.com/browse, abre una nueva pestaña del navegador y navega a cualquier página a la que sus permisos le otorguen acceso, la página se cargará como se espera mediante la cookie de sesión que ya se estableció en la pestaña original del navegador.

Lo mismo ocurre con los usuarios de incorporaciones. Los usuarios de incorporaciones son un poco más limitados en cuanto a las páginas a las que pueden acceder en la IU, ya que solo pueden acceder a las URLs de Vista, Panel y Explorar con el prefijo /embed. Sin embargo, tienen la libertad de navegar manualmente a cualquier panel al que los detalles de su cuenta de usuario les otorguen acceso. Supongamos que la URL de incorporación firmada original te redirecciona a https://mylookerinstance.cloud.looker.com/embed/dashboards/17 en una pestaña del navegador. Luego, abres una pestaña nueva del navegador y cargas un panel de incorporación diferente que se encuentra en la misma carpeta (y, por lo tanto, tiene las mismas restricciones de acceso): https://mylookerinstance.cloud.looker.com/embed/dashboards/19.

Aunque la URL de redireccionamiento que se especificó en la URL original de incorporación firmada era para el panel 17, puedes ver que el panel 19 se carga según lo esperado si ingresas manualmente la URL en una pestaña del navegador. Ten en cuenta que no fue necesaria otra URL incorporada firmada para cargar un panel diferente.

La estadística clave aquí es que todos los detalles de la cuenta de usuario establecidos en la URL (permisos, atributos de usuario, etc.) se aplican a toda la sesión de usuario, no solo al panel específico especificado en la URL firmada original. En otras palabras, como su nombre lo indica, los atributos de usuario son una función del usuario, no una función del panel, y deben usarse para determinar los niveles de acceso de un usuario específico en toda la aplicación, no solo en una pestaña específica.

Caso de uso del propietario de la cuenta

Al igual que con cualquier solución personalizable, hay ciertos enfoques que debes evitar. Con esto en mente, considera que también tienes propietarios de cuentas que pueden ser propietarios o administradores de varias marcas. En este ejemplo, el propietario de una cuenta administra las marcas Pied Piper y Hooli. Por lo tanto, tu app genera las URLs para ambos external_user_ids con las mismas entradas en la llamada a create_sso_embed_url que se mostró antes y crea una pestaña nueva en la app para cargar cada panel al que el propietario de la cuenta desea acceder. Por lo general, vemos a los desarrolladores implementar soluciones como esta, lo que da como resultado un flujo de trabajo incorrecto para el usuario:

  1. Navega a la pestaña del panel de Pied Piper.
  2. Navega a la pestaña del panel de Hooli.
  3. Regresa a la pestaña del panel de Pied Piper.
  4. Presiona el botón Volver a cargar en el panel de Pied Piper.

En el panel de Pied Piper, se muestran los datos de Hooli.

Puedes probar un enfoque similar, pero usar el mismo test_user external_user_id para ambas llamadas a create_sso_embed_url:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper"}
}

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Hooli"}
}

Sin embargo, el comportamiento es exactamente el mismo: una vez que la pestaña se vuelve a cargar con el panel de Pied Piper, se muestran los datos de Hooli.

¿Qué sucede aquí? ¿Cómo puedo asegurarme de que el panel de cada marca muestre solo los datos de esa marca?

El mismo enfoque desde una perspectiva no incorporada

Como se mencionó en la sección anterior, las sesiones de usuario con incorporación firmadas funcionan en esencia de la misma manera que las sesiones de usuario normales no incorporadas de Looker, por lo que puede ser útil replantear el enfoque problemático descrito anteriormente en el contexto de una sesión de usuario normal no incorporada de Looker y desglosar esos pasos para ayudar a comprender cómo implementar esta solución de una manera más sólida. Así es como se vería ese flujo de trabajo si le diera instrucciones a un usuario de IE estándar que tenga acceso a la IU de Looker:

  1. Puede crear dos cuentas de usuario diferentes en la página Administrador: Usuarios.
    1. En la página de edición de la primera cuenta de usuario, configuras el valor del atributo de usuario brand en pied_piper.
    2. En la página de edición de la segunda cuenta de usuario, establece el valor del atributo de usuario marca en hooli.
  2. Puede enviar correos electrónicos de configuración para ambas cuentas de usuario al propietario de la cuenta.
  3. El propietario de la cuenta configura credenciales de correo electrónico y contraseña independientes para cada cuenta.
  4. Debes otorgarle al propietario de la cuenta el vínculo al panel. (https://mylookerinstance.cloud.looker.com/dashboards/17) y dile que, para cambiar de marca, deberá volver a la página de acceso en otra pestaña y, luego, ingresar las credenciales de correo electrónico y contraseña de su otra cuenta de usuario. Luego, volverá a cargar el panel en esa pestaña.

El propietario de la cuenta sigue las instrucciones. Sin embargo, después de ingresar el nombre de usuario y la contraseña de la cuenta de usuario de Hooli en la segunda pestaña del navegador y luego volver a la primera pestaña donde ya estaba cargado el panel de Pied Piper, el propietario de la cuenta presiona el botón Volver a cargar. Para sorpresa del propietario de la cuenta, el panel muestra los datos de Hooli. Esto no debería ser sorprendente, ya que la misma situación se desarrolla en un contexto de incorporación.

Puedes desglosar rápidamente la última implementación (con el mismo test_user external_user_id con diferentes conjuntos de atributos de usuario) de la misma manera. El equivalente de la IU de ese flujo de trabajo se vería de la siguiente manera:

  1. Debes crear una cuenta de usuario para el propietario de la cuenta.
  2. Configura pied_piper como el valor del atributo brand de esa cuenta de usuario.
  3. Le dices al propietario de la cuenta que, para cambiar el panel de Pied Piper a Hooli, deberá comunicarse contigo para que puedas ir a la página de edición de esa cuenta de usuario y cambiar el valor del atributo brand de pied_piper a hooli durante la sesión.
  4. Después de que edites su atributo de usuario, podrán volver a cargar el panel en una pestaña nueva para ver los datos de Hooli.

Entonces, a esta altura, es posible que te estés pensando:

Parece ser un gran trabajo. ¿No hay una forma de otorgarles una sola cuenta de usuario y permitirles filtrar los datos en el panel de forma discreta para cada marca?

¡Claro que lo hay! Estas situaciones ayudan a ilustrar un principio que ya es trivial en el contexto de la no incorporación, pero que puede ocultarse debido a las abstracciones del contexto de incorporación: Un solo usuario humano debe estar asociado con una sola cuenta de usuario de Looker con un solo conjunto de valores de atributos de usuario. Esto también queda claro en la explicación de external_user_id en la documentación sobre incorporaciones firmadas.

Looker usa external_user_id para diferenciar a los usuarios de incorporaciones firmadas, por lo que cada usuario debe tener asignado un ID único.

Puedes crear un external_user_id para un usuario con cualquier cadena que desees, siempre que sea único para ese usuario. Cada ID se asocia con un conjunto de permisos, atributos de usuario y modelos. Un solo navegador puede admitir un solo external_user_id, o sesión de usuario, a la vez. No se pueden realizar cambios en los permisos ni en los atributos del usuario durante la sesión.

Poner en práctica estas prácticas recomendadas

Para aplicar estos principios a nuestro ejemplo, necesitarás un solo valor de atributo de usuario que otorgue acceso a TODOS los datos a los que el propietario de la cuenta debería tener acceso en toda la aplicación. Para ello, usa un valor separado por comas para el atributo brand Pied Piper,Hooli:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

Para que esta sintaxis funcione, deberás asegurarte de que tu atributo de usuario esté configurado como Filtro de cadenas (avanzado):

Ten en cuenta que aún puedes cambiar el conjunto de atributos de un usuario si algo cambia en sus niveles de acceso a los datos. Por ejemplo, si el propietario de la cuenta toma la propiedad de una tercera marca, puedes agregar esa tercera marca a la lista separada por comas que especificaste para su atributo de usuario brand. De esa manera, se aplicarán los cambios cuando salga y vuelva a acceder.

Filtra los resultados del panel

Entiendo que los atributos de usuario deben especificar todos los datos a los que un usuario puede acceder en la aplicación. Pero si especifico los atributos de usuario de esta manera, se mostrarán los datos de todas esas marcas en mi panel. ¿Cómo limito los resultados de un panel en particular a una marca determinada?

La forma correcta de filtrar un panel en particular es usar los filtros comunes del panel. (Esto puede parecer obvio ahora, pero vimos que algunas personas se detienen en los atributos del usuario como la única forma de aplicar filtros en el contexto de incorporación, quizás porque user_attributes es un parámetro en la URL de incorporación firmada y los filtros no lo son).

Asegúrate de exigir un valor de filtro y usar una de las opciones de control de selección única, como el menú desplegable:

Asegúrate de que el filtro se aplique al campo correcto en todos los mosaicos necesarios:

Ahora el propietario de la cuenta puede seleccionar entre esos dos valores (y solo esos dos), ya que las opciones disponibles en el menú desplegable están limitadas por los atributos de usuario:

Completa previamente los filtros del panel

Bien, ahora el panel se puede filtrar para una marca específica, pero me gustaría que el valor del filtro ya esté configurado para una marca específica cuando el usuario cargue el panel de esa marca en mi app.

Una vez más, es útil pensar en cómo funciona esto en un contexto no incorporado. ¿Cómo le envías a alguien un vínculo a un panel en el que ya se aplicó un valor de filtro específico? Quizás hayas notado que cuando seleccionas un valor de filtro, ese valor aparece en la URL del panel:

Incluye esa parte de la URL en tu target_url para la llamada a create_sso_embed_url:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17?Brand=Hooli",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

Si usas el SDK de incorporación, puedes usar withFilters para especificar los filtros iniciales que se aplicarán al contenido incorporado:

https://looker-open-source.github.io/embed-sdk/classes/EmbedBuilder.html#withFilters

Si utilizas tu propia secuencia de comandos personalizada, asegúrate de agregar el filtro a la URL antes de que se codifique la ruta. Es posible que algunos valores ya estén codificados en la cadena de filtro (por ejemplo, hay un espacio codificado como + en ?Brand=Pied+Piper), por lo que esos valores se codificarán dos veces en la URL final. Puedes consultar el artículo “SO incorporado panel - configurar el filtro del panel como parte de la URL” Publicación de Comunidad de Looker para conversar sobre estos matices. Si aún tienes problemas para aplicar los filtros, esa publicación de Comunidad también sería un buen lugar para agregar preguntas.

Oculta los filtros del panel

Bueno, veo cómo establecer los filtros iniciales en mis paneles, pero tampoco quiero que el propietario de la cuenta los cambie por su cuenta. SOLO se debe determinar el valor del filtro por el panel al que navegó el propietario de la cuenta en mi app. ¿Cómo puedo ocultar los filtros del panel?

Para ello, puedes usar temas. Los temas son una función pagada, por lo que, si aún no la tienes habilitada en tu instancia de Looker, comunícate con el equipo de Ventas de Looker para que la habilite.

Una vez que se habilite esa función, navega a la sección Controles del panel de la página Administrador - Temas, donde puedes borrar la opción Mostrar la barra de filtros:

Luego, puedes establecer tu tema como predeterminado o aplicarlo en la URL de tus paneles específicos. Nuevamente, esto iría en el target_url de la llamada a create_sso_embed_url:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17?Brand=Hooli&theme=test_theme",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

Puedes encontrar más información para ocultar filtros del panel de incorporación, incluidos algunos fragmentos de código del SDK de incorporación, en este instructivo de YouTube, Cómo incorporar Looker con filtros personalizados.

El resultado final debe ser idéntico a la experiencia del usuario de la pregunta original:

Sin embargo, ahora, debido a que los valores del filtro están codificados en las URLs de destino respectivas que están incorporadas en la app, el panel de cada marca siempre mostrará el panel filtrado con la marca correcta, incluso cuando alternas entre pestañas.

Usar la identidad de otros usuarios

Ahora la experiencia del usuario se ve muy similar a lo que imaginé originalmente. Sin embargo, en mi caso de uso, el propietario de la cuenta tiene permisos y configuraciones de usuario diferentes que los usuarios individuales con external_user_id=pied_piper y external_user_id=hooli no tienen, lo que genera diferentes opciones disponibles en la IU y solo una experiencia del usuario un poco diferente en general. Quiero que el propietario de la cuenta vea todo exactamente como lo ven los usuarios pied_piper y pied_piper como si el propietario de la cuenta hubiera accedido como esos usuarios. ¿Cómo puedo lograr esto?

Lo que estás solicitando se llama "sudo". Si quieres que el propietario de la cuenta pueda usar la función "sudo" en la configuración de cada usuario de marca individual, puedes compilar una función sudo similar en tu app que cargue las URLs incorporadas de external_user_id=pied_piper cuando el propietario de la cuenta use el sudo como usuario de Pied Piper, y las URLs incorporadas de external_user_id=hooli cuando el propietario de la cuenta use la identidad de Hooli. También puedes usar el extremo de la API de login_user para sudo como usuario de la marca con la API si tu app la usa.

Sin embargo, si piensas de nuevo en el contexto de no incorporación: en la página Administrador - Usuarios, no puedes sudo de manera simultánea como múltiples usuarios no incorporados en varias pestañas; la sesión sudo se aplicará a toda la ventana del navegador, al igual que todas las demás sesiones de usuario. Por lo tanto, debes diseñar el comando sudo para que funcione como un solo usuario a la vez. Y, si lo piensas, este diseño es más coherente con la imitación perfecta de la experiencia de los usuarios con los que estás suplantando. El usuario pied_piper, por ejemplo, tiene acceso solo al panel de Pied Piper y no tiene acceso para abrir paneles adicionales en pestañas adicionales. Por lo tanto, no deberías poder abrir distintos paneles en distintas pestañas cuando uses la identidad de este usuario.

Conclusión

Esperamos que esta guía te haya resultado útil y que te sientas bien preparado para avanzar en la compilación de tu contenido incorporado firmado de Looker. Trabajamos continuamente para que Looker sea la oferta de análisis de datos incorporada más flexible y sólida, y queremos conocer tus comentarios. Si tienes preguntas o quieres obtener más información, puedes interactuar con nosotros en la Comunidad de Looker y asistir a nuestros eventos de la Comunidad.