scimaenaga 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +314 -0
  4. data/Rakefile +34 -0
  5. data/app/controllers/concerns/scim_rails/exception_handler.rb +119 -0
  6. data/app/controllers/concerns/scim_rails/response.rb +94 -0
  7. data/app/controllers/scim_rails/application_controller.rb +72 -0
  8. data/app/controllers/scim_rails/scim_groups_controller.rb +96 -0
  9. data/app/controllers/scim_rails/scim_users_controller.rb +124 -0
  10. data/app/helpers/scim_rails/application_helper.rb +4 -0
  11. data/app/models/scim_rails/application_record.rb +5 -0
  12. data/app/models/scim_rails/authorize_api_request.rb +40 -0
  13. data/app/models/scim_rails/scim_count.rb +38 -0
  14. data/app/models/scim_rails/scim_query_parser.rb +47 -0
  15. data/config/initializers/mime_types.rb +5 -0
  16. data/config/routes.rb +12 -0
  17. data/lib/generators/scim_rails/USAGE +8 -0
  18. data/lib/generators/scim_rails/scim_rails_generator.rb +7 -0
  19. data/lib/generators/scim_rails/templates/initializer.rb +166 -0
  20. data/lib/scim_rails/config.rb +85 -0
  21. data/lib/scim_rails/encoder.rb +25 -0
  22. data/lib/scim_rails/engine.rb +12 -0
  23. data/lib/scim_rails/version.rb +5 -0
  24. data/lib/scim_rails.rb +6 -0
  25. data/lib/tasks/scim_rails_tasks.rake +4 -0
  26. data/spec/controllers/scim_rails/scim_groups_controller_spec.rb +494 -0
  27. data/spec/controllers/scim_rails/scim_groups_request_spec.rb +68 -0
  28. data/spec/controllers/scim_rails/scim_users_controller_spec.rb +681 -0
  29. data/spec/controllers/scim_rails/scim_users_request_spec.rb +77 -0
  30. data/spec/dummy/Rakefile +6 -0
  31. data/spec/dummy/app/assets/config/manifest.js +5 -0
  32. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  33. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  34. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  35. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  36. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  37. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  38. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  39. data/spec/dummy/app/jobs/application_job.rb +2 -0
  40. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  41. data/spec/dummy/app/models/application_record.rb +3 -0
  42. data/spec/dummy/app/models/company.rb +4 -0
  43. data/spec/dummy/app/models/group.rb +15 -0
  44. data/spec/dummy/app/models/group_user.rb +6 -0
  45. data/spec/dummy/app/models/user.rb +39 -0
  46. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  48. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  49. data/spec/dummy/bin/bundle +3 -0
  50. data/spec/dummy/bin/rails +4 -0
  51. data/spec/dummy/bin/rake +4 -0
  52. data/spec/dummy/bin/setup +34 -0
  53. data/spec/dummy/bin/update +29 -0
  54. data/spec/dummy/config/application.rb +15 -0
  55. data/spec/dummy/config/boot.rb +5 -0
  56. data/spec/dummy/config/cable.yml +9 -0
  57. data/spec/dummy/config/database.yml +25 -0
  58. data/spec/dummy/config/environment.rb +5 -0
  59. data/spec/dummy/config/environments/development.rb +54 -0
  60. data/spec/dummy/config/environments/production.rb +86 -0
  61. data/spec/dummy/config/environments/test.rb +42 -0
  62. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  63. data/spec/dummy/config/initializers/assets.rb +11 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  66. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/spec/dummy/config/initializers/inflections.rb +16 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  69. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  70. data/spec/dummy/config/initializers/scim_rails_config.rb +85 -0
  71. data/spec/dummy/config/initializers/session_store.rb +3 -0
  72. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  73. data/spec/dummy/config/locales/en.yml +23 -0
  74. data/spec/dummy/config/puma.rb +47 -0
  75. data/spec/dummy/config/routes.rb +3 -0
  76. data/spec/dummy/config/secrets.yml +22 -0
  77. data/spec/dummy/config/spring.rb +6 -0
  78. data/spec/dummy/config.ru +5 -0
  79. data/spec/dummy/db/migrate/20181206184304_create_users.rb +15 -0
  80. data/spec/dummy/db/migrate/20181206184313_create_companies.rb +11 -0
  81. data/spec/dummy/db/migrate/20210423075859_create_groups.rb +10 -0
  82. data/spec/dummy/db/migrate/20210423075950_create_group_users.rb +10 -0
  83. data/spec/dummy/db/schema.rb +53 -0
  84. data/spec/dummy/db/seeds.rb +14 -0
  85. data/spec/dummy/public/404.html +67 -0
  86. data/spec/dummy/public/422.html +67 -0
  87. data/spec/dummy/public/500.html +66 -0
  88. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  89. data/spec/dummy/public/apple-touch-icon.png +0 -0
  90. data/spec/dummy/public/favicon.ico +0 -0
  91. data/spec/factories/company.rb +10 -0
  92. data/spec/factories/group.rb +11 -0
  93. data/spec/factories/user.rb +9 -0
  94. data/spec/lib/scim_rails/encoder_spec.rb +62 -0
  95. data/spec/spec_helper.rb +17 -0
  96. data/spec/support/auth_helper.rb +7 -0
  97. data/spec/support/factory_bot.rb +3 -0
  98. data/spec/support/scim_rails_config.rb +59 -0
  99. metadata +339 -0
data/lib/scim_rails.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "scim_rails/engine"
2
+ require "scim_rails/config"
3
+ require "scim_rails/encoder"
4
+
5
+ module ScimRails
6
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :scim_rails do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,494 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe ScimRails::ScimGroupsController, type: :controller do
6
+ include AuthHelper
7
+
8
+ routes { ScimRails::Engine.routes }
9
+
10
+ describe "index" do
11
+ let(:company) { create(:company) }
12
+
13
+ context "when unauthorized" do
14
+ it "returns scim+json content type" do
15
+ get :index, as: :json
16
+
17
+ expect(response.media_type).to eq "application/scim+json"
18
+ end
19
+
20
+ it "fails with no credentials" do
21
+ get :index, as: :json
22
+
23
+ expect(response.status).to eq 401
24
+ end
25
+
26
+ it "fails with invalid credentials" do
27
+ request.env["HTTP_AUTHORIZATION"] =
28
+ ActionController::HttpAuthentication::Basic
29
+ .encode_credentials("unauthorized", "123456")
30
+
31
+ get :index, as: :json
32
+
33
+ expect(response.status).to eq 401
34
+ end
35
+ end
36
+
37
+ context "when authorized" do
38
+ before :each do
39
+ http_login(company)
40
+ end
41
+
42
+ it "returns scim+json content type" do
43
+ get :index, as: :json
44
+
45
+ expect(response.media_type).to eq "application/scim+json"
46
+ end
47
+
48
+ it "is successful with valid credentials" do
49
+ get :index, as: :json
50
+
51
+ expect(response.status).to eq 200
52
+ end
53
+
54
+ it "returns all results" do
55
+ create_list(:group, 5, company: company)
56
+
57
+ get :index, as: :json
58
+ response_body = JSON.parse(response.body)
59
+ expect(response_body.dig("schemas", 0)).to(
60
+ eq "urn:ietf:params:scim:api:messages:2.0:ListResponse"
61
+ )
62
+ expect(response_body["totalResults"]).to eq 5
63
+ end
64
+
65
+ it "defaults to 100 results" do
66
+ create_list(:group, 300, company: company)
67
+
68
+ get :index, as: :json
69
+ response_body = JSON.parse(response.body)
70
+ expect(response_body["totalResults"]).to eq 300
71
+ expect(response_body["Resources"].count).to eq 100
72
+ end
73
+
74
+ it "paginates results" do
75
+ create_list(:group, 400, company: company)
76
+ expect(company.groups.first.id).to eq 1
77
+
78
+ get :index, params: {
79
+ startIndex: 101,
80
+ count: 200
81
+ }, as: :json
82
+ response_body = JSON.parse(response.body)
83
+ expect(response_body["totalResults"]).to eq 400
84
+ expect(response_body["Resources"].count).to eq 200
85
+ expect(response_body.dig("Resources", 0, "id")).to eq 101
86
+ end
87
+
88
+ it "paginates results by configurable scim_groups_list_order" do
89
+ allow(ScimRails.config).to(
90
+ receive(:scim_groups_list_order).and_return(created_at: :desc)
91
+ )
92
+
93
+ create_list(:group, 400, company: company)
94
+ expect(company.groups.first.id).to eq 1
95
+
96
+ get :index, params: {
97
+ startIndex: 1,
98
+ count: 10
99
+ }, as: :json
100
+ response_body = JSON.parse(response.body)
101
+ expect(response_body["totalResults"]).to eq 400
102
+ expect(response_body["Resources"].count).to eq 10
103
+ expect(response_body.dig("Resources", 0, "id")).to eq 400
104
+ end
105
+
106
+ it "filters results by provided displayName filter" do
107
+ create(:group, name: "Foo", company: company)
108
+ create(:group, name: "Bar", company: company)
109
+
110
+ get :index, params: {
111
+ filter: "displayName eq Bar"
112
+ }, as: :json
113
+ response_body = JSON.parse(response.body)
114
+ expect(response_body["totalResults"]).to eq 1
115
+ expect(response_body["Resources"].count).to eq 1
116
+ expect(response_body.dig("Resources", 0, "displayName")).to eq "Bar"
117
+ end
118
+
119
+ it "returns no results for unfound filter parameters" do
120
+ get :index, params: {
121
+ filter: "displayName eq fake_not_there"
122
+ }, as: :json
123
+ response_body = JSON.parse(response.body)
124
+ expect(response_body["totalResults"]).to eq 0
125
+ expect(response_body["Resources"].count).to eq 0
126
+ end
127
+
128
+ it "returns no results for undefined filter queries" do
129
+ get :index, params: {
130
+ filter: "address eq 101 Nowhere USA"
131
+ }, as: :json
132
+ expect(response.status).to eq 400
133
+ response_body = JSON.parse(response.body)
134
+ expect(response_body.dig("schemas", 0)).to(
135
+ eq "urn:ietf:params:scim:api:messages:2.0:Error"
136
+ )
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "show" do
142
+ let(:company) { create(:company) }
143
+
144
+ context "when unauthorized" do
145
+ it "returns scim+json content type" do
146
+ get :show, params: { id: 1 }, as: :json
147
+
148
+ expect(response.media_type).to eq "application/scim+json"
149
+ end
150
+
151
+ it "fails with no credentials" do
152
+ get :show, params: { id: 1 }, as: :json
153
+
154
+ expect(response.status).to eq 401
155
+ end
156
+
157
+ it "fails with invalid credentials" do
158
+ request.env["HTTP_AUTHORIZATION"] =
159
+ ActionController::HttpAuthentication::Basic
160
+ .encode_credentials("unauthorized", "123456")
161
+
162
+ get :show, params: { id: 1 }, as: :json
163
+
164
+ expect(response.status).to eq 401
165
+ end
166
+ end
167
+
168
+ context "when authorized" do
169
+ before :each do
170
+ http_login(company)
171
+ end
172
+
173
+ it "returns scim+json content type" do
174
+ get :show, params: { id: 1 }, as: :json
175
+
176
+ expect(response.media_type).to eq "application/scim+json"
177
+ end
178
+
179
+ it "is successful with valid credentials" do
180
+ create(:group, id: 1, company: company)
181
+ get :show, params: { id: 1 }, as: :json
182
+
183
+ expect(response.status).to eq 200
184
+ end
185
+
186
+ it "returns :not_found for id that cannot be found" do
187
+ get :show, params: { id: "fake_id" }, as: :json
188
+
189
+ expect(response.status).to eq 404
190
+ end
191
+
192
+ it "returns :not_found for a correct id but unauthorized company" do
193
+ new_company = create(:company)
194
+ create(:group, company: new_company, id: 1)
195
+
196
+ get :show, params: { id: 1 }, as: :json
197
+
198
+ expect(response.status).to eq 404
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "create" do
204
+ let(:company) { create(:company) }
205
+
206
+ context "when unauthorized" do
207
+ it "returns scim+json content type" do
208
+ post :create, as: :json
209
+
210
+ expect(response.media_type).to eq "application/scim+json"
211
+ end
212
+
213
+ it "fails with no credentials" do
214
+ post :create, as: :json
215
+
216
+ expect(response.status).to eq 401
217
+ end
218
+
219
+ it "fails with invalid credentials" do
220
+ request.env["HTTP_AUTHORIZATION"] =
221
+ ActionController::HttpAuthentication::Basic
222
+ .encode_credentials("unauthorized", "123456")
223
+
224
+ post :create, as: :json
225
+
226
+ expect(response.status).to eq 401
227
+ end
228
+ end
229
+
230
+ context "when authorized" do
231
+ before :each do
232
+ http_login(company)
233
+ end
234
+
235
+ it "returns scim+json content type" do
236
+ post :create, params: {
237
+ displayName: "Test Group",
238
+ members: []
239
+ }, as: :json
240
+
241
+ expect(response.media_type).to eq "application/scim+json"
242
+ end
243
+
244
+ it "is successful with valid credentials" do
245
+ expect(company.groups.count).to eq 0
246
+
247
+ post :create, params: {
248
+ displayName: "Test Group",
249
+ members: []
250
+ }, as: :json
251
+
252
+ expect(response.status).to eq 201
253
+ expect(company.groups.count).to eq 1
254
+ group = company.groups.first
255
+ expect(group.persisted?).to eq true
256
+ expect(group.name).to eq "Test Group"
257
+ expect(group.users).to eq []
258
+ end
259
+
260
+ it "ignores unconfigured params" do
261
+ post :create, params: {
262
+ displayName: "Test Group",
263
+ department: "Best Department",
264
+ members: []
265
+ }, as: :json
266
+
267
+ expect(response.status).to eq 201
268
+ expect(company.groups.count).to eq 1
269
+ end
270
+
271
+ it "returns 422 if required params are missing" do
272
+ post :create, params: {
273
+ members: []
274
+ }, as: :json
275
+
276
+ expect(response.status).to eq 422
277
+ expect(company.users.count).to eq 0
278
+ end
279
+
280
+ it "returns 409 if group already exists" do
281
+ create(:group, name: "Test Group", company: company)
282
+
283
+ post :create, params: {
284
+ displayName: "Test Group",
285
+ members: []
286
+ }, as: :json
287
+
288
+ expect(response.status).to eq 409
289
+ expect(company.groups.count).to eq 1
290
+ end
291
+
292
+ it "creates group" do
293
+ users = create_list(:user, 3, company: company)
294
+
295
+ post :create, params: {
296
+ displayName: "Test Group",
297
+ members: users.map do |user|
298
+ { value: user.id.to_s, display: user.email }
299
+ end
300
+ }, as: :json
301
+
302
+ expect(response.status).to eq 201
303
+ expect(company.groups.count).to eq 1
304
+ group = company.groups.first
305
+ expect(group.name).to eq "Test Group"
306
+ expect(group.users.count).to eq 3
307
+ end
308
+ end
309
+ end
310
+
311
+ describe "put update" do
312
+ let(:company) { create(:company) }
313
+
314
+ context "when unauthorized" do
315
+ it "returns scim+json content type" do
316
+ put :put_update, params: { id: 1 }, as: :json
317
+
318
+ expect(response.media_type).to eq "application/scim+json"
319
+ end
320
+
321
+ it "fails with no credentials" do
322
+ put :put_update, params: { id: 1 }, as: :json
323
+
324
+ expect(response.status).to eq 401
325
+ end
326
+
327
+ it "fails with invalid credentials" do
328
+ request.env["HTTP_AUTHORIZATION"] =
329
+ ActionController::HttpAuthentication::Basic
330
+ .encode_credentials("unauthorized", "123456")
331
+
332
+ put :put_update, params: { id: 1 }, as: :json
333
+
334
+ expect(response.status).to eq 401
335
+ end
336
+ end
337
+
338
+ context "when authorized" do
339
+ let!(:group) { create(:group, id: 1, company: company) }
340
+
341
+ before :each do
342
+ http_login(company)
343
+ end
344
+
345
+ it "returns scim+json content type" do
346
+ put :put_update, params: put_params, as: :json
347
+
348
+ expect(response.media_type).to eq "application/scim+json"
349
+ end
350
+
351
+ it "is successful with with valid credentials" do
352
+ put :put_update, params: put_params, as: :json
353
+
354
+ expect(response.status).to eq 200
355
+ end
356
+
357
+ it "can add and delete Users from a Group at once" do
358
+ user1 = create(:user, company: company, groups: [group])
359
+ user2 = create(:user, company: company)
360
+
361
+ expect do
362
+ put :put_update, params: put_params(users: [user2]), as: :json
363
+ end.to change { group.reload.users }.from([user1]).to([user2])
364
+
365
+ expect(response.status).to eq 200
366
+ end
367
+
368
+ it "returns :not_found for id that cannot be found" do
369
+ put :put_update, params: { id: "fake_id" }, as: :json
370
+
371
+ expect(response.status).to eq 404
372
+ end
373
+
374
+ it "returns :not_found for a correct id but unauthorized company" do
375
+ new_company = create(:company)
376
+ create(:group, company: new_company, id: 1000)
377
+
378
+ put :put_update, params: { id: 1000 }, as: :json
379
+
380
+ expect(response.status).to eq 404
381
+ end
382
+
383
+ it "returns 422 with incomplete request" do
384
+ put :put_update, params: {
385
+ id: 1,
386
+ members: []
387
+ }, as: :json
388
+
389
+ expect(response.status).to eq 422
390
+ end
391
+ end
392
+ end
393
+
394
+ describe "destroy" do
395
+ let(:company) { create(:company) }
396
+
397
+ context "when unauthorized" do
398
+ it "returns scim+json content type" do
399
+ delete :destroy, params: { id: 1 }, as: :json
400
+
401
+ expect(response.media_type).to eq "application/scim+json"
402
+ end
403
+
404
+ it "fails with no credentials" do
405
+ delete :destroy, params: { id: 1 }, as: :json
406
+
407
+ expect(response.status).to eq 401
408
+ end
409
+
410
+ it "fails with invalid credentials" do
411
+ request.env["HTTP_AUTHORIZATION"] =
412
+ ActionController::HttpAuthentication::Basic
413
+ .encode_credentials("unauthorized", "123456")
414
+
415
+ delete :destroy, params: { id: 1 }, as: :json
416
+
417
+ expect(response.status).to eq 401
418
+ end
419
+ end
420
+
421
+ context "when authorized" do
422
+ let!(:group) { create(:group, id: 1, company: company) }
423
+
424
+ before :each do
425
+ http_login(company)
426
+ end
427
+
428
+ context "when Group destroy method is configured" do
429
+ before do
430
+ allow(ScimRails.config).to(
431
+ receive(:group_destroy_method).and_return(:destroy!)
432
+ )
433
+ end
434
+
435
+ it "returns empty response" do
436
+ delete :destroy, params: { id: 1 }, as: :json
437
+
438
+ expect(response.body).to be_empty
439
+ end
440
+
441
+ it "is successful with valid credentials" do
442
+ delete :destroy, params: { id: 1 }, as: :json
443
+
444
+ expect(response.status).to eq 204
445
+ end
446
+
447
+ it "returns :not_found for id that cannot be found" do
448
+ delete :destroy, params: { id: "fake_id" }, as: :json
449
+
450
+ expect(response.status).to eq 404
451
+ end
452
+
453
+ it "returns :not_found for a correct id but unauthorized company" do
454
+ new_company = create(:company)
455
+ create(:group, company: new_company, id: 1000)
456
+
457
+ delete :destroy, params: { id: 1000 }, as: :json
458
+
459
+ expect(response.status).to eq 404
460
+ end
461
+
462
+ it "successfully deletes Group" do
463
+ expect do
464
+ delete :destroy, params: { id: 1 }, as: :json
465
+ end.to change { company.groups.reload.count }.from(1).to(0)
466
+
467
+ expect(response.status).to eq 204
468
+ end
469
+ end
470
+
471
+ context "when Group destroy method is not configured" do
472
+ it "does not delete Group" do
473
+ allow(ScimRails.config).to(
474
+ receive(:group_destroy_method).and_return(nil)
475
+ )
476
+
477
+ expect do
478
+ delete :destroy, params: { id: 1 }, as: :json
479
+ end.not_to change { company.groups.reload.count }.from(1)
480
+
481
+ expect(response.status).to eq 501
482
+ end
483
+ end
484
+ end
485
+ end
486
+
487
+ def put_params(name: "Test Group", users: [])
488
+ {
489
+ id: 1,
490
+ displayName: name,
491
+ members: users.map { |user| { value: user.id.to_s, display: user.email } }
492
+ }
493
+ end
494
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe ScimRails::ScimGroupsController, type: :request do
6
+ let(:company) { create(:company) }
7
+ let(:credentials) do
8
+ Base64.encode64("#{company.subdomain}:#{company.api_token}")
9
+ end
10
+ let(:authorization) { "Basic #{credentials}" }
11
+
12
+ def post_request(content_type = "application/scim+json")
13
+ post "/scim/v2/Groups",
14
+ params: {
15
+ displayName: "Dummy Group",
16
+ members: []
17
+ }.to_json,
18
+ headers: {
19
+ Authorization: authorization,
20
+ 'Content-Type': content_type
21
+ }
22
+ end
23
+
24
+ describe "Content-Type" do
25
+ it "accepts scim+json" do
26
+ expect(company.groups.count).to eq 0
27
+
28
+ post_request("application/scim+json")
29
+
30
+ expect(request.params).to include :displayName
31
+ expect(response.status).to eq 201
32
+ expect(response.media_type).to eq "application/scim+json"
33
+ expect(company.groups.count).to eq 1
34
+ end
35
+
36
+ it "can not parse unfamiliar content types" do
37
+ expect(company.groups.count).to eq 0
38
+
39
+ post_request("text/csv")
40
+
41
+ expect(request.params).not_to include :displayName
42
+ expect(response.status).to eq 422
43
+ expect(company.groups.count).to eq 0
44
+ end
45
+ end
46
+
47
+ context "OAuth Bearer Authorization" do
48
+ context "with valid token" do
49
+ let(:authorization) { "Bearer #{company.api_token}" }
50
+
51
+ it "supports OAuth bearer authorization and succeeds" do
52
+ expect { post_request }.to change(company.groups, :count).from(0).to(1)
53
+
54
+ expect(response.status).to eq 201
55
+ end
56
+ end
57
+
58
+ context "with invalid token" do
59
+ let(:authorization) { "Bearer #{SecureRandom.hex}" }
60
+
61
+ it "The request fails" do
62
+ expect { post_request }.not_to change(company.groups, :count)
63
+
64
+ expect(response.status).to eq 401
65
+ end
66
+ end
67
+ end
68
+ end