Omar Montoya
Erick Villatoro
Juan Alberto Solís Castro
¿Cómo establecer reglas de acceso y permisos en una aplicación Rails?
En el desarrollo de aplicaciones web, no solo es necesario crear funcionalidades, sino también asegurar que cada usuario tenga acceso a las acciones y datos que le correspondan. En este contexto, surge la pregunta: ¿cómo evitar que un usuario modifique o acceda a recursos que no deberían ser de su dominio? Para ello, la gema KanKan ofrece una solución eficaz en el manejo de permisos y accesos personalizados en aplicaciones Rails. Este artículo te guiará en el proceso de implementación con esta herramienta.
¿Cómo integrar la gema KanKan en tu proyecto Rails?
Para empezar, es crucial integrar la gema KanKan en el archivo Gemfile. La siguiente línea debe añadirse debajo de la gema Cocoon:
gem 'cancancan'
Una vez añadida, es importante tener en cuenta que ciertas versiones de Rails y Ruby pueden generar conflictos con la configuración, concretamente con el precargador de la aplicación llamado Spring. Para evitar problemas durante el desarrollo, se sugiere comentar Spring, especialmente si no estás en un entorno de producción:
# gem 'spring'
Después de realizar estos cambios, ejecuta la instalación de la gema:
bundle install
¿Cómo definir reglas de acceso con KanKan?
Después de la instalación, KanKan ofrece un generador de Rails que crea un archivo de Ability. Este archivo es el corazón de las reglas de acceso en la aplicación:
rails generate cancan:ability
Dentro del archivo ability.rb, se definen las reglas de acceso. Por ejemplo, si queremos permitir que los usuarios solo gestionen sus propias tareas, lo configuramos así:
can :manage, Task, owner_id: user.id
¿Cómo se integra KanKan con los controladores?
Para asegurar que las reglas definidas se apliquen, las acciones del controlador deben validarse con el método load_and_authorize_resource. Por ejemplo, en el controlador de tareas, se integraría de la siguiente forma:
class TasksController < ApplicationController load_and_authorize_resource # resto del código del controlador end
¿Qué hacer con las excepciones de acceso denegado?
KanKan arroja excepciones AccessDenied cuando un usuario intenta ejecutar una acción para la cual no tiene permiso. Podríamos manejar esas excepciones utilizando el ApplicationController, de modo que redirija a un lugar seguro en vez de mostrar un error:
class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied do |exception| redirect_to root_path, alert: exception.message end end
¿Cómo restringir la visualización de listas de recursos?
Para reducir la lista de tareas visibles solo a aquellas que el usuario tiene permiso de ver o gestionar, podemos modificar el método index del controlador:
def index @tasks = Task.joins(:participants) .where('tasks.owner_id = ? OR participants.user_id = ?', current_user.id, current_user.id) .group('tasks.id') end
Utilizando métodos de ActiveRecord como joins combinados con condiciones específicas, aseguramos que solo se muestren las tareas relevantes.
¿Dónde encontrar más información sobre KanKan?
Para profundizar en más funcionalidades de KanKan, ofrece su documentación en GitHub, donde podrás explorar ejemplos y buenas prácticas en la definición de roles y permisos.
Implementar acceso controlado mejora no solo la seguridad, sino también la experiencia del usuario en tu aplicación. La utilización de gemas como KanKan simplifica este proceso, permitiéndote focalizar en lo realmente importante: el desarrollo de las características propias de tu aplicación.
Omar Montoya
Erick Villatoro
Juan Alberto Solís Castro
Oscar Julio Silvera Cruz
Jesus Junior Campos Vargas
Jesús Abraham Raya Gómez
Agustina Corvo
Jose Ramos
Diego Alexander Gomez Chavarria
José Gersain Osorio Morales
Héctor Manuel Rodríguez Castillo
Johan Tique
ProfesorEsteban Gómez Fernández
Johan Tique
ProfesorDaniel Morán
Johan Tique
ProfesorSebastian Kloster
Cancan es una gema que dejo de ser mantenido desde el 2013 por lo que podría en algún momento ocasionar inconvenientes con nuestro proyecto.
Pero no hay problema existe una gema que se llama cancancan y funciona igual que cancan .
Perdona, según veo el video la gema que se instala es cancancan. Sabes si sigue mantenida?
Sí, aún la mantienen, de hecho, la última actualización es de marzo 05 de 2023.
Para los que encuentran el siguiente error:
método indefinido expand_hash_conditions_for_aggregates 'para # <Clase.....
++- > El origen del error:++
Este error se origina cuando agregamos al abillity la siguiente linea: can :read, Task, participating_users: { user_id: user.id }
++-> La razón:++
investigando el error surge por un comportamiento de la gema cancancan el cual solo le permite trabajar con nombres de columnas de la misma tabla(intuyo que deben ser nativas las columnas). por lo tanto la asignación del ide del participante no lo acepta por asignación, asi que lo recomendado es trabajar con un bloque de código.
++-> La fuente de la solución++
el ultimo post explica tanto la razón del error como una posible solución de bloque.
++--->La solución al ejemplo de la clase++<---
Por lo tanto el abillity lo refactorice de la siguiente manera: class Ability include CanCan::Ability def initialize(user) # * si el usuario es responsible del post puede verlo, editarlo y borrarlo can :manage, Task, owner_id: user.id # * si el usuario es follower del post puede verlo, editarlo y borrarlo can :read, Task do |t| participants = t.participant_ids user = user.id participants.map { |id_participant| user == id_participant } end end end
Esto permite que los followers puedan solo ver el show del task y que solo el propietario pueda editarlos y eliminarlos.
Tengo un error cuando quiero eliminar una tarea:
ActiveRecord::InvalidForeignKey in TasksController#destroy PG::ForeignKeyViolation: ERROR: update o delete en «tasks» viola la llave foránea «fk_rails_bae2b13134» en la tabla «participants» DETAIL: La llave (id)=(1) todavía es referida desde la tabla «participants».
Hola, pudiste resolver ese error?
Si agregas dependent: destroy en la asociación participants en el modelo Task, se soluciona ese error.
La linea queda de la siguiente manera:
has_many :participants, through: :participating_users, source: :user, dependent: :destroy
Rails Guides
Tenia un error para sign_out. Me decía que no encontraba un get con sign_out (cuando debería de usar un delete como se le especifica). Tuve que agregar lo siguiente en routes:
Rails.application.routes.draw do devise_for :users devise_scope :user do get '/users/sign_out' => 'devise/sessions#destroy' end resources :tasks resources :categories root 'tasks#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
Buenos dias, al entrar a index me arrioja el siguiente error
ActionView::Template::Error (PG::UndefinedTable: ERROR: falta una entrada para la tabla «user» en la cláusula FROM LINE 1: ...= "participants"."user_id" WHERE (owner_id = 1 OR participan... ^ ): 12: %th 13: 14: %tbody 15: - @tasks.each do |task| 16: %tr 17: %td= task.name 18: %td= task.description app/views/tasks/index.html.haml:15
Alguien sabe la posible solucion? Gracias
¿Qué diferencia o ventajas tiene utilizar CanCan en vez de Pundit para manejar la autorización
Me marco el siguiente
método indefinido `expand_hash_conditions_for_aggregates 'para # <Clase: 0x000055f221282828>
cuando agregue el
can :read, Task, participating_users: { user_id: user.id }
Hola Hector, este método que no está encontrando hace parte de ActiveRecord y se usa para poder ayudar a construir consultas SQL mediante agregaciones (algo que en detalle no es necesario comprender), el problema entonces es porqué a pesar de Task ser un modelo no tenemos asociado ese metodo en sus relaciones... para ver cómo resolverlo, primero debemos ir a la definición de las relaciones en cada modelo asociado y en su esquema de base de datos, muestrame por favor el modelo Task el modelo Participant y la definición de sus esquemas en el schema.rb
Buenas, tengo un problema con esto NoMethodError in TasksController#index undefined method `klass' for nil:NilClass
Hola Steferenant, es algo extraño el error, pero podría estar asociado a las relaciones has many through de los modelos Task y User, trata de revisar si hay algún typo (error de digitación) en la definición de esas relaciones, especialmente en la opción que va justo después de la key :through en la definición del has_many, debes asegurarte de que sea igual al definición de la relación simple es decir: participating_users o participations
Tuve problemas iniciando cancan, es posible que tengas que hacer db:reset antes de iniciar el ability.rb de CanCan
Hola Daniel, pero a pesar del problema, pudiste seguir adelante?
Hola daniel, en mi caso no tuve que hacer db:reset antes de generar el abilty.rb Saludos