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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/brakeman-analysis.yml +44 -0
- data/.github/workflows/rubocop-analysis.yml +39 -0
- data/.gitignore +1 -2
- data/.ruby-version +1 -1
- data/.travis.yml +3 -4
- data/Gemfile +16 -10
- data/Gemfile.lock +307 -0
- data/Makefile +6 -1
- data/README.md +33 -1
- data/app/controllers/devise/masquerades_controller.rb +75 -59
- data/devise_masquerade.gemspec +5 -4
- data/features/back.feature +0 -1
- data/features/multiple_masquerading_models.feature +17 -0
- data/features/step_definitions/auth_steps.rb +1 -0
- data/features/step_definitions/back_steps.rb +18 -3
- data/features/step_definitions/url_helpers_steps.rb +11 -0
- data/features/support/env.rb +23 -4
- data/features/url_helpers.feature +14 -0
- data/lib/devise_masquerade.rb +3 -9
- data/lib/devise_masquerade/controllers/helpers.rb +27 -8
- data/lib/devise_masquerade/controllers/url_helpers.rb +16 -2
- data/lib/devise_masquerade/models.rb +9 -0
- data/lib/devise_masquerade/models/masqueradable.rb +13 -0
- data/lib/devise_masquerade/rails.rb +14 -4
- data/lib/devise_masquerade/routes.rb +11 -8
- data/lib/devise_masquerade/version.rb +1 -1
- data/spec/controllers/admin/dashboard_controller_spec.rb +3 -4
- data/spec/controllers/dashboard_controller_spec.rb +3 -5
- data/spec/controllers/devise/masquerades_controller_spec.rb +62 -38
- data/spec/controllers/masquerades_tests_controller_spec.rb +41 -0
- data/spec/dummy/app/controllers/admin/dashboard_controller.rb +1 -2
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/dashboard_controller.rb +5 -2
- data/spec/dummy/app/controllers/masquerades_tests_controller.rb +7 -0
- data/spec/dummy/app/controllers/students_controller.rb +8 -0
- data/spec/dummy/app/models/admin/user.rb +0 -7
- data/spec/dummy/app/models/student.rb +3 -0
- data/spec/dummy/app/models/user.rb +1 -10
- data/spec/dummy/app/views/admin/dashboard/index.html.erb +0 -2
- data/spec/dummy/app/views/dashboard/extra_params.html.erb +7 -0
- data/spec/dummy/app/views/dashboard/index.html.erb +0 -2
- data/spec/dummy/app/views/layouts/application.html.erb +7 -1
- data/spec/dummy/app/views/students/_student.html.erb +6 -0
- data/spec/dummy/app/views/students/index.html.erb +1 -0
- data/spec/dummy/app/views/users/_user.html.erb +1 -1
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/config/environment.rb +1 -0
- data/spec/dummy/config/routes.rb +9 -5
- data/spec/dummy/db/.gitignore +1 -0
- data/spec/dummy/db/migrate/20121119085620_devise_create_users.rb +1 -1
- data/spec/dummy/db/migrate/20140418160449_create_admin_users.rb +1 -1
- data/spec/dummy/db/migrate/20191022100000_create_students.rb +14 -0
- data/spec/dummy/db/schema.rb +37 -31
- data/spec/models/user_spec.rb +3 -30
- data/spec/orm/active_record.rb +5 -2
- data/spec/spec_helper.rb +3 -3
- data/spec/support/factories.rb +13 -9
- metadata +57 -19
- data/lib/devise_masquerade/model.rb +0 -42
- data/spec/controllers/masquerades_controller_spec.rb +0 -42
- data/spec/dummy/app/controllers/masquerades_controller.rb +0 -5
data/Makefile
CHANGED
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
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
|
-
|
29
|
-
request.env["devise.skip_trackable"] = "1"
|
23
|
+
request.env['devise.skip_trackable'] = '1'
|
30
24
|
|
31
|
-
masquerade_sign_in(
|
25
|
+
masquerade_sign_in(resource)
|
32
26
|
|
33
|
-
|
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
|
-
|
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
|
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(
|
56
|
-
request.env[
|
37
|
+
masquerade_sign_in(resource)
|
38
|
+
request.env['devise.skip_trackable'] = nil
|
57
39
|
|
58
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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}!", :
|
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
|
-
|
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[
|
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
|
-
|
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
|
data/devise_masquerade.gemspec
CHANGED
@@ -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', '>=
|
23
|
+
gem.add_development_dependency('bundler', '>= 2.0.0')
|
24
24
|
|
25
|
-
gem.add_runtime_dependency('railties', '>=
|
26
|
-
gem.add_runtime_dependency('devise', '>=
|
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
|
data/features/back.feature
CHANGED
@@ -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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Given /^I have a user for masquerade$/ do
|
2
|
-
@
|
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
|
-
|
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(@
|
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
|
data/features/support/env.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'cucumber/rails'
|
2
|
-
require '
|
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
|
-
|
12
|
+
ActiveSupport.on_load(:action_controller) do
|
13
|
+
self.allow_rescue = false
|
14
|
+
end
|
13
15
|
|
14
|
-
World(
|
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
|
-
|
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
|