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
@@ -0,0 +1,681 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe ScimRails::ScimUsersController, 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 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(:user, 10, company: company)
56
+
57
+ get :index, as: :json
58
+ response_body = JSON.parse(response.body)
59
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:ListResponse"
60
+ expect(response_body["totalResults"]).to eq 10
61
+ end
62
+
63
+ it "defaults to 100 results" do
64
+ create_list(:user, 300, company: company)
65
+
66
+ get :index, as: :json
67
+ response_body = JSON.parse(response.body)
68
+ expect(response_body["totalResults"]).to eq 300
69
+ expect(response_body["Resources"].count).to eq 100
70
+ end
71
+
72
+ it "paginates results" do
73
+ create_list(:user, 400, company: company)
74
+ expect(company.users.first.id).to eq 1
75
+
76
+ get :index, params: {
77
+ startIndex: 101,
78
+ count: 200
79
+ }, as: :json
80
+ response_body = JSON.parse(response.body)
81
+ expect(response_body["totalResults"]).to eq 400
82
+ expect(response_body["Resources"].count).to eq 200
83
+ expect(response_body.dig("Resources", 0, "id")).to eq 101
84
+ end
85
+
86
+ it "paginates results by configurable scim_users_list_order" do
87
+ allow(ScimRails.config).to receive(:scim_users_list_order).and_return({ created_at: :desc })
88
+
89
+ create_list(:user, 400, company: company)
90
+ expect(company.users.first.id).to eq 1
91
+
92
+ get :index, params: {
93
+ startIndex: 1,
94
+ count: 10
95
+ }, as: :json
96
+ response_body = JSON.parse(response.body)
97
+ expect(response_body["totalResults"]).to eq 400
98
+ expect(response_body["Resources"].count).to eq 10
99
+ expect(response_body.dig("Resources", 0, "id")).to eq 400
100
+ end
101
+
102
+ it "filters results by provided email filter" do
103
+ create(:user, email: "test1@example.com", company: company)
104
+ create(:user, email: "test2@example.com", company: company)
105
+
106
+ get :index, params: {
107
+ filter: "email eq test1@example.com"
108
+ }, as: :json
109
+ response_body = JSON.parse(response.body)
110
+ expect(response_body["totalResults"]).to eq 1
111
+ expect(response_body["Resources"].count).to eq 1
112
+ end
113
+
114
+ it "filters results by provided name filter" do
115
+ create(:user, first_name: "Chidi", last_name: "Anagonye", company: company)
116
+ create(:user, first_name: "Eleanor", last_name: "Shellstrop", company: company)
117
+
118
+ get :index, params: {
119
+ filter: "familyName eq Shellstrop"
120
+ }, as: :json
121
+ response_body = JSON.parse(response.body)
122
+ expect(response_body["totalResults"]).to eq 1
123
+ expect(response_body["Resources"].count).to eq 1
124
+ end
125
+
126
+ it "returns no results for unfound filter parameters" do
127
+ get :index, params: {
128
+ filter: "familyName eq fake_not_there"
129
+ }, as: :json
130
+ response_body = JSON.parse(response.body)
131
+ expect(response_body["totalResults"]).to eq 0
132
+ expect(response_body["Resources"].count).to eq 0
133
+ end
134
+
135
+ it "returns no results for undefined filter queries" do
136
+ get :index, params: {
137
+ filter: "address eq 101 Nowhere USA"
138
+ }, as: :json
139
+ expect(response.status).to eq 400
140
+ response_body = JSON.parse(response.body)
141
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
142
+ end
143
+ end
144
+ end
145
+
146
+ describe "show" do
147
+ let(:company) { create(:company) }
148
+
149
+ context "when unauthorized" do
150
+ it "returns scim+json content type" do
151
+ get :show, params: { id: 1 }, as: :json
152
+
153
+ expect(response.media_type).to eq "application/scim+json"
154
+ end
155
+
156
+ it "fails with no credentials" do
157
+ get :show, params: { id: 1 }, as: :json
158
+
159
+ expect(response.status).to eq 401
160
+ end
161
+
162
+ it "fails with invalid credentials" do
163
+ request.env["HTTP_AUTHORIZATION"] =
164
+ ActionController::HttpAuthentication::Basic
165
+ .encode_credentials("unauthorized", "123456")
166
+
167
+ get :show, params: { id: 1 }, as: :json
168
+
169
+ expect(response.status).to eq 401
170
+ end
171
+ end
172
+
173
+ context "when authorized" do
174
+ before :each do
175
+ http_login(company)
176
+ end
177
+
178
+ it "returns scim+json content type" do
179
+ get :show, params: { id: 1 }, as: :json
180
+
181
+ expect(response.media_type).to eq "application/scim+json"
182
+ end
183
+
184
+ it "is successful with valid credentials" do
185
+ create(:user, id: 1, company: company)
186
+ get :show, params: { id: 1 }, as: :json
187
+
188
+ expect(response.status).to eq 200
189
+ end
190
+
191
+ it "returns :not_found for id that cannot be found" do
192
+ get :show, params: { id: "fake_id" }, as: :json
193
+
194
+ expect(response.status).to eq 404
195
+ end
196
+
197
+ it "returns :not_found for a correct id but unauthorized company" do
198
+ new_company = create(:company)
199
+ create(:user, company: new_company, id: 1)
200
+
201
+ get :show, params: { id: 1 }, as: :json
202
+
203
+ expect(response.status).to eq 404
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "create" do
209
+ let(:company) { create(:company) }
210
+
211
+ context "when unauthorized" do
212
+ it "returns scim+json content type" do
213
+ post :create, as: :json
214
+
215
+ expect(response.media_type).to eq "application/scim+json"
216
+ end
217
+
218
+ it "fails with no credentials" do
219
+ post :create, as: :json
220
+
221
+ expect(response.status).to eq 401
222
+ end
223
+
224
+ it "fails with invalid credentials" do
225
+ request.env["HTTP_AUTHORIZATION"] =
226
+ ActionController::HttpAuthentication::Basic
227
+ .encode_credentials("unauthorized", "123456")
228
+
229
+ post :create, as: :json
230
+
231
+ expect(response.status).to eq 401
232
+ end
233
+ end
234
+
235
+ context "when authorized" do
236
+ before :each do
237
+ http_login(company)
238
+ end
239
+
240
+ it "returns scim+json content type" do
241
+ post :create, params: {
242
+ name: {
243
+ givenName: "New",
244
+ familyName: "User"
245
+ },
246
+ emails: [
247
+ {
248
+ value: "new@example.com"
249
+ }
250
+ ]
251
+ }, as: :json
252
+
253
+ expect(response.media_type).to eq "application/scim+json"
254
+ end
255
+
256
+ it "is successful with valid credentials" do
257
+ expect(company.users.count).to eq 0
258
+
259
+ post :create, params: {
260
+ name: {
261
+ givenName: "New",
262
+ familyName: "User"
263
+ },
264
+ emails: [
265
+ {
266
+ value: "new@example.com"
267
+ }
268
+ ]
269
+ }, as: :json
270
+
271
+ expect(response.status).to eq 201
272
+ expect(company.users.count).to eq 1
273
+ user = company.users.first
274
+ expect(user.persisted?).to eq true
275
+ expect(user.first_name).to eq "New"
276
+ expect(user.last_name).to eq "User"
277
+ expect(user.email).to eq "new@example.com"
278
+ end
279
+
280
+ it "ignores unconfigured params" do
281
+ post :create, params: {
282
+ name: {
283
+ formattedName: "New User",
284
+ givenName: "New",
285
+ familyName: "User"
286
+ },
287
+ emails: [
288
+ {
289
+ value: "new@example.com"
290
+ }
291
+ ]
292
+ }, as: :json
293
+
294
+ expect(response.status).to eq 201
295
+ expect(company.users.count).to eq 1
296
+ end
297
+
298
+ it "returns 422 if required params are missing" do
299
+ post :create, params: {
300
+ name: {
301
+ familyName: "User"
302
+ },
303
+ emails: [
304
+ {
305
+ value: "new@example.com"
306
+ }
307
+ ]
308
+ }, as: :json
309
+
310
+ expect(response.status).to eq 422
311
+ expect(company.users.count).to eq 0
312
+ end
313
+
314
+ it "returns 201 if user already exists and updates user" do
315
+ create(:user, email: "new@example.com", company: company)
316
+
317
+ post :create, params: {
318
+ name: {
319
+ givenName: "Not New",
320
+ familyName: "User"
321
+ },
322
+ emails: [
323
+ {
324
+ value: "new@example.com"
325
+ }
326
+ ]
327
+ }, as: :json
328
+
329
+ expect(response.status).to eq 201
330
+ expect(company.users.count).to eq 1
331
+ expect(company.users.first.first_name).to eq "Not New"
332
+ end
333
+
334
+ it "returns 409 if user already exists and config.scim_user_prevent_update_on_create is set to true" do
335
+ allow(ScimRails.config).to receive(:scim_user_prevent_update_on_create).and_return(true)
336
+ create(:user, email: "new@example.com", company: company)
337
+
338
+ post :create, params: {
339
+ name: {
340
+ givenName: "Not New",
341
+ familyName: "User"
342
+ },
343
+ emails: [
344
+ {
345
+ value: "new@example.com"
346
+ }
347
+ ]
348
+ }, as: :json
349
+
350
+ expect(response.status).to eq 409
351
+ expect(company.users.count).to eq 1
352
+ end
353
+
354
+ it "creates and archives inactive user" do
355
+ post :create, params: {
356
+ id: 1,
357
+ userName: "test@example.com",
358
+ name: {
359
+ givenName: "Test",
360
+ familyName: "User"
361
+ },
362
+ emails: [
363
+ {
364
+ value: "test@example.com"
365
+ }
366
+ ],
367
+ active: "false"
368
+ }, as: :json
369
+
370
+ expect(response.status).to eq 201
371
+ expect(company.users.count).to eq 1
372
+ user = company.users.first
373
+ expect(user.archived?).to eq true
374
+ end
375
+ end
376
+ end
377
+
378
+ describe "put update" do
379
+ let(:company) { create(:company) }
380
+
381
+ context "when unauthorized" do
382
+ it "returns scim+json content type" do
383
+ put :put_update, params: { id: 1 }, as: :json
384
+
385
+ expect(response.media_type).to eq "application/scim+json"
386
+ end
387
+
388
+ it "fails with no credentials" do
389
+ put :put_update, params: { id: 1 }, as: :json
390
+
391
+ expect(response.status).to eq 401
392
+ end
393
+
394
+ it "fails with invalid credentials" do
395
+ request.env["HTTP_AUTHORIZATION"] =
396
+ ActionController::HttpAuthentication::Basic
397
+ .encode_credentials("unauthorized", "123456")
398
+
399
+ put :put_update, params: { id: 1 }, as: :json
400
+
401
+ expect(response.status).to eq 401
402
+ end
403
+ end
404
+
405
+ context "when authorized" do
406
+ let!(:user) { create(:user, id: 1, company: company) }
407
+
408
+ before :each do
409
+ http_login(company)
410
+ end
411
+
412
+ it "returns scim+json content type" do
413
+ put :put_update, params: put_params, as: :json
414
+
415
+ expect(response.media_type).to eq "application/scim+json"
416
+ end
417
+
418
+ it "is successful with with valid credentials" do
419
+ put :put_update, params: put_params, as: :json
420
+
421
+ expect(response.status).to eq 200
422
+ end
423
+
424
+ it "deprovisions an active record" do
425
+ request.content_type = "application/scim+json"
426
+ put :put_update, params: put_params(active: false), as: :json
427
+
428
+ expect(response.status).to eq 200
429
+ expect(user.reload.active?).to eq false
430
+ end
431
+
432
+ it "reprovisions an inactive record" do
433
+ user.archive!
434
+ expect(user.reload.active?).to eq false
435
+ request.content_type = "application/scim+json"
436
+ put :put_update, params: put_params(active: true), as: :json
437
+
438
+ expect(response.status).to eq 200
439
+ expect(user.reload.active?).to eq true
440
+ end
441
+
442
+ it "returns :not_found for id that cannot be found" do
443
+ put :put_update, params: { id: "fake_id" }, as: :json
444
+
445
+ expect(response.status).to eq 404
446
+ end
447
+
448
+ it "returns :not_found for a correct id but unauthorized company" do
449
+ new_company = create(:company)
450
+ create(:user, company: new_company, id: 1000)
451
+
452
+ put :put_update, params: { id: 1000 }, as: :json
453
+
454
+ expect(response.status).to eq 404
455
+ end
456
+
457
+ it "is returns 422 with incomplete request" do
458
+ put :put_update, params: {
459
+ id: 1,
460
+ userName: "test@example.com",
461
+ emails: [
462
+ {
463
+ value: "test@example.com"
464
+ }
465
+ ],
466
+ active: "true"
467
+ }, as: :json
468
+
469
+ expect(response.status).to eq 422
470
+ end
471
+ end
472
+ end
473
+
474
+ describe "patch update" do
475
+ let(:company) { create(:company) }
476
+
477
+ context "when unauthorized" do
478
+ it "returns scim+json content type" do
479
+ patch :patch_update, params: patch_params(id: 1), as: :json
480
+
481
+ expect(response.media_type).to eq "application/scim+json"
482
+ end
483
+
484
+ it "fails with no credentials" do
485
+ patch :patch_update, params: patch_params(id: 1), as: :json
486
+
487
+ expect(response.status).to eq 401
488
+ end
489
+
490
+ it "fails with invalid credentials" do
491
+ request.env["HTTP_AUTHORIZATION"] =
492
+ ActionController::HttpAuthentication::Basic
493
+ .encode_credentials("unauthorized", "123456")
494
+
495
+ patch :patch_update, params: patch_params(id: 1), as: :json
496
+
497
+ expect(response.status).to eq 401
498
+ end
499
+ end
500
+
501
+ context "when authorized" do
502
+ let!(:user) { create(:user, id: 1, company: company) }
503
+
504
+ before :each do
505
+ http_login(company)
506
+ end
507
+
508
+ it "returns scim+json content type" do
509
+ patch :patch_update, params: patch_params(id: 1), as: :json
510
+
511
+ expect(response.media_type).to eq "application/scim+json"
512
+ end
513
+
514
+ it "is successful with valid credentials" do
515
+ patch :patch_update, params: patch_params(id: 1), as: :json
516
+
517
+ expect(response.status).to eq 200
518
+ end
519
+
520
+ it "returns :not_found for id that cannot be found" do
521
+ get :patch_update, params: patch_params(id: "fake_id"), as: :json
522
+
523
+ expect(response.status).to eq 404
524
+ end
525
+
526
+ it "returns :not_found for a correct id but unauthorized company" do
527
+ new_company = create(:company)
528
+ create(:user, company: new_company, id: 1000)
529
+
530
+ get :patch_update, params: patch_params(id: 1000), as: :json
531
+
532
+ expect(response.status).to eq 404
533
+ end
534
+
535
+ it "successfully archives user" do
536
+ expect(company.users.count).to eq 1
537
+ user = company.users.first
538
+ expect(user.archived?).to eq false
539
+
540
+ patch :patch_update, params: patch_params(id: 1), as: :json
541
+
542
+ expect(response.status).to eq 200
543
+ expect(company.users.count).to eq 1
544
+ user.reload
545
+ expect(user.archived?).to eq true
546
+ end
547
+
548
+ it "successfully restores user" do
549
+ expect(company.users.count).to eq 1
550
+ user = company.users.first.tap(&:archive!)
551
+ expect(user.archived?).to eq true
552
+
553
+ patch \
554
+ :patch_update,
555
+ params: patch_params(id: 1, active: true),
556
+ as: :json
557
+
558
+ expect(response.status).to eq 200
559
+ expect(company.users.count).to eq 1
560
+ user.reload
561
+ expect(user.archived?).to eq false
562
+ end
563
+
564
+ it "is case insensetive for op value" do
565
+ # Note, this is for backward compatibility. op should always
566
+ # be lower case and support for case insensitivity will be removed
567
+ patch :patch_update, params: {
568
+ id: 1,
569
+ Operations: [
570
+ {
571
+ op: "Replace",
572
+ value: {
573
+ active: false
574
+ }
575
+ }
576
+ ]
577
+ }, as: :json
578
+
579
+ expect(response.status).to eq 200
580
+ end
581
+
582
+ it "throws an error for non status updates" do
583
+ patch :patch_update, params: {
584
+ id: 1,
585
+ Operations: [
586
+ {
587
+ op: "replace",
588
+ value: {
589
+ name: {
590
+ givenName: "Francis"
591
+ }
592
+ }
593
+ }
594
+ ]
595
+ }, as: :json
596
+
597
+ expect(response.status).to eq 422
598
+ response_body = JSON.parse(response.body)
599
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
600
+ end
601
+
602
+ it "returns 422 when value is not an object" do
603
+ patch :patch_update, params: {
604
+ id: 1,
605
+ Operations: [
606
+ {
607
+ op: "replace",
608
+ path: "displayName",
609
+ value: "Francis"
610
+ }
611
+ ]
612
+ }
613
+
614
+ expect(response.status).to eq 422
615
+ response_body = JSON.parse(response.body)
616
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
617
+ end
618
+
619
+ it "returns 422 when value is missing" do
620
+ patch :patch_update, params: {
621
+ id: 1,
622
+ Operations: [
623
+ {
624
+ op: "replace"
625
+ }
626
+ ]
627
+ }, as: :json
628
+
629
+ expect(response.status).to eq 422
630
+ response_body = JSON.parse(response.body)
631
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
632
+ end
633
+
634
+ it "returns 422 operations key is missing" do
635
+ patch :patch_update, params: {
636
+ id: 1,
637
+ Foobars: [
638
+ {
639
+ op: "replace"
640
+ }
641
+ ]
642
+ }, as: :json
643
+
644
+ expect(response.status).to eq 422
645
+ response_body = JSON.parse(response.body)
646
+ expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
647
+ end
648
+ end
649
+ end
650
+
651
+ def patch_params(id:, active: false)
652
+ {
653
+ id: id,
654
+ Operations: [
655
+ {
656
+ op: "replace",
657
+ value: {
658
+ active: active
659
+ }
660
+ }
661
+ ]
662
+ }
663
+ end
664
+
665
+ def put_params(active: true)
666
+ {
667
+ id: 1,
668
+ userName: "test@example.com",
669
+ name: {
670
+ givenName: "Test",
671
+ familyName: "User"
672
+ },
673
+ emails: [
674
+ {
675
+ value: "test@example.com"
676
+ }
677
+ ],
678
+ active: active
679
+ }
680
+ end
681
+ end