devise_masquerade 1.0.0 → 1.3.2

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 (49) hide show
  1. checksums.yaml +4 -4
  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/.ruby-version +1 -1
  6. data/.travis.yml +1 -0
  7. data/Gemfile +4 -2
  8. data/Gemfile.lock +31 -18
  9. data/README.md +21 -1
  10. data/app/controllers/devise/masquerades_controller.rb +66 -24
  11. data/devise_masquerade.gemspec +1 -1
  12. data/features/back.feature +0 -1
  13. data/features/multiple_masquerading_models.feature +17 -0
  14. data/features/step_definitions/auth_steps.rb +1 -0
  15. data/features/step_definitions/back_steps.rb +18 -3
  16. data/features/step_definitions/url_helpers_steps.rb +11 -0
  17. data/features/url_helpers.feature +14 -0
  18. data/lib/devise_masquerade.rb +5 -5
  19. data/lib/devise_masquerade/controllers/helpers.rb +27 -6
  20. data/lib/devise_masquerade/controllers/url_helpers.rb +14 -2
  21. data/lib/devise_masquerade/models/masqueradable.rb +2 -27
  22. data/lib/devise_masquerade/rails.rb +5 -7
  23. data/lib/devise_masquerade/routes.rb +3 -2
  24. data/lib/devise_masquerade/version.rb +1 -1
  25. data/spec/controllers/admin/dashboard_controller_spec.rb +3 -4
  26. data/spec/controllers/dashboard_controller_spec.rb +3 -5
  27. data/spec/controllers/devise/masquerades_controller_spec.rb +60 -39
  28. data/spec/controllers/masquerades_tests_controller_spec.rb +41 -0
  29. data/spec/dummy/app/controllers/admin/dashboard_controller.rb +0 -1
  30. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  31. data/spec/dummy/app/controllers/dashboard_controller.rb +4 -1
  32. data/spec/dummy/app/controllers/masquerades_tests_controller.rb +7 -0
  33. data/spec/dummy/app/controllers/students_controller.rb +8 -0
  34. data/spec/dummy/app/models/student.rb +3 -0
  35. data/spec/dummy/app/views/admin/dashboard/index.html.erb +0 -2
  36. data/spec/dummy/app/views/dashboard/extra_params.html.erb +7 -0
  37. data/spec/dummy/app/views/dashboard/index.html.erb +0 -2
  38. data/spec/dummy/app/views/layouts/application.html.erb +8 -2
  39. data/spec/dummy/app/views/students/_student.html.erb +6 -0
  40. data/spec/dummy/app/views/students/index.html.erb +1 -0
  41. data/spec/dummy/app/views/users/_user.html.erb +1 -1
  42. data/spec/dummy/config/routes.rb +9 -5
  43. data/spec/dummy/db/migrate/20191022100000_create_students.rb +14 -0
  44. data/spec/dummy/db/schema.rb +10 -1
  45. data/spec/models/user_spec.rb +3 -30
  46. data/spec/support/factories.rb +8 -4
  47. metadata +34 -13
  48. data/spec/controllers/masquerades_controller_spec.rb +0 -42
  49. data/spec/dummy/app/controllers/masquerades_controller.rb +0 -5
@@ -24,5 +24,5 @@ Gem::Specification.new do |gem|
24
24
 
25
25
  gem.add_runtime_dependency('railties', '>= 5.2.0')
26
26
  gem.add_runtime_dependency('devise', '>= 4.7.0')
27
- gem.add_runtime_dependency('zeitwerk', '>= 2.2.0')
27
+ gem.add_runtime_dependency('globalid', '>= 0.3.6')
28
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&masquerade=')
11
+ end
@@ -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
@@ -1,8 +1,8 @@
1
- require 'zeitwerk'
2
- loader = Zeitwerk::Loader.for_gem
3
- loader.setup # ready!
4
-
5
1
  require 'devise'
2
+ require 'devise_masquerade/version'
3
+ require 'devise_masquerade/routes'
4
+ require 'devise_masquerade/controllers/helpers'
5
+ require 'devise_masquerade/controllers/url_helpers'
6
6
  require 'devise_masquerade/rails'
7
7
 
8
8
  module Devise
@@ -10,7 +10,7 @@ module Devise
10
10
  @@masquerade_param = 'masquerade'
11
11
 
12
12
  mattr_accessor :masquerade_expires_in
13
- @@masquerade_expires_in = 10.seconds
13
+ @@masquerade_expires_in = 1.minute
14
14
 
15
15
  mattr_accessor :masquerade_key_size
16
16
  @@masquerade_key_size = 16
@@ -6,23 +6,44 @@ module DeviseMasquerade
6
6
  class_name = mapping.class_name
7
7
 
8
8
  class_eval <<-METHODS, __FILE__, __LINE__ + 1
9
+ def masquerade!
10
+ return if params["#{Devise.masquerade_param}"].blank?
11
+
12
+ klass = unless params[:masqueraded_resource_class].blank?
13
+ params[:masqueraded_resource_class].constantize
14
+ else
15
+ if Devise.masqueraded_resource_class
16
+ Devise.masqueraded_resource_class
17
+ elsif defined?(User)
18
+ User
19
+ end
20
+ end
21
+ return unless klass
22
+
23
+ resource = GlobalID::Locator.locate_signed params[Devise.masquerade_param], for: 'masquerade'
24
+
25
+ if resource
26
+ masquerade_sign_in(resource)
27
+ end
28
+ end
29
+
9
30
  def masquerade_#{name}!
10
31
  return if params["#{Devise.masquerade_param}"].blank?
11
32
 
12
- #{name} = ::#{class_name}.find_by_masquerade_key(params["#{Devise.masquerade_param}"])
33
+ resource = GlobalID::Locator.locate_signed params[Devise.masquerade_param], for: 'masquerade'
13
34
 
14
- if #{name}
15
- masquerade_sign_in(#{name})
35
+ if resource
36
+ masquerade_sign_in(resource)
16
37
  end
17
38
  end
18
39
 
19
40
  def #{name}_masquerade?
20
- session[:"devise_masquerade_#{name}"].present?
41
+ ::Rails.cache.exist?(:"devise_masquerade_#{name}").present?
21
42
  end
22
43
 
23
44
  def #{name}_masquerade_owner
24
45
  return nil unless send(:#{name}_masquerade?)
25
- ::#{class_name}.to_adapter.find_first(:id => session[:"devise_masquerade_#{name}"])
46
+ GlobalID::Locator.locate_signed(Rails.cache.read(:"devise_masquerade_#{name}"), for: 'masquerade')
26
47
  end
27
48
 
28
49
  private
@@ -32,7 +53,7 @@ module DeviseMasquerade
32
53
  if respond_to?(:bypass_sign_in)
33
54
  bypass_sign_in(resource)
34
55
  else
35
- sign_in(resource, :bypass => true)
56
+ sign_in(resource, bypass: true)
36
57
  end
37
58
  else
38
59
  sign_in(resource)
@@ -1,15 +1,27 @@
1
+ require 'securerandom'
2
+
1
3
  module DeviseMasquerade
2
4
  module Controllers
3
5
 
4
6
  module UrlHelpers
5
7
  def masquerade_path(resource, *args)
6
8
  scope = Devise::Mapping.find_scope!(resource)
7
- send("#{scope}_masquerade_path", resource, *args)
9
+
10
+ opts = args.shift || {}
11
+ opts.merge!(masqueraded_resource_class: resource.class.name)
12
+
13
+ opts.merge!(Devise.masquerade_param => resource.masquerade_key)
14
+
15
+ send("#{scope}_masquerade_index_path", opts, *args)
8
16
  end
9
17
 
10
18
  def back_masquerade_path(resource, *args)
11
19
  scope = Devise::Mapping.find_scope!(resource)
12
- send("back_#{scope}_masquerade_index_path", *args)
20
+
21
+ opts = args.first || {}
22
+ opts.merge!(masqueraded_resource_class: resource.class.name)
23
+
24
+ send("back_#{scope}_masquerade_index_path", opts, *args)
13
25
  end
14
26
  end
15
27
 
@@ -4,35 +4,10 @@ module DeviseMasquerade
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- attr_reader :masquerade_key
8
-
9
- def masquerade!
10
- @masquerade_key = SecureRandom.urlsafe_base64(
11
- Devise.masquerade_key_size)
12
- cache_key = self.class.cache_masquerade_key_by(@masquerade_key)
13
- ::Rails.cache.write(
14
- cache_key, id, expires_in: Devise.masquerade_expires_in)
7
+ def masquerade_key
8
+ to_sgid(expires_in: Devise.masquerade_expires_in, for: 'masquerade')
15
9
  end
16
10
  end
17
-
18
- module ClassMethods
19
- def cache_masquerade_key_by(key)
20
- "#{self.name.pluralize.underscore}:#{key}:masquerade"
21
- end
22
-
23
- def remove_masquerade_key!(key)
24
- ::Rails.cache.delete(cache_masquerade_key_by(key))
25
- end
26
-
27
- def find_by_masquerade_key(key)
28
- id = ::Rails.cache.read(cache_masquerade_key_by(key))
29
-
30
- # clean up the cached masquerade key value
31
- remove_masquerade_key!(key)
32
-
33
- where(id: id).first
34
- end
35
- end # ClassMethods
36
11
  end
37
12
  end
38
13
  end
@@ -1,17 +1,15 @@
1
- require 'devise_masquerade/routes'
2
- require 'devise_masquerade/models'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module DeviseMasquerade
5
4
  module Rails
6
5
 
7
6
  class Engine < ::Rails::Engine
8
- ActiveSupport.on_load(:action_controller) do
9
- include DeviseMasquerade::Controllers::Helpers
10
- include DeviseMasquerade::Controllers::UrlHelpers
7
+ initializer "devise.url_helpers" do
8
+ Devise.include_helpers(DeviseMasquerade::Controllers)
11
9
  end
12
10
 
13
- ActiveSupport.on_load(:action_view) do
14
- include DeviseMasquerade::Controllers::UrlHelpers
11
+ ActiveSupport.on_load(:action_controller) do
12
+ include DeviseMasquerade::Controllers::Helpers
15
13
  end
16
14
  end
17
15
 
@@ -3,11 +3,12 @@ module DeviseMasquerade
3
3
 
4
4
  def devise_masquerade(mapping, controllers)
5
5
  resources :masquerade,
6
- only: :show,
7
6
  path: mapping.path_names[:masquerade],
8
- controller: controllers[:masquerades] do
7
+ controller: controllers[:masquerades],
8
+ only: [] do
9
9
 
10
10
  collection do
11
+ get :show
11
12
  get :back
12
13
  end
13
14
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseMasquerade
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.3.2'.freeze
3
3
  end
@@ -5,14 +5,13 @@ describe Admin::DashboardController, type: :controller do
5
5
  before { admin_logged_in }
6
6
 
7
7
  context 'and admin masquerade by user' do
8
- let!(:user) { create(:admin_user) }
8
+ let!(:mask) { create(:admin_user) }
9
9
 
10
10
  before do
11
- user.masquerade!
12
- get :index, params: { masquerade: user.masquerade_key }
11
+ get :index, params: { masquerade: mask.masquerade_key, masqueraded_resource_class: 'Admin::User' }
13
12
  end
14
13
 
15
- it { expect(current_admin_user.reload).to eq(user) }
14
+ it { expect(current_admin_user.reload).to eq(mask) }
16
15
  end
17
16
  end
18
17
  end
@@ -5,15 +5,13 @@ describe DashboardController, type: :controller do
5
5
  before { logged_in }
6
6
 
7
7
  context 'and admin masquerade by user' do
8
- let!(:user) { create(:user) }
8
+ let!(:mask) { create(:user) }
9
9
 
10
10
  before do
11
- user.masquerade!
12
-
13
- get :index, params: { masquerade: user.masquerade_key }
11
+ get :index, params: { masquerade: mask.masquerade_key }
14
12
  end
15
13
 
16
- it { expect(current_user.reload).to eq(user) }
14
+ it { expect(current_user.reload).to eq(mask) }
17
15
  end
18
16
  end
19
17
  end
@@ -7,72 +7,93 @@ describe Devise::MasqueradesController, type: :controller do
7
7
  context 'when logged in' do
8
8
  before { logged_in }
9
9
 
10
+ context 'with masqueradable_class param' do
11
+ let(:mask) { create(:student) }
12
+
13
+ before do
14
+ get :show, params: { id: mask.to_param, masqueraded_resource_class: mask.class.name, masquerade: mask.masquerade_key }
15
+ end
16
+
17
+ it { expect(Rails.cache.read('devise_masquerade_student')).to be }
18
+
19
+ it 'should have warden keys defined' do
20
+ expect(session["warden.user.student.key"].first.first).to eq(mask.id)
21
+ end
22
+
23
+ it { should redirect_to('/') }
24
+ end
25
+
10
26
  describe '#masquerade user' do
11
27
  let(:mask) { create(:user) }
12
28
 
13
29
  before do
14
- expect(SecureRandom).to receive(:urlsafe_base64) { "secure_key" }
15
- get :show, params: { id: mask.to_param }
30
+ get :show, params: { id: mask.to_param, masquerade: mask.masquerade_key }
16
31
  end
17
32
 
18
- it { expect(session.keys).to include('devise_masquerade_user') }
33
+ it { expect(Rails.cache.read('devise_masquerade_user')).to be }
19
34
  it { expect(session["warden.user.user.key"].first.first).to eq(mask.id) }
20
- it { should redirect_to("/?masquerade=secure_key") }
35
+ it { should redirect_to('/') }
21
36
 
22
37
  context 'and back' do
23
38
  before { get :back }
24
39
 
25
40
  it { should redirect_to(masquerade_page) }
26
41
  it { expect(current_user.reload).to eq(@user) }
27
- it { expect(session.keys).not_to include('devise_masquerade_user') }
42
+ it { expect(Rails.cache.read('devise_masquerade_user')).not_to be }
28
43
  end
44
+ end
29
45
 
30
- # Configure masquerade_routes_back setting
31
- describe 'config#masquerade_routes_back' do
32
- before { Devise.setup { |c| c.masquerade_routes_back = true } }
46
+ # Configure masquerade_routes_back setting
47
+ describe 'config#masquerade_routes_back' do
48
+ let(:mask) { create(:user) }
33
49
 
34
- after { Devise.masquerade_routes_back = false }
50
+ before { Devise.setup { |c| c.masquerade_routes_back = true } }
35
51
 
36
- context 'show' do
37
- before { expect(SecureRandom).to receive(:urlsafe_base64) { "secure_key" } }
52
+ after { Devise.masquerade_routes_back = false }
38
53
 
39
- context 'with http referrer' do
40
- before do
41
- @request.env['HTTP_REFERER'] = 'previous_location'
42
- get :show, params: { id: mask.to_param }
43
- end # before
54
+ context 'show' do
55
+ context 'with http referrer' do
56
+ before do
57
+ @request.env['HTTP_REFERER'] = 'previous_location'
58
+ get :show, params: { id: mask.to_param, masquerade: mask.masquerade_key }
59
+ end # before
44
60
 
45
- it { should redirect_to('previous_location') }
46
- end # context
61
+ it { should redirect_to('previous_location') }
62
+ end # context
47
63
 
48
- context 'no http referrer' do
49
- before do
50
- allow_any_instance_of(described_class).to(
51
- receive(:after_masquerade_path_for).and_return("/dashboard?color=red"))
52
- end
64
+ context 'no http referrer' do
65
+ before do
66
+ allow_any_instance_of(described_class).to(
67
+ receive(:after_masquerade_path_for).and_return("/dashboard?color=red"))
68
+ end
53
69
 
54
- before { get :show, params: { id: mask.to_param } }
70
+ before { get :show, params: { id: mask.to_param, masquerade: mask.masquerade_key } }
55
71
 
56
- it { should redirect_to("/dashboard?color=red&masquerade=secure_key") }
57
- end # context
72
+ it { should redirect_to("/dashboard?color=red") }
58
73
  end # context
74
+ end # context
59
75
 
60
- context '< Rails 5, and back' do
61
- before { get :back }
76
+ context 'and back' do
77
+ before do
78
+ get :show, params: { id: mask.to_param, masquerade: mask.masquerade_key }
62
79
 
63
- it { should redirect_to(masquerade_page) }
64
- end # context
80
+ get :back
81
+ end
65
82
 
66
- context '< Rails 5, and back fallback if http_referer not present' do
67
- before do
68
- @request.env['HTTP_REFERER'] = 'previous_location'
69
- get :back
70
- end
83
+ it { should redirect_to(masquerade_page) }
84
+ end # context
71
85
 
72
- it { should redirect_to('previous_location') }
73
- end # context
74
- end # describe
75
- end
86
+ context 'and back fallback if http_referer not present' do
87
+ before do
88
+ get :show, params: { id: mask.to_param, masquerade: mask.masquerade_key }
89
+
90
+ @request.env['HTTP_REFERER'] = 'previous_location'
91
+ get :back
92
+ end
93
+
94
+ it { should redirect_to('previous_location') }
95
+ end # context
96
+ end # describe
76
97
  end
77
98
 
78
99
  context 'when not logged in' do