scimitar 1.8.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scimitar/active_record_backed_resources_controller.rb +20 -94
  3. data/app/controllers/scimitar/application_controller.rb +13 -41
  4. data/app/controllers/scimitar/schemas_controller.rb +0 -5
  5. data/app/models/scimitar/complex_types/address.rb +6 -0
  6. data/app/models/scimitar/engine_configuration.rb +5 -13
  7. data/app/models/scimitar/error_response.rb +0 -12
  8. data/app/models/scimitar/lists/query_parser.rb +10 -25
  9. data/app/models/scimitar/resource_invalid_error.rb +1 -1
  10. data/app/models/scimitar/resources/base.rb +4 -17
  11. data/app/models/scimitar/resources/mixin.rb +42 -539
  12. data/app/models/scimitar/schema/address.rb +0 -1
  13. data/app/models/scimitar/schema/attribute.rb +5 -14
  14. data/app/models/scimitar/schema/base.rb +1 -1
  15. data/app/models/scimitar/schema/vdtp.rb +1 -1
  16. data/app/models/scimitar/service_provider_configuration.rb +3 -14
  17. data/config/initializers/scimitar.rb +3 -28
  18. data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +10 -140
  19. data/lib/scimitar/version.rb +2 -2
  20. data/lib/scimitar.rb +2 -7
  21. data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
  22. data/spec/apps/dummy/app/models/mock_group.rb +1 -1
  23. data/spec/apps/dummy/app/models/mock_user.rb +8 -36
  24. data/spec/apps/dummy/config/application.rb +1 -0
  25. data/spec/apps/dummy/config/environments/test.rb +28 -5
  26. data/spec/apps/dummy/config/initializers/scimitar.rb +10 -61
  27. data/spec/apps/dummy/config/routes.rb +7 -28
  28. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -10
  29. data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
  30. data/spec/apps/dummy/db/schema.rb +4 -11
  31. data/spec/controllers/scimitar/application_controller_spec.rb +3 -126
  32. data/spec/controllers/scimitar/resource_types_controller_spec.rb +2 -2
  33. data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -10
  34. data/spec/models/scimitar/complex_types/address_spec.rb +4 -3
  35. data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
  36. data/spec/models/scimitar/lists/query_parser_spec.rb +9 -76
  37. data/spec/models/scimitar/resources/base_spec.rb +70 -216
  38. data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
  39. data/spec/models/scimitar/resources/mixin_spec.rb +129 -1447
  40. data/spec/models/scimitar/schema/attribute_spec.rb +3 -22
  41. data/spec/models/scimitar/schema/base_spec.rb +1 -1
  42. data/spec/models/scimitar/schema/user_spec.rb +0 -10
  43. data/spec/requests/active_record_backed_resources_controller_spec.rb +68 -787
  44. data/spec/requests/application_controller_spec.rb +3 -16
  45. data/spec/spec_helper.rb +0 -8
  46. data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +0 -108
  47. metadata +14 -25
  48. data/LICENSE.txt +0 -21
  49. data/README.md +0 -710
  50. data/lib/scimitar/support/utilities.rb +0 -51
  51. data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +0 -25
  52. data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +0 -25
  53. data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +0 -24
  54. data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +0 -25
@@ -81,102 +81,6 @@ RSpec.describe Scimitar::Resources::Mixin do
81
81
  include Scimitar::Resources::Mixin
82
82
  end
83
83
 
84
- # A simple schema containing two attributes that looks very like complex
85
- # type "name", except shorter and with "familyName" never returned.
86
- #
87
- NestedReturnedNeverTestNameSchema = Class.new(Scimitar::Schema::Base) do
88
- def self.id
89
- 'nested-returned-never-name-id'
90
- end
91
-
92
- def self.scim_attributes
93
- @scim_attributes ||= [
94
- Scimitar::Schema::Attribute.new(name: 'familyName', type: 'string', required: true, returned: 'never'),
95
- Scimitar::Schema::Attribute.new(name: 'givenName', type: 'string', required: true)
96
- ]
97
- end
98
- end
99
-
100
- # A complex type that uses the above schema, giving us the ability to define
101
- # an attribute using this complex type, with therefore the *nested* attribute
102
- # "familyName" being never returned.
103
- #
104
- NestedReturnedNeverTestNameType = Class.new(Scimitar::ComplexTypes::Base) do
105
- set_schema NestedReturnedNeverTestNameSchema
106
- end
107
-
108
- # A test schema that uses the above type, the standard name type (but that
109
- # *entire* top-level attribute is never returned) and a simple String item.
110
- #
111
- NestedReturnedNeverTestSchema = Class.new(Scimitar::Schema::Base) do
112
- def self.id
113
- 'nested-returned-never-id'
114
- end
115
-
116
- def self.scim_attributes
117
- [
118
- Scimitar::Schema::Attribute.new(
119
- name: 'name', complexType: NestedReturnedNeverTestNameType
120
- ),
121
- Scimitar::Schema::Attribute.new(
122
- name: 'privateName', complexType: Scimitar::ComplexTypes::Name, returned: 'never'
123
- ),
124
- Scimitar::Schema::Attribute.new(
125
- name: 'simpleName', type: 'string'
126
- )
127
- ]
128
- end
129
- end
130
-
131
- # Define a resource that is based upon the above schema.
132
- #
133
- NestedReturnedNeverTestResourse = Class.new(Scimitar::Resources::Base) do
134
- set_schema NestedReturnedNeverTestSchema
135
- end
136
-
137
- # Create a testable model that is our internal representation of the above
138
- # resource.
139
- #
140
- class NestedReturnedNeverTest
141
- include ActiveModel::Model
142
-
143
- def self.scim_resource_type
144
- return NestedReturnedNeverTestResourse
145
- end
146
-
147
- attr_accessor :given_name,
148
- :last_name,
149
- :private_given_name,
150
- :private_last_name,
151
- :simple_name
152
-
153
- def self.scim_attributes_map
154
- return {
155
- name: {
156
- givenName: :given_name,
157
- familyName: :last_name
158
- },
159
-
160
- privateName: {
161
- givenName: :private_given_name,
162
- familyName: :private_last_name
163
- },
164
-
165
- simpleName: :simple_name
166
- }
167
- end
168
-
169
- def self.scim_mutable_attributes
170
- return nil
171
- end
172
-
173
- def self.scim_queryable_attributes
174
- return nil
175
- end
176
-
177
- include Scimitar::Resources::Mixin
178
- end
179
-
180
84
  # ===========================================================================
181
85
  # Errant class definitions
182
86
  # ===========================================================================
@@ -255,74 +159,41 @@ RSpec.describe Scimitar::Resources::Mixin do
255
159
  # =========================================================================
256
160
 
257
161
  context '#to_scim' do
258
- context 'with a UUID, renamed primary key column' do
259
- it 'compiles instance attribute values into a SCIM representation, but omits do-not-return fields' do
260
- uuid = SecureRandom.uuid
261
-
262
- instance = MockUser.new
263
- instance.primary_key = uuid
264
- instance.scim_uid = 'AA02984'
265
- instance.username = 'foo'
266
- instance.password = 'correcthorsebatterystaple'
267
- instance.first_name = 'Foo'
268
- instance.last_name = 'Bar'
269
- instance.work_email_address = 'foo.bar@test.com'
270
- instance.home_email_address = nil
271
- instance.work_phone_number = '+642201234567'
272
- instance.organization = 'SOMEORG'
273
-
274
- g1 = MockGroup.create!(display_name: 'Group 1')
275
- g2 = MockGroup.create!(display_name: 'Group 2')
276
- g3 = MockGroup.create!(display_name: 'Group 3')
277
-
278
- g1.mock_users << instance
279
- g3.mock_users << instance
280
-
281
- scim = instance.to_scim(location: "https://test.com/mock_users/#{uuid}")
282
- json = scim.to_json()
283
- hash = JSON.parse(json)
284
-
285
- expect(hash).to eql({
286
- 'userName' => 'foo',
287
- 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
288
- 'active' => true,
289
- 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
290
- 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
291
- 'id' => uuid,
292
- 'externalId' => 'AA02984',
293
- 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
294
- 'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
295
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
296
-
297
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
298
- 'organization' => 'SOMEORG',
299
- 'department' => nil
300
- }
301
- })
302
- end
303
- end # "context 'with a UUID, renamed primary key column' do"
304
-
305
- context 'with an integer, conventionally named primary key column' do
306
- it 'compiles instance attribute values into a SCIM representation' do
307
- instance = MockGroup.new
308
- instance.id = 42
309
- instance.scim_uid = 'GG02984'
310
- instance.display_name = 'Some group'
311
-
312
- scim = instance.to_scim(location: 'https://test.com/mock_groups/42')
313
- json = scim.to_json()
314
- hash = JSON.parse(json)
315
-
316
- expect(hash).to eql({
317
- 'displayName' => 'Some group',
318
- 'id' => '42', # Note, String
319
- 'externalId' => 'GG02984',
320
- 'members' => [],
321
- 'meta' => {'location'=>'https://test.com/mock_groups/42', 'resourceType'=>'Group'},
322
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
323
- })
324
- end
325
- end # "context 'with an integer, conventionally named primary key column' do"
162
+ it 'compiles instance attribute values into a SCIM representation' do
163
+ instance = MockUser.new
164
+ instance.id = 42
165
+ instance.scim_uid = 'AA02984'
166
+ instance.username = 'foo'
167
+ instance.first_name = 'Foo'
168
+ instance.last_name = 'Bar'
169
+ instance.work_email_address = 'foo.bar@test.com'
170
+ instance.home_email_address = nil
171
+ instance.work_phone_number = '+642201234567'
172
+
173
+ g1 = MockGroup.create!(display_name: 'Group 1')
174
+ g2 = MockGroup.create!(display_name: 'Group 2')
175
+ g3 = MockGroup.create!(display_name: 'Group 3')
176
+
177
+ g1.mock_users << instance
178
+ g3.mock_users << instance
179
+
180
+ scim = instance.to_scim(location: 'https://test.com/mock_users/42')
181
+ json = scim.to_json()
182
+ hash = JSON.parse(json)
183
+
184
+ expect(hash).to eql({
185
+ 'userName' => 'foo',
186
+ 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
187
+ 'active' => true,
188
+ 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
189
+ 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
190
+ 'id' => '42', # Note, String
191
+ 'externalId' => 'AA02984',
192
+ 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
193
+ 'meta' => {'location'=>'https://test.com/mock_users/42', 'resourceType'=>'User'},
194
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
195
+ })
196
+ end
326
197
 
327
198
  context 'with optional timestamps' do
328
199
  context 'creation only' do
@@ -421,9 +292,7 @@ RSpec.describe Scimitar::Resources::Mixin do
421
292
  ],
422
293
 
423
294
  'meta' => {'location'=>'https://test.com/static_map_test', 'resourceType'=>'User'},
424
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
425
-
426
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
295
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
427
296
  })
428
297
  end
429
298
  end # "context 'using static mappings' do"
@@ -450,38 +319,12 @@ RSpec.describe Scimitar::Resources::Mixin do
450
319
  ],
451
320
 
452
321
  'meta' => {'location'=>'https://test.com/dynamic_map_test', 'resourceType'=>'User'},
453
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
454
-
455
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
322
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
456
323
  })
457
324
  end
458
325
  end # "context 'using dynamic lists' do"
459
326
  end # "context 'with arrays' do"
460
327
 
461
- context 'with "returned: \'never\' fields' do
462
- it 'hides appropriate top-level and nested attributes' do
463
- instance = NestedReturnedNeverTest.new(
464
- given_name: 'One',
465
- last_name: 'Two',
466
- private_given_name: 'Three',
467
- private_last_name: 'Four',
468
- simple_name: 'Five'
469
- )
470
-
471
- scim = instance.to_scim(location: 'https://test.com/never_retutrned_test')
472
- json = scim.to_json()
473
- hash = JSON.parse(json)
474
-
475
- expect(hash).to eql({
476
- 'name' => { 'givenName' => 'One' },
477
- 'simpleName' => 'Five',
478
-
479
- 'meta' => {'location'=>'https://test.com/never_retutrned_test', 'resourceType'=>'NestedReturnedNeverTestResourse'},
480
- 'schemas' => ['nested-returned-never-id']
481
- })
482
- end
483
- end # "context 'with "returned: \'never\' fields' do"
484
-
485
328
  context 'with bad definitions' do
486
329
  it 'complains about non-Hash entries in mapping Arrays' do
487
330
  expect(StaticMapTest).to receive(:scim_attributes_map).and_return({
@@ -525,7 +368,6 @@ RSpec.describe Scimitar::Resources::Mixin do
525
368
  it 'ignoring read-only lists' do
526
369
  hash = {
527
370
  'userName' => 'foo',
528
- 'password' => 'staplebatteryhorsecorrect',
529
371
  'name' => {'givenName' => 'Foo', 'familyName' => 'Bar'},
530
372
  'active' => true,
531
373
  'emails' => [{'type' => 'work', 'primary' => true, 'value' => 'foo.bar@test.com'}],
@@ -534,12 +376,7 @@ RSpec.describe Scimitar::Resources::Mixin do
534
376
  'id' => '42', # Note, String
535
377
  'externalId' => 'AA02984',
536
378
  'meta' => {'location' => 'https://test.com/mock_users/42', 'resourceType' => 'User'},
537
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
538
-
539
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
540
- 'organization' => 'SOMEORG',
541
- 'DEPARTMENT' => 'SOMEDPT'
542
- }
379
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
543
380
  }
544
381
 
545
382
  hash = spec_helper_hupcase(hash) if force_upper_case
@@ -550,14 +387,11 @@ RSpec.describe Scimitar::Resources::Mixin do
550
387
 
551
388
  expect(instance.scim_uid ).to eql('AA02984')
552
389
  expect(instance.username ).to eql('foo')
553
- expect(instance.password ).to eql('staplebatteryhorsecorrect')
554
390
  expect(instance.first_name ).to eql('Foo')
555
391
  expect(instance.last_name ).to eql('Bar')
556
392
  expect(instance.work_email_address).to eql('foo.bar@test.com')
557
393
  expect(instance.home_email_address).to be_nil
558
394
  expect(instance.work_phone_number ).to eql('+642201234567')
559
- expect(instance.organization ).to eql('SOMEORG')
560
- expect(instance.department ).to eql('SOMEDPT')
561
395
  end
562
396
 
563
397
  it 'honouring read-write lists' do
@@ -571,8 +405,8 @@ RSpec.describe Scimitar::Resources::Mixin do
571
405
  'displayName' => 'Foo Group',
572
406
  'members' => [
573
407
  {'type' => 'Group', 'value' => g1.id.to_s},
574
- {'type' => 'User', 'value' => u1.primary_key.to_s},
575
- {'type' => 'User', 'value' => u3.primary_key.to_s}
408
+ {'type' => 'User', 'value' => u1.id.to_s},
409
+ {'type' => 'User', 'value' => u3.id.to_s}
576
410
  ],
577
411
  'externalId' => 'GG01536',
578
412
  'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
@@ -619,10 +453,8 @@ RSpec.describe Scimitar::Resources::Mixin do
619
453
  end # "context 'using upper case' do"
620
454
 
621
455
  it 'clears things not present in input' do
622
- uuid = SecureRandom.uuid
623
-
624
456
  instance = MockUser.new
625
- instance.primary_key = uuid
457
+ instance.id = 42
626
458
  instance.scim_uid = 'AA02984'
627
459
  instance.username = 'foo'
628
460
  instance.first_name = 'Foo'
@@ -633,7 +465,7 @@ RSpec.describe Scimitar::Resources::Mixin do
633
465
 
634
466
  instance.from_scim!(scim_hash: {})
635
467
 
636
- expect(instance.primary_key ).to eql(uuid)
468
+ expect(instance.id ).to eql(42)
637
469
  expect(instance.scim_uid ).to be_nil
638
470
  expect(instance.username ).to be_nil
639
471
  expect(instance.first_name ).to be_nil
@@ -822,8 +654,7 @@ RSpec.describe Scimitar::Resources::Mixin do
822
654
  nature: 'add',
823
655
  path: path,
824
656
  value: 'foo',
825
- altering_hash: scim_hash,
826
- with_attr_map: { userName: :user_name }
657
+ altering_hash: scim_hash
827
658
  )
828
659
 
829
660
  expect(scim_hash['userName']).to eql('foo')
@@ -838,30 +669,13 @@ RSpec.describe Scimitar::Resources::Mixin do
838
669
  nature: 'add',
839
670
  path: path,
840
671
  value: 'Baz',
841
- altering_hash: scim_hash,
842
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
672
+ altering_hash: scim_hash
843
673
  )
844
674
 
845
675
  expect(scim_hash['name']['givenName' ]).to eql('Baz')
846
676
  expect(scim_hash['name']['familyName']).to eql('Bar')
847
677
  end
848
678
 
849
- it 'with schema extensions: overwrites' do
850
- path = [ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', 'organization' ]
851
- scim_hash = { 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => { 'organization' => 'SOMEORG' } }.with_indifferent_case_insensitive_access()
852
-
853
- @instance.send(
854
- :from_patch_backend!,
855
- nature: 'add',
856
- path: path,
857
- value: 'OTHERORG',
858
- altering_hash: scim_hash,
859
- with_attr_map: { organization: :org_name }
860
- )
861
-
862
- expect(scim_hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization' ]).to eql('OTHERORG')
863
- end
864
-
865
679
  # For 'add', filter at end-of-path is nonsensical and not
866
680
  # supported by spec or Scimitar; we only test mid-path filters.
867
681
  #
@@ -886,13 +700,7 @@ RSpec.describe Scimitar::Resources::Mixin do
886
700
  nature: 'add',
887
701
  path: path,
888
702
  value: 'added_over_original@test.com',
889
- altering_hash: scim_hash,
890
- with_attr_map: {
891
- emails: [
892
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
893
- { match: 'type', with: 'work', using: { value: :work_email } },
894
- ]
895
- }
703
+ altering_hash: scim_hash
896
704
  )
897
705
 
898
706
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -918,13 +726,7 @@ RSpec.describe Scimitar::Resources::Mixin do
918
726
  nature: 'add',
919
727
  path: path,
920
728
  value: 'added_over_original@test.com',
921
- altering_hash: scim_hash,
922
- with_attr_map: {
923
- emails: [
924
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
925
- { match: 'type', with: 'work', using: { value: :work_email } },
926
- ]
927
- }
729
+ altering_hash: scim_hash
928
730
  )
929
731
 
930
732
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -951,13 +753,7 @@ RSpec.describe Scimitar::Resources::Mixin do
951
753
  nature: 'add',
952
754
  path: path,
953
755
  value: 'added_over_original@test.com',
954
- altering_hash: scim_hash,
955
- with_attr_map: {
956
- emails: [
957
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
958
- { match: 'type', with: 'work', using: { value: :work_email } },
959
- ]
960
- }
756
+ altering_hash: scim_hash
961
757
  )
962
758
 
963
759
  expect(scim_hash['emails'][0]['value']).to eql('added_over_original@test.com')
@@ -981,13 +777,7 @@ RSpec.describe Scimitar::Resources::Mixin do
981
777
  nature: 'add',
982
778
  path: path,
983
779
  value: [ { 'type' => 'work', 'value' => 'work@test.com' } ], # NOTE - to-add value is an Array (and must be)
984
- altering_hash: scim_hash,
985
- with_attr_map: {
986
- emails: [
987
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
988
- { match: 'type', with: 'work', using: { value: :work_email } },
989
- ]
990
- }
780
+ altering_hash: scim_hash
991
781
  )
992
782
 
993
783
  expect(scim_hash['emails'].size).to eql(2)
@@ -1035,12 +825,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1035
825
  nature: 'add',
1036
826
  path: ['root'],
1037
827
  value: {'members' => [{'value' => '3'}]},
1038
- altering_hash: scim_hash,
1039
- with_attr_map: {
1040
- members: [
1041
- { list: :members, using: { value: :id } }
1042
- ]
1043
- }
828
+ altering_hash: scim_hash
1044
829
  )
1045
830
 
1046
831
  expect(scim_hash['root']['members']).to match_array([{'value' => '1'}, {'value' => '2'}, {'value' => '3'}])
@@ -1058,8 +843,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1058
843
  nature: 'add',
1059
844
  path: path,
1060
845
  value: 'foo',
1061
- altering_hash: scim_hash,
1062
- with_attr_map: { userName: :user_name }
846
+ altering_hash: scim_hash
1063
847
  )
1064
848
 
1065
849
  expect(scim_hash['userName']).to eql('foo')
@@ -1074,29 +858,12 @@ RSpec.describe Scimitar::Resources::Mixin do
1074
858
  nature: 'add',
1075
859
  path: path,
1076
860
  value: 'Baz',
1077
- altering_hash: scim_hash,
1078
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
861
+ altering_hash: scim_hash
1079
862
  )
1080
863
 
1081
864
  expect(scim_hash['name']['givenName']).to eql('Baz')
1082
865
  end
1083
866
 
1084
- it 'with schema extensions: adds' do
1085
- path = [ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', 'organization' ]
1086
- scim_hash = {}.with_indifferent_case_insensitive_access()
1087
-
1088
- @instance.send(
1089
- :from_patch_backend!,
1090
- nature: 'add',
1091
- path: path,
1092
- value: 'SOMEORG',
1093
- altering_hash: scim_hash,
1094
- with_attr_map: { organization: :org_name }
1095
- )
1096
-
1097
- expect(scim_hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization' ]).to eql('SOMEORG')
1098
- end
1099
-
1100
867
  context 'with filter mid-path: adds' do
1101
868
  it 'by string match' do
1102
869
  path = [ 'emails[type eq "work"]', 'value' ]
@@ -1117,13 +884,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1117
884
  nature: 'add',
1118
885
  path: path,
1119
886
  value: 'added@test.com',
1120
- altering_hash: scim_hash,
1121
- with_attr_map: {
1122
- emails: [
1123
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1124
- { match: 'type', with: 'work', using: { value: :work_email } },
1125
- ]
1126
- }
887
+ altering_hash: scim_hash
1127
888
  )
1128
889
 
1129
890
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -1148,13 +909,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1148
909
  nature: 'add',
1149
910
  path: path,
1150
911
  value: 'added@test.com',
1151
- altering_hash: scim_hash,
1152
- with_attr_map: {
1153
- emails: [
1154
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1155
- { match: 'type', with: 'work', using: { value: :work_email } },
1156
- ]
1157
- }
912
+ altering_hash: scim_hash
1158
913
  )
1159
914
 
1160
915
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -1170,13 +925,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1170
925
  nature: 'add',
1171
926
  path: path,
1172
927
  value: 'added@test.com',
1173
- altering_hash: scim_hash,
1174
- with_attr_map: {
1175
- emails: [
1176
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1177
- { match: 'type', with: 'work', using: { value: :work_email } },
1178
- ]
1179
- }
928
+ altering_hash: scim_hash
1180
929
  )
1181
930
 
1182
931
  expect(scim_hash['emails'][0]['value']).to eql('added@test.com')
@@ -1200,13 +949,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1200
949
  nature: 'add',
1201
950
  path: path,
1202
951
  value: 'added@test.com',
1203
- altering_hash: scim_hash,
1204
- with_attr_map: {
1205
- emails: [
1206
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1207
- { match: 'type', with: 'work', using: { value: :work_email } },
1208
- ]
1209
- }
952
+ altering_hash: scim_hash
1210
953
  )
1211
954
 
1212
955
  expect(scim_hash['emails'][0]['value']).to eql('added@test.com')
@@ -1223,13 +966,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1223
966
  nature: 'add',
1224
967
  path: path,
1225
968
  value: [ { 'type' => 'work', 'value' => 'work@test.com' } ], # NOTE - to-add value is an Array (and must be)
1226
- altering_hash: scim_hash,
1227
- with_attr_map: {
1228
- emails: [
1229
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1230
- { match: 'type', with: 'work', using: { value: :work_email } },
1231
- ]
1232
- }
969
+ altering_hash: scim_hash
1233
970
  )
1234
971
 
1235
972
  expect(scim_hash['emails'].size).to eql(1)
@@ -1245,7 +982,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1245
982
  #
1246
983
  context 'remove' do
1247
984
  context 'when prior value already exists' do
1248
- it 'simple value: clears to "nil" in order to remove' do
985
+ it 'simple value: removes' do
1249
986
  path = [ 'userName' ]
1250
987
  scim_hash = { 'userName' => 'bar' }.with_indifferent_case_insensitive_access()
1251
988
 
@@ -1254,14 +991,13 @@ RSpec.describe Scimitar::Resources::Mixin do
1254
991
  nature: 'remove',
1255
992
  path: path,
1256
993
  value: nil,
1257
- altering_hash: scim_hash,
1258
- with_attr_map: { userName: :user_name }
994
+ altering_hash: scim_hash
1259
995
  )
1260
996
 
1261
- expect(scim_hash).to eql({ 'userName' => nil })
997
+ expect(scim_hash).to be_empty
1262
998
  end
1263
999
 
1264
- it 'nested simple value: clears to "nil" in order to remove' do
1000
+ it 'nested simple value: removes' do
1265
1001
  path = [ 'name', 'givenName' ]
1266
1002
  scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }.with_indifferent_case_insensitive_access()
1267
1003
 
@@ -1270,15 +1006,15 @@ RSpec.describe Scimitar::Resources::Mixin do
1270
1006
  nature: 'remove',
1271
1007
  path: path,
1272
1008
  value: nil,
1273
- altering_hash: scim_hash,
1274
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
1009
+ altering_hash: scim_hash
1275
1010
  )
1276
1011
 
1277
- expect(scim_hash).to eql({ 'name' => { 'givenName' => nil, 'familyName' => 'Bar' } })
1012
+ expect(scim_hash['name']).to_not have_key('givenName')
1013
+ expect(scim_hash['name']['familyName']).to eql('Bar')
1278
1014
  end
1279
1015
 
1280
1016
  context 'with filter mid-path' do
1281
- it 'by string match: clears to "nil" in order to remove' do
1017
+ it 'by string match: removes' do
1282
1018
  path = [ 'emails[type eq "work"]', 'value' ]
1283
1019
  scim_hash = {
1284
1020
  'emails' => [
@@ -1298,30 +1034,14 @@ RSpec.describe Scimitar::Resources::Mixin do
1298
1034
  nature: 'remove',
1299
1035
  path: path,
1300
1036
  value: nil,
1301
- altering_hash: scim_hash,
1302
- with_attr_map: {
1303
- emails: [
1304
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1305
- { match: 'type', with: 'work', using: { value: :work_email } },
1306
- ]
1307
- }
1037
+ altering_hash: scim_hash
1308
1038
  )
1309
1039
 
1310
- expect(scim_hash).to eql({
1311
- 'emails' => [
1312
- {
1313
- 'type' => 'home',
1314
- 'value' => 'home@test.com'
1315
- },
1316
- {
1317
- 'type' => 'work',
1318
- 'value' => nil
1319
- }
1320
- ]
1321
- })
1040
+ expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1041
+ expect(scim_hash['emails'][1]).to_not have_key('value')
1322
1042
  end
1323
1043
 
1324
- it 'by boolean match: clears to "nil" in order to remove' do
1044
+ it 'by boolean match: removes' do
1325
1045
  path = [ 'emails[primary eq true]', 'value' ]
1326
1046
  scim_hash = {
1327
1047
  'emails' => [
@@ -1340,29 +1060,14 @@ RSpec.describe Scimitar::Resources::Mixin do
1340
1060
  nature: 'remove',
1341
1061
  path: path,
1342
1062
  value: nil,
1343
- altering_hash: scim_hash,
1344
- with_attr_map: {
1345
- emails: [
1346
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1347
- { match: 'type', with: 'work', using: { value: :work_email } },
1348
- ]
1349
- }
1063
+ altering_hash: scim_hash
1350
1064
  )
1351
1065
 
1352
- expect(scim_hash).to eql({
1353
- 'emails' => [
1354
- {
1355
- 'value' => 'home@test.com'
1356
- },
1357
- {
1358
- 'value' => nil,
1359
- 'primary' => true
1360
- }
1361
- ]
1362
- })
1066
+ expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1067
+ expect(scim_hash['emails'][1]).to_not have_key('value')
1363
1068
  end
1364
1069
 
1365
- it 'multiple matches: clears all to "nil" in order to remove' do
1070
+ it 'multiple matches: removes all' do
1366
1071
  path = [ 'emails[type eq "work"]', 'value' ]
1367
1072
  scim_hash = {
1368
1073
  'emails' => [
@@ -1373,11 +1078,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1373
1078
  {
1374
1079
  'type' => 'work',
1375
1080
  'value' => 'work_2@test.com'
1376
- },
1377
- {
1378
- 'type' => 'home',
1379
- 'value' => 'home@test.com'
1380
- },
1081
+ }
1381
1082
  ]
1382
1083
  }.with_indifferent_case_insensitive_access()
1383
1084
 
@@ -1386,36 +1087,16 @@ RSpec.describe Scimitar::Resources::Mixin do
1386
1087
  nature: 'remove',
1387
1088
  path: path,
1388
1089
  value: nil,
1389
- altering_hash: scim_hash,
1390
- with_attr_map: {
1391
- emails: [
1392
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1393
- { match: 'type', with: 'work', using: { value: :work_email } },
1394
- ]
1395
- }
1090
+ altering_hash: scim_hash
1396
1091
  )
1397
1092
 
1398
- expect(scim_hash).to eql({
1399
- 'emails' => [
1400
- {
1401
- 'type' => 'work',
1402
- 'value' => nil
1403
- },
1404
- {
1405
- 'type' => 'work',
1406
- 'value' => nil
1407
- },
1408
- {
1409
- 'type' => 'home',
1410
- 'value' => 'home@test.com'
1411
- },
1412
- ]
1413
- })
1093
+ expect(scim_hash['emails'][0]).to_not have_key('value')
1094
+ expect(scim_hash['emails'][1]).to_not have_key('value')
1414
1095
  end
1415
1096
  end # "context 'with filter mid-path' do"
1416
1097
 
1417
1098
  context 'with filter at end of path' do
1418
- it 'by string match: clears to "nil" in order to remove' do
1099
+ it 'by string match: removes entire matching array entry' do
1419
1100
  path = [ 'emails[type eq "work"]' ]
1420
1101
  scim_hash = {
1421
1102
  'emails' => [
@@ -1435,39 +1116,23 @@ RSpec.describe Scimitar::Resources::Mixin do
1435
1116
  nature: 'remove',
1436
1117
  path: path,
1437
1118
  value: nil,
1438
- altering_hash: scim_hash,
1439
- with_attr_map: {
1440
- emails: [
1441
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1442
- { match: 'type', with: 'work', using: { value: :work_email } },
1443
- ]
1444
- }
1119
+ altering_hash: scim_hash
1445
1120
  )
1446
1121
 
1447
- expect(scim_hash).to eql({
1448
- 'emails' => [
1449
- {
1450
- 'type' => 'home',
1451
- 'value' => 'home@test.com'
1452
- },
1453
- {
1454
- 'type' => 'work',
1455
- 'value' => nil
1456
- }
1457
- ]
1458
- })
1122
+ expect(scim_hash['emails'].size).to eql(1)
1123
+ expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1459
1124
  end
1460
1125
 
1461
- it 'by boolean match: clears to "nil" in order to remove' do
1126
+ it 'by boolean match: removes entire matching array entry' do
1462
1127
  path = [ 'emails[primary eq true]' ]
1463
1128
  scim_hash = {
1464
1129
  'emails' => [
1465
1130
  {
1466
- 'value' => 'home@test.com',
1467
- 'primary' => true
1131
+ 'value' => 'home@test.com'
1468
1132
  },
1469
1133
  {
1470
- 'value' => 'work@test.com'
1134
+ 'value' => 'work@test.com',
1135
+ 'primary' => true
1471
1136
  }
1472
1137
  ]
1473
1138
  }.with_indifferent_case_insensitive_access()
@@ -1477,29 +1142,14 @@ RSpec.describe Scimitar::Resources::Mixin do
1477
1142
  nature: 'remove',
1478
1143
  path: path,
1479
1144
  value: nil,
1480
- altering_hash: scim_hash,
1481
- with_attr_map: {
1482
- emails: [
1483
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1484
- { match: 'type', with: 'work', using: { value: :work_email } },
1485
- ]
1486
- }
1145
+ altering_hash: scim_hash
1487
1146
  )
1488
1147
 
1489
- expect(scim_hash).to eql({
1490
- 'emails' => [
1491
- {
1492
- 'value' => nil,
1493
- 'primary' => true
1494
- },
1495
- {
1496
- 'value' => 'work@test.com'
1497
- }
1498
- ]
1499
- })
1148
+ expect(scim_hash['emails'].size).to eql(1)
1149
+ expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1500
1150
  end
1501
1151
 
1502
- it 'multiple matches: clears all to "nil" in order to remove' do
1152
+ it 'multiple matches: removes all matching array entries' do
1503
1153
  path = [ 'emails[type eq "work"]' ]
1504
1154
  scim_hash = {
1505
1155
  'emails' => [
@@ -1523,45 +1173,21 @@ RSpec.describe Scimitar::Resources::Mixin do
1523
1173
  nature: 'remove',
1524
1174
  path: path,
1525
1175
  value: nil,
1526
- altering_hash: scim_hash,
1527
- with_attr_map: {
1528
- emails: [
1529
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1530
- { match: 'type', with: 'work', using: { value: :work_email } },
1531
- ]
1532
- }
1176
+ altering_hash: scim_hash
1533
1177
  )
1534
1178
 
1535
- expect(scim_hash).to eql({
1536
- 'emails' => [
1537
- {
1538
- 'type' => 'work',
1539
- 'value' => nil
1540
- },
1541
- {
1542
- 'type' => 'work',
1543
- 'value' => nil
1544
- },
1545
- {
1546
- 'type' => 'home',
1547
- 'value' => 'home@test.com'
1548
- },
1549
- ]
1550
- })
1179
+ expect(scim_hash['emails'].size).to eql(1)
1180
+ expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1551
1181
  end
1552
1182
  end # "context 'with filter at end of path' do"
1553
1183
 
1554
- it 'whole array: clears mapped values to "nil" to remove them' do
1184
+ it 'whole array: removes' do
1555
1185
  path = [ 'emails' ]
1556
1186
  scim_hash = {
1557
1187
  'emails' => [
1558
1188
  {
1559
1189
  'type' => 'home',
1560
1190
  'value' => 'home@test.com'
1561
- },
1562
- {
1563
- 'type' => 'work',
1564
- 'value' => 'work@test.com'
1565
1191
  }
1566
1192
  ]
1567
1193
  }.with_indifferent_case_insensitive_access()
@@ -1571,741 +1197,11 @@ RSpec.describe Scimitar::Resources::Mixin do
1571
1197
  nature: 'remove',
1572
1198
  path: path,
1573
1199
  value: nil,
1574
- altering_hash: scim_hash,
1575
- with_attr_map: {
1576
- emails: [
1577
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1578
- { match: 'type', with: 'work', using: { value: :work_email } },
1579
- ]
1580
- }
1200
+ altering_hash: scim_hash
1581
1201
  )
1582
1202
 
1583
- expect(scim_hash).to eql({
1584
- 'emails' => [
1585
- {
1586
- 'type' => 'home',
1587
- 'value' => nil
1588
- },
1589
- {
1590
- 'type' => 'work',
1591
- 'value' => nil
1592
- }
1593
- ]
1594
- })
1203
+ expect(scim_hash).to_not have_key('emails')
1595
1204
  end
1596
-
1597
- # What we expect:
1598
- #
1599
- # https://tools.ietf.org/html/rfc7644#section-3.5.2.2
1600
- # https://docs.snowflake.com/en/user-guide/scim-intro.html#patch-scim-v2-groups-id
1601
- #
1602
- # ...vs accounting for the unusual payloads we sometimes get,
1603
- # tested here.
1604
- #
1605
- context 'special cases' do
1606
-
1607
- # https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
1608
- #
1609
- context 'Microsoft-style payload' do
1610
- context 'removing a user from a group' do
1611
- it 'removes identified user' do
1612
- path = [ 'members' ]
1613
- value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
1614
- scim_hash = {
1615
- 'displayname' => 'Mock group',
1616
- 'members' => [
1617
- {
1618
- 'value' => '50ca93d04ab0c2de4772',
1619
- 'display' => 'Ingrid Smith',
1620
- 'type' => 'User'
1621
- },
1622
- {
1623
- 'value' => 'f648f8d5ea4e4cd38e9c',
1624
- 'display' => 'Fred Smith',
1625
- 'type' => 'User'
1626
- },
1627
- {
1628
- 'value' => 'a774d480e8112101375b',
1629
- 'display' => 'Taylor Smith',
1630
- 'type' => 'User'
1631
- }
1632
- ]
1633
- }.with_indifferent_case_insensitive_access()
1634
-
1635
- @instance.send(
1636
- :from_patch_backend!,
1637
- nature: 'remove',
1638
- path: path,
1639
- value: value,
1640
- altering_hash: scim_hash,
1641
- with_attr_map: {
1642
- members: [
1643
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1644
- ]
1645
- }
1646
- )
1647
-
1648
- expect(scim_hash).to eql({
1649
- 'displayname' => 'Mock group',
1650
- 'members' => [
1651
- {
1652
- 'value' => '50ca93d04ab0c2de4772',
1653
- 'display' => 'Ingrid Smith',
1654
- 'type' => 'User'
1655
- },
1656
- {
1657
- 'value' => 'a774d480e8112101375b',
1658
- 'display' => 'Taylor Smith',
1659
- 'type' => 'User'
1660
- }
1661
- ]
1662
- })
1663
- end
1664
-
1665
- it 'removes multiple identified users' do
1666
- path = [ 'members' ]
1667
- value = [
1668
- { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' },
1669
- { '$ref' => nil, 'value' => '50ca93d04ab0c2de4772' }
1670
- ]
1671
- scim_hash = {
1672
- 'displayname' => 'Mock group',
1673
- 'members' => [
1674
- {
1675
- 'value' => '50ca93d04ab0c2de4772',
1676
- 'display' => 'Ingrid Smith',
1677
- 'type' => 'User'
1678
- },
1679
- {
1680
- 'value' => 'f648f8d5ea4e4cd38e9c',
1681
- 'display' => 'Fred Smith',
1682
- 'type' => 'User'
1683
- },
1684
- {
1685
- 'value' => 'a774d480e8112101375b',
1686
- 'display' => 'Taylor Smith',
1687
- 'type' => 'User'
1688
- }
1689
- ]
1690
- }.with_indifferent_case_insensitive_access()
1691
-
1692
- @instance.send(
1693
- :from_patch_backend!,
1694
- nature: 'remove',
1695
- path: path,
1696
- value: value,
1697
- altering_hash: scim_hash,
1698
- with_attr_map: {
1699
- members: [
1700
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1701
- ]
1702
- }
1703
- )
1704
-
1705
- expect(scim_hash).to eql({
1706
- 'displayname' => 'Mock group',
1707
- 'members' => [
1708
- {
1709
- 'value' => 'a774d480e8112101375b',
1710
- 'display' => 'Taylor Smith',
1711
- 'type' => 'User'
1712
- }
1713
- ]
1714
- })
1715
- end
1716
-
1717
- it 'removes all users individually without error' do
1718
- path = [ 'members' ]
1719
- value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
1720
- scim_hash = {
1721
- 'displayname' => 'Mock group',
1722
- 'members' => [
1723
- {
1724
- 'value' => 'f648f8d5ea4e4cd38e9c',
1725
- 'display' => 'Fred Smith',
1726
- 'type' => 'User'
1727
- }
1728
- ]
1729
- }.with_indifferent_case_insensitive_access()
1730
-
1731
- @instance.send(
1732
- :from_patch_backend!,
1733
- nature: 'remove',
1734
- path: path,
1735
- value: value,
1736
- altering_hash: scim_hash,
1737
- with_attr_map: {
1738
- members: [
1739
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1740
- ]
1741
- }
1742
- )
1743
-
1744
- expect(scim_hash).to eql({
1745
- 'displayname' => 'Mock group',
1746
- 'members' => []
1747
- })
1748
- end
1749
-
1750
- it 'can match on multiple attributes' do
1751
- path = [ 'members' ]
1752
- value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'User' } ]
1753
- scim_hash = {
1754
- 'displayname' => 'Mock group',
1755
- 'members' => [
1756
- {
1757
- 'value' => 'f648f8d5ea4e4cd38e9c',
1758
- 'display' => 'Fred Smith',
1759
- 'type' => 'User'
1760
- }
1761
- ]
1762
- }.with_indifferent_case_insensitive_access()
1763
-
1764
- @instance.send(
1765
- :from_patch_backend!,
1766
- nature: 'remove',
1767
- path: path,
1768
- value: value,
1769
- altering_hash: scim_hash,
1770
- with_attr_map: {
1771
- members: [
1772
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1773
- ]
1774
- }
1775
- )
1776
-
1777
- expect(scim_hash).to eql({
1778
- 'displayname' => 'Mock group',
1779
- 'members' => []
1780
- })
1781
- end
1782
-
1783
- it 'ignores unrecognised users' do
1784
- path = [ 'members' ]
1785
- value = [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ]
1786
- scim_hash = {
1787
- 'displayname' => 'Mock group',
1788
- 'members' => [
1789
- {
1790
- 'value' => 'f648f8d5ea4e4cd38e9c',
1791
- 'display' => 'Fred Smith',
1792
- 'type' => 'User'
1793
- }
1794
- ]
1795
- }.with_indifferent_case_insensitive_access()
1796
-
1797
- @instance.send(
1798
- :from_patch_backend!,
1799
- nature: 'remove',
1800
- path: path,
1801
- value: value,
1802
- altering_hash: scim_hash,
1803
- with_attr_map: {
1804
- members: [
1805
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1806
- ]
1807
- }
1808
- )
1809
-
1810
- # The 'value' mismatched, so the user was not removed.
1811
- #
1812
- expect(scim_hash).to eql({
1813
- 'displayname' => 'Mock group',
1814
- 'members' => [
1815
- {
1816
- 'value' => 'f648f8d5ea4e4cd38e9c',
1817
- 'display' => 'Fred Smith',
1818
- 'type' => 'User'
1819
- }
1820
- ]
1821
- })
1822
- end
1823
-
1824
- it 'ignores a mismatch on (for example) "type"' do
1825
- path = [ 'members' ]
1826
- value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'Group' } ]
1827
- scim_hash = {
1828
- 'displayname' => 'Mock group',
1829
- 'members' => [
1830
- {
1831
- 'value' => 'f648f8d5ea4e4cd38e9c',
1832
- 'display' => 'Fred Smith',
1833
- 'type' => 'User'
1834
- }
1835
- ]
1836
- }.with_indifferent_case_insensitive_access()
1837
-
1838
- @instance.send(
1839
- :from_patch_backend!,
1840
- nature: 'remove',
1841
- path: path,
1842
- value: value,
1843
- altering_hash: scim_hash,
1844
- with_attr_map: {
1845
- members: [
1846
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1847
- ]
1848
- }
1849
- )
1850
-
1851
- # Type 'Group' mismatches 'User', so the user was not
1852
- # removed.
1853
- #
1854
- expect(scim_hash).to eql({
1855
- 'displayname' => 'Mock group',
1856
- 'members' => [
1857
- {
1858
- 'value' => 'f648f8d5ea4e4cd38e9c',
1859
- 'display' => 'Fred Smith',
1860
- 'type' => 'User'
1861
- }
1862
- ]
1863
- })
1864
- end
1865
-
1866
- it 'matches keys case-insensitive (but preserves case in response)' do
1867
- path = [ 'members' ]
1868
- value = [ { '$ref' => nil, 'VALUe' => 'f648f8d5ea4e4cd38e9c' } ]
1869
- scim_hash = {
1870
- 'displayname' => 'Mock group',
1871
- 'memBERS' => [
1872
- {
1873
- 'vaLUe' => 'f648f8d5ea4e4cd38e9c',
1874
- 'display' => 'Fred Smith',
1875
- 'type' => 'User'
1876
- }
1877
- ]
1878
- }.with_indifferent_case_insensitive_access()
1879
-
1880
- @instance.send(
1881
- :from_patch_backend!,
1882
- nature: 'remove',
1883
- path: path,
1884
- value: value,
1885
- altering_hash: scim_hash,
1886
- with_attr_map: {
1887
- members: [
1888
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1889
- ]
1890
- }
1891
- )
1892
-
1893
- expect(scim_hash).to eql({
1894
- 'displayname' => 'Mock group',
1895
- 'memBERS' => []
1896
- })
1897
- end
1898
-
1899
- it 'matches values case-sensitive' do
1900
- path = [ 'members' ]
1901
- value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'USER' } ]
1902
- scim_hash = {
1903
- 'displayName' => 'Mock group',
1904
- 'members' => [
1905
- {
1906
- 'value' => 'f648f8d5ea4e4cd38e9c',
1907
- 'display' => 'Fred Smith',
1908
- 'type' => 'User'
1909
- }
1910
- ]
1911
- }.with_indifferent_case_insensitive_access()
1912
-
1913
- @instance.send(
1914
- :from_patch_backend!,
1915
- nature: 'remove',
1916
- path: path,
1917
- value: value,
1918
- altering_hash: scim_hash,
1919
- with_attr_map: {
1920
- members: [
1921
- { list: :members, using: { value: :id, display: :full_name, type: 'User' } }
1922
- ]
1923
- }
1924
- )
1925
-
1926
- # USER mismatches User, so the user was not removed.
1927
- #
1928
- expect(scim_hash).to eql({
1929
- 'displayName' => 'Mock group',
1930
- 'members' => [
1931
- {
1932
- 'value' => 'f648f8d5ea4e4cd38e9c',
1933
- 'display' => 'Fred Smith',
1934
- 'type' => 'User'
1935
- }
1936
- ]
1937
- })
1938
- end
1939
- end # "context 'removing a user from a group' do"
1940
-
1941
- context 'generic use' do
1942
- it 'clears static map matched items to "nil" in order to remove' do
1943
- path = [ 'emails' ]
1944
- value = [ { 'type' => 'work' } ]
1945
- scim_hash = {
1946
- 'emails' => [
1947
- {
1948
- 'type' => 'home',
1949
- 'value' => 'home@test.com'
1950
- },
1951
- {
1952
- 'type' => 'work',
1953
- 'value' => 'work@test.com'
1954
- }
1955
- ]
1956
- }.with_indifferent_case_insensitive_access()
1957
-
1958
- @instance.send(
1959
- :from_patch_backend!,
1960
- nature: 'remove',
1961
- path: path,
1962
- value: value,
1963
- altering_hash: scim_hash,
1964
- with_attr_map: {
1965
- emails: [
1966
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
1967
- { match: 'type', with: 'work', using: { value: :work_email } },
1968
- ]
1969
- }
1970
- )
1971
-
1972
- expect(scim_hash).to eql({
1973
- 'emails' => [
1974
- {
1975
- 'type' => 'home',
1976
- 'value' => 'home@test.com'
1977
- },
1978
- {
1979
- 'type' => 'work',
1980
- 'value' => nil
1981
- }
1982
- ]
1983
- })
1984
- end
1985
-
1986
- it 'ignores unmatched items' do
1987
- path = [ 'emails' ]
1988
- value = [ { 'type' => 'missing' } ]
1989
- scim_hash = {
1990
- 'emails' => [
1991
- {
1992
- 'type' => 'home',
1993
- 'value' => 'home@test.com'
1994
- },
1995
- {
1996
- 'type' => 'work',
1997
- 'value' => 'work@test.com'
1998
- }
1999
- ]
2000
- }.with_indifferent_case_insensitive_access()
2001
-
2002
- @instance.send(
2003
- :from_patch_backend!,
2004
- nature: 'remove',
2005
- path: path,
2006
- value: value,
2007
- altering_hash: scim_hash,
2008
- with_attr_map: {
2009
- emails: [
2010
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2011
- { match: 'type', with: 'work', using: { value: :work_email } },
2012
- ]
2013
- }
2014
- )
2015
-
2016
- expect(scim_hash).to eql({
2017
- 'emails' => [
2018
- {
2019
- 'type' => 'home',
2020
- 'value' => 'home@test.com'
2021
- },
2022
- {
2023
- 'type' => 'work',
2024
- 'value' => 'work@test.com'
2025
- }
2026
- ]
2027
- })
2028
- end
2029
-
2030
- it 'compares string forms' do
2031
- path = [ 'test' ]
2032
- value = [
2033
- { 'active' => true, 'value' => '12' },
2034
- { 'active' => 'false', 'value' => 42 }
2035
- ]
2036
- scim_hash = {
2037
- 'test' => [
2038
- {
2039
- 'active' => 'true',
2040
- 'value' => 12
2041
- },
2042
- {
2043
- 'active' => false,
2044
- 'value' => '42'
2045
- },
2046
- {
2047
- 'active' => 'hello',
2048
- 'value' => 'world'
2049
- }
2050
- ]
2051
- }.with_indifferent_case_insensitive_access()
2052
-
2053
- @instance.send(
2054
- :from_patch_backend!,
2055
- nature: 'remove',
2056
- path: path,
2057
- value: value,
2058
- altering_hash: scim_hash,
2059
- with_attr_map: {
2060
- test: [
2061
- {
2062
- list: :test,
2063
- using: {
2064
- active: :active,
2065
- value: :value
2066
- }
2067
- }
2068
- ]
2069
- }
2070
- )
2071
-
2072
- expect(scim_hash).to eql({
2073
- 'test' => [
2074
- {
2075
- 'active' => 'hello',
2076
- 'value' => 'world'
2077
- }
2078
- ]
2079
- })
2080
- end
2081
-
2082
- it 'handles a singular to-remove value rather than an array' do
2083
- path = [ 'emails' ]
2084
- value = { 'type' => 'work' }
2085
- scim_hash = {
2086
- 'emails' => [
2087
- {
2088
- 'type' => 'home',
2089
- 'value' => 'home@test.com'
2090
- },
2091
- {
2092
- 'type' => 'work',
2093
- 'value' => 'work@test.com'
2094
- }
2095
- ]
2096
- }.with_indifferent_case_insensitive_access()
2097
-
2098
- @instance.send(
2099
- :from_patch_backend!,
2100
- nature: 'remove',
2101
- path: path,
2102
- value: value,
2103
- altering_hash: scim_hash,
2104
- with_attr_map: {
2105
- emails: [
2106
- { match: 'type', with: 'home', using: { value: :home_email } },
2107
- { match: 'type', with: 'work', using: { value: :work_email } },
2108
- ]
2109
- }
2110
- )
2111
-
2112
- expect(scim_hash).to eql({
2113
- 'emails' => [
2114
- {
2115
- 'type' => 'home',
2116
- 'value' => 'home@test.com'
2117
- },
2118
- {
2119
- 'type' => 'work',
2120
- 'value' => nil
2121
- }
2122
- ]
2123
- })
2124
- end
2125
-
2126
- it 'handles simple values rather than object (Hash) values' do
2127
- path = [ 'test' ]
2128
- value = 42
2129
- scim_hash = {
2130
- 'test' => [
2131
- '21',
2132
- '42',
2133
- '15'
2134
- ]
2135
- }.with_indifferent_case_insensitive_access()
2136
-
2137
- @instance.send(
2138
- :from_patch_backend!,
2139
- nature: 'remove',
2140
- path: path,
2141
- value: value,
2142
- altering_hash: scim_hash,
2143
- with_attr_map: {
2144
- test: []
2145
- }
2146
- )
2147
-
2148
- expect(scim_hash).to eql({
2149
- 'test' => [
2150
- '21',
2151
- '15'
2152
- ]
2153
- })
2154
- end
2155
- end
2156
- end # "context 'Microsoft-style payload' do"
2157
-
2158
- # https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
2159
- #
2160
- context 'Salesforce-style payload' do
2161
- it 'removes identified user' do
2162
- path = [ 'members' ]
2163
- value = { 'members' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
2164
- scim_hash = {
2165
- 'displayname' => 'Mock group',
2166
- 'members' => [
2167
- {
2168
- 'value' => '50ca93d04ab0c2de4772',
2169
- 'display' => 'Ingrid Smith',
2170
- 'type' => 'User'
2171
- },
2172
- {
2173
- 'value' => 'f648f8d5ea4e4cd38e9c',
2174
- 'display' => 'Fred Smith',
2175
- 'type' => 'User'
2176
- }
2177
- ]
2178
- }.with_indifferent_case_insensitive_access()
2179
-
2180
- @instance.send(
2181
- :from_patch_backend!,
2182
- nature: 'remove',
2183
- path: path,
2184
- value: value,
2185
- altering_hash: scim_hash,
2186
- with_attr_map: {
2187
- displayName: :name,
2188
- members: [
2189
- list: :members,
2190
- using: {
2191
- value: :id,
2192
- display: :full_name,
2193
- type: 'User'
2194
- }
2195
- ]
2196
- }
2197
- )
2198
-
2199
- expect(scim_hash).to eql({
2200
- 'displayname' => 'Mock group',
2201
- 'members' => [
2202
- {
2203
- 'value' => '50ca93d04ab0c2de4772',
2204
- 'display' => 'Ingrid Smith',
2205
- 'type' => 'User'
2206
- }
2207
- ]
2208
- })
2209
- end
2210
-
2211
- it 'matches the "members" key case-insensitive' do
2212
- path = [ 'members' ]
2213
- value = { 'MEMBERS' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
2214
- scim_hash = {
2215
- 'displayname' => 'Mock group',
2216
- 'members' => [
2217
- {
2218
- 'value' => 'f648f8d5ea4e4cd38e9c',
2219
- 'display' => 'Fred Smith',
2220
- 'type' => 'User'
2221
- },
2222
- {
2223
- 'value' => 'a774d480e8112101375b',
2224
- 'display' => 'Taylor Smith',
2225
- 'type' => 'User'
2226
- }
2227
- ]
2228
- }.with_indifferent_case_insensitive_access()
2229
-
2230
- @instance.send(
2231
- :from_patch_backend!,
2232
- nature: 'remove',
2233
- path: path,
2234
- value: value,
2235
- altering_hash: scim_hash,
2236
- with_attr_map: {
2237
- displayName: :name,
2238
- members: [
2239
- list: :members,
2240
- using: {
2241
- value: :id,
2242
- display: :full_name,
2243
- type: 'User'
2244
- }
2245
- ]
2246
- }
2247
- )
2248
-
2249
- expect(scim_hash).to eql({
2250
- 'displayname' => 'Mock group',
2251
- 'members' => [
2252
- {
2253
- 'value' => 'a774d480e8112101375b',
2254
- 'display' => 'Taylor Smith',
2255
- 'type' => 'User'
2256
- }
2257
- ]
2258
- })
2259
- end
2260
-
2261
- it 'ignores unrecognised users' do
2262
- path = [ 'members' ]
2263
- value = { 'members' => [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ] }
2264
- scim_hash = {
2265
- 'displayname' => 'Mock group',
2266
- 'members' => [
2267
- {
2268
- 'value' => 'f648f8d5ea4e4cd38e9c',
2269
- 'display' => 'Fred Smith',
2270
- 'type' => 'User'
2271
- }
2272
- ]
2273
- }.with_indifferent_case_insensitive_access()
2274
-
2275
- @instance.send(
2276
- :from_patch_backend!,
2277
- nature: 'remove',
2278
- path: path,
2279
- value: value,
2280
- altering_hash: scim_hash,
2281
- with_attr_map: {
2282
- displayName: :name,
2283
- members: [
2284
- list: :members,
2285
- using: {
2286
- value: :id,
2287
- display: :full_name,
2288
- type: 'User'
2289
- }
2290
- ]
2291
- }
2292
- )
2293
-
2294
- # The 'value' mismatched, so the user was not removed.
2295
- #
2296
- expect(scim_hash).to eql({
2297
- 'displayname' => 'Mock group',
2298
- 'members' => [
2299
- {
2300
- 'value' => 'f648f8d5ea4e4cd38e9c',
2301
- 'display' => 'Fred Smith',
2302
- 'type' => 'User'
2303
- }
2304
- ]
2305
- })
2306
- end
2307
- end # "context 'Salesforce-style payload' do"
2308
- end # "context 'special cases' do"
2309
1205
  end # context 'when prior value already exists' do
2310
1206
 
2311
1207
  context 'when value is not present' do
@@ -2318,8 +1214,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2318
1214
  nature: 'remove',
2319
1215
  path: path,
2320
1216
  value: nil,
2321
- altering_hash: scim_hash,
2322
- with_attr_map: { userName: :user_name }
1217
+ altering_hash: scim_hash
2323
1218
  )
2324
1219
 
2325
1220
  expect(scim_hash).to be_empty
@@ -2334,8 +1229,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2334
1229
  nature: 'remove',
2335
1230
  path: path,
2336
1231
  value: nil,
2337
- altering_hash: scim_hash,
2338
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
1232
+ altering_hash: scim_hash
2339
1233
  )
2340
1234
 
2341
1235
  expect(scim_hash['name']).to_not have_key('givenName')
@@ -2359,13 +1253,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2359
1253
  nature: 'remove',
2360
1254
  path: path,
2361
1255
  value: nil,
2362
- altering_hash: scim_hash,
2363
- with_attr_map: {
2364
- emails: [
2365
- { match: 'type', with: 'home', using: { value: :home_email } },
2366
- { match: 'type', with: 'work', using: { value: :work_email } },
2367
- ]
2368
- }
1256
+ altering_hash: scim_hash
2369
1257
  )
2370
1258
 
2371
1259
  expect(scim_hash['emails'].size).to eql(1)
@@ -2387,13 +1275,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2387
1275
  nature: 'remove',
2388
1276
  path: path,
2389
1277
  value: nil,
2390
- altering_hash: scim_hash,
2391
- with_attr_map: {
2392
- emails: [
2393
- { match: 'type', with: 'home', using: { value: :home_email } },
2394
- { match: 'type', with: 'work', using: { value: :work_email } },
2395
- ]
2396
- }
1278
+ altering_hash: scim_hash
2397
1279
  )
2398
1280
 
2399
1281
  expect(scim_hash['emails'].size).to eql(1)
@@ -2416,13 +1298,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2416
1298
  nature: 'remove',
2417
1299
  path: path,
2418
1300
  value: nil,
2419
- altering_hash: scim_hash,
2420
- with_attr_map: {
2421
- emails: [
2422
- { match: 'type', with: 'home', using: { value: :home_email } },
2423
- { match: 'type', with: 'work', using: { value: :work_email } },
2424
- ]
2425
- }
1301
+ altering_hash: scim_hash
2426
1302
  )
2427
1303
 
2428
1304
  expect(scim_hash['emails'].size).to eql(1)
@@ -2440,13 +1316,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2440
1316
  nature: 'remove',
2441
1317
  path: path,
2442
1318
  value: nil,
2443
- altering_hash: scim_hash,
2444
- with_attr_map: {
2445
- emails: [
2446
- { match: 'type', with: 'home', using: { value: :home_email } },
2447
- { match: 'type', with: 'work', using: { value: :work_email } },
2448
- ]
2449
- }
1319
+ altering_hash: scim_hash
2450
1320
  )
2451
1321
 
2452
1322
  expect(scim_hash).to be_empty
@@ -2468,13 +1338,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2468
1338
  nature: 'remove',
2469
1339
  path: path,
2470
1340
  value: nil,
2471
- altering_hash: scim_hash,
2472
- with_attr_map: {
2473
- emails: [
2474
- { match: 'type', with: 'home', using: { value: :home_email } },
2475
- { match: 'type', with: 'work', using: { value: :work_email } },
2476
- ]
2477
- }
1341
+ altering_hash: scim_hash
2478
1342
  )
2479
1343
 
2480
1344
  expect(scim_hash['emails'].size).to eql(1)
@@ -2491,13 +1355,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2491
1355
  nature: 'remove',
2492
1356
  path: path,
2493
1357
  value: nil,
2494
- altering_hash: scim_hash,
2495
- with_attr_map: {
2496
- emails: [
2497
- { match: 'type', with: 'home', using: { value: :home_email } },
2498
- { match: 'type', with: 'work', using: { value: :work_email } },
2499
- ]
2500
- }
1358
+ altering_hash: scim_hash
2501
1359
  )
2502
1360
 
2503
1361
  expect(scim_hash).to_not have_key('emails')
@@ -2523,8 +1381,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2523
1381
  nature: 'replace',
2524
1382
  path: path,
2525
1383
  value: 'foo',
2526
- altering_hash: scim_hash,
2527
- with_attr_map: { userName: :user_name }
1384
+ altering_hash: scim_hash
2528
1385
  )
2529
1386
 
2530
1387
  expect(scim_hash['userName']).to eql('foo')
@@ -2539,8 +1396,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2539
1396
  nature: 'replace',
2540
1397
  path: path,
2541
1398
  value: 'Baz',
2542
- altering_hash: scim_hash,
2543
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
1399
+ altering_hash: scim_hash
2544
1400
  )
2545
1401
 
2546
1402
  expect(scim_hash['name']['givenName' ]).to eql('Baz')
@@ -2568,13 +1424,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2568
1424
  nature: 'replace',
2569
1425
  path: path,
2570
1426
  value: 'added_over_original@test.com',
2571
- altering_hash: scim_hash,
2572
- with_attr_map: {
2573
- emails: [
2574
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2575
- { match: 'type', with: 'work', using: { value: :work_email } },
2576
- ]
2577
- }
1427
+ altering_hash: scim_hash
2578
1428
  )
2579
1429
 
2580
1430
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -2600,13 +1450,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2600
1450
  nature: 'replace',
2601
1451
  path: path,
2602
1452
  value: 'added_over_original@test.com',
2603
- altering_hash: scim_hash,
2604
- with_attr_map: {
2605
- emails: [
2606
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2607
- { match: 'type', with: 'work', using: { value: :work_email } },
2608
- ]
2609
- }
1453
+ altering_hash: scim_hash
2610
1454
  )
2611
1455
 
2612
1456
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -2633,13 +1477,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2633
1477
  nature: 'replace',
2634
1478
  path: path,
2635
1479
  value: 'added_over_original@test.com',
2636
- altering_hash: scim_hash,
2637
- with_attr_map: {
2638
- emails: [
2639
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2640
- { match: 'type', with: 'work', using: { value: :work_email } },
2641
- ]
2642
- }
1480
+ altering_hash: scim_hash
2643
1481
  )
2644
1482
 
2645
1483
  expect(scim_hash['emails'][0]['value']).to eql('added_over_original@test.com')
@@ -2668,13 +1506,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2668
1506
  nature: 'replace',
2669
1507
  path: path,
2670
1508
  value: {'type' => 'home', 'primary' => true, 'value' => 'home@test.com'},
2671
- altering_hash: scim_hash,
2672
- with_attr_map: {
2673
- emails: [
2674
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2675
- { match: 'type', with: 'work', using: { value: :work_email } },
2676
- ]
2677
- }
1509
+ altering_hash: scim_hash
2678
1510
  )
2679
1511
 
2680
1512
  expect(scim_hash['emails'].size).to eql(2)
@@ -2708,13 +1540,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2708
1540
  nature: 'replace',
2709
1541
  path: path,
2710
1542
  value: {'type' => 'workinate', 'value' => 'replaced@test.com'},
2711
- altering_hash: scim_hash,
2712
- with_attr_map: {
2713
- emails: [
2714
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2715
- { match: 'type', with: 'work', using: { value: :work_email } },
2716
- ]
2717
- }
1543
+ altering_hash: scim_hash
2718
1544
  )
2719
1545
 
2720
1546
  expect(scim_hash['emails'].size).to eql(3)
@@ -2743,13 +1569,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2743
1569
  nature: 'replace',
2744
1570
  path: path,
2745
1571
  value: [ { 'type' => 'work', 'value' => 'work@test.com' } ], # NOTE - to-add value is an Array (and must be)
2746
- altering_hash: scim_hash,
2747
- with_attr_map: {
2748
- emails: [
2749
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2750
- { match: 'type', with: 'work', using: { value: :work_email } },
2751
- ]
2752
- }
1572
+ altering_hash: scim_hash
2753
1573
  )
2754
1574
 
2755
1575
  expect(scim_hash['emails'].size).to eql(1)
@@ -2768,8 +1588,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2768
1588
  nature: 'replace',
2769
1589
  path: path,
2770
1590
  value: 'foo',
2771
- altering_hash: scim_hash,
2772
- with_attr_map: { userName: :user_name }
1591
+ altering_hash: scim_hash
2773
1592
  )
2774
1593
 
2775
1594
  expect(scim_hash['userName']).to eql('foo')
@@ -2784,8 +1603,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2784
1603
  nature: 'replace',
2785
1604
  path: path,
2786
1605
  value: 'Baz',
2787
- altering_hash: scim_hash,
2788
- with_attr_map: { name: { givenName: :first_name, familyName: :last_name } }
1606
+ altering_hash: scim_hash
2789
1607
  )
2790
1608
 
2791
1609
  expect(scim_hash['name']['givenName']).to eql('Baz')
@@ -2811,13 +1629,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2811
1629
  nature: 'replace',
2812
1630
  path: path,
2813
1631
  value: 'added@test.com',
2814
- altering_hash: scim_hash,
2815
- with_attr_map: {
2816
- emails: [
2817
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2818
- { match: 'type', with: 'work', using: { value: :work_email } },
2819
- ]
2820
- }
1632
+ altering_hash: scim_hash
2821
1633
  )
2822
1634
 
2823
1635
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -2842,13 +1654,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2842
1654
  nature: 'replace',
2843
1655
  path: path,
2844
1656
  value: 'added@test.com',
2845
- altering_hash: scim_hash,
2846
- with_attr_map: {
2847
- emails: [
2848
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2849
- { match: 'type', with: 'work', using: { value: :work_email } },
2850
- ]
2851
- }
1657
+ altering_hash: scim_hash
2852
1658
  )
2853
1659
 
2854
1660
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
@@ -2873,13 +1679,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2873
1679
  nature: 'replace',
2874
1680
  path: path,
2875
1681
  value: 'added@test.com',
2876
- altering_hash: scim_hash,
2877
- with_attr_map: {
2878
- emails: [
2879
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2880
- { match: 'type', with: 'work', using: { value: :work_email } },
2881
- ]
2882
- }
1682
+ altering_hash: scim_hash
2883
1683
  )
2884
1684
 
2885
1685
  expect(scim_hash['emails'][0]['value']).to eql('added@test.com')
@@ -2897,13 +1697,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2897
1697
  nature: 'replace',
2898
1698
  path: path,
2899
1699
  value: {'type' => 'work', 'value' => 'work@test.com'},
2900
- altering_hash: scim_hash,
2901
- with_attr_map: {
2902
- emails: [
2903
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2904
- { match: 'type', with: 'work', using: { value: :work_email } },
2905
- ]
2906
- }
1700
+ altering_hash: scim_hash
2907
1701
  )
2908
1702
 
2909
1703
  expect(scim_hash['emails'].size).to eql(1)
@@ -2927,13 +1721,7 @@ RSpec.describe Scimitar::Resources::Mixin do
2927
1721
  nature: 'replace',
2928
1722
  path: path,
2929
1723
  value: {'type' => 'work', 'value' => 'work@test.com'},
2930
- altering_hash: scim_hash,
2931
- with_attr_map: {
2932
- emails: [
2933
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2934
- { match: 'type', with: 'work', using: { value: :work_email } },
2935
- ]
2936
- }
1724
+ altering_hash: scim_hash
2937
1725
  )
2938
1726
 
2939
1727
  expect(scim_hash['emails'].size).to eql(2)
@@ -2952,41 +1740,13 @@ RSpec.describe Scimitar::Resources::Mixin do
2952
1740
  nature: 'replace',
2953
1741
  path: path,
2954
1742
  value: [ { 'type' => 'work', 'value' => 'work@test.com' } ], # NOTE - to-add value is an Array (and must be)
2955
- altering_hash: scim_hash,
2956
- with_attr_map: {
2957
- emails: [
2958
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
2959
- { match: 'type', with: 'work', using: { value: :work_email } },
2960
- ]
2961
- }
1743
+ altering_hash: scim_hash
2962
1744
  )
2963
1745
 
2964
1746
  expect(scim_hash['emails'].size).to eql(1)
2965
1747
  expect(scim_hash['emails'][0]['type' ]).to eql('work')
2966
1748
  expect(scim_hash['emails'][0]['value']).to eql('work@test.com')
2967
1749
  end
2968
-
2969
- context 'when prior value already exists, and no path' do
2970
- it 'simple value: overwrites' do
2971
- path = [ 'root' ]
2972
- scim_hash = { 'root' => { 'userName' => 'bar', 'active' => true } }.with_indifferent_case_insensitive_access()
2973
-
2974
- @instance.send(
2975
- :from_patch_backend!,
2976
- nature: 'replace',
2977
- path: path,
2978
- value: { 'active' => false }.with_indifferent_case_insensitive_access(),
2979
- altering_hash: scim_hash,
2980
- with_attr_map: {
2981
- userName: :user_name,
2982
- active: :active
2983
- }
2984
- )
2985
-
2986
- expect(scim_hash['root']['userName']).to eql('bar')
2987
- expect(scim_hash['root']['active']).to eql(false)
2988
- end
2989
- end
2990
1750
  end # context 'when value is not present' do
2991
1751
  end # "context 'replace' do"
2992
1752
 
@@ -3101,8 +1861,7 @@ RSpec.describe Scimitar::Resources::Mixin do
3101
1861
  nature: 'add',
3102
1862
  path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
3103
1863
  value: [{ 'deeper' => 'addition' }],
3104
- altering_hash: scim_hash,
3105
- with_attr_map: @contrived_class.scim_attributes_map()
1864
+ altering_hash: scim_hash
3106
1865
  )
3107
1866
 
3108
1867
  expect(scim_hash.dig('complex', 0, 'data', 'nested', 0, 'info').count).to eql(1) # Unchanged
@@ -3125,8 +1884,7 @@ RSpec.describe Scimitar::Resources::Mixin do
3125
1884
  nature: 'replace',
3126
1885
  path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
3127
1886
  value: [{ 'deeper' => 'addition' }],
3128
- altering_hash: scim_hash,
3129
- with_attr_map: @contrived_class.scim_attributes_map()
1887
+ altering_hash: scim_hash
3130
1888
  )
3131
1889
 
3132
1890
  expect(scim_hash.dig('complex', 0, 'data', 'nested', 0, 'info').count).to eql(1) # Unchanged?
@@ -3143,25 +1901,24 @@ RSpec.describe Scimitar::Resources::Mixin do
3143
1901
  expect(scim_hash.dig('complex', 2, 'data', 'nested', 0, 'info', 0, 'deep')).to eql('nature2deep3') # Unchanged
3144
1902
  end
3145
1903
 
3146
- it 'removes via clearing to "nil" or empty Array across multiple deep matching points' do
1904
+ it 'removes across multiple deep matching points' do
3147
1905
  scim_hash = @original_hash.deep_dup().with_indifferent_case_insensitive_access()
3148
1906
  contrived_instance = @contrived_class.new
3149
1907
  contrived_instance.send(
3150
1908
  :from_patch_backend!,
3151
1909
  nature: 'remove',
3152
1910
  path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
3153
- value: nil,
3154
- altering_hash: scim_hash,
3155
- with_attr_map: @contrived_class.scim_attributes_map()
1911
+ value: [{ 'deeper' => 'addition' }],
1912
+ altering_hash: scim_hash
3156
1913
  )
3157
1914
 
3158
1915
  expect(scim_hash.dig('complex', 0, 'data', 'nested', 0, 'info').count).to eql(1) # Unchanged
3159
- expect(scim_hash.dig('complex', 0, 'data', 'nested', 1, 'info')).to eql([])
1916
+ expect(scim_hash.dig('complex', 0, 'data', 'nested', 1, 'info')).to be_nil
3160
1917
  expect(scim_hash.dig('complex', 0, 'data', 'nested', 2, 'nature')).to be_present
3161
- expect(scim_hash.dig('complex', 0, 'data', 'nested', 1, 'info')).to eql([])
1918
+ expect(scim_hash.dig('complex', 0, 'data', 'nested', 1, 'info')).to be_nil
3162
1919
  expect(scim_hash.dig('complex', 0, 'data', 'nested', 2, 'nature')).to be_present
3163
1920
 
3164
- expect(scim_hash.dig('complex', 1, 'data', 'nested', 0, 'info')).to eql([])
1921
+ expect(scim_hash.dig('complex', 1, 'data', 'nested', 0, 'info')).to be_nil
3165
1922
  expect(scim_hash.dig('complex', 1, 'data', 'nested', 0, 'nature')).to be_present
3166
1923
 
3167
1924
  expect(scim_hash.dig('complex', 2, 'data', 'nested', 0, 'info').count).to eql(1) # Unchanged
@@ -3195,13 +1952,7 @@ RSpec.describe Scimitar::Resources::Mixin do
3195
1952
  nature: 'replace',
3196
1953
  path: path,
3197
1954
  value: 'ignored',
3198
- altering_hash: scim_hash,
3199
- with_attr_map: {
3200
- emails: [
3201
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
3202
- { match: 'type', with: 'work', using: { value: :work_email } },
3203
- ]
3204
- }
1955
+ altering_hash: scim_hash
3205
1956
  )
3206
1957
  end.to raise_error(Scimitar::ErrorResponse) { |e| expect(e.as_json['scimType']).to eql('invalidSyntax') }
3207
1958
  end
@@ -3218,8 +1969,7 @@ RSpec.describe Scimitar::Resources::Mixin do
3218
1969
  nature: 'replace',
3219
1970
  path: path,
3220
1971
  value: 'ignored',
3221
- altering_hash: scim_hash,
3222
- with_attr_map: { userName: :user_name }
1972
+ altering_hash: scim_hash
3223
1973
  )
3224
1974
  end.to raise_error(Scimitar::ErrorResponse) { |e| expect(e.as_json['scimType']).to eql('invalidSyntax') }
3225
1975
  end
@@ -3239,13 +1989,7 @@ RSpec.describe Scimitar::Resources::Mixin do
3239
1989
  nature: 'replace',
3240
1990
  path: path,
3241
1991
  value: 'ignored',
3242
- altering_hash: scim_hash,
3243
- with_attr_map: {
3244
- emails: [
3245
- { match: 'type', with: 'home', using: { value: :home_email, primary: true } },
3246
- { match: 'type', with: 'work', using: { value: :work_email } },
3247
- ]
3248
- }
1992
+ altering_hash: scim_hash
3249
1993
  )
3250
1994
  end.to raise_error(Scimitar::ErrorResponse) { |e| expect(e.as_json['scimType']).to eql('invalidSyntax') }
3251
1995
  end
@@ -3259,14 +2003,6 @@ RSpec.describe Scimitar::Resources::Mixin do
3259
2003
  #
3260
2004
  context 'public interface' do
3261
2005
  shared_examples 'a patcher' do | force_upper_case: |
3262
- it 'gives the user a comprehensible error when operations are missing' do
3263
- patch = { 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'] }
3264
-
3265
- expect do
3266
- @instance.from_scim_patch!(patch_hash: patch)
3267
- end.to raise_error Scimitar::InvalidSyntaxError, "Missing PATCH \"operations\""
3268
- end
3269
-
3270
2006
  it 'which updates simple values' do
3271
2007
  @instance.update!(username: 'foo')
3272
2008
 
@@ -3288,28 +2024,6 @@ RSpec.describe Scimitar::Resources::Mixin do
3288
2024
  expect(@instance.username).to eql('1234')
3289
2025
  end
3290
2026
 
3291
- it 'which updates nested values using root syntax' do
3292
- @instance.update!(first_name: 'Foo', last_name: 'Bar')
3293
-
3294
- path = 'name.givenName'
3295
- path = path.upcase if force_upper_case
3296
-
3297
- patch = {
3298
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
3299
- 'Operations' => [
3300
- {
3301
- 'op' => 'replace',
3302
- 'value' => {
3303
- path => 'Baz'
3304
- }
3305
- }
3306
- ]
3307
- }
3308
-
3309
- @instance.from_scim_patch!(patch_hash: patch)
3310
- expect(@instance.first_name).to eql('Baz')
3311
- end
3312
-
3313
2027
  it 'which updates nested values' do
3314
2028
  @instance.update!(first_name: 'Foo', last_name: 'Bar')
3315
2029
 
@@ -3331,38 +2045,6 @@ RSpec.describe Scimitar::Resources::Mixin do
3331
2045
  expect(@instance.first_name).to eql('Baz')
3332
2046
  end
3333
2047
 
3334
- # Note odd ":" separating schema ID from first attribute, although
3335
- # the nature of JSON rendering / other payloads might lead you to
3336
- # expect a "." as with any other path component.
3337
- #
3338
- # Note the ":" separating the schema ID (URN) from the attribute.
3339
- # The nature of JSON rendering / other payloads might lead you to
3340
- # expect a "." as with any complex types, but that's not the case;
3341
- # see https://tools.ietf.org/html/rfc7644#section-3.10, or
3342
- # https://tools.ietf.org/html/rfc7644#section-3.5.2 of which in
3343
- # particular, https://tools.ietf.org/html/rfc7644#page-35.
3344
- #
3345
- it 'which updates attributes defined by extension schema' do
3346
- @instance.update!(department: 'SOMEDPT')
3347
-
3348
- path = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department'
3349
- path = path.upcase if force_upper_case
3350
-
3351
- patch = {
3352
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
3353
- 'Operations' => [
3354
- {
3355
- 'op' => 'replace',
3356
- 'path' => path,
3357
- 'value' => 'OTHERDPT'
3358
- }
3359
- ]
3360
- }
3361
-
3362
- @instance.from_scim_patch!(patch_hash: patch)
3363
- expect(@instance.department).to eql('OTHERDPT')
3364
- end
3365
-
3366
2048
  it 'which updates with filter match' do
3367
2049
  @instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
3368
2050