Allow Admin To Log On As Another User In A Rails App
In a lot of the apps I build, I continue to assist the users of the app with technical support help.
In some apps it makes sense to build a backend admin interface where we have separate controllers and views and even design. Other apps we may use an open source gem like ActiveAdmin.
However, in some cases, it just helps to be able to see what the user sees. If you've ever tried to help someone pair, troubleshoot a technical issue, setup an email account, etc, you'll know that there's no substitute for being able to share screens and see what they're seeing.
I just recently implemented the ability for an admin to log into a user's account and wanted to share to
- see if anyone thought there was any issue/danger in doing this
- Hopefully help anyone do the same (as long as #1 is ok :-)
So here's how I did it:
So a user with the admin role has a view to see a list of all authors (my user in this case was an author). Next to each author's name is a link to sign in as the author.
The Routes:
resources :impersonates, only: [:create, :destroy]
A controller:
class ImpersonatesController < ApplicationController
load_and_authorize_resource :author, :only => :create
def create
authorize! :impersonate, @author
session[:admin_logged_in] = current_user.id
sign_out current_user
sign_in @author
redirect_to dashboard_author_path(@author), :notice => "Signed in as #{@author.name}"
end
def destroy
if session[:admin_logged_in].present?
if author_signed_in?
sign_out current_author
end
user = User.find(session[:admin_logged_in])
sign_in user
session[:admin_logged_in] = nil
redirect_to user, :notice => "Signed back in as admin"
else
redirect_to root_path
end
end
end
There are 2 basic actions: 1) admin can sign in as another author and 2) the admin can sign back in as admin (without having to reenter user/pass).
Authorization:
As you can see, I'm using cancan to authorize my controller
actions. The main action of concern is the "author" action because that
actually logs a user in as another user. We don't want
that being exploited. We load and authorize the author that we want to
log into. As you can see below, authors only have the ability to manage
themselves. I add an extra layer of security authorize! :impersonate,
@author
that will prevent an author from being able to give themself
the :admin_logged_in
flag. So that should make it impossible for any
non authorized users or guests to exploit the action.
One problem I ran into initially was after I was able to log into an
author's account, I couldn't perform any 'admin' type actions because I
was actually logged in as the author. To get around this, I passed in
my session to my ability class and added the ability to :admin
all
actions if the :admin_logged_in
session has was set. This allowed me
to do this in the views: if can?(:admin, @author) ...
# app/controllers/application_controller.rb
# you need to override the current_ability method to pass in the session
def current_ability
@current_ability ||= Ability.new(user, session)
end
# app/models/ability.rb
def initialize(user, session)
user ||= User.new(:role => 'guest') # guest user (not logged in)
admin_logged_in = session[:admin_logged_in]
....
## Authors
can :manage, Author, :id => user.id
cannot [:index, :impersonate], Author
if admin_logged_in.present?
can :admin, :all
end
...
end
The views:
# the admin's list of authors
...
=link_to "Sign in as this author", impersonates_path(author_id: author.id), :method => :post
# the ability for the admin to sign back in as admin without having to
reenter credintials
...
- if session[:admin_logged_in].present?
=link_to "Sign in as admin", impersonate_path("revert"), method: :delete
Tests
What good is code with tests? To keep the article a concise as possible, I left them out, but posted them here for your reference:
So that's pretty much it. Now the admin can sign in as an author, view
the app as the author, even perform some admin actions and then sign back in as the admin.
What do you think?
UPDATE:
I really appreciate Andy Lindeman and Jonathan Wallace reviewing my article and making some great suggestions: