Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use "switch back" feature with links #55

Open
davidstosik opened this issue Jan 29, 2014 · 13 comments
Open

Use "switch back" feature with links #55

davidstosik opened this issue Jan 29, 2014 · 13 comments

Comments

@davidstosik
Copy link

Hello,

I don't want to use the switch_user_select (because we have too many users, and it does not fit the way we're doing things). Instead, I'm generating a switch user link, as described in the README:

= link_to "Sign in as #{user.login}", "/switch_user?scope_identifier=user_#{user.id}"

How can I enable the switch back feature, and allow a user to switch back to their previous login, if they used the link above to switch, in the first place?

Is there something I could do to save the original user, and then check if present, to allow switch back?

Thanks for your help,

David

@lcowell
Copy link
Collaborator

lcowell commented Mar 4, 2014

Hi David, you've got a couple of choices.

  1. Adjust which users are showing up in the list by configuring SwitchUser.available_users
  2. Use the switch back feature by sending http requests yourself.

If you want to use 2. The switch back feature is use by:

  1. Telling the server you want to remember the current user by sending an http request to /switch_user/remember_user?remember=1
  2. Now SwitchUserController#available? will consider this remembered user as well as the current user. That's how we know whether to let you continue to use switch user even if devise (or whatever your current authentication provider is) thinks you're a user which might not normally have access to switch user (eg a customer account).

@ghost
Copy link

ghost commented Mar 21, 2014

Is it correct that the only way to remember the original user is to make a distinct request ahead of switching?

My use case is fairly straightforward in that I want to facilitate admins signing in as other users and then back again. Our user database has a few tens of thousands of records, so a dropdown doesn't work, and instead I am using a small form with a text field with user ID. However since I need to send a request to #remember_user first, this form in itself is inadequate.

I've tried hacking around this. Ideally, I could switch users and remember the original user in a single request, e.g. /switch_user?scope_identifier=user_1234&remember=true. In some ways I feel that the remember=true is overly explicit. I know that there are security concerns here, but respectfully the idea that I have to sign out to resume my original user is counter-intuitive to me. If there is rationale that I am overlooking for why this approach would be inadvisable, please correct me :)

@lcowell
Copy link
Collaborator

lcowell commented Mar 22, 2014

I think what you might be missing is how we know whether to let you switch back or not. If you switch to a customer all I would know, without remembering that you were an admin before, is that you're a customer. We don't show customers the switch_user select box, so you're now locked out.

@ghost
Copy link

ghost commented Mar 24, 2014

I still don't understand why we can't (or wouldn't want to) remember the original user and switch users in a single request.

@lcowell
Copy link
Collaborator

lcowell commented Mar 24, 2014

You definitely could, I didn't realize that's what you were asking. The feature was added with a little haste, but I'm sure it could be refactored.

@ghost
Copy link

ghost commented Mar 24, 2014

Given my longwinded-ness, I probably didn't realize it was what I was asking either, heh. I just wanted to know if there was a security concern that prevented such an implementation. I will look into making such adjustments.

@ghost
Copy link

ghost commented Mar 30, 2014

I stumbled across the same issue and couldn't understand why there wasn't a single entry point remembering and switching. I already had a case where I wanted to pass the redirect path via the url so my redirect path already looked like this:

  config.redirect_path = lambda { |request, params| params[:path].presence || request.referrer || '/' }

My link to become a user looks like this (in HAML):

  - next_path = ERB::Util.url_encode("/switch_user?scope_identifier=user_#{@user.id}&path=/")
    = link_to "Login as #{@user.full_name}", "/switch_user/remember_user?remember=true&path=#{next_path}"

The return path looks like this

-  original_user = user_signed_in? && SwitchUser::Provider.init(controller).original_user
- if original_user
  - next_path = ERB::Util.url_encode "/switch_user/remember_user?remember=false&path=#{ops_user_path(current_user.id)}"
  = link_to "Back to #{original_user.simple_name}", "/switch_user?scope_identifier=user_#{original_user.id}&path=#{next_path}"

Maybe this will help. Seems to work for me. Still wouldn't mind a more direct way of doing this.

@kevinvangelder
Copy link

I agree with the others that a switch-back feature would be ideal. I don't care about the user being able to switch to a third user, just switching back to their original admin account.

I tried implementing Helloenvoy's recommendation but ran into issues because I have a wildcard route that interferes with manually linking to the "/switch_user/remember_user" path. I could probably dig into the source code to figure out what controller action handles that and manually add a route, but for now I'll just force the admin to sign out and back in again.

@jhenkens
Copy link
Contributor

jhenkens commented Nov 1, 2014

I added a patch that allows one to disable the default routes. I then just extended the default controller to handle a single request for remembering and switching. It seems to work. There's probably more elegant ways to get it done, but it works for me.

_widget.html.erb

<u>Login as another user</u>
<%= form_tag users_switch_user_path, method: :get, class: 'form' do %>
    <div class="form-group">
      <%= label_tag 'scope_identifier', 'User', class: 'sr-only' %>
      <%= select_tag 'scope_identifier', options_from_collection_for_select(options, :scope_id, :label, current_scope), class: 'form-control' %>
    </div>
    <% if SwitchUser.switch_back %>
        <div class="checkbox">
          <%= label_tag 'remember' do %>
              <% concat check_box_tag 'remember', 'true', provider.original_user.present? %>
              <% concat 'Log me back in when I am done' %>
          <% end %>
        </div>
    <% end %>
    <button type="submit" class="btn btn-default">Switch to User</button>
<% end %>
users/switch_user_controller.rb

class Users::SwitchUserController < SwitchUserController

  def set_current_user
    if params[:remember] == 'true'
      remember_user
      handle_request(params)
    else
      super
    end
  end

end

Lastly, to support the notion of 'switch back', I just overrode devise's sessions controller to sign in the original user upon destroy.

users/sessions.rb

class Users::SessionsController < Devise::SessionsController
  def destroy
    su_provider = SwitchUser::Provider.init(self)
    original_user = su_provider.original_user
    super
    if original_user
      sign_in original_user
    end
  end

end

@wkrsz
Copy link

wkrsz commented Nov 8, 2014

@jhenkens can you submit a pull request?

@jhenkens
Copy link
Contributor

jhenkens commented Nov 8, 2014

@WojtekKruszewski The ability to disable the routes (and thus, allow you to definite your own routes, replacing the controller) was pulled in via PR with 6759686. Everything else I did that I detailed above was personal changes within the directory of my own application, not within the gem itself.

@wkrsz
Copy link

wkrsz commented Nov 9, 2014

Actually, only now I realized warden.set_user(new_user, :scope => :user) in the controller is all it takes to switch user. I removed switch_user gem and added some custom code for my existing admin infrastructure. 50 lines, including a big red button reminding that you're logged in as someone else and allowing switching back. Still, switch_user gem saved me some time and distraction a year ago when I started the project, so thank you @lcowell.

@DavidMMelin
Copy link

This issue brought me here and I got inspired by @WojtekKruszewski and ended up writing my own to work with devise. Here is my controller

class MasqueradeController < ApplicationController
  authorize_resource class: false
  before_filter :authorize_masquerade!

  def start
    session[:original_user_id] = current_user.id
    sign_in(:user, User.find(params[:user_id]))
    redirect_to root_url
  end

  def stop
    sign_in(:user, User.find(session[:original_user_id]))
    session.delete(:original_user_id)
    redirect_to root_url
  end

  private

  def authorize_masquerade!
    user = session[:original_user_id] ? User.find_by(id: session[:original_user_id]) : current_user
    redirect_to root_path unless user && user.role?(:admin)
  end
end

Just check the session token in the view with erb if you should show the token.

MarcusSky added a commit to jaya/switch_user that referenced this issue Oct 25, 2016
`#remember_user`.

We need to be able to tell the gem that we want the user to be
remembered so that we can switch back to it. It was unclear how it
should've been done, so this is making sure that we don't have to make
two calls to the `SwitchUser API`, just to make sure the original user
will be remembered.

Related Issue: flyerhzm/issues/55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants