pretender 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/Gemfile +1 -1
- data/README.md +37 -54
- data/Rakefile +7 -0
- data/lib/pretender.rb +10 -6
- data/lib/pretender/version.rb +1 -1
- data/pretender.gemspec +10 -7
- data/test/pretender_test.rb +60 -0
- data/test/test_helper.rb +26 -0
- metadata +39 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cf9079c429f9aa0c9eb9f298f6c26ca15e983cc
|
4
|
+
data.tar.gz: 79f8b2328f1ec9ea662ee1feb112db4b1bd8d57d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7e6895927961d7cfed49fdc450b0c3102425fbfbf027ae4b6cb12bd3e29eb134399e452d2974d52925146505ec589a74ef5d0beea4719fb1d301424de216c12
|
7
|
+
data.tar.gz: e6695c73ed14d15359114f0a779b77bb186e299a18bf679f5774921e66759c57a6852c3bf4f59b56dfab75e9a5e3c32a3dde543987b868df247dc29cdb20f3da
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,69 +1,64 @@
|
|
1
1
|
# Pretender
|
2
2
|
|
3
|
-
As an admin, there are times you want to see exactly what another user sees
|
3
|
+
As an admin, there are times you want to see exactly what another user sees. Meet Pretender.
|
4
4
|
|
5
|
-
-
|
5
|
+
- Easily to switch between users
|
6
6
|
- Minimal code changes
|
7
|
-
- Plays nicely with auditing tools
|
7
|
+
- Plays nicely with auditing tools
|
8
8
|
|
9
|
-
[Rock on](http://www.youtube.com/watch?v=SBjQ9tuuTJQ)
|
9
|
+
:boom: [Rock on](http://www.youtube.com/watch?v=SBjQ9tuuTJQ)
|
10
10
|
|
11
|
-
Pretender is
|
11
|
+
Pretender is flexible and lightweight - less than 40 lines of code :-)
|
12
12
|
|
13
|
-
|
14
|
-
(devise, authlogic, sorcery, and many more - it’s agnostic)
|
13
|
+
Works with any authentication system - [Devise](https://github.com/plataformatec/devise), [Authlogic](https://github.com/binarylogic/authlogic), and [Sorcery](https://github.com/NoamB/sorcery) to name a few.
|
15
14
|
|
16
|
-
|
15
|
+
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
17
16
|
|
18
|
-
##
|
17
|
+
## Installation
|
19
18
|
|
20
19
|
Add this line to your application’s Gemfile:
|
21
20
|
|
22
21
|
```ruby
|
23
|
-
# Gemfile
|
24
22
|
gem 'pretender'
|
25
23
|
```
|
26
24
|
|
27
|
-
And add this
|
25
|
+
And add this to your `ApplicationController`:
|
28
26
|
|
29
27
|
```ruby
|
30
|
-
# app/controllers/application_controller.rb
|
31
28
|
class ApplicationController < ActionController::Base
|
32
29
|
impersonates :user
|
33
30
|
end
|
34
31
|
```
|
35
32
|
|
36
|
-
|
33
|
+
If `current_user` is defined in your `ApplicationController`, it must come **before** the `impersonates` method.
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
## How It Works
|
36
|
+
|
37
|
+
Sign in as another user with:
|
41
38
|
|
39
|
+
```
|
42
40
|
impersonate_user(user)
|
43
|
-
|
41
|
+
```
|
44
42
|
|
45
|
-
|
46
|
-
# become yourself again
|
47
|
-
```
|
43
|
+
The `current_user` method now returns the impersonated user.
|
48
44
|
|
49
|
-
|
45
|
+
You can access the true user with:
|
50
46
|
|
51
|
-
```
|
52
|
-
|
53
|
-
# now returns:
|
54
|
-
# - if impersonating, the impersonated user
|
55
|
-
# - otherwise, the true user
|
47
|
+
```
|
48
|
+
true_user
|
56
49
|
```
|
57
50
|
|
58
|
-
|
51
|
+
And stop impersonating with:
|
59
52
|
|
60
|
-
|
53
|
+
```ruby
|
54
|
+
stop_impersonating_user
|
55
|
+
```
|
61
56
|
|
62
57
|
### Sample Implementation
|
63
58
|
|
64
59
|
```ruby
|
65
60
|
class Admin::UsersController < ApplicationController
|
66
|
-
before_filter :require_admin
|
61
|
+
before_filter :require_admin!
|
67
62
|
|
68
63
|
def impersonate
|
69
64
|
user = User.find(params[:id])
|
@@ -75,50 +70,42 @@ class Admin::UsersController < ApplicationController
|
|
75
70
|
stop_impersonating_user
|
76
71
|
redirect_to root_path
|
77
72
|
end
|
78
|
-
|
79
73
|
end
|
80
74
|
```
|
81
75
|
|
82
|
-
|
76
|
+
Show when someone is signed in as another user in your application layout.
|
83
77
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
- # app/views/layouts/application.html.haml
|
90
|
-
- if current_user != true_user
|
91
|
-
.alert
|
92
|
-
You (#{true_user.name}) are signed in as #{current_user.name}
|
93
|
-
= link_to "Back to admin", stop_impersonating_path
|
78
|
+
```erb
|
79
|
+
<% if current_user != true_user %>
|
80
|
+
You (<%= true_user.name %>) are signed in as <%= current_user.name %>
|
81
|
+
<%= link_to "Back to admin", stop_impersonating_path %>
|
82
|
+
<% end %>
|
94
83
|
```
|
95
84
|
|
96
|
-
|
85
|
+
## Audits
|
97
86
|
|
98
|
-
If you keep audit logs with a library like [
|
87
|
+
If you keep audit logs with a library like [Audited](https://github.com/collectiveidea/audited), make sure it uses the **true user**.
|
99
88
|
|
100
89
|
```ruby
|
101
90
|
Audited.current_user_method = :true_user
|
102
91
|
```
|
103
92
|
|
104
|
-
|
93
|
+
## Configuration
|
105
94
|
|
106
95
|
Pretender is super flexible. You can change the names of methods and even impersonate multiple roles at the same time. Here’s the default configuration.
|
107
96
|
|
108
97
|
```ruby
|
109
|
-
# app/controllers/application_controller.rb
|
110
98
|
impersonates :user,
|
111
|
-
:
|
112
|
-
:
|
99
|
+
method: :current_user,
|
100
|
+
with: -> (id) { User.find_by(id: id) }
|
113
101
|
```
|
114
102
|
|
115
103
|
Mold it to fit your application.
|
116
104
|
|
117
105
|
```ruby
|
118
|
-
# app/controllers/application_controller.rb
|
119
106
|
impersonates :account,
|
120
|
-
:
|
121
|
-
:
|
107
|
+
method: :authenticated_account,
|
108
|
+
with: -> (id) { EnterpriseAccount.find_by(id: id) }
|
122
109
|
```
|
123
110
|
|
124
111
|
This creates three methods:
|
@@ -129,8 +116,6 @@ impersonate_account
|
|
129
116
|
stop_impersonating_account
|
130
117
|
```
|
131
118
|
|
132
|
-
Also, authenticated_account is overridden with `EnterpriseAccount.where(:id => id).first`
|
133
|
-
|
134
119
|
## Contributing
|
135
120
|
|
136
121
|
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
@@ -139,5 +124,3 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
139
124
|
- Fix bugs and [submit pull requests](https://github.com/ankane/pretender/pulls)
|
140
125
|
- Write, clarify, or fix documentation
|
141
126
|
- Suggest or add new features
|
142
|
-
|
143
|
-
That’s all folks!
|
data/Rakefile
CHANGED
data/lib/pretender.rb
CHANGED
@@ -1,29 +1,32 @@
|
|
1
1
|
require "pretender/version"
|
2
|
+
require "active_support"
|
2
3
|
|
3
4
|
module Pretender
|
5
|
+
class Error < StandardError; end
|
4
6
|
|
5
7
|
def impersonates(scope = :user, opts = {})
|
6
8
|
impersonated_method = opts[:method] || :"current_#{scope}"
|
7
|
-
impersonate_with = opts[:with] || proc{|id| scope.to_s.classify.constantize.where(:id => id).first }
|
9
|
+
impersonate_with = opts[:with] || proc { |id| scope.to_s.classify.constantize.where(:id => id).first }
|
8
10
|
true_method = :"true_#{scope}"
|
9
11
|
session_key = :"impersonated_#{scope}_id"
|
10
12
|
impersonated_var = :"@impersonated_#{scope}"
|
11
13
|
|
12
14
|
# define methods
|
13
|
-
if
|
15
|
+
if method_defined?(impersonated_method)
|
14
16
|
alias_method true_method, impersonated_method
|
15
17
|
else
|
16
18
|
define_method true_method do
|
19
|
+
raise Pretender::Error, "Cannot find method: #{impersonated_method}" unless ActionController::Base.method_defined?(impersonated_method)
|
17
20
|
ActionController::Base.instance_method(impersonated_method).bind(self).call
|
18
21
|
end
|
19
22
|
end
|
20
23
|
helper_method true_method
|
21
24
|
|
22
25
|
define_method impersonated_method do
|
23
|
-
|
26
|
+
unless instance_variable_get(impersonated_var)
|
24
27
|
# only fetch impersonation if user is logged in and impersonation_id exists
|
25
28
|
true_resource = send(true_method)
|
26
|
-
if session[session_key]
|
29
|
+
if session[session_key] && !true_resource
|
27
30
|
session[session_key] = nil
|
28
31
|
end
|
29
32
|
value = (session[session_key] && impersonate_with.call(session[session_key])) || true_resource
|
@@ -42,7 +45,8 @@ module Pretender
|
|
42
45
|
session[session_key] = nil
|
43
46
|
end
|
44
47
|
end
|
45
|
-
|
46
48
|
end
|
47
49
|
|
48
|
-
|
50
|
+
ActiveSupport.on_load(:action_controller) do
|
51
|
+
ActionController::Base.send(:extend, Pretender)
|
52
|
+
end
|
data/lib/pretender/version.rb
CHANGED
data/pretender.gemspec
CHANGED
@@ -1,23 +1,26 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "pretender/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "pretender"
|
8
8
|
spec.version = Pretender::VERSION
|
9
9
|
spec.authors = ["Andrew Kane"]
|
10
|
-
spec.email = ["
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage = "
|
10
|
+
spec.email = ["andrew@chartkick.com"]
|
11
|
+
spec.description = "Simple, powerful user impersonation for Rails"
|
12
|
+
spec.summary = "Easy to switch back and forth between roles, minimal code changes, and plays nicely with auditing tools"
|
13
|
+
spec.homepage = "https://github.com/ankane/pretender"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split(
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "actionpack"
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
24
|
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest"
|
23
26
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
module TheTruth
|
4
|
+
def test_original_state
|
5
|
+
@controller.current_user = @impersonator
|
6
|
+
|
7
|
+
assert_equal @impersonator, @controller.true_user
|
8
|
+
assert_equal @impersonator, @controller.current_user
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_impersonates
|
12
|
+
@controller.current_user = @impersonator
|
13
|
+
@controller.impersonate_user @impersonated
|
14
|
+
|
15
|
+
assert_equal @impersonator, @controller.true_user
|
16
|
+
assert_equal @impersonated, @controller.current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_impersonated_state
|
20
|
+
@controller.current_user = @impersonator
|
21
|
+
@controller.session[:impersonated_user_id] = @impersonated.id
|
22
|
+
|
23
|
+
assert_equal @impersonator, @controller.true_user
|
24
|
+
assert_equal @impersonated, @controller.current_user
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_stops_impersonating
|
28
|
+
@controller.current_user = @impersonator
|
29
|
+
@controller.session[:impersonated_user_id] = @impersonated.id
|
30
|
+
@controller.stop_impersonating_user
|
31
|
+
|
32
|
+
assert_equal @impersonator, @controller.true_user
|
33
|
+
assert_equal @impersonator, @controller.current_user
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class PretenderTest < Minitest::Test
|
38
|
+
include TheTruth
|
39
|
+
|
40
|
+
def setup
|
41
|
+
@impersonator = User.new("impersonator")
|
42
|
+
@impersonated = User.new("impersonated")
|
43
|
+
@controller = ApplicationController.new
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class SuperPretenderTest < Minitest::Test
|
48
|
+
include TheTruth
|
49
|
+
|
50
|
+
def setup
|
51
|
+
@impersonator = User.new("impersonator")
|
52
|
+
@impersonated = User.new("impersonated")
|
53
|
+
@controller = ApplicationController.new
|
54
|
+
class << @controller
|
55
|
+
def current_user
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
Bundler.require(:default)
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "minitest/pride"
|
5
|
+
require "action_controller"
|
6
|
+
|
7
|
+
User = Struct.new(:id) do
|
8
|
+
def self.where(id: nil)
|
9
|
+
[new(id)]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ActionController
|
14
|
+
class Base
|
15
|
+
attr_reader :session
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@session = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ApplicationController < ActionController::Base
|
24
|
+
attr_accessor :current_user
|
25
|
+
impersonates :user
|
26
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pretender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,14 +52,29 @@ dependencies:
|
|
38
52
|
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
description: Simple, powerful user impersonation for Rails
|
42
70
|
email:
|
43
|
-
-
|
71
|
+
- andrew@chartkick.com
|
44
72
|
executables: []
|
45
73
|
extensions: []
|
46
74
|
extra_rdoc_files: []
|
47
75
|
files:
|
48
76
|
- ".gitignore"
|
77
|
+
- CHANGELOG.md
|
49
78
|
- Gemfile
|
50
79
|
- LICENSE.txt
|
51
80
|
- README.md
|
@@ -53,7 +82,9 @@ files:
|
|
53
82
|
- lib/pretender.rb
|
54
83
|
- lib/pretender/version.rb
|
55
84
|
- pretender.gemspec
|
56
|
-
|
85
|
+
- test/pretender_test.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
homepage: https://github.com/ankane/pretender
|
57
88
|
licenses:
|
58
89
|
- MIT
|
59
90
|
metadata: {}
|
@@ -73,9 +104,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
104
|
version: '0'
|
74
105
|
requirements: []
|
75
106
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.4.5.1
|
77
108
|
signing_key:
|
78
109
|
specification_version: 4
|
79
110
|
summary: Easy to switch back and forth between roles, minimal code changes, and plays
|
80
111
|
nicely with auditing tools
|
81
|
-
test_files:
|
112
|
+
test_files:
|
113
|
+
- test/pretender_test.rb
|
114
|
+
- test/test_helper.rb
|