Pregunta

He colocado todo mi código de autenticación de usuario en un lugar, a saber, lib / auth.rb. Se parece a esto:

lib/auth.rb

module Admin

  def do_i_have_permission_to?(permission)
    # Code to check all of this goes here
  end

end

Incluyo este módulo como parte de la aplicación auxiliar, por lo que estas funciones están disponibles en todas las vistas:

application_helper.rb

require 'auth'
module ApplicationHelper
  include Admin
  # other stuff here
end

Y también lo incluyo como parte del controlador de la aplicación, por lo que los controladores también pueden llamar a las funciones:

application.rb

require 'auth'
class ApplicationController < ActionController::Base
  include Admin
end

Hasta ahora, todo bien.

El problema es que mi aplicación no es como una aplicación web normal. Específicamente, más de un usuario puede iniciar sesión en el sistema desde la misma computadora al mismo tiempo (usando el mismo navegador). Realizo la autenticación de las acciones observando a todas las personas que han iniciado sesión desde esa IP y, si todas pueden hacerlo, pasa.

Lo que esto significa es que, si un administrador quiere hacer algo, ese administrador tiene que cerrar la sesión de todos los demás primero, lo que es molesto. Pero queremos el sello de aprobación de administrador en todo lo que hace el administrador. Por lo tanto, la sugerencia que me dieron fue que el administrador pueda proporcionar un combo de nombre de usuario / contraseña en cualquier página a la que normalmente no tendrían acceso (por ejemplo, una página 'editar usuario' tendría estos campos de entrada adicionales) y las rutinas de autenticación chequea eso Esto significa

Admin::do_i_have_permission_to?(permission)

necesita llegar a los parámetros de solicitud actuales. No puedo usar params [: foo] como lo haría en un controlador, porque params no está definido; de manera similar, request.parameters [: foo] tampoco funcionará. Mi búsqueda ha revelado:

  • Los parámetros de búsqueda actuales están en la solicitud actual,
  • La solicitud actual está en el controlador actual,
  • El controlador actual está en el distribuidor actual, y
  • No estoy seguro de que el despachador actual se mantenga en cualquier lugar.

Dicho esto, la experiencia me dice que cuando estoy saltando a través de tantos aros, es muy probable que lo esté haciendo mal. Entonces, ¿cuál es la forma correcta de hacerlo? Las opciones que he considerado son:

  • Simplemente mueva todas las funciones actualmente en auth.rb a ApplicationHelper donde (creo) tendrán acceso a la solicitud y demás. Funciona, pero desordena el ayudante.
  • Mueva todas las funciones a otro lugar donde verán esos métodos (no sé dónde)
  • Simplemente me estoy perdiendo algo.
¿Fue útil?

Solución

En una aplicación Rails típica, la información de autenticación se almacena en la sesión activa, no en los parámetros. Como tal, es bastante sencillo escribir un ayudante que haga lo que usted quiere.

Parece bastante poco ortodoxo crear un módulo que luego se incluye en ApplicationHelper. El enfoque tradicional es crear un ayudante por separado que, en este caso, probablemente se llamaría AutenticaciónAyuda. Esto puede incluirse en cualquier controlador requerido, o si lo prefiere, cargarlo en ApplicationController para que esté disponible universalmente.

En términos generales, los Ayudantes no deben incluir a otros Ayudantes. Es mejor simplemente cargar varios ayudantes en un controlador dado.

Los métodos de ayuda tienen acceso completo a cualquier variable de instancia declarada dentro del contexto del controlador desde el que están operando. Para ser específicos, estas son solo variables de instancia (@nombre) y no variables locales (nombre). Los métodos de ayuda también se ejecutan para una vista particular.

Además, no estoy seguro de por qué un usuario estaría proporcionando credenciales y realizando una operación en el mismo paso, al menos para las aplicaciones tradicionales basadas en la web. Por lo general, el proceso es iniciar sesión y luego realizar una acción por separado.

Sin embargo, en el caso de una API en la que cada transacción es una operación independiente, el enfoque más sencillo que se debe hacer es extraer los parámetros de solicitud relevantes que se ocupan de la autenticación, establecer algunas variables de instancia del controlador y luego proceder a realizar la Solicitud particular dadas las restricciones que imponen las credenciales.

El enfoque que suelo seguir para este tipo de cosas es colocar una estructura de autenticación en el propio ApplicationController que puede realizar las comprobaciones necesarias. Estos son métodos protegidos.

¿Es tentador rodar en un montón de ellos como can_edit_user? y can_create_group? Estos muy rápidamente se salen de las manos. ¿Es un diseño más simple de colocar en un gancho para un can_perform de propósito general? o has_authority_to? Método que se pasa una operación y cualquier parámetro requerido.

Por ejemplo, una implementación muy aproximada:

  class ApplicationController < ActionController::Base
  protected
    def has_authority_to?(operation, conditions = { })
      AuthenticationCheck.send(operation, conditions)
    rescue
      false
    end
  end

  module AuthenticationCheck
    def self.edit_user?(conditions)
      session_user == conditions[:user]
    end
  end

  class UserController
    # ...

    def edit
      @user = User.find(params[:id])

      unless (has_authority_to?(:edit_user, :user => @user))
        render(:partial => 'common/access_denied', :status => :forbidden)
      end
    rescue ActiveRecord::RecordNotFound
      render(:partial => 'users/not_found')
    end
  end

Obviamente, querría pasar muchas de las comprobaciones de autoridad en los bloques before_filter para evitar repeticiones y promover la coherencia.

Un ejemplo de marco completo podría ser de mayor ayuda, como el sistema de autenticación de usuario de Wristband:

http://github.com/theworkinggroup/wristband/tree/master

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top