Search code examples
ruby-on-railsdeviseruby-on-rails-5.1

Devise - bypass_sign_in without active_for_authentication? callback


I have functionality of inactive account in my application for handling this i override active_for_authentication? method as below

def active_for_authentication?
  super && activated?
end

But In my application super admin can also directly login in to other user account, whether it is active or not active

bypass_sign_in(User.find(resource.id))

I used above method for by pass sign in, it allows me to directly sign in only for activated user, when i login for non activated user it goes in infinite loop .

Any solutions to over come this issue or don't run active_for_authentication? callback when bypass_sign_in?


Solution

  • When admin logs in to another user account you can store some additional data in session, that makes it clear that this is the super admin mode.

    def login_as(another_user)
      return unless current_user.super_admin?
    
      session[:super_admin_mode] = true
      bypass_sign_in(another_user)
    end
    

    Unfortunately, you can't access session in Rails models, but you can store needed session information in some per-request global variable that is available in models. The solution might be like this:

    module SessionInfo
      def self.super_user_mode?
        !!Thread.current[:super_user_mode]
      end
    
      def self.super_user_mode=(value)
        Thread.current[:super_user_mode] = value
      end
    end
    

    In the ApplicationController:

    class ApplicationController < ActionController::Base
      before_filter :store_session_info
    
      private
    
      def store_session_info
        SessionInfo.super_user_mode = session[:super_admin_mode]
      end
    end
    

    In the model:

    def active_for_authentication?
      super && (activated? || SessionInfo.super_user_mode?)
    end
    

    Also, you should make sure that the :super_admin_mode flag is removed from session when the super user logs out. Maybe it happens automatically, I am not sure. Maybe you will need to do it manually overriding Devise::SessionsController#destroy method (see the example below)

      def destroy
        session[:super_admin_mode] = nil
        super
      end
    

    Also read this for better understanding of how devise handles session Stop Devise from clearing session