Crear y desplegar un Protected Custom Metadata Type con información confidencial

Vimos en la entrada anterior las alternativas para almacenar información confidencial en Salesforce y vimos que el mecanismo preferido inicialmente, era la creación y despliegue de un Protected Custom Metadata Type.

En este artículo veremos paso a paso y en detalle:

  • La arquitectura recomendada por Salesforce para el Managed Package y asegurar la confidencialidad de la información (y alguna variante que propongo).
  • Cómo construir y configurar el Custom Metadata Type que albergue esa información.
  • Cómo desplegar el paquete en la ORG destino.

Para ello seguiremos un caso de uso real: ofrecer el acceso a un servicio remoto, sin mostrar nunca sus credenciales ni las URL de acceso.

Características de un Managed Package

¿Managed Packages en el 2019, estás obsoleto?

En estas fechas, a inicios del 2019, hablar del empaquetamiento tradicional de Salesforce con Managed Packages, es algo peculiar, ¿por qué? Pues porque en breve se liberará en GA, el nuevo sistema de 2nd Generation Packaging en Salesforce DX, que aporta muchas novedades.

Pero en estos momentos, aunque tenemos muy claro que los Unlocked Packages tienen ventajas sobre los Unmanaged Packages, aún queda por ver, o al menos yo no lo he visto aún, como serán en detalle los Locked Packages: sus características, como será su ciclo de vida dentro de un proceso de CI/CD, etc.

Por eso, y porque actualmente la totalidad de empresas han usado este Managed Packages, este artículo se basa, en el sistema tradicional, pero probado, robusto y disponible actualmente de los Managed Packages.

Aún así, por ser un tema que me apasiona y porque creo que aún hay mucho camino por recorrer (dediqué 4 artículos de este blog) te recomiendo, que si estás interesado, sigas con atención este tema, porque creo será un tema importante durante el 2019.

Recordar que los Managed Packages, no son solo para ISV (Independent Software Vendor) que construyen aplicaciones para distribuir en la App Exchange, sino que puede ser relevante para cualquier cliente de Salesforce, por sus capacidades y características.

Anatomía de un Manage Package + Custom Metadata Type con información confidencial: caso de uso práctico

Bajo, el que seguramente es el título más largo de este blog  😉  veamos como debe ser la arquitectura del Package.

Enumeramos primero nuestros requerimientos:

  1. Únicamente se podrá acceder a la información mediante ciertos métodos designados (recuerda el modificador global, ningún otro podrá ofrecer acceso.
  2. El Custom Metadata Type debe albergar 2 tipos de información:  información confidencial, solo puede ser leída, nunca modificada y otra información que debe ser actualizada a cada invocación.
  3. El Package debe poder ser actualizado en cualquier momento, sin advertencia por parte del usuario del paquete, y sin ninguna incidencia en su utilización.

Anatomía de un Managed Package

Como ya vimos la estructura de un Custom MetadataType, ahora te muestro como está formado un Managed Package.

Imagen procedente de la documentación oficial de Salesforce, sobre la estructura de un Package

Un Managed Package está formado por componentes, y a su vez, estos en atributos. Un ejemplo muy sencillo es:

  • Componente: un Custom Object.
  • Atributo: un Custom Field en el Custom Object.

Para los componentes, debes entender quien podrá modificarlos, teniendo las siguientes opciones:

  • Upgradeable Component: indica que el componente puede ser actualizado al distribuir una nueva versión del Package.
  • Subscriber Deletable: el usuario del Package puede eliminar el componente.
  • Developer Deletable: el componente puede ser eliminado por el developer, bajo ciertas condiciones.
  • Editable: en la documentación existe un listado exhaustivo indicando para cada componente, si el Developer, el Developer y el Suscribier o ninguno de los 2, tiene derecho a la edición según los parámetros anteriores.
  • Protectable:
    • el developer puede marcar el componente como protected.
    • esto impide que nadie fuera del paquete pueda referenciar o enlazar este componente, este aspecto es crucial para nuestro caso de uso.
    • y además le asegura que podrá eliminar el componente sin incidencias.
  • IP Protection: ciertos componentes son ofuscados de forma automática para proteger la Propiedad Intelectual del developer, típicamente código Apex, exceptuando los métodos que se marcan como Global, y este es el otro aspecto crucial para nuestro caso de uso.

Los actores que intervienen son 2:

  • Developer: es el creador del package, puedes ser tu ó una empresa que no conoces que distribuye una aplicación vía App Exchange.
  • Subscriber: es el que instala la aplicación en su ORG, que puede ser tu, o tus clientes como integrador, o tus clientes como suscriptores de tu app via App Exchange.

Hasta aquí la teoría mínima, y veamos ahora la implantación de un caso de uso real.

Descripción del Caso de uso

Nos planteamos el siguiente caso de uso, con estos requerimientos:

  1. Ofrecer acceso a un servicio remoto accesible por web service callout, para obtener información sobre el riesgo de conceder un crédito bancario a un cliente que lo está solicitando.
  2. Este servicio recibe como parámetro de entrada, un identificador (DNI/NIF/NIE) y retorna el nivel de riesgo del cliente (para simplificarlo en gran medida, he obviado los parámetros del crédito, que en un caso real serían imprescindibles).
  3. Es condición sine qua non, no desvelar la URL destino, ni las credenciales usadas para acceder al servicio.
  4. La URL de acceso y/o sus credenciales de acceso pueden ser modificadas en cualquier momento, y esto no debe afectar al usuario del Package.

Para simplificar algunos aspectos de implementación no relevantes para el artículo verás que he tomado 2 compromisos:

  • el mecanismo de autenticación es rudimentario mediante cabeceras con username/password. Por supuesto que no te recomiendo esto para un entorno real, sino que en su lugar deberías utilizar un diálogo oAuth bajo comunicación cifrada en modo 2-way SSL (doble certificado).
  • la invocación a la callout la realiza en modo síncrono desde la clase Proxy, y en modo asíncrono desde el VF Controller, para que tengas un ejemplo de cada. En una implantación real, no te recomiendo la invocación de un Web Service en un contexto síncrono, sin siempre asíncrono.

Arquitectura recomendada

Desarrollaremos el ejemplo bajo el esquema recomendado por el equipo de Product Security de Salesforce, y añadiré 2 alternativas.

Arquitectura recomendada por Salesforce para un Managed Package que debe albergar información confidencial con un Protected Custom Setting, pero extensible para un Custom Metadata Type.

He adaptado el diagrama para que sea algo más agradable y comprensible, quedando de la siguiente manera:

Implementación del caso de uso

Alternativa 1

Esta alternativa es la implementación de la arquitectura recomendada:

Veamos cada componente para entender que función realiza y como interactúa con el resto. Cada punto de lista a continuación se corresponde con un componente del gráfico:

  1. Esta clase con modificador global, actúa como punto de acceso desde el código Apex de la ORG del Subscriber. La he denominado clase Proxy, porque nos permite acceder al servicio desde varios puntos. Sus funciones principales son:
    1. Acceder al Custom Metadata Type, para obtener credenciales y URL.
    2. Invocar al Web Service con la URL adecuada (productiva o sin coste, ahora veremos). De esta manera conseguimos que el usuario del Package acceda al servicio de forma transparente pero sin ninguna visibilidad sobre las credenciales ni a la URL destino.
    3. Pero, ¿por qué tiene acceso bidireccional al Custom Metadata Type Esteve? Pues porque, es el componente que realizará la actualización del número de invocaciones realizadas.
    4. Esta clase debería además, realizar funciones de validación, logging, etc., o invocar esas funciones a clases específicas, pero que en este caso he simplificado al máximo.
  2. El Custom Metadata Type (CMT) contiene las credenciales y la URL de los servicios. ¿Por qué hay 2? Es muy habitual tener 2 modalidades de un servicio cuando tiene coste asociado:
    1. Una modalidad sin coste que nos permite realizar pruebas pero cuyo resultados, disponibilidad y rendimiento no suelen ser como el servicio productivo.
    2. La modalidad con coste, es el servicio real con coste para nuestra instancia productiva.
  3. Ofreceremos también un componente Visualforce, para su uso desde la interfaz de usuario. Para ello, construiremos un VF Component reutilizable en páginas VF del usuario, que podrá incrustar fácilmente.
  4. Será el VF Controller de este VF Component, que accederá al CMT para obtener las credenciales y acceder al Web Service, aislando al usuario de la Suscriber ORG de la información confidencial. Dado que el controller está dentro del Managed Packaged y sin modificador global, el cliente no puede acceder a él directamente y no tendrá ninguna visibilidad sobre él. Este Controller invoca a los servicios de la clase Proxy e invocar al callout.
  5. El componente denominado Web service, realiza la llamada al servicio remoto de forma agnóstica, no conoce la URL.
  6. Es el servicio remoto que proporciona la información (poco importante para el ejemplo). En mi caso, lo he implementado en Heroku, pero podría ser en cualquier Cloud u OnPremise.
  7. Análoga a su tocayo (6) pero recordando que solo debería ser accedido desde una ORG productiva.
  8. Código Apex en la ORG del Suscriber que invoca a la clase Proxy para acceder al servicio, ningún misterio como verás.
  9. Es la VF Page creada en la ORG del Suscriber para albergar el VF Component ofrecido dentro del Managed Package y acceder al servicio.

Alternativa 2

El esquema anterior implementa el modelo canónico recomendado por Salesforce, pero: ¿Y si quiero usarlo en una Visualforce simplificando el esquema de componentes, es posible? Yo creo que si, y esta es el esquema que te propongo:

Consiste en los siguientes componentes:

  • El CMT (2) y los servicios (6 y 7) son los mismos
  • El componente diferenciador es el VF Controller no para un Component, sino para una VF Page (11). En él, implemento un método global de acceso con el resto de métodos no accesibles. Mediante una Continuation (acceso asíncrono a un callout) accedo directamente al CMT e invoco el servicio. Podríamos decir que el VF Controller ha engullido a la clase Proxy que veíamos antes,  para simplificar el esquema.
  • La VF Page utiliza el Controller, que invoca un único servicio visible (o varios dependiendo de lo que queramos ofrecer) para acceder al servicio remoto.

Esquema de instancias necesario para el caso de uso

Para llevar a cabo este caso de uso, y en general para trabajar con Managed Packages requerimos de las siguientes ORGs:

  1. ORG en la que crearemos el Managed Package y sus componentes, y que requiere de un Namespace propio.
  2. ORG de Suscriber en la que realizaremos nuestra primera instalación y consiguientes actualizaciones y donde utilizaremos el componente.
  3. ORG en la que llevaremos a cabo un Patch, para distribuir de forma urgente sin nueva release (actualmente este es un requerimiento de Salesforce).

En nuestro caso, para reducir el artículo solo utilizaremos las 2 primeras.

Esquema gráfico de las ORGs que se usan para la creación, utilización y parcheado del componente

Diseño del Custom Metadata Type

El diseño del Custom Metadata Type (CMT), es muy sencillo y se asemeja a la creación de un Custom Object.

Aún así el primer paso es la activación del namespace en la ORG, ¿por qué? Porque cuando creemos el Managed Package, necesitaremos de este namespace, por tanto es conveniente anticipar su creación.

En estas 2 capturas, puedes ver como partiendo de una ORG donde no se puede utilizar Managed Packages, he activado la opción y he asignado un namespace en este caso ege. De momento no he creado el Package, para no liarte.

Veamos ahora como es el diseño del Custom Metadata Type el CMT que he denominado RiskCredit__mdt:

Muy parecido a la definición de un Custom Object, definición de fields, layouts, etc., pero he marcado en rojo los aspectos de visibilidad que restringiran el acceso según vimos en el primer apartado.
Estos 2 registros son los que inicialmente contendrá el CMT, que contienen las credenciales (información confidencial para las 2 modalidades de servicio).

Cabe observar que tanto el CMT como sus registros están protegidos, para que sean ofuscados cuando se introduzcan dentro del Managed Package.

Clase Proxy

El método que te muestro a continuación es el core de la clase. Son 3 pasos:

    1. Obtener la información confidencial del CMT.
    2. Invocar al servicio remoto.
    3. Actualizar el CMT para indicar que hemos realizado una nueva invocación exitosa

Obtener la información del CMT te va a ser muy familiar.

Actualizar el CMT, es decir, actualizar los registros, consiste en un Deployment.

Visualforce Component y Controller

El VF Component alberga un <attribute> que contendrá el identificador de cliente del que queremos obtener el nivel de riesgo,  e invoca al método del controller para obtenerlo:

Controller minimalista, cuya única función es invocar al servicio de la clase Proxy.

Visualforce Page que incrusta el VF Component y pasa como valor del Attribute un identificador de cliente.

Acceso desde la ORG del Suscriber a la clase Proxy

Como ejemplo de acceso a la clase ORG del Suscriber te dejo este ejemplo muy sencillo:

Desde la ORG del Suscriber, utilizar la clase Proxy es como invocar cualquier otra clase, excepto que debe incluirse el namespace del Package.

Managed Package

Como vimos, el Package es el contenedor de los objetos que queremos deployar en la ORG destino, siendo el principal de ellos, el Custom Metadata Type. Al incluir un objeto, la plataforma intenta incluir sus dependencias, aunque debemos estar atentos que no falte ninguna, dado que no es un proceso infalible.

  • Deben incluirse todos los objetos dependientes, incluyendo clases de Test, Named Credentials, Apps, etc., que utilizamos directa o indirectamente.

A continuación vemos en la captura, como es la configuración y contenido del Package que he creado:

Así definimos los componentes del Managed Package.
El paso final para el Upload, es definir, si nuestro Package, entra dentro del ciclo de Release o Beta. En Release podremos incluso desplegarlo en la App Exchange, y realizar actualizaciones, mientras que en Beta, seguimos en la fase de pruebas tediosa (dado que hay que desinstalarlo volverlo a instalar cada vez en la ORG del Suscriber).
Finalizadas las comprobaciones (ejecución de Tests, etc., se genera el enlace de instalación) y se aumenta la versión del Package.

Proceso de instalación en la ORG del Suscriber

El proceso de instalación es muy sencillo, como cualquier otro Package. Adjunto las capturas de pantalla, a modo informativo, pero no requiere de ningún conocimiento especial:

Dado que los servicios remotos están desplegados en Heroku sin protocolo Https, se pide el acceso explícito.
Captura correspondiente al primer despliegue, pasaron más de 56 versiones hasta finalizarlo, soy un poco paquete 🙂

Una vez instalado el Package, la visibilidad sobre su contenido vendrá condicionada por las opciones que hemos seleccionado previamente, y en ningún podremos ver el código de las clases que no sean globales, solo su firma:

Y a partir de estos momentos, utilizamos los componentes a los que tengamos  acceso, pero dado que el Custom Metadata Type está protegido dentro de un Managed Package, y hemos limitado el acceso a métodos concretos de la clase Proxy, solo podremos acceder al servicio de forma limitada, y nunca ofreciéndole la información confidencial.

Código disponible

Todo el código está disponible en estos 2 Repositorios Públicos:

  1. Credit Risk Level Web Service Heroku
  2. Custom Metadata Type Managed Package

Conclusiones

La utilización  de un Managed Package junto con un Custom Metadata Type para almacenar y distribuir información confidencial, está al alcance de todos sin coste adicional, aunque requiere los conocimientos básicos de empaquetado y características de seguridad de acceso.

Por supuesto, estos Packages tienen más funcionalidades, como deprecar métodos, los Scripts de PRE/POST instalación, Patches posteriores, pero que son características propias del ciclo de vida de un paquete, y que no he comentado en este artículo, pero te animo a estudiar.

El proceso de generación de versiones durante la etapa de construcción, es tedioso si se realiza manualmente, y requiere de automatización mediante scripts, siendo ahora mucho más fácil con el uso de la Salesforce CLI (que te recomiendo encarecidamente).

Hasta la llegada de la 2nd Gen Package, este mecanismo es el recomendado, probado y robusto para la distribución de información confidencial, y es interesante tanto si eres un ISV como un cliente final de Salesforce con esta necesidad.

Espero que haya sido de ayuda.

Enlaces Interesantes

    1. Understanding Packages – Documentación oficial
    2. Custom Metadata Types – Documentación oficial
    3. Video – Secure Coding: Storing Secrets in your Salesforce instance
    4. Custome Metadata Class – Documentación oficial
    5. Stack Exchange: Impossible to test  Metadata.Operations.enqueueDeployment method
    6. Stack Exchange: How do I update a namespaced Custom Metadata Type record after making a package?
    7. Custom Metadata Types  Implementation Guide Preview Spring 2019
    8. Using Custom Components in a Visualforce Page – Documentación oficial