4

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?

Vishal
  • 7,113
  • 6
  • 31
  • 61

2 Answers2

2

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

chumakoff
  • 6,807
  • 2
  • 23
  • 45
  • We can't use session in model – Vishal May 18 '18 at 11:20
  • Right ) Edited the answer – chumakoff May 18 '18 at 12:17
  • Thank you so much, i already done this before answer, but thanks for your answer, will upvote and accept it. one more things, first we don't need to over ride destroy method of sessions controller and second, we need to include `SessionInfo` module in to model, so we directly access `super_user_mode?` method. can you please update your answer with this change ? and if question is good than you can also upvote my question ;) – Vishal May 18 '18 at 12:56
  • 1) It doesn't make sense to include `SessionInfo` in `User` model. 2) It is not needed to override `destroy` method in your case, but somebody will probably need to do that, so I won't change this. – chumakoff May 18 '18 at 13:26
0

I recently came across a similar issue where I needed to allow an Admin to sign in as regular Users who were not active in Devise. I came up with the following solution that doesn't involve using Thread.current (which after looking into further online it seems like using Thread.current could be a precarious solution to this problem).

You can create a subclass of User called ProxyUser that has the active_for_authentication? return true. Something like this:

class ProxyUser < User
  # If you have a type column on User then uncomment this line below
  # as you dont want to expect ProxyUser to have type 'ProxyUser'
  #
  # self.inheritance_column = :_type_disabled
  
  devise :database_authenticatable
  
   def active_for_authentication?
    true
  end
 end

Then in the controller you want something like this:

proxy_user = ProxyUser.find(params[:user_id])

sign_in :proxy_user, proxy_user

Also in your routes you will need devise to expect ProxyUser so include:

  devise_for :proxy_users

And finally when you sign this user out (assuming you can sign the user out in your controller code) make sure to tell devise the scope of the sign out, so you would do

sign_out :proxy_user

And then finally note that in your app you may be expecting current_user in different places (such as if you use CanCanCan for authorization) and now when you sign in as a proxy_user your app will return current_user as nil. Your app will instead have an object called current_proxy_user that will be your signed-in ProxyUser object. There are many ways to handle the issues resulting from your current_user returning nil in this case (including overwriting current_user in your application controller).