user_impersonate2 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +231 -0
  4. data/Rakefile +41 -0
  5. data/app/assets/javascripts/user_impersonate/application.js +15 -0
  6. data/app/assets/javascripts/user_impersonate/impersonate.js +2 -0
  7. data/app/assets/stylesheets/user_impersonate/application.css +13 -0
  8. data/app/assets/stylesheets/user_impersonate/impersonate.css +4 -0
  9. data/app/controllers/user_impersonate/application_controller.rb +4 -0
  10. data/app/controllers/user_impersonate/impersonate_controller.rb +146 -0
  11. data/app/helpers/user_impersonate/application_helper.rb +11 -0
  12. data/app/views/layouts/user_impersonate/application.html.erb +14 -0
  13. data/app/views/user_impersonate/_header.html.erb +60 -0
  14. data/app/views/user_impersonate/impersonate/index.html.erb +30 -0
  15. data/config/cucumber.yml +8 -0
  16. data/config/routes.rb +6 -0
  17. data/lib/generators/user_impersonate/USAGE +6 -0
  18. data/lib/generators/user_impersonate/templates/app/views/user_impersonate/_header.html.erb +60 -0
  19. data/lib/generators/user_impersonate/templates/config/initializers/user_impersonate.rb +15 -0
  20. data/lib/generators/user_impersonate/user_impersonate_generator.rb +15 -0
  21. data/lib/tasks/user_impersonate_tasks.rake +4 -0
  22. data/lib/user_impersonate.rb +5 -0
  23. data/lib/user_impersonate/devise_helpers.rb +31 -0
  24. data/lib/user_impersonate/engine.rb +17 -0
  25. data/lib/user_impersonate/version.rb +4 -0
  26. data/test/dummy/README.rdoc +261 -0
  27. data/test/dummy/Rakefile +7 -0
  28. data/test/dummy/app/assets/javascripts/application.js +15 -0
  29. data/test/dummy/app/assets/javascripts/home.js +2 -0
  30. data/test/dummy/app/assets/stylesheets/application.css +16 -0
  31. data/test/dummy/app/assets/stylesheets/home.css +7 -0
  32. data/test/dummy/app/controllers/application_controller.rb +3 -0
  33. data/test/dummy/app/controllers/home_controller.rb +4 -0
  34. data/test/dummy/app/helpers/application_helper.rb +2 -0
  35. data/test/dummy/app/helpers/home_helper.rb +2 -0
  36. data/test/dummy/app/models/user.rb +12 -0
  37. data/test/dummy/app/views/home/index.html.erb +11 -0
  38. data/test/dummy/app/views/layouts/application.html.erb +22 -0
  39. data/test/dummy/app/views/user_impersonate/_header.html.erb +65 -0
  40. data/test/dummy/config.ru +4 -0
  41. data/test/dummy/config/application.rb +60 -0
  42. data/test/dummy/config/boot.rb +10 -0
  43. data/test/dummy/config/database.yml +25 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +40 -0
  46. data/test/dummy/config/environments/production.rb +67 -0
  47. data/test/dummy/config/environments/test.rb +37 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/dummy/config/initializers/devise.rb +232 -0
  50. data/test/dummy/config/initializers/inflections.rb +15 -0
  51. data/test/dummy/config/initializers/mime_types.rb +5 -0
  52. data/test/dummy/config/initializers/secret_token.rb +7 -0
  53. data/test/dummy/config/initializers/session_store.rb +8 -0
  54. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/test/dummy/config/locales/devise.en.yml +58 -0
  56. data/test/dummy/config/locales/en.yml +5 -0
  57. data/test/dummy/config/routes.rb +8 -0
  58. data/test/dummy/db/migrate/20120914174453_devise_create_users.rb +48 -0
  59. data/test/dummy/db/migrate/20120914184123_add_staff_flag_to_users.rb +5 -0
  60. data/test/dummy/db/schema.rb +36 -0
  61. data/test/dummy/db/test.sqlite3 +0 -0
  62. data/test/dummy/lib/tasks/cucumber.rake +65 -0
  63. data/test/dummy/log/test.log +15 -0
  64. data/test/dummy/public/404.html +26 -0
  65. data/test/dummy/public/422.html +26 -0
  66. data/test/dummy/public/500.html +25 -0
  67. data/test/dummy/public/favicon.ico +0 -0
  68. data/test/dummy/script/rails +6 -0
  69. data/test/dummy/test/fixtures/users.yml +14 -0
  70. data/test/dummy/test/unit/helpers/home_helper_test.rb +4 -0
  71. data/test/dummy/test/unit/user_test.rb +7 -0
  72. data/test/integration/navigation_test.rb +10 -0
  73. data/test/test_helper.rb +15 -0
  74. data/test/unit/helpers/user_impersonate/impersonate_helper_test.rb +6 -0
  75. data/test/user_impersonate_test.rb +7 -0
  76. metadata +268 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 682a1471167a96c1fbf29afe05c351f5672c323d
4
+ data.tar.gz: 8b3324d882edf3c2bdbb8f1c8952469126ee6589
5
+ SHA512:
6
+ metadata.gz: e968520eb6e202e4423a9636d007568f58e486f8ddd079497ce451c10d21020deda7dc780c1a168686a46eb1b3d99ad12fa406041caea1091763939dd3e504e0
7
+ data.tar.gz: 0e43043f7da986d10a310fe7f3b7c0991dfd284184e81a40232eaca6b558ba3d67d5992a5bdfe2f76de4b699d22d0ca8b6acced1ddacb66f38522c864917f9ae
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # `user_impersonate2`
2
+
3
+ [![Install gem](https://badge.fury.io/rb/user_impersonate2.png)](https://rubygems.org/gems/user_impersonate2)
4
+ [![Build status](https://travis-ci.org/rcook/user_impersonate2.png)](https://travis-ci.org/rcook/user_impersonate2)
5
+ [![Coverage status](https://coveralls.io/repos/rcook/user_impersonate2/badge.png?branch=master)](https://coveralls.io/r/rcook/user_impersonate2)
6
+
7
+ # Note
8
+
9
+ This is a fork of Engine Yard's no-longer-maintained
10
+ [User Impersonate](https://github.com/engineyard/user_impersonate) gem. It supports Rails 3.2.x and
11
+ later as well as Rails 4.
12
+
13
+ # Overview
14
+
15
+ Allow staff users to impersonate your normal users: see what they see, only do what they can do.
16
+
17
+ This concept and code was extracted from [Engine Yard Cloud](http://www.engineyard.com/products/cloud), which we use when we want to help support a customer remotely.
18
+
19
+ This Rails engine currently supports the following Rails authentication systems:
20
+
21
+ * [Devise](https://github.com/plataformatec/devise)
22
+
23
+ # Example usage
24
+
25
+ When you are impersonating a user you see what they see, with a header section above:
26
+
27
+ <img src="https://img.skitch.com/20120919-c8382rgdcub7gsh2p82k8reng3.png" alt="Impersonating a user" />
28
+
29
+ # Installation
30
+
31
+ Add the gem to your Rails application's Gemfile and run `bundle`:
32
+
33
+ ``` ruby
34
+ gem 'user_impersonate2', :require => 'user_impersonate'
35
+ ```
36
+
37
+ Note that `:require => 'user_impersonate'` is required as this gem currently
38
+ maintains the same internal directory structure as the original
39
+ [User Impersonate](https://github.com/engineyard/user_impersonate) gem. This may
40
+ change in future versions.
41
+
42
+ Run the (sort of optional) generator:
43
+
44
+ ```
45
+ bundle
46
+ rails g user_impersonate
47
+ ```
48
+
49
+ This adds the following line within your `config/routes.rb`:
50
+
51
+ ``` ruby
52
+ mount UserImpersonate::Engine => "/impersonate", as: "impersonate_engine"
53
+ ```
54
+
55
+ Include in your layout files support for `flash[:error]` and `flash[:notice]`, such as:
56
+
57
+ ``` erb
58
+ <p class="notice"><%= flash[:notice] %></p>
59
+ <p class="alert"><%= flash[:error] %></p>
60
+ ```
61
+
62
+ Next, add the impersonation header to your layouts:
63
+
64
+ ``` erb
65
+ <% if current_staff_user %>
66
+ <%= render 'user_impersonate/header' %>
67
+ <% end %>
68
+ ```
69
+
70
+ Next, add staff concept to your User model.
71
+
72
+ To test the engine out, make all users staff!
73
+
74
+ ``` ruby
75
+ # app/models/user.rb
76
+
77
+ def staff?
78
+ true
79
+ end
80
+
81
+ # String to represent a user (email, name, etc)
82
+ def to_s
83
+ email
84
+ end
85
+ ```
86
+
87
+ You can now go to [http://localhost:3000/impersonate](http://localhost:3000/impersonate) to see the list of users, except your own user account. If you impersonate one you will see the magic!
88
+
89
+ # Integration
90
+
91
+ To support this Rails engine, you need to add some things.
92
+
93
+ * `current_user` helper within controllers & helpers
94
+ * `current_user.staff?` - your `User` model needs a `staff?` to identify if the current user is allowed to impersonate other users; if missing, no user can access impersonation system
95
+
96
+ ## User#staff?
97
+
98
+ One way to add this helper is to add a column to your User model:
99
+
100
+ ```
101
+ rails g migration add_staff_flag_to_users staff:boolean
102
+ rake db:migrate db:test:prepare
103
+ ```
104
+
105
+ # Customization
106
+
107
+ ## Header
108
+
109
+
110
+ You can override the bright red header by creating a `app/views/user_impersonate/_header.html.erb` file (or whatever template system you like).
111
+
112
+ For example, the Engine Yard Cloud uses a header that looks like:
113
+
114
+ ![](https://img.skitch.com/20120915-mk8mnpdsu5nuym3bxs678qf1a8.png)
115
+
116
+ The `app/views/user_impersonate/_header.html.haml` HAML partial for this header would be:
117
+
118
+ ``` haml
119
+ %div#impersonating
120
+ .impersonate-controls.page
121
+ .impersonate-info.grid_12
122
+ You (
123
+ %span.admin_name= current_staff_user
124
+ ) are impersonating
125
+ %span.user_name= link_to current_user, url_for([:admin, current_user])
126
+ ( User id:
127
+ %span.user_id= current_user.id
128
+ )
129
+ - if current_user.no_accounts?
130
+ ( No accounts )
131
+ - else
132
+ ( Account name:
133
+ %span.account_id= link_to current_user.accounts.first, url_for([:admin, current_user.accounts.first])
134
+ , id:
135
+ %strong= current_user.accounts.first.id
136
+ )
137
+ .impersonate-buttons.grid_12
138
+ = form_tag url_for([:ssh_key, :admin, current_user]), :method => "put" do
139
+ %span Support SSH Key
140
+ = select_tag 'public_key', options_for_select(current_staff_user.keys.map {|k| k})
141
+ %button{:type => "submit"} Install SSH Key
142
+ or
143
+ = form_tag [:admin, :revert], :method => :delete, :class => 'revert-form' do
144
+ %button{:type => "submit"} Revert to admin
145
+ ```
146
+
147
+ ## Redirects
148
+
149
+ By default, when you impersonate and when you stop impersonating a user you are redirected to the root url.
150
+
151
+ Configure alternate paths in `config/initializers/user_impersonate.rb`, which is created by the generator above.
152
+
153
+ ``` ruby
154
+ # config/initializers/user_impersonate.rb
155
+ module UserImpersonate
156
+ class Engine < Rails::Engine
157
+ config.redirect_on_impersonate = "/"
158
+ config.redirect_on_revert = "/impersonate"
159
+ end
160
+ end
161
+ ```
162
+
163
+ ## User model & lookup
164
+
165
+ By default, it assumes the User model is `User`, that you use `User.find(id)` to find a user, and `aUser.id` to get the related id value.
166
+
167
+ You can fix this default behavior in `config/initializers/user_impersonate.rb`, which is created by the generator above.
168
+
169
+ ``` ruby
170
+ # config/initializers/user_impersonate.rb
171
+ module UserImpersonate
172
+ class Engine < Rails::Engine
173
+ config.user_class = "User"
174
+ config.user_finder = "find" # User.find
175
+ config.user_id_column = "id" # Such that User.find(aUser.id) works
176
+ config.user_is_staff_method = "staff?" # current_user.staff?
177
+ end
178
+ end
179
+ ```
180
+
181
+ ## Spree specific stuff
182
+
183
+ Modify User and add current_user helper
184
+ ``` ruby
185
+ Spree::User.class_eval do
186
+ def staff?
187
+ has_spree_role?('admin')
188
+ end
189
+
190
+ def to_s
191
+ email
192
+ end
193
+ end
194
+
195
+ ApplicationController.class_eval do
196
+ helper_method :current_user
197
+ def current_user
198
+ spree_current_user
199
+ end
200
+ end
201
+ ```
202
+
203
+ Initializer
204
+ ``` ruby
205
+ # config/initializers/user_impersonate.rb
206
+ module UserImpersonate
207
+ class Engine < Rails::Engine
208
+ config.user_class = "Spree::User"
209
+ config.user_finder = "find" # User.find
210
+ config.user_id_column = "id" # Such that User.find(aUser.id) works
211
+ config.user_is_staff_method = "staff?" # current_user.staff?
212
+ config.authenticate_user_method = "authenticate_spree_user!"
213
+ config.redirect_on_impersonate = "/"
214
+ config.redirect_on_revert = "/"
215
+ config.user_name_column = "users"
216
+ end
217
+ end
218
+ ```
219
+
220
+ Deface to add header
221
+ ``` ruby
222
+ Deface::Override.new(:virtual_path => "spree/layouts/spree_application",
223
+ :name => "impersonate_header",
224
+ :insert_before => "div.container",
225
+ :text => "<% if current_staff_user %><%= render 'user_impersonate/header' %><% end %>")
226
+ ```
227
+
228
+ # Licence
229
+
230
+ `user_impersonate2` is released under the MIT licence.
231
+
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'UserImpersonate'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+ task :cucumber => 'app:cucumber'
40
+ # task :default => [:test, :spec, :cucumber]
41
+ task :default => [:test, :cucumber]
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ module UserImpersonate
2
+ class ApplicationController < ::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,146 @@
1
+ require_dependency "user_impersonate/application_controller"
2
+
3
+ module UserImpersonate
4
+ class ImpersonateController < ApplicationController
5
+ before_filter :authenticate_the_user
6
+ before_filter :current_user_must_be_staff!, except: ["destroy"]
7
+
8
+ # Display list of all users, except current (staff) user
9
+ # Is this exclusion unnecessary complexity?
10
+ # Normal apps wouldn't bother with this action; rather they would
11
+ # go straight to GET /impersonate/user/123 (create action)
12
+ def index
13
+ users_table = Arel::Table.new(user_table.to_sym) # e.g. :users
14
+ id_column = users_table[user_id_column.to_sym] # e.g. users_table[:id]
15
+ @users = user_class.order("updated_at DESC").
16
+ where(
17
+ id_column.not_in [
18
+ current_user.send(user_id_column.to_sym) # e.g. current_user.id
19
+ ])
20
+ if params[:search]
21
+ @users = @users.where("#{user_name_column} like ?", "%#{params[:search]}%")
22
+ end
23
+ end
24
+
25
+ # Perform the user impersonate action
26
+ # GET /impersonate/user/123
27
+ def create
28
+ @user = find_user(params[:user_id])
29
+ impersonate(@user)
30
+ redirect_on_impersonate(@user)
31
+ end
32
+
33
+ # Revert the user impersonation
34
+ # DELETE /impersonation/revert
35
+ def destroy
36
+ unless current_staff_user
37
+ flash[:notice] = "You weren't impersonating anyone"
38
+ redirect_on_revert and return
39
+ end
40
+ user = current_user
41
+ revert_impersonate
42
+ if user
43
+ flash[:notice] = "No longer impersonating #{user}"
44
+ redirect_on_revert(user)
45
+ else
46
+ flash[:notice] = "No longer impersonating a user"
47
+ redirect_on_revert
48
+ end
49
+ end
50
+
51
+ private
52
+ def current_user_must_be_staff!
53
+ unless user_is_staff?(current_user)
54
+ flash[:error] = "You don't have access to this section."
55
+ redirect_to :back
56
+ end
57
+ rescue ActionController::RedirectBackError
58
+ redirect_to '/'
59
+ end
60
+
61
+ # current_user changes from a staff user to
62
+ # +new_user+; current user stored in +session[:staff_user_id]+
63
+ def impersonate(new_user)
64
+ session[:staff_user_id] = current_user.id #
65
+ sign_in_user new_user
66
+ end
67
+
68
+ # revert the +current_user+ back to the staff user
69
+ # stored in +session[:staff_user_id]+
70
+ def revert_impersonate
71
+ return unless current_staff_user
72
+ sign_in_user current_staff_user
73
+ session[:staff_user_id] = nil
74
+ end
75
+
76
+ def sign_in_user(user)
77
+ method = config_or_default :sign_in_user_method, "sign_in"
78
+ self.send(method.to_sym, user)
79
+ end
80
+
81
+ def authenticate_the_user
82
+ method = config_or_default :authenticate_user_method, "authenticate_user!"
83
+ self.send(method.to_sym)
84
+ end
85
+
86
+ # Helper to load a User, using all the UserImpersonate config options
87
+ def find_user(id)
88
+ user_class.send(user_finder_method, id)
89
+ end
90
+
91
+ # Similar to user.staff?
92
+ # Using all the UserImpersonate config options
93
+ def user_is_staff?(user)
94
+ current_user.respond_to?(user_is_staff_method.to_sym) &&
95
+ current_user.send(user_is_staff_method.to_sym)
96
+ end
97
+
98
+ def user_finder_method
99
+ (config_or_default :user_finder, "find").to_sym
100
+ end
101
+
102
+ def user_class_name
103
+ config_or_default :user_class, "User"
104
+ end
105
+
106
+ def user_class
107
+ user_class_name.constantize
108
+ end
109
+
110
+ def user_table
111
+ user_class_name.tableize.tr('/', '_')
112
+ end
113
+
114
+ def user_id_column
115
+ config_or_default :user_id_column, "id"
116
+ end
117
+
118
+ def user_name_column
119
+ config_or_default :user_name_column, "name"
120
+ end
121
+
122
+ def user_is_staff_method
123
+ config_or_default :user_is_staff_method, "staff?"
124
+ end
125
+
126
+ def redirect_on_impersonate(impersonated_user)
127
+ url = config_or_default :redirect_on_impersonate, root_url
128
+ redirect_to url
129
+ end
130
+
131
+ def redirect_on_revert(impersonated_user = nil)
132
+ url = config_or_default :redirect_on_revert, root_url
133
+ redirect_to url
134
+ end
135
+
136
+ # gets overridden config value for engine, else returns default
137
+ def config_or_default(attribute, default)
138
+ attribute = attribute.to_sym
139
+ if UserImpersonate::Engine.config.respond_to?(attribute)
140
+ UserImpersonate::Engine.config.send(attribute)
141
+ else
142
+ default
143
+ end
144
+ end
145
+ end
146
+ end