g5_authenticatable 0.4.2 → 0.5.0

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