devise_masquerade 1.0.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
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