user_signed_in rails ruby-on-rails devise

ruby-on-rails - user_signed_in - rails admin



Diseñar: realice una solicitud sin restablecer la cuenta regresiva hasta que un usuario cierre la sesión debido a inactividad (4)

Estoy trabajando en una aplicación RoR con Devise. Quiero permitir que los clientes envíen una solicitud al servidor para ver cuánto tiempo queda hasta que el usuario en el cliente se desconecte automáticamente debido a la inactividad (usando el módulo de tiempo de Timeoutable ). No quiero que esta solicitud provoque que Devise restablezca la cuenta regresiva hasta que el usuario cierre la sesión. ¿Cómo puedo configurar esto?

Este es el código que tengo ahora:

class SessionTimeoutController < ApplicationController before_filter :authenticate_user! # Calculates the number of seconds until the user is # automatically logged out due to inactivity. Unlike most # requests, it should not reset the timeout countdown itself. def check_time_until_logout @time_left = current_user.timeout_in end # Determines whether the user has been logged out due to # inactivity or not. Unlike most requests, it should not reset the # timeout countdown itself. def has_user_timed_out @has_timed_out = current_user.timedout? (Time.now) end # Resets the clock used to determine whether to log the user out # due to inactivity. def reset_user_clock # Receiving an arbitrary request from a client automatically # resets the Devise Timeoutable timer. head :ok end end

SessionTimeoutController#reset_user_clock funciona porque cada vez que RoR recibe una solicitud de un usuario autenticado, Timeoutable#timeout_in se restablece a cualquier cosa que haya configurado en Devise#timeout_in . ¿Cómo puedo evitar que se reinicie en check_time_until_logout y has_user_timed_out ?


Cuando veo esto correctamente (probablemente dependiendo de la versión del Timeoutable el Timeoutable maneja el cierre de sesión a través del módulo Timeoutable .

esto está conectado a Warden, que forma parte de la pila de middleware del rack.

Si miras el código, entonces puedes encontrar esta parte:

unless warden.request.env[''devise.skip_trackable''] warden.session(scope)[''last_request_at''] = Time.now.utc end

así que por lo que puedo ver aquí, deberías poder configurar devise.skip_trackable en true antes de que se devise.skip_trackable el middleware de devise.skip_trackable .

Creo que este problema aquí explica cómo usarlo realmente: https://github.com/plataformatec/devise/issues/953


Las dos respuestas principales de anteponer un request.env["devise.skip_trackable"] = true acción request.env["devise.skip_trackable"] = true no funcionó para mí inicialmente.

¡Parece que esto solo funciona si está utilizando el authenticate_user! "clásico" authenticate_user! Antes de la acción, y no cuando se utilizan rutas autenticadas. Si está utilizando rutas autenticadas en su config/routes.rb , como por ejemplo:

authenticate :user do resources :some_resources end

La suspensión de la acción no parece afectar a la cadena de Devise a tiempo, por lo que debe comentar el bloqueo de authenticate , utilice un identificador_usuario authenticate_user! antes de la acción en su app/controllers/application_controller.rb , y agregue una acción skip_trackable que establezca skip_trackable en true en un controlador específico.


Su código para el tiempo restante es incorrecto. El cálculo correcto será:

def check_time_until_logout @time_left = Devise.timeout_in.seconds - (Time.now.to_i - user_session["last_request_at"]).round end


Terminé teniendo que hacer varios cambios en mi código. Mostraré con qué terminé cuando terminé, luego explicaré lo que hace:

class SessionTimeoutController < ApplicationController # These are what prevent check_time_until_logout and # reset_user_clock from resetting users'' Timeoutable # Devise "timers" prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end skip_before_filter :authenticate_user!, only: [:has_user_timed_out] def check_time_until_logout @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round end def has_user_timed_out @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) end def reset_user_clock # Receiving an arbitrary request from a client automatically # resets the Devise Timeoutable timer. head :ok end end

Estos son los cambios que hice:

Usando env["devise.skip_trackable"]

Este es el código que evita que Devise restaure el tiempo que espera antes de cerrar la sesión de un usuario debido a la inactividad:

prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end

Este código cambia un valor de hash que Devise utiliza internamente para decidir si actualizar el valor que almacena para realizar un seguimiento de cuándo tuvo la última actividad el usuario. Específicamente, este es el código de dispositivo con el que estamos interactuando ( link ):

Warden::Manager.after_set_user do |record, warden, options| scope = options[:scope] env = warden.request.env if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false last_request_at = warden.session(scope)[''last_request_at''] if record.timedout?(last_request_at) && !env[''devise.skip_timeout''] warden.logout(scope) if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout record.reset_authentication_token! end throw :warden, :scope => scope, :message => :timeout end unless env[''devise.skip_trackable''] warden.session(scope)[''last_request_at''] = Time.now.utc end end end

(Tenga en cuenta que este código se ejecuta cada vez que Rails procesa una solicitud de un cliente).

Estas líneas cerca del final son interesantes para nosotros:

unless env[''devise.skip_trackable''] warden.session(scope)[''last_request_at''] = Time.now.utc end

Este es el código que "reinicia" la cuenta regresiva hasta que el usuario cierre la sesión debido a inactividad. Solo se ejecuta si env[''devise.skip_trackable''] no es true , por lo que necesitamos cambiar ese valor antes de que Devise procese la solicitud del usuario.

Para hacer eso, le decimos a Rails que cambie el valor de env[''devise.skip_trackable''] antes de que haga algo más. De nuevo, desde mi código final:

prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] def skip_timeout request.env["devise.skip_trackable"] = true end

Todo lo que está por encima de este punto es lo que necesitaba cambiar para responder mi pregunta. Sin embargo, había otros cambios que necesitaba hacer para que mi código funcionara como quería, así que también los documentaré aquí.

Usando Timeoutable correctamente

He leído mal la documentación sobre el módulo Timeoutable , por lo que mi código en mi pregunta tiene otros problemas.

Primero, mi método check_time_until_logout iba a devolver siempre el mismo valor. Esta es la versión incorrecta de la acción que tuve:

def check_time_until_logout @time_left = current_user.timeout_in end

Pensé que Timeoutable#timeout_in devolvería la cantidad de tiempo hasta que el usuario se desconectara automáticamente. En su lugar, devuelve la cantidad de tiempo que Devise está configurado para esperar antes de cerrar la sesión de los usuarios. Necesitamos calcular cuánto tiempo más nos ha dejado el usuario.

Para calcular esto, necesitamos saber cuándo fue la última vez que el usuario tuvo una actividad que Devise reconoció. Este código, de la fuente de Devise que vimos anteriormente, determina cuándo fue la última vez que el usuario estuvo activo:

last_request_at = warden.session(scope)[''last_request_at'']

Necesitamos obtener un identificador para el objeto devuelto por warden.session(scope) . Parece que el hash de user_session , que Devise nos proporciona como los identificadores current_user y user_signed_in? , es ese objeto.

Usando el hash user_session y calculando el tiempo restante, el método check_time_until_logout se convierte en

def check_time_until_logout @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round end

¿También Timeoutable#timedout? mal la documentación de Timeoutable#timedout? . Comprueba si el usuario ha agotado el tiempo de espera cuando pasa la última vez que estuvo activo, no cuando pasa la hora actual. El cambio que debemos hacer es sencillo: en lugar de pasar Time.now , necesitamos pasar el tiempo en el hash user_session :

def has_user_timed_out @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) end

Una vez que hice estos tres cambios, mi controlador actuó como yo esperaba.