He usado gemas de autorización declarativa como cancan y cancancan en el pasado para controlar el acceso a los datos. Sin embargo, en esta nueva aplicación estoy tratando de controlar el acceso a las funciones reales.

Es su modelo SaaS típico, donde progresivamente hay más funciones disponibles según el nivel al que se haya suscrito.

La mayoría de las funciones están disponibles haciendo clic en diferentes íconos o elementos de menú en toda la aplicación.

Escribo para preguntar si hay un patrón de implementación bien conocido para esto que no conozco antes de lanzar el mío.

Estos son los diferentes tipos de cosas que se limitarán según el nivel de suscripción:

  1. Funciones accesibles por icono
  2. Funciones accesibles por elemento de menú
  3. Ciertos informes (cada uno de los cuales tiene una definición de informe que lo define).
  4. Ciertas cargas (cada una de las cuales tiene un FileType que lo define).
  5. Ciertos BackgroundProcesses (cada uno de los cuales tiene un ProcessType que lo define).

Cada suscripción tiene un plan. Es lo suficientemente simple como para vincular ese Plan en los elementos 3, 4 y 5 anteriores y utilizar cancancan para accesibilidad por current_user. Sin embargo, las características 1 y 2 son una historia diferente. Puedo ver envolviendo su accesibilidad en una vista auxiliar que verifica una lista de Características / Plan. Pero eso solo maneja la vista. Si el usuario conoce la URL, aún podrá acceder a la función escribiendo la URL directamente. En ese caso, ¿tengo que manejar la autorización nuevamente en el nivel de acción del controlador, o hay algún tipo de middleware que pueda poner para limitar la accesibilidad a las funciones?

Muchas gracias.

3
AKWF 18 ene. 2018 a las 00:42

3 respuestas

La mejor respuesta

Si es una aplicación simple, simplemente agrego una columna admin en User. Si hay más de 2 tipos de usuarios (admin / non-admin / author / editor / etc), lo convertiría en un campo Enum en lugar de boolean.

Luego, dentro de user.rb agrego varios métodos ...

def is_admin?
  admin?
end

def is_author?
  !admin?
end

A partir de ahí, también agrego un código en application_controller.rb que genera una excepción:

def unauthorized
  head(:unauthorized)
end

def unprocessable
  head(:unprocessable_entity)
end

También agrego un método current_user en application_controller.rb:

helper_method :current_user

def current_user
  @user ||= User.find(session[:user_id])
end

Luego, desde mi punto de vista, ahí es donde manejo cosas "ocultas" o deshabilitando botones.

<%= if current_user.is_admin? %>
  <button>Admin button</button>
<% else %>
  <button>Author button</button>
<% end %>

Por supuesto, esto NO es seguridad (solo cambia la capa de vista), por lo que también regreso temprano de los controladores usando los métodos anteriores que expuse:

def destroy
  return unauthorized unless current_user.is_admin?
  # ... delete code here
end

Para el ejemplo anterior, no olvide usar return o el código seguirá ejecutándose.

Para cosas que son más que simples, uso Pundit.

2
Tallboy 18 ene. 2018 a las 02:29

https://github.com/jnunemaker/flipper es una buena solución y hace exactamente lo que está buscando para.

De lo contrario, como dijiste, cancancan es bueno para una solución ingenua.

0
Josh Brody 17 ene. 2018 a las 23:04

Simplemente rodaría el mío usando enum para establecer diferentes niveles de acceso o suscripción. Luego solo escriba un before_action llamado can_access? para bloquear acciones enteras. Luego, establecería algunos condicionales o view_helpers en la vista para bloquear el acceso a ciertos elementos de la interfaz de usuario.

1
Verty00 17 ene. 2018 a las 23:22
48310464