Quería hacer una biblioteca para ACL (Access Control Lists) pero pensé en la primera regla de la Ingeniería del Software: mira a ver si está hecho. Eso hice, eché un vistazo y di con 16 opciones diferentes. ¿Cómo podemos determinar cuál de ellas utilizar?

No os voy a mentir, aunque tenía en mente cuáles serían los criterios para elegir una biblioteca quise comprobar primero buscando en Internet, mismo principio para ver si alguien lo había hecho antes y como mi forma de buscar actualmente es usando los Grandes Modelos de Lenguaje (LLM en inglés) pues tomé nota de lo que dijo y enumero más abajo pero quise buscar las fuentes (no me las dio), así que finalmente busqué en Google:

Lo importante a tener en cuenta es:

Si fueras un chef de un restaurante exclusivo con estrella Michelín, ¿comprarías las verduras y la carne de proveedores aleatorios y con una cuestionable calidad?

Podemos tener la aplicación más impresionante y cuidada, pero si finalmente no prestamos atención a las dependencias que agregamos todo puede desmoronarse.

Dependency from xkcd.com

Los criterios de selección

Los criterios que he encontrado y me han parecido coherentes son los siguientes, los introduciré de forma rápida y después iremos poco a poco desgranando cada uno haciendo un ejercicio práctico:

  • Necesidad real ¿Realmente necesitamos una dependencia?
  • Mantenimiento ¿Es un desarrollo activo? ¿Abandonado o demasiado volátil?
  • Comunidad ¿Cuánta gente hay detrás?
  • Documentación ¿Tiene documentación? ¿Actualizada y extensa?
  • Compatibilidad ¿Encaja bien o choca por tener dependencias incompatibles con el proyecto? ¿Se lleva bien con otras dependencias?
  • Rendimiento ¿Afecta al funcionamiento normal de tu proyecto? ¿Está justificado y es aceptable esta caída de rendimiento?
  • Licencia ¿Se puede utilizar libremente para tu fin? ¿Qué requisitos ha impuesto el autor?
  • Seguridad ¿Tiene vulnerabilidades?
  • Alternativas ¿Existen mejores opciones?

La finalidad ya la presenté en el preámbulo, quiero una biblioteca ACL para un proyecto de Phoenix Framework, según la página de Elixir Toolbox las opciones son 17:

  • acl
  • bodyguard
  • canary
  • canada
  • policy_wonk
  • let_me
  • authorize
  • terminator
  • pundit
  • can
  • aegis
  • policy
  • auval_office
  • chalk_authorization
  • can_i
  • meseeks
  • simple_can

NOTA: he agregado acl aunque inicialmente no está en la página de Elixir Toolbox, este me apareció buscando ACL en la página de Hex.

Necesidad real

En este punto determinamos si realmente necesitamos buscar o crear una biblioteca. Normalmente, las bibliotecas responden a una necesidad muy concreta que no forma parte de la lógica de negocio de la aplicación.

Por ejemplo, aunque el acceso a la base de datos es muy necesario, programar el código para conectar y trabajar con una base de datos queda fuera de alcance, esta necesidad pertenece a la arquitectura de soporte.

Por tanto, algo tan genérico como el soporte de un ACL sí que responde a una necesidad real. No obstante, incluso en este caso cabría preguntarse si la necesidad de esta librería es completamente necesaria o simplemente preferible y en caso de simplificar mucho el ACL y dado un alcance pequeño, este puede implementarse como parte de la aplicación.

Como a mi me gusta separar por capas mis desarrollos y nunca aceptaría implementar en la capa de negocio algo perteneciente a la capa de seguridad, encajar una biblioteca tiene sentido para mi caso de uso.

Las preguntas clave:

  • ¿Es la funcionalidad de la biblioteca parte fundamental de tu lógica de negocio? (p. ej. cálculo de nóminas)
  • ¿Puede proporcionarse como biblioteca manteniendo independiente de ese código a tu lógica de negocio? (p. ej. conexión con base de datos)

A partir de este punto vamos con los descartes. Es decir, hemos determinado necesaria la adición de una biblioteca, tanto si necesitamos buscarla como si necesitamos crearla, ahora veamos el factor mantenimiento.

Mantenimiento

El mantenimiento se refiere a revisar ciertos factores para determinar si un fallo en la dependencia puede afectarnos negativamente y cuánto tiempo podría demorar su corrección.

Para hacernos una idea podemos echar un vistazo a datos precisos como:

  • Cuántos commits tiene el proyecto y cuál fue la fecha del último. Si la fecha supera un año hay que preocuparse y si además el número de commits es muy bajo (menos de 10) es también para preocuparse.
  • Cuántos tickets tiene el proyecto y cuál fue es la fecha promedio de estos. Si el número de tickets abiertos es alto es motivo de preocuparse, sobre todo si son referentes a fallos y además, si la fecha de los tickets es superior a un año también es para preocuparse.

Revisando los 16 proyectos en la página de Elixir Toolkit vemos que sus datos en base a última actualización, número de versión y tickets abiertos es la siguiente:

  • acl: 0.5.0 (24 Junio 2024), 0 tickets
  • bodyguard: 2.4.3 (16 Marzo 2024), 4 tickets
  • canary: 1.2.0 (12 Febrero 2025), 9 tickets
  • canada: 2.0.0 (7 Mayo 2019), 2 tickets
  • policy_wonk: 1.0.0 (13 Diciembre 2018) 1 ticket
  • let_me: 1.2.5 (26 Marzo 2025) 0 tickets
  • authorize: 1.0.0 (9 Septiembre 2018) 2 tickets
  • terminator: 0.5.2 (6 January 2019) 2 tickets
  • pundit: 1.0.2 (10 Febrero 2023) 0 tickets
  • can: 0.0.4 (10 Abril 2016) 0 tickets
  • aegis: 0.2.0 (12 Junio 2018) 0 tickets
  • policy: 1.0.0 (31 Agosto 2016) 0 tickets
  • auval_office: 0.1.0 (7 Noviembre 2020) 0 tickets
  • chalk_authorization: 0.1.1 (12 Mayo 2021) 0 tickets
  • can_i: 0.1.1 (26 Febrero 2018) 0 tickets
  • meseeks: 0.1.0 (11 Julio 2020) 0 tickets
  • simple_can: 1.0.0 (10 Diciembre 2017) 0 tickets

Hemos tachado los que tienen una versión última lanzada de hace más de 2 años, esto mezclado a que hay versiones 0 las cuales no han alcanzado una estabilidad como para alcanzar la versión 1 nos da a pensar que fueron proyectos abandonados.

De este criterio han pasado solo 5 de los 17 proyectos iniciales, así que tenemos que revisar menos en los siguientes puntos.

Comunidad

Ahora nos basaremos en los datos de popularidad y mantenedores que hay en el proyecto. Es importante que los proyectos sean mantenidos por diferentes personas, esto en empresas se conoce como la métrica de bus hit o cuántas personas de tu empresa pueden ser atropelladas por un autobús sin que signifique el cierre de tu empresa.

En los proyectos de software libre pasa igual. Si solo hay una persona manteniendo el proyecto y por algún motivo esta persona desaparece del proyecto, ¿puede seguir adelante?

Veamos cuánta gente hay manteniendo estos proyectos:

  • acl: 3 contribuidores, 2 estrellas
  • bodyguard: 20 contribuidores, 774 estrellas
  • canary: 13 contribuidores, 477 estrellas
  • let_me: 4 contribuidores, 277 estrellas
  • pundit: 3 contribuidores, 38 estrellas

En principio parece que la mayoría de repositorios pertenecen a una única persona y no hay forma de ver si hay permisos para alguien más, pero dado el bajo número de estrellas y contribuciones, he decidido descartar dos: acl y pundit.

Sigamos al siguiente punto.

Documentación

Es importante encontrar buena documentación dentro del proyecto, esto no solo incluye una explicación de cómo debe ser usado sino también la definición de las funciones, ejemplos y todo de forma comprensible.

Así que veamos la página de documentación de cada uno:

  • bodyguard: tiene buen formato, comienza con un ejemplo rápido e información extensa. Tienen canal de Slack donde preguntar más y cada módulo tiene su documentación.
  • canary: tiene un formato escueto y comienza con un ejemplo detallado. Tiene únicamente dos módulos y solo uno de ellos está documentado.
  • let_me: tiene buen formato y es una documentación muy extensa. Incluso contiene cheat-sheets.

En este punto elimino a canary porque no dispone de una documentación tan cuidada y extensa como las otras dos opciones, hay que recordar que la misión es elegir una opción.

Compatibilidad

En este punto debemos revisar las dependencias propias de cada uno de estos proyectos. Lo que me interesa en este punto son las dependencias de ejecución:

  • bodyguard: requiere plug >= 1.4 && < 2.0.
  • let_me: no tiene dependencias.

En este punto debemos pensar que la primera opción está bien, la dependencia de Plug de mi proyecto es 1.17 y por tanto está dentro, no hay incompatibilidad.

Rendimiento

Me temo que este punto no es tan fácil de medir y requiere una puesta en escena, una prueba de concepto de la implementación para ambas soluciones.

Voy a hacer algo aún más fácil, echar un vistazo al código en sí:

  • bodyguard: utiliza un sistema de callbacks que se resuelven en tiempo de ejecución.
  • let_me: utiliza DSL para fijar las llamadas a las políticas que se resuelve en tiempo de compilación.

Por rendimiento, let_me parece mejor pero realmente no creo que haya un cambio significativo entre uno y otro. Por el momento dejo ambas opciones.

Licencia

La licencia puede afectar, en caso de estar realizando un proyecto privado para tu empresa o startup de forma que tengas que dejar el código accesible. Licencias como AGPL son muy dañinas en este sentido.

No obstante, como son bibliotecas, hay licencias que se pueden emplear sin perjuicio como MIT, APL (Apache) o LGPL, ya que estas no afectan al código que emplea las bibliotecas.

Las librerías de estos proyectos son:

  • bodyguard: MIT
  • let_me: MIT

Nada que objetar, son licencias de: haz lo que quieras y no nos pidas responsabilidades si te sucedió algo.

Seguridad

La seguridad implica muchos factores. Se puede solicitar información acerca de las versiones al gestor de paquetes y si tiene conocimiento de algún CVE entonces determinar si queremos usar esa biblioteca o no.

En principio, no hay CVEs abiertos para estas bibliotecas. Nada oficial que reprochar.

Sobre el aspecto de rendimiento vimos una ligera diferencia entre ambas, mientras que bodyguard resuelve la ejecución de las políticas en tiempo de ejecución, let_me lo hace en tiempo de compilación. Este hecho determina que un fallo escribiendo el nombre del módulo es conocido mientras compilas para let_me mientras que con bodyguard solo es detectado cuando está en ejecución.

Por este motivo:

  • bodyguard: posible fallo detectable en ejecución.
  • let_me: posible fallo detectable en compilación.

Así que tenemos ganador. No obstante, nos falta una última sección.

¿Existen alternativas?

Haciendo una búsqueda sobre cómo implementar ACL puedes encontrar otros mecanismos que definen cómo hacer ACL de formas muy diferentes, por ejemplo, casbin define cómo hacer ACL, RBAC y ABAC a través de ficheros de configuración que son válidos no solo para esta biblioteca de Elixir sino también para Go, Java, C/C++, JavaScript, PHP, Python, C#, Delphi, Rust, Ruby, Swift, Lua y Dart.

En caso de estar haciendo un sistema multi-lenguaje o definir la seguridad de forma externa para distintos elementos del sistema entonces esta alternativa tendría mucho sentido.

De la misma forma si requerimos un sistema de control de acceso basado en roles (RBAC) o control de acceso basado en atributos del sujeto (ABAC) entonces sí necesitaríamos revisar las dependencias de otra forma diferente.

Conclusiones

Pues nada, mi decisión ha sido tomada. Espero estos pasos te hayan servido para tener menos incertidumbre la próxima vez que te toque elegir una dependencia para tu proyecto y cualquier duda, déjame un comentario.

Los comentarios solo están disponibles para los miembros de la comunidad y entrar en la comunidad requiere de haber comprado o adquirido un libro, así que si llegaste hasta aquí y te interesa Elixir, echa un vistazo a esta oferta.

Comentarios

No hay comentarios. Inicia sesión para enviar un comentario.