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

[Bug #32446] Assuming identity is very slow #3

Open
wants to merge 69 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
e6147fb
Devise provider- don't store sign_in details
dylanjha Feb 17, 2015
fe984ea
security :guardsman:
sergey-alekseev Mar 25, 2015
987a673
Merge pull request #68 from MustWin/master
flyerhzm Mar 28, 2015
41703f7
Merge pull request #70 from sergey-alekseev/master
flyerhzm Mar 28, 2015
bc007ed
Revert "Allow routes to be disabled"
flyerhzm Jun 16, 2015
d18b453
Revert "Devise provider- don't store sign_in details"
flyerhzm Jun 16, 2015
aab2f22
use rspec 3.3.0
flyerhzm Jun 16, 2015
a979673
add travis-ci
flyerhzm Jun 16, 2015
837f3ac
Devise provider- don't store sign_in details
dylanjha Jun 17, 2015
5b53632
add README note to run tests
dylanjha Jun 17, 2015
8dd6902
Merge pull request #74 from MustWin/master
flyerhzm Jun 17, 2015
cdef839
only load necessary data #72
flyerhzm Jun 20, 2015
3827d60
Bumping version to 1.0.0
flyerhzm Jun 22, 2015
77ce40a
update README
flyerhzm Jun 22, 2015
775dd28
GuestDataSource#users should be renamed to GuestDataSource#all #75
flyerhzm Jun 23, 2015
03be237
allow custom method as available_users_names
flyerhzm Jun 26, 2015
501a372
Bumping version to 1.0.1
flyerhzm Jun 26, 2015
e8705c0
1. Use String#sub, not String#delete to remove scope str from scope_i…
etipton Jun 27, 2015
3c606c5
Merge pull request #76 from renterup/sub_not_delete
flyerhzm Jun 27, 2015
464f285
Bumping version to 1.0.2
flyerhzm Jun 29, 2015
a0e0de1
rails 3.2 compatibility
flyerhzm Jul 1, 2015
7d7719d
Bumping version to 1.1.0
flyerhzm Jul 1, 2015
1e83673
replace select option to group_option
vkill Sep 19, 2015
bd39823
add helper rspec
vkill Sep 19, 2015
9d883f2
Merge pull request #82 from vkill/master
flyerhzm Sep 20, 2015
57efa2a
update README for rails 3.2
flyerhzm Dec 1, 2015
12004c3
Bumping version to 1.2.0
flyerhzm Dec 1, 2015
075d10f
fix grouped_options_for_select bug for rails 3.2
vkill Dec 22, 2015
39a52e9
Merge pull request #86 from vkill/master
flyerhzm Dec 22, 2015
18cc9f9
Bumping version to 1.2.1
flyerhzm Dec 22, 2015
f7df7bf
add class and style options to switch_user_select
westonganger Feb 11, 2016
2f28311
fix travis setting
flyerhzm Feb 11, 2016
e7cff85
fix selected_user_of_helper bug
vkill Apr 14, 2016
a0a0bd7
support capybara
vkill Apr 16, 2016
6e8531f
fix 'rspec do not raise exception' but
vkill Apr 16, 2016
1019be5
rename /dummys/open to /dummy/open
vkill Apr 16, 2016
0b5d021
edit rspec/feature_helpers_spec.rb
vkill Apr 16, 2016
b9ec5e6
Update switch_user_controller.rb
colorfulberry Apr 18, 2016
d7825ce
Merge pull request #90 from colorfulberry/patch-1
flyerhzm Apr 19, 2016
93d8cd4
Merge pull request #89 from vkill/hotfix/rspec_do_not_raise_exception
flyerhzm Apr 19, 2016
b390489
Merge branch 'master' into feature/support_capybara
vkill Apr 20, 2016
9f76149
Merge pull request #91 from vkill/feature/support_capybara
flyerhzm Apr 20, 2016
d7d6f9d
make it work with relative url root
westonganger Jul 21, 2016
3dff40e
Merge pull request #87 from westonganger/master
flyerhzm Jul 22, 2016
3549e35
fix travis
flyerhzm Jul 22, 2016
be1d086
remove Rails.relative_root_url
flyerhzm Jul 22, 2016
fc1e1db
apply before each only for type feature
flyerhzm Jul 23, 2016
25ab079
Bumping version to 1.3.0
flyerhzm Jul 23, 2016
1a474df
Merge pull request #88 from vkill/hotfix/selected_user_of_helper
flyerhzm Jul 23, 2016
5258eac
Respect the relative_url_root for applications deployed to sub-direct…
westonganger Jul 22, 2016
e7b4f98
Respect relative_url_root
westonganger Jul 23, 2016
17e8fc3
Merge pull request #95 from westonganger/master
flyerhzm Jul 24, 2016
35fdffa
Bumping version to 1.3.1
flyerhzm Aug 21, 2016
92d4e00
add ability to store_sign_in info with devise provider
westonganger Oct 3, 2016
933b613
Merge pull request #100 from westonganger/master
flyerhzm Oct 5, 2016
e2a7bb8
Bumping version to 1.4.0
flyerhzm Oct 21, 2016
dd0b6b1
Allow `SwitchUser.switch_back` to be considered even when not calling
MarcusSky Oct 25, 2016
1d45ec9
change controller and add spec
prem-prakash Nov 16, 2016
aa57c81
Rails.env.production?
stereodenis Jan 20, 2017
8499bdc
Merge pull request #102 from stereodenis/patch-1
flyerhzm Jan 20, 2017
1ed426e
Merge pull request #103 from jaya/master
flyerhzm Feb 18, 2017
86d2e1f
use new syntax
flyerhzm Feb 18, 2017
e828646
Fix 'available_users' config syntax
rubendinho Mar 2, 2017
a59be6a
Merge pull request #104 from rubendinho/patch-1
flyerhzm Mar 2, 2017
9f799fb
updated documentation about before_action in Rails < 4
Mar 18, 2017
1d32080
updated documentation about before_action in Rails < 4
Mar 18, 2017
f2b328c
Merge pull request #105 from acesuares/master
flyerhzm Mar 19, 2017
2f73206
Merge remote-tracking branch 'upstream/master' into rm32446
aomran May 1, 2017
89af98f
bump version
aomran May 1, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ pkg/*
*.gem
.bundle
.rvmrc
.ruby-version
.versions.conf
Gemfile.lock
log/*.log
.DS_Store
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language: ruby
rvm:
- 2.3.0
env:
- DB=sqlite
before_install:
- gem update bundler
script: bundle exec rspec spec
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Next Release

## 1.4.0 (10/21/2016)

* Add ability to `store_sign_in` info with devise provider
* Respect `relative_url_root`
* Fix `selected_user_of_helper` bug

## 1.3.0 (07/23/2016)

* Add capybara support
* Add class and style options to `switch_user_select`
* Fix `grouped_options_for_select` for rails 3.2

## 1.2.0 (12/01/2015)

* Replace select option to `group_option`

## 1.1.0 (07/01/2015)

* Rails 3.x compatibility
* Don't attempt to query db if scope str isn't in `scope_id` str
* Allow custom method as `available_users_names`

## 1.0.0 (06/22/2015)

* Performance improved, only load necessary data.
* Devise provider- don't store `sign_in` details
* Security :guardsman:, raise RoutingError in `developer_modes_only`
* Restore original user after sorcery logout
82 changes: 62 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# switch_user

[![Build Status](https://secure.travis-ci.org/flyerhzm/switch_user.png)](http://travis-ci.org/flyerhzm/switch_user)

Inspired from [hobo][0], switch_user provides a convenient way to switch current user without needing to log out and log in manually.

## Use Case
Expand All @@ -12,7 +14,7 @@ switch_user is very useful in such use cases

## Example

Visit here: <http://switch-user-example.heroku.com>, switch the current user in the select box.
Visit here: <http://switch-user-example.heroku.com/admin>, switch the current user in the select box.

And source code here: <https://github.com/flyerhzm/switch_user_example>

Expand All @@ -22,6 +24,9 @@ Add in Gemfile.
```ruby
gem "switch_user"
```

If you get the following error: **undefined method `before_action' for SwitchUserController:Class**, you are probably using an older version of Rails (<4). You can use this gem: https://github.com/pschambacher/rails3-before_action

## Usage

Add following code into your layout page.
Expand All @@ -34,6 +39,12 @@ or haml

= switch_user_select

If you want to add a class or styles

<%= switch_user_select class: 'special-select', styles: 'width: 220px' %>

= switch_user_select class: 'special-select', styles: 'width: 220px'

If there are too many users (on production), the switch_user_select is not a good choice, you should call the switch user request by yourself.

<%= link_to user.login, "/switch_user?scope_identifier=user_#{user.id}" %>
Expand All @@ -45,7 +56,8 @@ If there are too many users (on production), the switch_user_select is not a goo
If you have a wildcard route in your project, add a route before the wildcard route.
```ruby
# config/routes.rb
get 'switch_user' => 'switch_user#set_current_user'
get 'switch_user', to: 'switch_user#set_current_user'
get 'switch_user/remember_user', to: 'switch_user#remember_user'
# wildcard route that will get
get ':id' => 'pages#show'
```
Expand All @@ -54,69 +66,69 @@ get ':id' => 'pages#show'
By default, you can switch between Guest and all users in users table, you don't need to do anything. The following is some of the more commonly used configuration options.
```ruby
SwitchUser.setup do |config|
# provider may be :devise, :authlogic, :clearance, :restful_authentication or :sorcery
# provider may be :devise, :authlogic, :clearance, :restful_authentication, :sorcery, or {name: :devise, store_sign_in: true}
config.provider = :devise

# available_users is a hash,
# key is the model name of user (:user, :admin, or any name you use),
# value is a block that return the users that can be switched.
config.available_users = { :user => lambda { User.all } }
config.available_users = { user: -> { User.all } } # use User.scoped instead for rails 3.2

# available_users_identifiers is a hash,
# keys in this hash should match a key in the available_users hash
# value is the name of the identifying column to find by,
# defaults to id
# this hash is to allow you to specify a different column to
# expose for instance a username on a User model instead of id
config.available_users_identifiers = { :user => :id }
config.available_users_identifiers = { user: :id }

# available_users_names is a hash,
# keys in this hash should match a key in the available_users hash
# value is the column name which will be displayed in select box
config.available_users_names = { :user => :email }
config.available_users_names = { user: :email }

# controller_guard is a block,
# if it returns true, the request will continue,
# else the request will be refused and returns "Permission Denied"
# if you switch from "admin" to user, the current_user param is "admin"
config.controller_guard = lambda { |current_user, request| Rails.env.development? }
config.controller_guard = ->(current_user, request) { Rails.env.development? }

# view_guard is a block,
# if it returns true, the switch user select box will be shown,
# else the select box will not be shown
# if you switch from admin to "user", the current_user param is "user"
config.view_guard = lambda { |current_user, request| Rails.env.development? }
config.view_guard = ->(current_user, request) { Rails.env.development? }

# redirect_path is a block, it returns which page will be redirected
# after switching a user.
config.redirect_path = lambda { |request, params| '/' }
config.redirect_path = ->(request, params) { '/' }
end
```
If you need to override the default configuration, run <code>rails g switch_user:install</code> and a copy of the configuration file will be copied to <code>config/initializers/switch_user.rb</code> in your project.

If you want to switch both available users and available admins
```ruby
config.available_users = { :user => lambda { User.available }, :admin => lambda { Admin.available } }
config.available_users = { :user => -> { User.available }, :admin => -> { Admin.available } }
```
If you want to use name column as the user identifier
```ruby
config.available_users_identifiers => { :user => :name }
config.available_users_identifiers => { user: :name }
```
If you want to display the login field in switch user select box
```ruby
config.available_users_names = { :user => :login }
config.available_users_names = { user: :login }
```
If you only allow switching from admin to user in production environment
```ruby
config.controller_guard = lambda { |current_user, request| Rails.env == "production" and current_user.admin? }
config.controller_guard = ->(current_user, request) { Rails.env.production? && current_user.admin? }
```
If you only want to display switch user select box for admins in production environment
```ruby
config.view_guard = lambda { |current_user, request| Rails.env == "production" and current_user and current_user.admin? }
config.view_guard = ->(current_user, request) { Rails.env.production? && current_user && current_user.admin? }
```
If you want to redirect user to "/dashboard" page
```ruby
config.redirect_path = lambda { |request, params| "/dashboard" }
config.redirect_path = ->(request, params) { "/dashboard" }
```
If you want to hide a 'Guest' item in the helper dropdown list
```ruby
Expand All @@ -128,26 +140,56 @@ Sometimes you'll want to be able to switch to an unprivileged user and then back
You will need to make the following modifications to your configuration:
```ruby
config.switch_back = true
config.controller_guard = lambda { |current_user, request, original_user|
current_user && current_user.admin? || original_user && original_user.super_admin?
}
config.controller_guard = ->(current_user, request, original_user) { current_user && current_user.admin? || original_user && original_user.super_admin? }
# Do something similar for the view_guard as well.
```
This example would allow an admin user to user switch_user, but would only let you switch back to another user if the original user was a super admin.

## Using SwitchUser with RSpec and Capybara

Add the following code to spec/support/switch_user.rb or spec/spec_helper.rb:

```ruby
require 'switch_user/rspec'
```

You can now write your specs like so :

```ruby
feature "Your feature", type: :feature do
background do
@user = User.make(email: '[email protected]', password: 'password')
end

scenario "Your scenario" do
switch_user @user
# or
# switch_user :user, @user.id

visit '/'
end
end
```

### How it works

Click the checkbox next to switch_user_select menu to remember that user for this session. Once this
has been checked, that user is passed in as the 3rd option to the view and controller guards.
has been checked, that user is passed in as the 3rd option to the view and controller guards.
This allows you to check against current_user as well as that original_user to see if the
switch_user action should be allowed.

### Warning

This feature should be used with extreme caution because of the security implications. This is especially true in a production environment.

## Contributing

#### Run tests

`bundle exec rspec spec`

## Credit

Copyright © 2010 - 2012 Richard Huang ([email protected]), released under the MIT license
Copyright © 2010 - 2017 Richard Huang ([email protected]), released under the MIT license

[0]: https://github.com/tablatom/hobo
23 changes: 15 additions & 8 deletions app/controllers/switch_user_controller.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
class SwitchUserController < ApplicationController
before_filter :developer_modes_only
before_action :developer_modes_only, :switch_back

def set_current_user
handle_request(params)

redirect_to(SwitchUser.redirect_path.call(request, params))
end

def remember_user
redirect_to(SwitchUser.redirect_path.call(request, params))
end

private

def switch_back
if SwitchUser.switch_back
provider.remember_current_user(true) if params[:remember] == "true"
provider.remember_current_user(false) if params[:remember] == "false"
end
end

def developer_modes_only
render :text => "Permission Denied", :status => 403 unless available?
raise ActionController::RoutingError.new('Do not try to hack us.') unless available?
end

def available?
SwitchUser.guard_class.new(self, provider).controller_available?
end

def handle_request(params)
if SwitchUser.switch_back
provider.remember_current_user(params[:remember] == "true")
end

if params[:scope_identifier].blank?
provider.logout_all
else
Expand All @@ -31,9 +38,9 @@ def handle_request(params)
return
end
if SwitchUser.login_exclusive
provider.login_exclusive(record.user, :scope => record.scope)
provider.login_exclusive(record.user, scope: record.scope)
else
provider.login_inclusive(record.user, :scope => record.scope)
provider.login_inclusive(record.user, scope: record.scope)
end
end
end
Expand Down
33 changes: 24 additions & 9 deletions app/helpers/switch_user_helper.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
module SwitchUserHelper
SelectOption = Struct.new(:label, :scope_id)
def switch_user_select
def switch_user_select(options = {})
return unless available?

if provider.current_user
selected_user = "user_#{current_user.id}"
else
selected_user = nil
selected_user = nil

grouped_options_container = {}.tap do |h|
SwitchUser.all_users.each do |record|
scope = record.is_a?(SwitchUser::GuestRecord) ? :Guest : record.scope.to_s.capitalize
h[scope] ||= []
h[scope] << [record.label, record.scope_id]

if selected_user.nil?
unless record.is_a?(SwitchUser::GuestRecord)
if provider.current_user?(record.user, record.scope)
selected_user = record.scope_id
end
end
end
end
end

render :partial => "switch_user/widget",
:locals => {
:options => SwitchUser.all_users,
:current_scope => selected_user
option_tags = grouped_options_for_select(grouped_options_container.to_a, selected_user)

render partial: "switch_user/widget",
locals: {
option_tags: option_tags,
classes: options[:class],
styles: options[:style],
}
end

Expand Down
4 changes: 2 additions & 2 deletions app/views/switch_user/_widget.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<% if SwitchUser.switch_back %>
<%= check_box_tag "remember_user", "remember_user", provider.original_user.present?, :onchange => "location.href = '/switch_user/remember_user?remember=' + encodeURIComponent(this.checked)" %>
<%= check_box_tag "remember_user", "remember_user", provider.original_user.present?, onchange: "location.href = '#{ActionController::Base.relative_url_root || '/'}switch_user/remember_user?remember=' + encodeURIComponent(this.checked)" %>
<% end %>
<%= select_tag "switch_user_identifier", options_from_collection_for_select(options, :scope_id, :label, current_scope), :onchange => "location.href = '/switch_user?scope_identifier=' + encodeURIComponent(this.options[this.selectedIndex].value)" %>
<%= select_tag "switch_user_identifier", option_tags, onchange: "location.href = '#{ActionController::Base.relative_url_root || '/'}switch_user?scope_identifier=' + encodeURIComponent(this.options[this.selectedIndex].value)", class: classes, style: styles %>
7 changes: 3 additions & 4 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
if SwitchUser.generate_routes
Rails.application.routes.draw do
get :switch_user, :to => 'switch_user#set_current_user'
end
Rails.application.routes.draw do
get :switch_user, to: 'switch_user#set_current_user'
get 'switch_user/remember_user', to: 'switch_user#remember_user'
end
Loading