mr_mime 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3e2047a91eb58ca1d3f799031c3649fc8e4add6
4
+ data.tar.gz: b08268e26e4b204ac74b67d8aea4d7efec4b4482
5
+ SHA512:
6
+ metadata.gz: 3cdf3560ca2b5b0fcf659633cfeefbcb0c5fad2cde0020eb2ee34a38ebaaaf01881a62d7be38d3ba5ac94ca7fad4b54b51ccafdbb055e51e255bbcf4f7a90906
7
+ data.tar.gz: 7da15de7885351774d7230e7580045783a21de7dc989dc1a07597d6e1e160b57264cac6fd9d0827d7a4e95284278bc3d295cc75d2015c6522ea5c830777abef0
@@ -0,0 +1,16 @@
1
+ .mr-mime-impersonation-footer {
2
+ padding-top: 25px;
3
+ font-size: 20px;
4
+ position: fixed;
5
+ height: 75px;
6
+ bottom: 0px;
7
+ background-color: #fbefd9;
8
+ width: 100%;
9
+ z-index: 10;
10
+ text-align: center;
11
+ border-top: 3px solid #4a4a4a;
12
+ }
13
+
14
+ .mr-mime-new-impersonation {
15
+ display: inline-block;
16
+ }
@@ -0,0 +1,5 @@
1
+ module MrMime
2
+ class ApplicationController < ActionController::Base
3
+ include ImpersonationBehavior
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module MrMime
2
+ module ImpersonationBehavior
3
+ def self.included(base)
4
+ base.helper_method :current_impersonator, :impersonator?, :impersonator_id
5
+ base.helper MrMime::ImpersonationHelper
6
+ end
7
+
8
+ def current_impersonator
9
+ MrMime::Config.user_class.find_by(id: impersonator_id) if impersonator?
10
+ end
11
+
12
+ def impersonator?
13
+ impersonator_id.present?
14
+ end
15
+
16
+ def impersonator_id
17
+ session['mr_mime.impersonator_id']
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ module MrMime
2
+ class ImpersonationsController < MrMime::ApplicationController
3
+ before_filter :require_login
4
+
5
+ def create
6
+ if impersonation.save
7
+ redirect_to main_app.root_url, notice: 'Impersonation started'
8
+ else
9
+ redirect_to :back, flash: { error: impersonation.error_messages }
10
+ end
11
+ end
12
+
13
+ def destroy
14
+ impersonation.revert
15
+
16
+ redirect_to impersonation.return_to, notice: 'Impersonation ended'
17
+ end
18
+
19
+ private
20
+
21
+ def impersonation
22
+ @impersonation ||= MrMime::Impersonation.new(
23
+ context: self,
24
+ store: session,
25
+ params: impersonation_params
26
+ )
27
+ end
28
+
29
+ def impersonation_params
30
+ params.fetch(:impersonation, {})
31
+ .merge(
32
+ impersonator_id: impersonator.id,
33
+ referer: request.referer
34
+ )
35
+ end
36
+
37
+ def impersonator
38
+ current_impersonator || current_user
39
+ end
40
+
41
+ def require_login
42
+ unless current_user
43
+ redirect_to main_app.root_url, flash: {
44
+ error: 'You do not have permission to do this'
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,7 @@
1
+ module MrMime
2
+ module ImpersonationHelper
3
+ def button_to_impersonate(user_id, options = {})
4
+ render 'mr_mime/impersonate_button', options.merge(user_id: user_id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module MrMime
2
+ module Adapters
3
+ class Base
4
+ def initialize(context)
5
+ @context = context
6
+ end
7
+
8
+ private
9
+
10
+ attr_reader :context
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module MrMime
2
+ module Adapters
3
+ class DeviseAdapter < Base
4
+ def set_current_user(user)
5
+ context.sign_out
6
+ context.sign_in(user)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module MrMime
2
+ module Adapters
3
+ class SorceryAdapter < Base
4
+ def set_current_user(user)
5
+ context.auto_login user
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,85 @@
1
+ require 'active_model'
2
+
3
+ module MrMime
4
+ class Impersonation
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Validations
7
+ include ActiveModel::Conversion
8
+
9
+ validates :impersonator, :impersonated, presence: true
10
+ validate :impersonation_allowed
11
+
12
+ delegate :user_class, :adapter_class, to: MrMime::Config
13
+ delegate :set_current_user, to: :adapter
14
+
15
+ attr_reader :return_to
16
+
17
+ def initialize(options = {})
18
+ @adapter = adapter_class.new(options.fetch(:context))
19
+ @store = MrMime::Store.new(options[:store] || {})
20
+ @params = options[:params] || {}
21
+ @return_to = store.get(:return_to)
22
+ end
23
+
24
+ def save
25
+ if valid?
26
+ set_user_keys
27
+ true
28
+ end
29
+ end
30
+
31
+ def revert
32
+ revert_user_keys
33
+ end
34
+
35
+ def impersonator
36
+ @impersonator ||= find_user(params[:impersonator_id])
37
+ end
38
+
39
+ def impersonated
40
+ @impersonated ||= find_user(params[:impersonated_id])
41
+ end
42
+
43
+ def error_messages
44
+ errors.full_messages.join(', ')
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :adapter, :store, :params
50
+
51
+ def set_user_keys
52
+ set_current_user impersonated
53
+ store.set_keys(
54
+ impersonator_id: impersonator.id,
55
+ return_to: params[:referer]
56
+ )
57
+ end
58
+
59
+ def revert_user_keys
60
+ set_current_user impersonator
61
+ store.set_keys(
62
+ impersonator_id: nil,
63
+ return_to: nil
64
+ )
65
+ end
66
+
67
+ def impersonation_allowed
68
+ unless impersonation_allowed?
69
+ errors.add(:base, 'You do not have permission to impersonate this user')
70
+ end
71
+ end
72
+
73
+ def impersonation_allowed?
74
+ MrMime::ImpersonationPolicy.allowed?(impersonator, impersonated)
75
+ end
76
+
77
+ def find_user(id)
78
+ user_class.find_by(id: id)
79
+ end
80
+
81
+ def persisted?
82
+ false
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,22 @@
1
+ module MrMime
2
+ class ImpersonationPolicy
3
+ delegate :user_permission_check, to: MrMime::Config
4
+
5
+ attr_reader :impersonator, :impersonated
6
+
7
+ def self.allowed?(*args)
8
+ new(*args).allowed?
9
+ end
10
+
11
+ def initialize(impersonator, impersonated)
12
+ @impersonator = impersonator
13
+ @impersonated = impersonated
14
+ end
15
+
16
+ def allowed?
17
+ return true unless user_permission_check
18
+
19
+ impersonator.public_send(user_permission_check)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module MrMime
2
+ class Store < SimpleDelegator
3
+ PREFIX = 'mr_mime'
4
+
5
+ def set_keys(values = {})
6
+ values.each{ |k,v| set(k, v) }
7
+ end
8
+
9
+ def set(key, value)
10
+ self["#{PREFIX}.#{key}"] = value
11
+ end
12
+
13
+ def get(key)
14
+ self["#{PREFIX}.#{key}"]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ <%=
2
+ button_to mr_mime.impersonation_url, {
3
+ class: local_assigns[:button_class],
4
+ form_class: 'mr-mime-new-impersonation',
5
+ params: {
6
+ impersonation: { impersonated_id: user_id }
7
+ }
8
+ } do
9
+ local_assigns[:button_text] || 'Impersonate User'
10
+ end
11
+ %>
@@ -0,0 +1,6 @@
1
+ <% if impersonator? %>
2
+ <div class='mr-mime-impersonation-footer'>
3
+ You are impersonating <strong><%= current_user.email %></strong>. At any time you may
4
+ <%= link_to 'return to your regular account', mr_mime.impersonation_path, method: :delete %>.
5
+ </div>
6
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ MrMime::Engine.routes.draw do
2
+ resource :impersonation, only: [:create, :destroy]
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'rails/generators/base'
2
+ require 'securerandom'
3
+
4
+ module MrMime
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ source_root File.expand_path('../../templates', __FILE__)
8
+
9
+ desc 'Creates a MrMime initializer'
10
+
11
+ def copy_initializer
12
+ template 'mr_mime.rb', 'config/initializers/mr_mime.rb'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ MrMime.configure do |config|
2
+ # Configure your user authentication adapter.
3
+ # Supported adapters are :devise, :sorcery
4
+ config.adapter = :devise
5
+
6
+ # Configure the name of the user model used for authentication.
7
+ config.user_model = 'User'
8
+
9
+ # Configure the user method used for impersonation permission checks.
10
+ # Your configured user model must respond to this method
11
+ # If the method returns falsey, the user will not be permitted to impersonate
12
+ # If the method returns truthy, the user will be permitted to impersonate
13
+ # Default: nil
14
+ # config.user_permission_check = :admin?
15
+ end
@@ -0,0 +1,27 @@
1
+ module MrMime
2
+ module Config
3
+ mattr_accessor :adapter
4
+ @@adapter = :devise
5
+
6
+ mattr_accessor :user_model
7
+ @@user_model = 'User'
8
+
9
+ mattr_accessor :user_permission_check
10
+ @@user_permission_check = nil
11
+
12
+ def self.adapters
13
+ {
14
+ sorcery: MrMime::Adapters::SorceryAdapter,
15
+ devise: MrMime::Adapters::DeviseAdapter
16
+ }
17
+ end
18
+
19
+ def self.adapter_class
20
+ @@adapter_class ||= adapters[@@adapter]
21
+ end
22
+
23
+ def self.user_class
24
+ @@user_class ||= user_model.constantize
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module MrMime
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace MrMime
4
+
5
+ initializer 'mr_mime', before: :load_config_initializers do |app|
6
+ Rails.application.routes.append do
7
+ mount MrMime::Engine, at: '/mr_mime'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ VERSION = '0.0.1'
data/lib/mr_mime.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'mr_mime/engine'
2
+ require 'mr_mime/version'
3
+ require 'mr_mime/config'
4
+
5
+ module MrMime
6
+ def self.configure
7
+ yield MrMime::Config
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'helpers/mr_mime/impersonation_helper'
3
+ require 'controllers/mr_mime/impersonation_behavior'
4
+
5
+ module MrMime
6
+ class DummyController < ActionController::Base
7
+ include ImpersonationBehavior
8
+ end
9
+
10
+ RSpec.describe DummyController do
11
+ let(:controller) { described_class.new }
12
+
13
+ before { allow(controller).to receive(:session).and_return({}) }
14
+
15
+ describe 'helpers' do
16
+ it 'correctly includes all relevant helper methods' do
17
+ expect(described_class.helpers.methods).to include(
18
+ :current_impersonator,
19
+ :impersonator?,
20
+ :impersonator_id,
21
+ :button_to_impersonate
22
+ )
23
+ end
24
+ end
25
+
26
+ describe '#current_impersonator' do
27
+ it 'returns nil if there is no impersonator' do
28
+ expect(controller.current_impersonator).to be_nil
29
+ end
30
+
31
+ context 'with impersonator' do
32
+ let(:user_class) { double }
33
+
34
+ before do
35
+ allow(MrMime::Config).to receive(:user_class).and_return(user_class)
36
+ set_impersonator 21
37
+ end
38
+
39
+ it 'returns nil if the impersonating user cannot be found' do
40
+ find_user nil
41
+ expect(controller.current_impersonator).to be_nil
42
+ end
43
+
44
+ it 'returns the impersonating user if present' do
45
+ user = double
46
+ find_user user
47
+ expect(controller.current_impersonator).to eq user
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#impersonator?' do
53
+ it 'returns true if an impersonator id exists' do
54
+ set_impersonator 21
55
+ expect(controller).to be_impersonator
56
+ end
57
+
58
+ it 'returns false otherwise' do
59
+ expect(controller).not_to be_impersonator
60
+ end
61
+ end
62
+
63
+ def set_impersonator(id)
64
+ controller.session['mr_mime.impersonator_id'] = id
65
+ end
66
+
67
+ def find_user(return_value)
68
+ allow(user_class).to receive(:find_by)
69
+ .with(id: 21)
70
+ .and_return(return_value)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'models/mr_mime/impersonation_policy'
3
+
4
+ module MrMime
5
+ RSpec.describe ImpersonationPolicy, type: :model do
6
+ describe '#allow?' do
7
+ it 'returns true if no permission check is set' do
8
+ expect(described_class.new(double, double)).to be_allowed
9
+ end
10
+
11
+ context 'with permission check' do
12
+ before { MrMime::Config.user_permission_check = :check? }
13
+
14
+ it 'allows admins to impersonate any user' do
15
+ impersonator = double(check?: true)
16
+ expect(described_class.new(impersonator, double)).to be_allowed
17
+ end
18
+
19
+ it 'does not allow a regular user to impersonate' do
20
+ impersonator = double(check?: false)
21
+ expect(described_class.new(impersonator, double)).not_to be_allowed
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+ require 'models/mr_mime/store'
3
+ require 'models/mr_mime/impersonation_policy'
4
+ require 'models/mr_mime/impersonation'
5
+
6
+ module MrMime
7
+ RSpec.describe Impersonation, type: :model do
8
+ class UserClass; end
9
+ class AdapterClass; end
10
+
11
+ let(:context) { double }
12
+ let(:adapter) { double(set_current_user: true) }
13
+ let(:impersonator) { double(id: 3, email: 'impersonator@example.com') }
14
+ let(:impersonated) { double(id: 8, email: 'impersonated@example.com') }
15
+ let(:impersonation) do
16
+ described_class.new(
17
+ context: context,
18
+ store: store,
19
+ params: params
20
+ )
21
+ end
22
+
23
+ before do
24
+ stub_config
25
+ stub_adapter
26
+ stub_users
27
+ end
28
+
29
+ describe '#save' do
30
+ let(:store) { {} }
31
+ let(:params) do
32
+ {
33
+ impersonator_id: impersonator.id,
34
+ impersonated_id: impersonated.id,
35
+ referer: 'www.example.com/origin'
36
+ }
37
+ end
38
+
39
+ context 'with a valid impersonation' do
40
+ before { stub_policy(true) }
41
+
42
+ it 'returns true' do
43
+ expect(impersonation.save).to be_truthy
44
+ end
45
+
46
+ it 'sets the current user' do
47
+ expect(adapter).to receive(:set_current_user).with(impersonated)
48
+ impersonation.save
49
+ end
50
+
51
+ it 'sets the impersonator_id in the store' do
52
+ impersonation.save
53
+ expect_store_value :impersonator_id, impersonator.id
54
+ end
55
+
56
+ it 'sets the return_to in the store' do
57
+ impersonation.save
58
+ expect_store_value :return_to, 'www.example.com/origin'
59
+ end
60
+ end
61
+
62
+ context 'with an invalid impersonation' do
63
+ before { stub_policy(false) }
64
+
65
+ it 'returns false' do
66
+ expect(impersonation.save).to be_falsey
67
+ end
68
+
69
+ it 'does not set the current user' do
70
+ expect(adapter).to_not receive(:set_current_user)
71
+ impersonation.save
72
+ end
73
+
74
+ it 'does not set the impersonator_id' do
75
+ impersonation.save
76
+ expect_store_value :impersonator_id, nil
77
+ end
78
+
79
+ it 'does not set the return_to' do
80
+ impersonation.save
81
+ expect_store_value :return_to, nil
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#revert' do
87
+ let(:store) do
88
+ {
89
+ 'mr_mime.impersonator_id' => impersonator.id,
90
+ 'mr_mime.return_to' => 'www.example.com/origin'
91
+ }
92
+ end
93
+ let(:params) do
94
+ {
95
+ impersonator_id: impersonator.id,
96
+ referer: 'www.example.com/new'
97
+ }
98
+ end
99
+
100
+ it 'reverts the current user' do
101
+ expect(adapter).to receive(:set_current_user).with(impersonator)
102
+ impersonation.revert
103
+ end
104
+
105
+ it 'clears the impersonator_id' do
106
+ impersonation.revert
107
+ expect_store_value :impersonator_id, nil
108
+ end
109
+
110
+ it 'clears the return_to' do
111
+ impersonation.revert
112
+ expect_store_value :return_to, nil
113
+ end
114
+
115
+ it 'retains the correct return_to' do
116
+ impersonation.revert
117
+ expect(impersonation.return_to).to eq 'www.example.com/origin'
118
+ end
119
+ end
120
+
121
+ def expect_store_value(key, value)
122
+ expect(store["mr_mime.#{key}"]).to eq value
123
+ end
124
+
125
+ def stub_config
126
+ allow(MrMime::Config).to receive_messages(
127
+ user_class: UserClass,
128
+ adapter_class: AdapterClass
129
+ )
130
+ end
131
+
132
+ def stub_adapter
133
+ allow(AdapterClass).to receive(:new).with(context).and_return(adapter)
134
+ end
135
+
136
+ def stub_users
137
+ allow(UserClass).to receive(:find_by) do |options|
138
+ if options[:id] == impersonator.id then impersonator
139
+ elsif options[:id] == impersonated.id then impersonated
140
+ else nil
141
+ end
142
+ end
143
+ end
144
+
145
+ def stub_policy(allowed)
146
+ allow(MrMime::ImpersonationPolicy).to receive(:allowed?)
147
+ .with(impersonator, impersonated)
148
+ .and_return(allowed)
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'models/mr_mime/store'
3
+
4
+ module MrMime
5
+ RSpec.describe Store do
6
+ let(:store) { described_class.new({}) }
7
+
8
+ before { stub_const("#{described_class}::PREFIX", 'prefix')}
9
+
10
+ describe '#set_keys' do
11
+ it 'correctly stores all prefixed key-value pairs' do
12
+ store.set_keys(one: 1, two: 2)
13
+
14
+ expect(store['prefix.one']).to eq 1
15
+ expect(store['prefix.two']).to eq 2
16
+ end
17
+ end
18
+
19
+ describe '#set' do
20
+ it 'correctly stores a prefixed key-value pair' do
21
+ store.set(:one, 1)
22
+ expect(store['prefix.one']).to eq 1
23
+ end
24
+ end
25
+
26
+ describe '#get' do
27
+ it 'correctly retrieves a prefixed key-value pair' do
28
+ store.set(:one, 1)
29
+ expect(store.get(:one)).to eq 1
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ require 'rails/all'
2
+ require 'mr_mime'
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mr_mime
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Edson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: |-
56
+ MrMime is a Rails engine built to easily add user
57
+ impersonation functionality to your existing Rails application
58
+ email: rubygems@foraker.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - app/assets/stylesheets/mr_mime/_mr_mime.css
64
+ - app/controllers/mr_mime/application_controller.rb
65
+ - app/controllers/mr_mime/impersonation_behavior.rb
66
+ - app/controllers/mr_mime/impersonations_controller.rb
67
+ - app/helpers/mr_mime/impersonation_helper.rb
68
+ - app/models/mr_mime/adapters/base.rb
69
+ - app/models/mr_mime/adapters/devise_adapter.rb
70
+ - app/models/mr_mime/adapters/sorcery_adapter.rb
71
+ - app/models/mr_mime/impersonation.rb
72
+ - app/models/mr_mime/impersonation_policy.rb
73
+ - app/models/mr_mime/store.rb
74
+ - app/views/mr_mime/_impersonate_button.html.erb
75
+ - app/views/mr_mime/_impersonation_warning.html.erb
76
+ - config/routes.rb
77
+ - lib/generators/mr_mime/install_generator.rb
78
+ - lib/generators/templates/mr_mime.rb
79
+ - lib/mr_mime.rb
80
+ - lib/mr_mime/config.rb
81
+ - lib/mr_mime/engine.rb
82
+ - lib/mr_mime/version.rb
83
+ - spec/controllers/mr_mime/impersonation_behavior_spec.rb
84
+ - spec/models/mr_mime/impersonation_policy_spec.rb
85
+ - spec/models/mr_mime/impersonation_spec.rb
86
+ - spec/models/mr_mime/store_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: http://www.github.com/foraker/mr_mime
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5.1
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: User impersonation for Rails applications
112
+ test_files:
113
+ - spec/controllers/mr_mime/impersonation_behavior_spec.rb
114
+ - spec/models/mr_mime/impersonation_policy_spec.rb
115
+ - spec/models/mr_mime/impersonation_spec.rb
116
+ - spec/models/mr_mime/store_spec.rb
117
+ - spec/spec_helper.rb