scimaenaga 0.4.1

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 (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