g5_authenticatable 0.4.2 → 0.5.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +2 -2
  5. data/README.md +186 -1
  6. data/app/controllers/concerns/g5_authenticatable/authorization.rb +21 -0
  7. data/app/models/g5_authenticatable/role.rb +8 -0
  8. data/app/models/g5_authenticatable/user.rb +41 -0
  9. data/app/policies/g5_authenticatable/base_policy.rb +73 -0
  10. data/config/initializers/rolify.rb +8 -0
  11. data/g5_authenticatable.gemspec +3 -0
  12. data/lib/g5_authenticatable/engine.rb +3 -0
  13. data/lib/g5_authenticatable/rspec.rb +1 -0
  14. data/lib/g5_authenticatable/test/factory.rb +51 -1
  15. data/lib/g5_authenticatable/test/feature_helpers.rb +15 -2
  16. data/lib/g5_authenticatable/version.rb +1 -1
  17. data/lib/generators/g5_authenticatable/install/USAGE +7 -1
  18. data/lib/generators/g5_authenticatable/install/install_generator.rb +33 -6
  19. data/lib/generators/g5_authenticatable/install/templates/403.html +26 -0
  20. data/lib/generators/g5_authenticatable/install/templates/application_policy.rb +4 -0
  21. data/lib/generators/g5_authenticatable/install/templates/{g5_authenticatable.rb → initializer.rb} +0 -0
  22. data/lib/generators/g5_authenticatable/install/templates/migrate/add_g5_authenticatable_users_contact_info.rb +11 -0
  23. data/lib/generators/g5_authenticatable/install/templates/migrate/create_g5_authenticatable_roles.rb +20 -0
  24. data/lib/generators/g5_authenticatable/install/templates/{create_g5_authenticatable_users.rb → migrate/create_g5_authenticatable_users.rb} +0 -0
  25. data/spec/controllers/application_controller_spec.rb +12 -0
  26. data/spec/controllers/concerns/g5_authenticatable/authorization.rb +50 -0
  27. data/spec/dummy/app/assets/javascripts/posts.js +2 -0
  28. data/spec/dummy/app/assets/stylesheets/posts.css +4 -0
  29. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +1 -0
  31. data/spec/dummy/app/controllers/posts_controller.rb +74 -0
  32. data/spec/dummy/app/helpers/posts_helper.rb +2 -0
  33. data/spec/dummy/app/models/post.rb +3 -0
  34. data/spec/dummy/app/policies/application_policy.rb +4 -0
  35. data/spec/dummy/app/policies/post_policy.rb +4 -0
  36. data/spec/dummy/app/views/home/index.html.erb +40 -0
  37. data/spec/dummy/app/views/posts/_form.html.erb +21 -0
  38. data/spec/dummy/app/views/posts/edit.html.erb +6 -0
  39. data/spec/dummy/app/views/posts/index.html.erb +30 -0
  40. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  41. data/spec/dummy/app/views/posts/show.html.erb +17 -0
  42. data/spec/dummy/config/database.yml.ci +1 -2
  43. data/spec/dummy/config/initializers/g5_authenticatable.rb +8 -0
  44. data/spec/dummy/config/routes.rb +2 -0
  45. data/spec/dummy/db/migrate/20150428182339_add_g5_authenticatable_users_contact_info.rb +11 -0
  46. data/spec/dummy/db/migrate/20150429212919_create_g5_authenticatable_roles.rb +20 -0
  47. data/spec/dummy/db/migrate/20150509061150_create_posts.rb +9 -0
  48. data/spec/dummy/db/schema.rb +37 -4
  49. data/spec/dummy/public/403.html +26 -0
  50. data/spec/factories/post.rb +6 -0
  51. data/spec/features/default_role_authorization_spec.rb +254 -0
  52. data/spec/features/sign_in_spec.rb +144 -8
  53. data/spec/lib/generators/g5_authenticatable/install_generator_spec.rb +72 -1
  54. data/spec/models/g5_authenticatable/role_spec.rb +81 -0
  55. data/spec/models/g5_authenticatable/user_spec.rb +340 -3
  56. data/spec/models/post_spec.rb +12 -0
  57. data/spec/policies/application_policy_spec.rb +171 -0
  58. data/spec/policies/post_policy_spec.rb +35 -0
  59. data/spec/requests/default_role_authorization_spec.rb +169 -0
  60. data/spec/spec_helper.rb +0 -3
  61. data/spec/support/shared_examples/super_admin_authorizer.rb +33 -0
  62. metadata +109 -5
  63. data/circle.yml +0 -4
@@ -5,6 +5,8 @@ describe G5Authenticatable::User do
5
5
  let(:user) { G5Authenticatable::User.create(user_attributes) }
6
6
  let(:user_attributes) { FactoryGirl.attributes_for(:g5_authenticatable_user) }
7
7
 
8
+ it { is_expected.to have_and_belong_to_many(:roles) }
9
+
8
10
  it 'should expose the email' do
9
11
  expect(user.email).to eq(user_attributes[:email])
10
12
  end
@@ -29,11 +31,346 @@ describe G5Authenticatable::User do
29
31
  expect(user).to respond_to(:update_tracked_fields!)
30
32
  end
31
33
 
32
- it { should validate_presence_of(:email) }
33
- it { should validate_uniqueness_of(:email) }
34
- it { should validate_uniqueness_of(:uid).scoped_to(:provider) }
34
+ it { is_expected.to validate_presence_of(:email) }
35
+ it { is_expected.to validate_uniqueness_of(:email) }
36
+ it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) }
35
37
 
36
38
  it 'should support timeouts' do
37
39
  expect(user.timeout_in).to be > 0
38
40
  end
41
+
42
+ it 'should expose the first_name' do
43
+ expect(user.first_name).to eq(user_attributes[:first_name])
44
+ end
45
+
46
+ it 'should expose the last_name' do
47
+ expect(user.last_name).to eq(user_attributes[:last_name])
48
+ end
49
+
50
+ it 'should expose the phone number' do
51
+ expect(user.phone_number).to eq(user_attributes[:phone_number])
52
+ end
53
+
54
+ it 'should expose the title' do
55
+ expect(user.title).to eq(user_attributes[:title])
56
+ end
57
+
58
+ it 'should expose the organization name' do
59
+ expect(user.organization_name).to eq(user_attributes[:organization_name])
60
+ end
61
+
62
+ describe '.new_with_session' do
63
+ subject(:new_user) { G5Authenticatable::User.new_with_session(params, session) }
64
+
65
+ let(:params) { Hash.new }
66
+ let(:auth_data) do
67
+ OmniAuth::AuthHash.new({
68
+ 'provider' => new_user_attributes[:provider],
69
+ 'uid' => new_user_attributes[:uid],
70
+ 'info' => {
71
+ 'email' => new_user_attributes[:email],
72
+ 'name' => "#{new_user_attributes[:first_name]} #{new_user_attributes[:last_name]}",
73
+ 'first_name' => new_user_attributes[:first_name],
74
+ 'last_name' => new_user_attributes[:last_name],
75
+ 'phone' => new_user_attributes[:phone_number]
76
+ },
77
+ 'credentials' => {
78
+ 'token' => new_user_attributes[:g5_access_token],
79
+ 'expires' => true,
80
+ 'expires_at' => Time.now + 1000
81
+ },
82
+ 'extra' => {
83
+ 'title' => new_user_attributes[:title],
84
+ 'organization_name' => new_user_attributes[:organization_name],
85
+ 'roles' => [
86
+ {'name' => new_role_attributes[:name]}
87
+ ],
88
+ 'raw_info' => {}
89
+ }
90
+ })
91
+ end
92
+
93
+ let(:new_user_attributes) { FactoryGirl.attributes_for(:g5_authenticatable_user) }
94
+ let(:new_role_attributes) { FactoryGirl.attributes_for(:g5_authenticatable_role) }
95
+
96
+ context 'when there is auth data in the session' do
97
+ let(:session) { {'omniauth.auth' => auth_data} }
98
+
99
+ it 'should initialize a new user' do
100
+ expect(new_user).to be_a_new_record
101
+ end
102
+
103
+ it 'should not persist the user' do
104
+ expect { new_user }.to_not change { G5Authenticatable::User.count }
105
+ end
106
+
107
+ it 'should set the provider from the session data' do
108
+ expect(new_user.provider).to eq(new_user_attributes[:provider])
109
+ end
110
+
111
+ it 'should set the uid from the session data' do
112
+ expect(new_user.uid).to eq(new_user_attributes[:uid])
113
+ end
114
+
115
+ it 'should set the email from the session data' do
116
+ expect(new_user.email).to eq(new_user_attributes[:email])
117
+ end
118
+
119
+ it 'should set the first_name from the session data' do
120
+ expect(new_user.first_name).to eq(new_user_attributes[:first_name])
121
+ end
122
+
123
+ it 'should set the last_name from the session data' do
124
+ expect(new_user.last_name).to eq(new_user_attributes[:last_name])
125
+ end
126
+
127
+ it 'should set the phone_number from the session data' do
128
+ expect(new_user.phone_number).to eq(new_user_attributes[:phone_number])
129
+ end
130
+
131
+ it 'should set the title from the session data' do
132
+ expect(new_user.title).to eq(new_user_attributes[:title])
133
+ end
134
+
135
+ it 'should set the organization_name from the session data' do
136
+ expect(new_user.organization_name).to eq(new_user_attributes[:organization_name])
137
+ end
138
+
139
+ it 'should assign the role from the session data' do
140
+ expect(new_user).to have_role(new_role_attributes[:name])
141
+ end
142
+ end
143
+
144
+ context 'when there is no auth data in the session' do
145
+ let(:session) { Hash.new }
146
+
147
+ it 'should initialize a new user' do
148
+ expect(new_user).to be_a_new_record
149
+ end
150
+
151
+ it 'should not persist the user' do
152
+ expect { new_user }.to_not change { G5Authenticatable::User.count }
153
+ end
154
+
155
+ it 'should not set the uid' do
156
+ expect(new_user.uid).to be_nil
157
+ end
158
+
159
+ it 'should set the provider to the default value' do
160
+ expect(new_user.provider).to eq('g5')
161
+ end
162
+
163
+ it 'should not set the email' do
164
+ expect(new_user.email).to be_blank
165
+ end
166
+
167
+ it 'should not assign a role to the user' do
168
+ expect(new_user.roles).to be_empty
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '.find_and_update_for_g5_oauth' do
174
+ subject(:updated_user) { G5Authenticatable::User.find_and_update_for_g5_oauth(auth_data) }
175
+
176
+ let(:user_attributes) do
177
+ FactoryGirl.attributes_for(:g5_authenticatable_user,
178
+ first_name: nil,
179
+ last_name: nil,
180
+ phone_number: nil,
181
+ title: nil,
182
+ organization_name: nil
183
+ )
184
+ end
185
+ let(:role_name) { :my_role }
186
+
187
+ before do
188
+ user
189
+ user.add_role(role_name)
190
+ end
191
+
192
+ let(:auth_data) do
193
+ OmniAuth::AuthHash.new({
194
+ 'provider' => user_attributes[:provider],
195
+ 'uid' => user_attributes[:uid],
196
+ 'info' => {
197
+ 'email' => updated_attributes[:email],
198
+ 'first_name' => updated_attributes[:first_name],
199
+ 'last_name' => updated_attributes[:last_name],
200
+ 'phone' => updated_attributes[:phone_number]
201
+ },
202
+ 'credentials' => {
203
+ 'token' => updated_attributes[:g5_access_token],
204
+ 'expires' => true,
205
+ 'expires_at' => Time.now + 1000
206
+ },
207
+ 'extra' => {
208
+ 'title' => updated_attributes[:title],
209
+ 'organization_name' => updated_attributes[:organization_name],
210
+ 'roles' => [
211
+ {name: updated_role_name}
212
+ ],
213
+ 'raw_info' => {}
214
+ }
215
+ })
216
+ end
217
+
218
+ context 'when user info is the same' do
219
+ let(:updated_attributes) do
220
+ user_attributes.merge(g5_access_token: 'updatedtoken42')
221
+ end
222
+ let(:updated_role_name) { role_name }
223
+
224
+ it 'should update the access token' do
225
+ expect { updated_user }.to change { user.reload.g5_access_token }.to(updated_attributes[:g5_access_token])
226
+ end
227
+
228
+ it 'should return the updated user' do
229
+ expect(updated_user).to eq(user.reload)
230
+ end
231
+
232
+ it 'should not change the user email' do
233
+ expect { updated_user }.to_not change { user.reload.email }
234
+ end
235
+
236
+ it 'should not change the user first_name' do
237
+ expect { updated_user }.to_not change { user.reload.first_name }
238
+ end
239
+
240
+ it 'should not change the user last_name' do
241
+ expect { updated_user }.to_not change { user.reload.last_name }
242
+ end
243
+
244
+ it 'should not change the user phone_number' do
245
+ expect { updated_user }.to_not change { user.reload.phone_number }
246
+ end
247
+
248
+ it 'should not change the user title' do
249
+ expect { updated_user }.to_not change { user.reload.title }
250
+ end
251
+
252
+ it 'should not change the user organization_name' do
253
+ expect { updated_user }.to_not change { user.reload.organization_name }
254
+ end
255
+
256
+ it 'should not change the user roles' do
257
+ expect { updated_user }.to_not change { user.reload.roles }
258
+ end
259
+ end
260
+
261
+ context 'when user info has changed' do
262
+ let(:updated_attributes) do
263
+ {
264
+ uid: user.uid,
265
+ provider: user.provider,
266
+ g5_access_token: 'updatedtoken42',
267
+ first_name: 'Updated First Name',
268
+ last_name: 'Updated Last Name',
269
+ phone_number: '555.555.5555 x123',
270
+ title: 'Recently Promoted',
271
+ organization_name: 'Updated Department'
272
+ }
273
+ end
274
+
275
+ let(:updated_role_name) { 'super_admin' }
276
+
277
+ it 'should update the access token' do
278
+ expect { updated_user }.to change { user.reload.g5_access_token }.to(updated_attributes[:g5_access_token])
279
+ end
280
+
281
+ it 'should return the updated user' do
282
+ expect(updated_user).to eq(user.reload)
283
+ end
284
+
285
+ it 'should not change the uid' do
286
+ expect { updated_user }.to_not change { user.reload.uid }
287
+ end
288
+
289
+ it 'should not change the provider' do
290
+ expect { updated_user }.to_not change { user.reload.provider }
291
+ end
292
+
293
+ it 'should not change the email' do
294
+ expect { updated_user }.to_not change { user.reload.email }
295
+ end
296
+
297
+ it 'should update the first name' do
298
+ expect { updated_user }.to change { user.reload.first_name }.to(updated_attributes[:first_name])
299
+ end
300
+
301
+ it 'should update the last name' do
302
+ expect { updated_user }.to change { user.reload.last_name }.to(updated_attributes[:last_name])
303
+ end
304
+
305
+ it 'should update the phone number' do
306
+ expect { updated_user }.to change { user.reload.phone_number }.to(updated_attributes[:phone_number])
307
+ end
308
+
309
+ it 'should update the title' do
310
+ expect { updated_user }.to change { user.reload.title }.to(updated_attributes[:title])
311
+ end
312
+
313
+ it 'should update the organization_name' do
314
+ expect { updated_user }.to change { user.reload.organization_name }.to(updated_attributes[:organization_name])
315
+ end
316
+
317
+ it 'should unassign the old role' do
318
+ expect(updated_user).to_not have_role(role_name)
319
+ end
320
+
321
+ it 'should assign the new role' do
322
+ expect(updated_user).to have_role(updated_role_name)
323
+ end
324
+ end
325
+ end
326
+
327
+ describe '#add_role' do
328
+ subject(:add_role) { user.add_role(role_name) }
329
+
330
+ context 'when role already exists' do
331
+ let(:role) { FactoryGirl.create(:g5_authenticatable_role) }
332
+ let(:role_name) { role.name }
333
+
334
+ it 'should assign a role to the user' do
335
+ expect { add_role }.to change { user.roles.count }.by(1)
336
+ end
337
+
338
+ it 'should assign the existing role' do
339
+ add_role
340
+ expect(user.roles).to include(role)
341
+ end
342
+ end
343
+
344
+ context 'when role does not exist' do
345
+ let(:role_name) { :some_new_role }
346
+
347
+ it 'should assign a role to the user' do
348
+ expect { add_role }.to change { user.roles.count }.by(1)
349
+ end
350
+
351
+ it 'should create the new role' do
352
+ add_role
353
+ expect(G5Authenticatable::Role.exists?(name: role_name)).to be_truthy
354
+ end
355
+ end
356
+ end
357
+
358
+ describe '#has_role?' do
359
+ subject(:has_role?) { user.has_role?(role_name) }
360
+ let(:role_name) { :my_role }
361
+
362
+ context 'when user has been assigned the role' do
363
+ before { user.add_role(role_name) }
364
+
365
+ it 'should return true' do
366
+ expect(has_role?).to be_truthy
367
+ end
368
+ end
369
+
370
+ context 'when user has not been assigned the role' do
371
+ it 'should return false' do
372
+ expect(has_role?).to be_falsey
373
+ end
374
+ end
375
+ end
39
376
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Post do
4
+ subject { post }
5
+ let(:post) { FactoryGirl.create(:post) }
6
+
7
+ it { is_expected.to belong_to(:author) }
8
+
9
+ it 'should have a G5Authenticatable::User as the author' do
10
+ expect(post.author).to be_a(G5Authenticatable::User)
11
+ end
12
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationPolicy do
4
+ subject(:policy) { described_class }
5
+
6
+ let(:user) { FactoryGirl.create(:g5_authenticatable_user) }
7
+ let(:record) { FactoryGirl.create(:post) }
8
+
9
+ permissions :index? do
10
+ it_behaves_like 'a super_admin authorizer'
11
+ end
12
+
13
+ permissions :show? do
14
+ context 'when user is a super_admin' do
15
+ let(:user) { FactoryGirl.create(:g5_authenticatable_super_admin) }
16
+
17
+ context 'when record exists in scope' do
18
+ it 'permits access' do
19
+ expect(policy).to permit(user, record)
20
+ end
21
+ end
22
+
23
+ context 'when record does not exist in scope' do
24
+ let(:record) { FactoryGirl.build(:post) }
25
+
26
+ it 'denies access' do
27
+ expect(policy).to_not permit(user, record)
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'when user is not a super_admin' do
33
+ it 'denies access' do
34
+ expect(policy).to_not permit(user, record)
35
+ end
36
+ end
37
+ end
38
+
39
+ permissions :create? do
40
+ it_behaves_like 'a super_admin authorizer'
41
+ end
42
+
43
+ permissions :new? do
44
+ it_behaves_like 'a super_admin authorizer'
45
+ end
46
+
47
+ permissions :update? do
48
+ it_behaves_like 'a super_admin authorizer'
49
+ end
50
+
51
+ permissions :edit? do
52
+ it_behaves_like 'a super_admin authorizer'
53
+ end
54
+
55
+ permissions :destroy? do
56
+ it_behaves_like 'a super_admin authorizer'
57
+ end
58
+
59
+ describe '#scope' do
60
+ subject(:scope) { policy.new(user, record).scope }
61
+
62
+ it 'should look up the correct scope based on the record class' do
63
+ post_scope = PostPolicy::Scope.new(user, record.class)
64
+ expect(scope).to eq(post_scope.resolve)
65
+ end
66
+ end
67
+
68
+ describe '#super_admin?' do
69
+ subject(:super_admin?) { policy.new(user, record).super_admin? }
70
+
71
+ context 'when there is no user' do
72
+ let(:user) {}
73
+
74
+ it 'is false' do
75
+ expect(super_admin?).to eq(false)
76
+ end
77
+ end
78
+
79
+ context 'when user does not have super_admin role' do
80
+ it 'is false' do
81
+ expect(super_admin?).to eq(false)
82
+ end
83
+ end
84
+
85
+ context 'when user has the super_admin role' do
86
+ let(:user) { FactoryGirl.create(:g5_authenticatable_super_admin) }
87
+
88
+ it 'is true' do
89
+ expect(super_admin?).to eq(true)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#admin?' do
95
+ subject(:admin?) { policy.new(user, record).admin? }
96
+
97
+ context 'when there is no user' do
98
+ let(:user) {}
99
+
100
+ it 'is false' do
101
+ expect(admin?).to eq(false)
102
+ end
103
+ end
104
+
105
+ context 'when user does not have admin role' do
106
+ it 'is false' do
107
+ expect(admin?).to eq(false)
108
+ end
109
+ end
110
+
111
+ context 'when user has the admin role' do
112
+ let(:user) { FactoryGirl.create(:g5_authenticatable_admin) }
113
+
114
+ it 'is true' do
115
+ expect(admin?).to eq(true)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#editor?' do
121
+ subject(:editor?) { policy.new(user, record).editor? }
122
+
123
+ context 'when there is no user' do
124
+ let(:user) {}
125
+
126
+ it 'is false' do
127
+ expect(editor?).to eq(false)
128
+ end
129
+ end
130
+
131
+ context 'when user does not have editor role' do
132
+ it 'is false' do
133
+ expect(editor?).to eq(false)
134
+ end
135
+ end
136
+
137
+ context 'when user has the editor role' do
138
+ let(:user) { FactoryGirl.create(:g5_authenticatable_editor) }
139
+
140
+ it 'is true' do
141
+ expect(editor?).to eq(true)
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#viewer?' do
147
+ subject(:viewer?) { policy.new(user, record).viewer? }
148
+
149
+ context 'when there is no user' do
150
+ let(:user) {}
151
+
152
+ it 'is false' do
153
+ expect(viewer?).to eq(false)
154
+ end
155
+ end
156
+
157
+ context 'when user does not have viewer role' do
158
+ let(:user) { FactoryGirl.create(:g5_authenticatable_editor) }
159
+
160
+ it 'is false' do
161
+ expect(viewer?).to eq(false)
162
+ end
163
+ end
164
+
165
+ context 'when user has the viewer role' do
166
+ it 'is true' do
167
+ expect(viewer?).to eq(true)
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe PostPolicy do
4
+ subject(:policy) { described_class }
5
+
6
+ let(:record) { FactoryGirl.create(:post) }
7
+
8
+ permissions :index? do
9
+ it_behaves_like 'a super_admin authorizer'
10
+ end
11
+
12
+ permissions :show? do
13
+ it_behaves_like 'a super_admin authorizer'
14
+ end
15
+
16
+ permissions :new? do
17
+ it_behaves_like 'a super_admin authorizer'
18
+ end
19
+
20
+ permissions :create? do
21
+ it_behaves_like 'a super_admin authorizer'
22
+ end
23
+
24
+ permissions :edit? do
25
+ it_behaves_like 'a super_admin authorizer'
26
+ end
27
+
28
+ permissions :update? do
29
+ it_behaves_like 'a super_admin authorizer'
30
+ end
31
+
32
+ permissions :destroy? do
33
+ it_behaves_like 'a super_admin authorizer'
34
+ end
35
+ end