Saltar al contenido

Autorización simple en aplicaciones Ruby on Rails

En Elabs, hemos estado usando CanCan para la autorización en varias aplicaciones. Ryan Bates logró construir un sistema de autorización que es a la vez simple y poderoso. Un paso más allá del inflado sistema basado en roles disponible en ese momento, pero más sofisticado que simplemente agregar métodos en los modelos ActiveRecord.

Sin embargo, con el tiempo nos hemos enfrentado a algunas quejas con CanCan.

Y finalmente: en el momento de escribir este artículo, CanCan tiene 128 problemas abiertos, 28 solicitudes de extracción abiertas. La funcionalidad importante de la gema está rota y los intentos de solucionarlo mediante solicitudes de extracción se ignoran. El conjunto de pruebas depende de ActiveRecord

En un proyecto reciente en el que trabajamos, estábamos luchando contra errores en CanCan que nos obligaron a ejecutar una versión bifurcada, y estábamos luchando contra un archivo de habilidades que crecía fuera de control. Decidimos que necesitábamos una nueva forma de abordar el problema.

Volver a lo básico

Realmente nos gusta el enfoque simple de CanCan. El archivo de capacidad aísla toda la lógica de autorización y le deja libertad para manejar la autorización como desee. Puede hacer crecer su sistema de autorización desde un único rol de usuario hasta la complejidad que necesite. Estábamos decididos a mantener esta flexibilidad.

Sin embargo, queríamos algo más simple. Algo que podemos implementar sin necesidad de una biblioteca. Queríamos tener un control total sobre cómo funciona el sistema de autorización.

Nos inspiramos en objectify y en la excelente publicación de blog de Bryan Helmkamp 7 Patterns to Refactor Fat ActiveRecord Models, entre otros, y reducimos todo a la creación de una clase Ruby simple para cada modelo de dominio.

Llamamos a estas clases políticas y las ponemos en app/policies. Pueden verse así:

Usar estas clases desde el controlador es bastante fácil:

Esto funciona bastante bien, pero desafortunadamente es mucho más código para escribir en el controlador. Los controladores no están lo suficientemente SECOS como están; tenemos que hacer esto más fácil. Es bastante simple introducir un método auxiliar para obtener una política para un registro determinado:

Ahora podemos simplificar un poco nuestro método de creación.

Podemos envolver fácilmente este patrón en otro método:

Y terminamos con esto:

Eso se parece mucho más a lo que tenemos en CanCan.

Este patrón funciona bien para 6 de las 7 acciones de descanso, pero ¿qué pasa con #index?

CanCan puede construir automáticamente una consulta basada en los permisos que ha especificado, siempre que se utilice la sintaxis basada en hash, de todos modos. Desafortunadamente, hemos descubierto que esta magia es propensa a errores y, a veces, insuficiente. Realmente queremos usar ámbitos para esto, pero no queremos que contaminen los objetos de nuestro modelo. Nuevamente, inspirándonos en la publicación del blog de Bryan, creamos una clase para esto:

El uso para esto desde la acción de índice también es bastante fácil:

Nuevamente, podemos simplificar esto con un método auxiliar:

Ambos policy y policy_scope Los métodos son especialmente útiles en las vistas. Podemos hacer cosas como esta:

Nuestras vistas se mantienen bastante agradables y SECAS, al igual que con CanCan.

Hemos reunido a estos ayudantes en una joya muy simple que llamamos Pundit. Tiene algunos trucos más bajo la manga, pero básicamente hace exactamente lo que describe esta publicación. Descubrimos que podíamos reemplazar CanCan con este patrón de manera muy efectiva. El código resultante es más simple, más fácil de comprender y de probar.

Si bien creemos que Pundit nos ha sido útil, hay una gran conclusión de esto. Tuvimos un problema y lanzamos una biblioteca grande con un DSL complicado al problema y, como dice el viejo refrán, ahora teníamos dos problemas. A veces, la solución más simple es mejor. A veces tiene sentido aprovechar Ruby en lugar de crear su propio mini lenguaje.

Dedique algún tiempo a reconsiderar las dependencias que tiene en su aplicación y si realmente lo están ayudando, o si pasa más tiempo combatiéndolas de lo que está saliendo.

Este contenido se publicó originalmente aquí.