devise_masquerade 0.6.5 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/brakeman-analysis.yml +44 -0
  4. data/.github/workflows/rubocop-analysis.yml +39 -0
  5. data/.gitignore +1 -2
  6. data/.ruby-version +1 -1
  7. data/.travis.yml +3 -4
  8. data/Gemfile +16 -10
  9. data/Gemfile.lock +307 -0
  10. data/Makefile +6 -1
  11. data/README.md +33 -1
  12. data/app/controllers/devise/masquerades_controller.rb +75 -59
  13. data/devise_masquerade.gemspec +5 -4
  14. data/features/back.feature +0 -1
  15. data/features/multiple_masquerading_models.feature +17 -0
  16. data/features/step_definitions/auth_steps.rb +1 -0
  17. data/features/step_definitions/back_steps.rb +18 -3
  18. data/features/step_definitions/url_helpers_steps.rb +11 -0
  19. data/features/support/env.rb +23 -4
  20. data/features/url_helpers.feature +14 -0
  21. data/lib/devise_masquerade.rb +3 -9
  22. data/lib/devise_masquerade/controllers/helpers.rb +27 -8
  23. data/lib/devise_masquerade/controllers/url_helpers.rb +16 -2
  24. data/lib/devise_masquerade/models.rb +9 -0
  25. data/lib/devise_masquerade/models/masqueradable.rb +13 -0
  26. data/lib/devise_masquerade/rails.rb +14 -4
  27. data/lib/devise_masquerade/routes.rb +11 -8
  28. data/lib/devise_masquerade/version.rb +1 -1
  29. data/spec/controllers/admin/dashboard_controller_spec.rb +3 -4
  30. data/spec/controllers/dashboard_controller_spec.rb +3 -5
  31. data/spec/controllers/devise/masquerades_controller_spec.rb +62 -38
  32. data/spec/controllers/masquerades_tests_controller_spec.rb +41 -0
  33. data/spec/dummy/app/controllers/admin/dashboard_controller.rb +1 -2
  34. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  35. data/spec/dummy/app/controllers/dashboard_controller.rb +5 -2
  36. data/spec/dummy/app/controllers/masquerades_tests_controller.rb +7 -0
  37. data/spec/dummy/app/controllers/students_controller.rb +8 -0
  38. data/spec/dummy/app/models/admin/user.rb +0 -7
  39. data/spec/dummy/app/models/student.rb +3 -0
  40. data/spec/dummy/app/models/user.rb +1 -10
  41. data/spec/dummy/app/views/admin/dashboard/index.html.erb +0 -2
  42. data/spec/dummy/app/views/dashboard/extra_params.html.erb +7 -0
  43. data/spec/dummy/app/views/dashboard/index.html.erb +0 -2
  44. data/spec/dummy/app/views/layouts/application.html.erb +7 -1
  45. data/spec/dummy/app/views/students/_student.html.erb +6 -0
  46. data/spec/dummy/app/views/students/index.html.erb +1 -0
  47. data/spec/dummy/app/views/users/_user.html.erb +1 -1
  48. data/spec/dummy/config/application.rb +2 -0
  49. data/spec/dummy/config/environment.rb +1 -0
  50. data/spec/dummy/config/routes.rb +9 -5
  51. data/spec/dummy/db/.gitignore +1 -0
  52. data/spec/dummy/db/migrate/20121119085620_devise_create_users.rb +1 -1
  53. data/spec/dummy/db/migrate/20140418160449_create_admin_users.rb +1 -1
  54. data/spec/dummy/db/migrate/20191022100000_create_students.rb +14 -0
  55. data/spec/dummy/db/schema.rb +37 -31
  56. data/spec/models/user_spec.rb +3 -30
  57. data/spec/orm/active_record.rb +5 -2
  58. data/spec/spec_helper.rb +3 -3
  59. data/spec/support/factories.rb +13 -9
  60. metadata +57 -19
  61. data/lib/devise_masquerade/model.rb +0 -42
  62. data/spec/controllers/masquerades_controller_spec.rb +0 -42
  63. data/spec/dummy/app/controllers/masquerades_controller.rb +0 -5
data/Makefile CHANGED
@@ -1,6 +1,11 @@
1
+ release:
2
+ bundle exec rake release
3
+ .PHONY: release
4
+
1
5
  setup:
2
6
  cd spec/dummy && \
3
- RAILS_ENV=test rake db:setup
7
+ bundle exec rails db:environment:set RAILS_ENV=test && \
8
+ RAILS_ENV=test bundle exec rails db:setup
4
9
  .PHONY: setup
5
10
 
6
11
  rspec:
data/README.md CHANGED
@@ -33,18 +33,30 @@ In the view you can use url helper for defining link:
33
33
 
34
34
  = link_to "Login As", masquerade_path(user)
35
35
 
36
+ `masquerade_path` would create specific `/masquerade` path with query params `masquerade`(key) and `masqueraded_resource_class` to know
37
+ which model to choose to search and sign in by masquerade key.
38
+
36
39
  In the model you'll need to add the parameter :masqueradable to the existing comma separated values in the devise method:
37
40
 
38
41
  ```ruby
39
42
  devise :invitable, :confirmable, :database_authenticatable, :registerable, :masqueradable
40
43
  ```
41
44
 
42
- Add into your application_controller.rb:
45
+ Add into your `application_controller.rb` if you want to have custom way on sign in by using masquerade token otherwise you can still
46
+ use only `masquerade_path` in your view to generate temporary token and link to make `Login As`:
43
47
 
44
48
  ```ruby
45
49
  before_action :masquerade_user!
46
50
  ```
47
51
 
52
+ or
53
+
54
+ ```ruby
55
+ before_action :masquerade!
56
+ ```
57
+
58
+ `masquerade!` is generic way in case if you want to support multiple models on masquerade.
59
+
48
60
  Instead of user you can use your resource name admin, student or another names.
49
61
 
50
62
  If you want to back to the owner of masquerade action user you could use
@@ -109,6 +121,18 @@ In your view:
109
121
  end
110
122
  ```
111
123
 
124
+ ## Custom url redirect after finishing masquerade:
125
+
126
+ ```ruby
127
+ class Admin::MasqueradesController < Devise::MasqueradesController
128
+ protected
129
+
130
+ def after_back_masquerade_path_for(resource)
131
+ "/custom_url"
132
+ end
133
+ end
134
+ ```
135
+
112
136
  ## Overriding the finder
113
137
 
114
138
  For example, if you use FriendlyId:
@@ -155,6 +179,14 @@ in `routes.rb`:
155
179
  And check http://localhost:3000/, use for login user1@example.com and
156
180
  'password'
157
181
 
182
+ ## Troubleshooting
183
+
184
+ Are you working in development mode and wondering why masquerade attempts result in a [Receiving "You are already signed in" flash[:error]](https://github.com/oivoodoo/devise_masquerade/issues/58) message? `Filter chain halted as :require_no_authentication rendered or redirected` showing up in your logfile? Chances are that you need to enable caching:
185
+
186
+ rails dev:cache
187
+
188
+ This is a one-time operation, so you can set it and forget it. Should you ever need to disable caching in development, you can re-run the command as required.
189
+
158
190
  ## Test project
159
191
 
160
192
  make test
@@ -1,68 +1,43 @@
1
1
  class Devise::MasqueradesController < DeviseController
2
- if respond_to?(:prepend_before_action)
3
- prepend_before_action :authenticate_scope!, :masquerade_authorize!
4
- else
5
- prepend_before_filter :authenticate_scope!, :masquerade_authorize!
2
+ Devise.mappings.each do |name, _|
3
+ class_eval <<-METHODS, __FILE__, __LINE__ + 1
4
+ skip_before_action :masquerade_#{name}!, raise: false
5
+ METHODS
6
6
  end
7
+ skip_before_action :masquerade!, raise: false
7
8
 
8
- if respond_to?(:before_action)
9
- before_action :save_masquerade_owner_session, :only => :show
10
- else
11
- before_filter :save_masquerade_owner_session, :only => :show
12
- end
9
+ prepend_before_action :authenticate_scope!, :masquerade_authorize!
13
10
 
14
- if respond_to?(:after_action)
15
- after_action :cleanup_masquerade_owner_session, :only => :back
16
- else
17
- after_filter :cleanup_masquerade_owner_session, :only => :back
18
- end
11
+ before_action :save_masquerade_owner_session, only: :show
12
+
13
+ after_action :cleanup_masquerade_owner_session, only: :back
19
14
 
20
15
  def show
21
16
  self.resource = find_resource
22
17
 
23
- unless self.resource
18
+ unless resource
24
19
  flash[:error] = "#{masqueraded_resource_class} not found."
25
20
  redirect_to(new_user_session_path) and return
26
21
  end
27
22
 
28
- self.resource.masquerade!
29
- request.env["devise.skip_trackable"] = "1"
23
+ request.env['devise.skip_trackable'] = '1'
30
24
 
31
- masquerade_sign_in(self.resource)
25
+ masquerade_sign_in(resource)
32
26
 
33
- if Devise.masquerade_routes_back && Rails::VERSION::MAJOR == 5
34
- redirect_back(fallback_location: after_masquerade_full_path_for(resource))
35
- elsif Devise.masquerade_routes_back && request.env['HTTP_REFERER'].present?
36
- redirect_to :back
37
- else
38
- redirect_to(after_masquerade_full_path_for(resource))
39
- end
27
+ go_back(resource, path: after_masquerade_full_path_for(resource))
40
28
  end
41
29
 
42
30
  def back
43
- user_id = session[session_key]
44
-
45
- owner_user = if user_id.present?
46
- masquerading_resource_class.to_adapter.find_first(:id => user_id)
47
- else
48
- send(:"current_#{masquerading_resource_name}")
49
- end
31
+ self.resource = find_owner_resource
50
32
 
51
- if masquerading_resource_class != masqueraded_resource_class
33
+ if resource.class != masqueraded_resource_class
52
34
  sign_out(send("current_#{masqueraded_resource_name}"))
53
35
  end
54
36
 
55
- masquerade_sign_in(owner_user)
56
- request.env["devise.skip_trackable"] = nil
37
+ masquerade_sign_in(resource)
38
+ request.env['devise.skip_trackable'] = nil
57
39
 
58
- if Devise.masquerade_routes_back && Rails::VERSION::MAJOR == 5
59
- # If using the masquerade_routes_back and Rails 5
60
- redirect_back(fallback_location: after_back_masquerade_path_for(owner_user))
61
- elsif Devise.masquerade_routes_back && request.env['HTTP_REFERER'].present?
62
- redirect_to :back
63
- else
64
- redirect_to after_back_masquerade_path_for(owner_user)
65
- end
40
+ go_back(resource, path: after_back_masquerade_path_for(resource))
66
41
  end
67
42
 
68
43
  protected
@@ -76,13 +51,35 @@ class Devise::MasqueradesController < DeviseController
76
51
  end
77
52
 
78
53
  def find_resource
79
- masqueraded_resource_class.to_adapter.find_first(:id => params[:id])
54
+ GlobalID::Locator.locate_signed params[Devise.masquerade_param], for: 'masquerade'
55
+ end
56
+
57
+ def find_owner_resource
58
+ GlobalID::Locator.locate_signed(Rails.cache.read(session_key), for: 'masquerade')
59
+ end
60
+
61
+ def go_back(user, path:)
62
+ if Devise.masquerade_routes_back
63
+ redirect_back(fallback_location: path)
64
+ else
65
+ redirect_to path
66
+ end
80
67
  end
81
68
 
82
69
  private
83
70
 
84
71
  def masqueraded_resource_class
85
- Devise.masqueraded_resource_class || resource_class
72
+ @masqueraded_resource_class ||= begin
73
+ unless params[:masqueraded_resource_class].blank?
74
+ params[:masqueraded_resource_class].constantize
75
+ else
76
+ unless session[session_key_masqueraded_resource_class].blank?
77
+ session[session_key_masquerading_resource_class].constantize
78
+ else
79
+ Devise.masqueraded_resource_class || resource_class
80
+ end
81
+ end
82
+ end
86
83
  end
87
84
 
88
85
  def masqueraded_resource_name
@@ -90,7 +87,17 @@ class Devise::MasqueradesController < DeviseController
90
87
  end
91
88
 
92
89
  def masquerading_resource_class
93
- Devise.masquerading_resource_class || resource_class
90
+ @masquerading_resource_class ||= begin
91
+ unless params[:masquerading_resource_class].blank?
92
+ params[:masquerading_resource_class].constantize
93
+ else
94
+ unless session[session_key_masquerading_resource_class].blank?
95
+ session[session_key_masquerading_resource_class].constantize
96
+ else
97
+ Devise.masquerading_resource_class || resource_class
98
+ end
99
+ end
100
+ end
94
101
  end
95
102
 
96
103
  def masquerading_resource_name
@@ -98,23 +105,15 @@ class Devise::MasqueradesController < DeviseController
98
105
  end
99
106
 
100
107
  def authenticate_scope!
101
- send(:"authenticate_#{masquerading_resource_name}!", :force => true)
108
+ send(:"authenticate_#{masquerading_resource_name}!", force: true)
102
109
  end
103
110
 
104
111
  def after_masquerade_path_for(resource)
105
- "/"
112
+ '/'
106
113
  end
107
114
 
108
115
  def after_masquerade_full_path_for(resource)
109
- if after_masquerade_path_for(resource) =~ /\?/
110
- "#{after_masquerade_path_for(resource)}&#{after_masquerade_param_for(resource)}"
111
- else
112
- "#{after_masquerade_path_for(resource)}?#{after_masquerade_param_for(resource)}"
113
- end
114
- end
115
-
116
- def after_masquerade_param_for(resource)
117
- "#{Devise.masquerade_param}=#{resource.masquerade_key}"
116
+ after_masquerade_path_for(resource)
118
117
  end
119
118
 
120
119
  def after_back_masquerade_path_for(resource)
@@ -122,16 +121,33 @@ class Devise::MasqueradesController < DeviseController
122
121
  end
123
122
 
124
123
  def save_masquerade_owner_session
124
+ resource_gid = send("current_#{masquerading_resource_name}").to_sgid(
125
+ expires_in: Devise.masquerade_expires_in, for: 'masquerade')
126
+ # skip sharing owner id via session
127
+ Rails.cache.write(session_key, resource_gid, expires_in: Devise.masquerade_expires_in)
128
+
125
129
  unless session.key?(session_key)
126
- session[session_key] = send("current_#{masquerading_resource_name}").id
130
+ session[session_key_masquerading_resource_class] = masquerading_resource_class.name
131
+ session[session_key_masqueraded_resource_class] = masqueraded_resource_class.name
127
132
  end
128
133
  end
129
134
 
130
135
  def cleanup_masquerade_owner_session
131
- session.delete(session_key)
136
+ Rails.cache.delete(session_key)
137
+
138
+ session.delete(session_key_masqueraded_resource_class)
139
+ session.delete(session_key_masquerading_resource_class)
132
140
  end
133
141
 
134
142
  def session_key
135
143
  "devise_masquerade_#{masqueraded_resource_name}".to_sym
136
144
  end
145
+
146
+ def session_key_masqueraded_resource_class
147
+ "devise_masquerade_masqueraded_resource_class"
148
+ end
149
+
150
+ def session_key_masquerading_resource_class
151
+ "devise_masquerade_masquerading_resource_class"
152
+ end
137
153
  end
@@ -11,7 +11,7 @@ Gem::Specification.new do |gem|
11
11
  gem.email = ['alex.korsak@gmail.com']
12
12
  gem.description = 'devise masquerade library'
13
13
  gem.summary = 'use for login as functionallity on your admin users pages'
14
- gem.homepage = 'http://github.com/oivoodoo/devise_masquerade/'
14
+ gem.homepage = 'http://github.com/oivoodoo/devise_masquerade'
15
15
 
16
16
  gem.files = `git ls-files`.split($/)
17
17
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
@@ -20,8 +20,9 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.license = 'MIT'
22
22
 
23
- gem.add_development_dependency('bundler', '>= 1.1.0')
23
+ gem.add_development_dependency('bundler', '>= 2.0.0')
24
24
 
25
- gem.add_runtime_dependency('railties', '>= 3.0')
26
- gem.add_runtime_dependency('devise', '>= 2.1.0')
25
+ gem.add_runtime_dependency('railties', '>= 5.2.0')
26
+ gem.add_runtime_dependency('devise', '>= 4.7.0')
27
+ gem.add_runtime_dependency('globalid', '>= 0.3.6')
27
28
  end
@@ -13,4 +13,3 @@ Feature: Use back button for returning to the owner of the masquerade action.
13
13
 
14
14
  When I press back masquerade button
15
15
  Then I should be login as owner user
16
-
@@ -0,0 +1,17 @@
1
+ Feature: Use various models for masquerading
2
+ In order to use various models for masquerading
3
+ As an masquerade user
4
+ I want to be able to press press masquerade as link for different models
5
+
6
+ Scenario: Use masquerade button on student and user models
7
+ Given I logged in
8
+ And I have a user for masquerade
9
+ And I have a student for masquerade
10
+
11
+ When I am on the users page
12
+ And I login as one user
13
+ Then I should be login as this user
14
+
15
+ When I am on the students page
16
+ And I login as one student
17
+ Then I should be login as this student
@@ -8,3 +8,4 @@ Given /^I logged in$/ do
8
8
 
9
9
  click_on 'Log in'
10
10
  end
11
+
@@ -1,5 +1,5 @@
1
1
  Given /^I have a user for masquerade$/ do
2
- @mask = create(:user)
2
+ @user_mask = create(:user)
3
3
  end
4
4
 
5
5
  When /^I am on the users page$/ do
@@ -7,11 +7,11 @@ When /^I am on the users page$/ do
7
7
  end
8
8
 
9
9
  When /^I login as one user$/ do
10
- click_on "Login as"
10
+ find('.login_as').click
11
11
  end
12
12
 
13
13
  Then /^I should be login as this user$/ do
14
- find('.current_user').should have_content(@mask.email)
14
+ find('.current_user').should have_content(@user_mask.email)
15
15
  end
16
16
 
17
17
  When /^I press back masquerade button$/ do
@@ -22,3 +22,18 @@ Then /^I should be login as owner user$/ do
22
22
  find('.current_user').should have_content(@user.email)
23
23
  end
24
24
 
25
+ Given /^I have a student for masquerade$/ do
26
+ @student_mask = create(:student)
27
+ end
28
+
29
+ When /^I am on the students page$/ do
30
+ visit '/students'
31
+ end
32
+
33
+ When /^I login as one student$/ do
34
+ find('.login_as').click
35
+ end
36
+
37
+ Then /^I should be login as this student$/ do
38
+ find('.current_student').should have_content(@student_mask.email)
39
+ end
@@ -0,0 +1,11 @@
1
+ Then("I should see maquerade url") do
2
+ page.html.should include('href="/users/masquerade?masquerade=')
3
+ end
4
+
5
+ When("I am on the users page with extra params") do
6
+ visit '/extra_params'
7
+ end
8
+
9
+ Then("I should see maquerade url with extra params") do
10
+ page.html.should include('href="/users/masquerade?key1=value1&amp;masquerade=')
11
+ end
@@ -1,5 +1,5 @@
1
1
  require 'cucumber/rails'
2
- require 'factory_girl'
2
+ require 'factory_bot'
3
3
  require 'database_cleaner'
4
4
  require 'cucumber/rspec/doubles'
5
5
 
@@ -9,9 +9,11 @@ ENV["RAILS_ENV"] = "test"
9
9
 
10
10
  Capybara.default_selector = :css
11
11
 
12
- ActionController::Base.allow_rescue = false
12
+ ActiveSupport.on_load(:action_controller) do
13
+ self.allow_rescue = false
14
+ end
13
15
 
14
- World(FactoryGirl::Syntax::Methods)
16
+ World(FactoryBot::Syntax::Methods)
15
17
 
16
18
  begin
17
19
  DatabaseCleaner.strategy = :transaction
@@ -20,7 +22,24 @@ rescue NameError
20
22
  end
21
23
 
22
24
  Cucumber::Rails::Database.javascript_strategy = :truncation
23
- Capybara.javascript_driver = :webkit
25
+
26
+ Capybara.register_driver :chrome do |app|
27
+ Capybara::Selenium::Driver.new(app, browser: :chrome)
28
+ end
29
+
30
+ Capybara.register_driver :headless_chrome do |app|
31
+ caps = Selenium::WebDriver::Remote::Capabilities.chrome(loggingPrefs: { browser: 'ALL' })
32
+ opts = Selenium::WebDriver::Chrome::Options.new
33
+
34
+ chrome_args = %w[--headless --window-size=1920,1080 --no-sandbox --disable-dev-shm-usage]
35
+ chrome_args.each { |arg| opts.add_argument(arg) }
36
+ Capybara::Selenium::Driver.new(app, browser: :chrome, options: opts, desired_capabilities: caps)
37
+ end
38
+
39
+ Capybara.configure do |config|
40
+ # change this to :chrome to observe tests in a real browser
41
+ config.javascript_driver = :headless_chrome
42
+ end
24
43
 
25
44
  Before do
26
45
  allow_any_instance_of(DeviseController).to receive(:devise_mapping) { Devise.mappings[:user] }
@@ -0,0 +1,14 @@
1
+ Feature: Use masquerade path to generate routes on page
2
+ In order to have the way to render masquerade path
3
+ As an user
4
+ I want to be able to see the url and use it
5
+
6
+ Scenario: Use masquerade path helper
7
+ Given I logged in
8
+ And I have a user for masquerade
9
+
10
+ When I am on the users page
11
+ Then I should see maquerade url
12
+
13
+ When I am on the users page with extra params
14
+ Then I should see maquerade url with extra params