scimitar 1.5.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +6 -27
- data/app/controllers/scimitar/application_controller.rb +9 -29
- data/app/models/scimitar/engine_configuration.rb +3 -7
- data/app/models/scimitar/error_response.rb +0 -12
- data/app/models/scimitar/errors.rb +1 -1
- data/app/models/scimitar/lists/query_parser.rb +4 -14
- data/app/models/scimitar/resources/base.rb +1 -1
- data/app/models/scimitar/resources/mixin.rb +4 -113
- data/app/models/scimitar/schema/address.rb +0 -1
- data/app/models/scimitar/schema/attribute.rb +1 -1
- data/app/models/scimitar/schema/base.rb +3 -1
- data/app/models/scimitar/schema/vdtp.rb +1 -1
- data/config/initializers/scimitar.rb +70 -86
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
- data/spec/apps/dummy/app/models/mock_group.rb +1 -1
- data/spec/apps/dummy/app/models/mock_user.rb +8 -19
- data/spec/apps/dummy/config/application.rb +1 -0
- data/spec/apps/dummy/config/environments/test.rb +28 -5
- data/spec/apps/dummy/config/initializers/scimitar.rb +9 -44
- data/spec/apps/dummy/config/routes.rb +0 -4
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -9
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
- data/spec/apps/dummy/db/schema.rb +4 -10
- data/spec/controllers/scimitar/application_controller_spec.rb +1 -70
- data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -2
- data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +9 -9
- data/spec/models/scimitar/resources/base_spec.rb +66 -161
- data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
- data/spec/models/scimitar/resources/mixin_spec.rb +43 -757
- data/spec/models/scimitar/resources/user_spec.rb +4 -4
- data/spec/models/scimitar/schema/attribute_spec.rb +3 -0
- data/spec/models/scimitar/schema/base_spec.rb +1 -1
- data/spec/models/scimitar/schema/user_spec.rb +0 -10
- data/spec/requests/active_record_backed_resources_controller_spec.rb +40 -309
- data/spec/requests/application_controller_spec.rb +3 -17
- metadata +7 -7
@@ -159,73 +159,41 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
159
159
|
# =========================================================================
|
160
160
|
|
161
161
|
context '#to_scim' do
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
|
198
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
|
199
|
-
|
200
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
|
201
|
-
'organization' => 'SOMEORG',
|
202
|
-
'department' => nil
|
203
|
-
}
|
204
|
-
})
|
205
|
-
end
|
206
|
-
end # "context 'with a UUID, renamed primary key column' do"
|
207
|
-
|
208
|
-
context 'with an integer, conventionally named primary key column' do
|
209
|
-
it 'compiles instance attribute values into a SCIM representation' do
|
210
|
-
instance = MockGroup.new
|
211
|
-
instance.id = 42
|
212
|
-
instance.scim_uid = 'GG02984'
|
213
|
-
instance.display_name = 'Some group'
|
214
|
-
|
215
|
-
scim = instance.to_scim(location: 'https://test.com/mock_groups/42')
|
216
|
-
json = scim.to_json()
|
217
|
-
hash = JSON.parse(json)
|
218
|
-
|
219
|
-
expect(hash).to eql({
|
220
|
-
'displayName' => 'Some group',
|
221
|
-
'id' => '42', # Note, String
|
222
|
-
'externalId' => 'GG02984',
|
223
|
-
'members' => [],
|
224
|
-
'meta' => {'location'=>'https://test.com/mock_groups/42', 'resourceType'=>'Group'},
|
225
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
|
226
|
-
})
|
227
|
-
end
|
228
|
-
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
|
229
197
|
|
230
198
|
context 'with optional timestamps' do
|
231
199
|
context 'creation only' do
|
@@ -324,9 +292,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
324
292
|
],
|
325
293
|
|
326
294
|
'meta' => {'location'=>'https://test.com/static_map_test', 'resourceType'=>'User'},
|
327
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User'
|
328
|
-
|
329
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
|
295
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
330
296
|
})
|
331
297
|
end
|
332
298
|
end # "context 'using static mappings' do"
|
@@ -353,9 +319,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
353
319
|
],
|
354
320
|
|
355
321
|
'meta' => {'location'=>'https://test.com/dynamic_map_test', 'resourceType'=>'User'},
|
356
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User'
|
357
|
-
|
358
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
|
322
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
359
323
|
})
|
360
324
|
end
|
361
325
|
end # "context 'using dynamic lists' do"
|
@@ -412,12 +376,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
412
376
|
'id' => '42', # Note, String
|
413
377
|
'externalId' => 'AA02984',
|
414
378
|
'meta' => {'location' => 'https://test.com/mock_users/42', 'resourceType' => 'User'},
|
415
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User'
|
416
|
-
|
417
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
|
418
|
-
'organization' => 'SOMEORG',
|
419
|
-
'DEPARTMENT' => 'SOMEDPT'
|
420
|
-
}
|
379
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
421
380
|
}
|
422
381
|
|
423
382
|
hash = spec_helper_hupcase(hash) if force_upper_case
|
@@ -433,8 +392,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
433
392
|
expect(instance.work_email_address).to eql('foo.bar@test.com')
|
434
393
|
expect(instance.home_email_address).to be_nil
|
435
394
|
expect(instance.work_phone_number ).to eql('+642201234567')
|
436
|
-
expect(instance.organization ).to eql('SOMEORG')
|
437
|
-
expect(instance.department ).to eql('SOMEDPT')
|
438
395
|
end
|
439
396
|
|
440
397
|
it 'honouring read-write lists' do
|
@@ -448,8 +405,8 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
448
405
|
'displayName' => 'Foo Group',
|
449
406
|
'members' => [
|
450
407
|
{'type' => 'Group', 'value' => g1.id.to_s},
|
451
|
-
{'type' => 'User', 'value' => u1.
|
452
|
-
{'type' => 'User', 'value' => u3.
|
408
|
+
{'type' => 'User', 'value' => u1.id.to_s},
|
409
|
+
{'type' => 'User', 'value' => u3.id.to_s}
|
453
410
|
],
|
454
411
|
'externalId' => 'GG01536',
|
455
412
|
'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
|
@@ -496,10 +453,8 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
496
453
|
end # "context 'using upper case' do"
|
497
454
|
|
498
455
|
it 'clears things not present in input' do
|
499
|
-
uuid = SecureRandom.uuid
|
500
|
-
|
501
456
|
instance = MockUser.new
|
502
|
-
instance.
|
457
|
+
instance.id = 42
|
503
458
|
instance.scim_uid = 'AA02984'
|
504
459
|
instance.username = 'foo'
|
505
460
|
instance.first_name = 'Foo'
|
@@ -510,7 +465,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
510
465
|
|
511
466
|
instance.from_scim!(scim_hash: {})
|
512
467
|
|
513
|
-
expect(instance.
|
468
|
+
expect(instance.id ).to eql(42)
|
514
469
|
expect(instance.scim_uid ).to be_nil
|
515
470
|
expect(instance.username ).to be_nil
|
516
471
|
expect(instance.first_name ).to be_nil
|
@@ -721,21 +676,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
721
676
|
expect(scim_hash['name']['familyName']).to eql('Bar')
|
722
677
|
end
|
723
678
|
|
724
|
-
it 'with schema extensions: overwrites' do
|
725
|
-
path = [ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', 'organization' ]
|
726
|
-
scim_hash = { 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => { 'organization' => 'SOMEORG' } }.with_indifferent_case_insensitive_access()
|
727
|
-
|
728
|
-
@instance.send(
|
729
|
-
:from_patch_backend!,
|
730
|
-
nature: 'add',
|
731
|
-
path: path,
|
732
|
-
value: 'OTHERORG',
|
733
|
-
altering_hash: scim_hash
|
734
|
-
)
|
735
|
-
|
736
|
-
expect(scim_hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization' ]).to eql('OTHERORG')
|
737
|
-
end
|
738
|
-
|
739
679
|
# For 'add', filter at end-of-path is nonsensical and not
|
740
680
|
# supported by spec or Scimitar; we only test mid-path filters.
|
741
681
|
#
|
@@ -924,21 +864,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
924
864
|
expect(scim_hash['name']['givenName']).to eql('Baz')
|
925
865
|
end
|
926
866
|
|
927
|
-
it 'with schema extensions: adds' do
|
928
|
-
path = [ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', 'organization' ]
|
929
|
-
scim_hash = {}.with_indifferent_case_insensitive_access()
|
930
|
-
|
931
|
-
@instance.send(
|
932
|
-
:from_patch_backend!,
|
933
|
-
nature: 'add',
|
934
|
-
path: path,
|
935
|
-
value: 'SOMEORG',
|
936
|
-
altering_hash: scim_hash
|
937
|
-
)
|
938
|
-
|
939
|
-
expect(scim_hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization' ]).to eql('SOMEORG')
|
940
|
-
end
|
941
|
-
|
942
867
|
context 'with filter mid-path: adds' do
|
943
868
|
it 'by string match' do
|
944
869
|
path = [ 'emails[type eq "work"]', 'value' ]
|
@@ -1277,595 +1202,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
1277
1202
|
|
1278
1203
|
expect(scim_hash).to_not have_key('emails')
|
1279
1204
|
end
|
1280
|
-
|
1281
|
-
# What we expect:
|
1282
|
-
#
|
1283
|
-
# https://tools.ietf.org/html/rfc7644#section-3.5.2.2
|
1284
|
-
# https://docs.snowflake.com/en/user-guide/scim-intro.html#patch-scim-v2-groups-id
|
1285
|
-
#
|
1286
|
-
# ...vs accounting for the unusual payloads we sometimes get,
|
1287
|
-
# tested here.
|
1288
|
-
#
|
1289
|
-
context 'special cases' do
|
1290
|
-
|
1291
|
-
# https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
|
1292
|
-
#
|
1293
|
-
context 'Microsoft-style payload' do
|
1294
|
-
context 'removing a user from a group' do
|
1295
|
-
it 'removes identified user' do
|
1296
|
-
path = [ 'members' ]
|
1297
|
-
value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
|
1298
|
-
scim_hash = {
|
1299
|
-
'displayname' => 'Mock group',
|
1300
|
-
'members' => [
|
1301
|
-
{
|
1302
|
-
'value' => '50ca93d04ab0c2de4772',
|
1303
|
-
'display' => 'Ingrid Smith',
|
1304
|
-
'type' => 'User'
|
1305
|
-
},
|
1306
|
-
{
|
1307
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1308
|
-
'display' => 'Fred Smith',
|
1309
|
-
'type' => 'User'
|
1310
|
-
},
|
1311
|
-
{
|
1312
|
-
'value' => 'a774d480e8112101375b',
|
1313
|
-
'display' => 'Taylor Smith',
|
1314
|
-
'type' => 'User'
|
1315
|
-
}
|
1316
|
-
]
|
1317
|
-
}.with_indifferent_case_insensitive_access()
|
1318
|
-
|
1319
|
-
@instance.send(
|
1320
|
-
:from_patch_backend!,
|
1321
|
-
nature: 'remove',
|
1322
|
-
path: path,
|
1323
|
-
value: value,
|
1324
|
-
altering_hash: scim_hash
|
1325
|
-
)
|
1326
|
-
|
1327
|
-
expect(scim_hash).to eql({
|
1328
|
-
'displayname' => 'Mock group',
|
1329
|
-
'members' => [
|
1330
|
-
{
|
1331
|
-
'value' => '50ca93d04ab0c2de4772',
|
1332
|
-
'display' => 'Ingrid Smith',
|
1333
|
-
'type' => 'User'
|
1334
|
-
},
|
1335
|
-
{
|
1336
|
-
'value' => 'a774d480e8112101375b',
|
1337
|
-
'display' => 'Taylor Smith',
|
1338
|
-
'type' => 'User'
|
1339
|
-
}
|
1340
|
-
]
|
1341
|
-
})
|
1342
|
-
end
|
1343
|
-
|
1344
|
-
it 'removes multiple identified users' do
|
1345
|
-
path = [ 'members' ]
|
1346
|
-
value = [
|
1347
|
-
{ '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' },
|
1348
|
-
{ '$ref' => nil, 'value' => '50ca93d04ab0c2de4772' }
|
1349
|
-
]
|
1350
|
-
scim_hash = {
|
1351
|
-
'displayname' => 'Mock group',
|
1352
|
-
'members' => [
|
1353
|
-
{
|
1354
|
-
'value' => '50ca93d04ab0c2de4772',
|
1355
|
-
'display' => 'Ingrid Smith',
|
1356
|
-
'type' => 'User'
|
1357
|
-
},
|
1358
|
-
{
|
1359
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1360
|
-
'display' => 'Fred Smith',
|
1361
|
-
'type' => 'User'
|
1362
|
-
},
|
1363
|
-
{
|
1364
|
-
'value' => 'a774d480e8112101375b',
|
1365
|
-
'display' => 'Taylor Smith',
|
1366
|
-
'type' => 'User'
|
1367
|
-
}
|
1368
|
-
]
|
1369
|
-
}.with_indifferent_case_insensitive_access()
|
1370
|
-
|
1371
|
-
@instance.send(
|
1372
|
-
:from_patch_backend!,
|
1373
|
-
nature: 'remove',
|
1374
|
-
path: path,
|
1375
|
-
value: value,
|
1376
|
-
altering_hash: scim_hash
|
1377
|
-
)
|
1378
|
-
|
1379
|
-
expect(scim_hash).to eql({
|
1380
|
-
'displayname' => 'Mock group',
|
1381
|
-
'members' => [
|
1382
|
-
{
|
1383
|
-
'value' => 'a774d480e8112101375b',
|
1384
|
-
'display' => 'Taylor Smith',
|
1385
|
-
'type' => 'User'
|
1386
|
-
}
|
1387
|
-
]
|
1388
|
-
})
|
1389
|
-
end
|
1390
|
-
|
1391
|
-
it 'removes all users individually without error' do
|
1392
|
-
path = [ 'members' ]
|
1393
|
-
value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
|
1394
|
-
scim_hash = {
|
1395
|
-
'displayname' => 'Mock group',
|
1396
|
-
'members' => [
|
1397
|
-
{
|
1398
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1399
|
-
'display' => 'Fred Smith',
|
1400
|
-
'type' => 'User'
|
1401
|
-
}
|
1402
|
-
]
|
1403
|
-
}.with_indifferent_case_insensitive_access()
|
1404
|
-
|
1405
|
-
@instance.send(
|
1406
|
-
:from_patch_backend!,
|
1407
|
-
nature: 'remove',
|
1408
|
-
path: path,
|
1409
|
-
value: value,
|
1410
|
-
altering_hash: scim_hash
|
1411
|
-
)
|
1412
|
-
|
1413
|
-
expect(scim_hash).to eql({
|
1414
|
-
'displayname' => 'Mock group',
|
1415
|
-
'members' => []
|
1416
|
-
})
|
1417
|
-
end
|
1418
|
-
|
1419
|
-
it 'can match on multiple attributes' do
|
1420
|
-
path = [ 'members' ]
|
1421
|
-
value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'User' } ]
|
1422
|
-
scim_hash = {
|
1423
|
-
'displayname' => 'Mock group',
|
1424
|
-
'members' => [
|
1425
|
-
{
|
1426
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1427
|
-
'display' => 'Fred Smith',
|
1428
|
-
'type' => 'User'
|
1429
|
-
}
|
1430
|
-
]
|
1431
|
-
}.with_indifferent_case_insensitive_access()
|
1432
|
-
|
1433
|
-
@instance.send(
|
1434
|
-
:from_patch_backend!,
|
1435
|
-
nature: 'remove',
|
1436
|
-
path: path,
|
1437
|
-
value: value,
|
1438
|
-
altering_hash: scim_hash
|
1439
|
-
)
|
1440
|
-
|
1441
|
-
expect(scim_hash).to eql({
|
1442
|
-
'displayname' => 'Mock group',
|
1443
|
-
'members' => []
|
1444
|
-
})
|
1445
|
-
end
|
1446
|
-
|
1447
|
-
it 'ignores unrecognised users' do
|
1448
|
-
path = [ 'members' ]
|
1449
|
-
value = [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ]
|
1450
|
-
scim_hash = {
|
1451
|
-
'displayname' => 'Mock group',
|
1452
|
-
'members' => [
|
1453
|
-
{
|
1454
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1455
|
-
'display' => 'Fred Smith',
|
1456
|
-
'type' => 'User'
|
1457
|
-
}
|
1458
|
-
]
|
1459
|
-
}.with_indifferent_case_insensitive_access()
|
1460
|
-
|
1461
|
-
@instance.send(
|
1462
|
-
:from_patch_backend!,
|
1463
|
-
nature: 'remove',
|
1464
|
-
path: path,
|
1465
|
-
value: value,
|
1466
|
-
altering_hash: scim_hash
|
1467
|
-
)
|
1468
|
-
|
1469
|
-
# The 'value' mismatched, so the user was not removed.
|
1470
|
-
#
|
1471
|
-
expect(scim_hash).to eql({
|
1472
|
-
'displayname' => 'Mock group',
|
1473
|
-
'members' => [
|
1474
|
-
{
|
1475
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1476
|
-
'display' => 'Fred Smith',
|
1477
|
-
'type' => 'User'
|
1478
|
-
}
|
1479
|
-
]
|
1480
|
-
})
|
1481
|
-
end
|
1482
|
-
|
1483
|
-
it 'ignores a mismatch on (for example) "type"' do
|
1484
|
-
path = [ 'members' ]
|
1485
|
-
value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'Group' } ]
|
1486
|
-
scim_hash = {
|
1487
|
-
'displayname' => 'Mock group',
|
1488
|
-
'members' => [
|
1489
|
-
{
|
1490
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1491
|
-
'display' => 'Fred Smith',
|
1492
|
-
'type' => 'User'
|
1493
|
-
}
|
1494
|
-
]
|
1495
|
-
}.with_indifferent_case_insensitive_access()
|
1496
|
-
|
1497
|
-
@instance.send(
|
1498
|
-
:from_patch_backend!,
|
1499
|
-
nature: 'remove',
|
1500
|
-
path: path,
|
1501
|
-
value: value,
|
1502
|
-
altering_hash: scim_hash
|
1503
|
-
)
|
1504
|
-
|
1505
|
-
# Type 'Group' mismatches 'User', so the user was not
|
1506
|
-
# removed.
|
1507
|
-
#
|
1508
|
-
expect(scim_hash).to eql({
|
1509
|
-
'displayname' => 'Mock group',
|
1510
|
-
'members' => [
|
1511
|
-
{
|
1512
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1513
|
-
'display' => 'Fred Smith',
|
1514
|
-
'type' => 'User'
|
1515
|
-
}
|
1516
|
-
]
|
1517
|
-
})
|
1518
|
-
end
|
1519
|
-
|
1520
|
-
it 'matches keys case-insensitive' do
|
1521
|
-
path = [ 'members' ]
|
1522
|
-
value = [ { '$ref' => nil, 'VALUe' => 'f648f8d5ea4e4cd38e9c' } ]
|
1523
|
-
scim_hash = {
|
1524
|
-
'displayname' => 'Mock group',
|
1525
|
-
'memBERS' => [
|
1526
|
-
{
|
1527
|
-
'vaLUe' => 'f648f8d5ea4e4cd38e9c',
|
1528
|
-
'display' => 'Fred Smith',
|
1529
|
-
'type' => 'User'
|
1530
|
-
}
|
1531
|
-
]
|
1532
|
-
}.with_indifferent_case_insensitive_access()
|
1533
|
-
|
1534
|
-
@instance.send(
|
1535
|
-
:from_patch_backend!,
|
1536
|
-
nature: 'remove',
|
1537
|
-
path: path,
|
1538
|
-
value: value,
|
1539
|
-
altering_hash: scim_hash
|
1540
|
-
)
|
1541
|
-
|
1542
|
-
expect(scim_hash).to eql({
|
1543
|
-
'displayname' => 'Mock group',
|
1544
|
-
'members' => []
|
1545
|
-
})
|
1546
|
-
end
|
1547
|
-
|
1548
|
-
it 'matches values case-sensitive' do
|
1549
|
-
path = [ 'members' ]
|
1550
|
-
value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'USER' } ]
|
1551
|
-
scim_hash = {
|
1552
|
-
'displayname' => 'Mock group',
|
1553
|
-
'members' => [
|
1554
|
-
{
|
1555
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1556
|
-
'display' => 'Fred Smith',
|
1557
|
-
'type' => 'User'
|
1558
|
-
}
|
1559
|
-
]
|
1560
|
-
}.with_indifferent_case_insensitive_access()
|
1561
|
-
|
1562
|
-
@instance.send(
|
1563
|
-
:from_patch_backend!,
|
1564
|
-
nature: 'remove',
|
1565
|
-
path: path,
|
1566
|
-
value: value,
|
1567
|
-
altering_hash: scim_hash
|
1568
|
-
)
|
1569
|
-
|
1570
|
-
# USER mismatchs User, so the user was not removed.
|
1571
|
-
#
|
1572
|
-
expect(scim_hash).to eql({
|
1573
|
-
'displayname' => 'Mock group',
|
1574
|
-
'members' => [
|
1575
|
-
{
|
1576
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1577
|
-
'display' => 'Fred Smith',
|
1578
|
-
'type' => 'User'
|
1579
|
-
}
|
1580
|
-
]
|
1581
|
-
})
|
1582
|
-
end
|
1583
|
-
end # "context 'removing a user from a group' do"
|
1584
|
-
|
1585
|
-
context 'generic use' do
|
1586
|
-
it 'removes matched items' do
|
1587
|
-
path = [ 'emails' ]
|
1588
|
-
value = [ { 'type' => 'work' } ]
|
1589
|
-
scim_hash = {
|
1590
|
-
'emails' => [
|
1591
|
-
{
|
1592
|
-
'type' => 'home',
|
1593
|
-
'value' => 'home@test.com'
|
1594
|
-
},
|
1595
|
-
{
|
1596
|
-
'type' => 'work',
|
1597
|
-
'value' => 'work@test.com'
|
1598
|
-
}
|
1599
|
-
]
|
1600
|
-
}.with_indifferent_case_insensitive_access()
|
1601
|
-
|
1602
|
-
@instance.send(
|
1603
|
-
:from_patch_backend!,
|
1604
|
-
nature: 'remove',
|
1605
|
-
path: path,
|
1606
|
-
value: value,
|
1607
|
-
altering_hash: scim_hash
|
1608
|
-
)
|
1609
|
-
|
1610
|
-
expect(scim_hash).to eql({
|
1611
|
-
'emails' => [
|
1612
|
-
{
|
1613
|
-
'type' => 'home',
|
1614
|
-
'value' => 'home@test.com'
|
1615
|
-
}
|
1616
|
-
]
|
1617
|
-
})
|
1618
|
-
end
|
1619
|
-
|
1620
|
-
it 'ignores unmatched items' do
|
1621
|
-
path = [ 'emails' ]
|
1622
|
-
value = [ { 'type' => 'missing' } ]
|
1623
|
-
scim_hash = {
|
1624
|
-
'emails' => [
|
1625
|
-
{
|
1626
|
-
'type' => 'home',
|
1627
|
-
'value' => 'home@test.com'
|
1628
|
-
},
|
1629
|
-
{
|
1630
|
-
'type' => 'work',
|
1631
|
-
'value' => 'work@test.com'
|
1632
|
-
}
|
1633
|
-
]
|
1634
|
-
}.with_indifferent_case_insensitive_access()
|
1635
|
-
|
1636
|
-
@instance.send(
|
1637
|
-
:from_patch_backend!,
|
1638
|
-
nature: 'remove',
|
1639
|
-
path: path,
|
1640
|
-
value: value,
|
1641
|
-
altering_hash: scim_hash
|
1642
|
-
)
|
1643
|
-
|
1644
|
-
expect(scim_hash).to eql({
|
1645
|
-
'emails' => [
|
1646
|
-
{
|
1647
|
-
'type' => 'home',
|
1648
|
-
'value' => 'home@test.com'
|
1649
|
-
},
|
1650
|
-
{
|
1651
|
-
'type' => 'work',
|
1652
|
-
'value' => 'work@test.com'
|
1653
|
-
}
|
1654
|
-
]
|
1655
|
-
})
|
1656
|
-
end
|
1657
|
-
|
1658
|
-
it 'compares string forms' do
|
1659
|
-
path = [ 'test' ]
|
1660
|
-
value = [
|
1661
|
-
{ 'active' => true, 'value' => '12' },
|
1662
|
-
{ 'active' => 'false', 'value' => 42 }
|
1663
|
-
]
|
1664
|
-
scim_hash = {
|
1665
|
-
'test' => [
|
1666
|
-
{
|
1667
|
-
'active' => 'true',
|
1668
|
-
'value' => 12
|
1669
|
-
},
|
1670
|
-
{
|
1671
|
-
'active' => false,
|
1672
|
-
'value' => '42'
|
1673
|
-
}
|
1674
|
-
]
|
1675
|
-
}.with_indifferent_case_insensitive_access()
|
1676
|
-
|
1677
|
-
@instance.send(
|
1678
|
-
:from_patch_backend!,
|
1679
|
-
nature: 'remove',
|
1680
|
-
path: path,
|
1681
|
-
value: value,
|
1682
|
-
altering_hash: scim_hash
|
1683
|
-
)
|
1684
|
-
|
1685
|
-
expect(scim_hash).to eql({'test' => []})
|
1686
|
-
end
|
1687
|
-
|
1688
|
-
it 'handles a singular to-remove value rather than an array' do
|
1689
|
-
path = [ 'emails' ]
|
1690
|
-
value = { 'type' => 'work' }
|
1691
|
-
scim_hash = {
|
1692
|
-
'emails' => [
|
1693
|
-
{
|
1694
|
-
'type' => 'home',
|
1695
|
-
'value' => 'home@test.com'
|
1696
|
-
},
|
1697
|
-
{
|
1698
|
-
'type' => 'work',
|
1699
|
-
'value' => 'work@test.com'
|
1700
|
-
}
|
1701
|
-
]
|
1702
|
-
}.with_indifferent_case_insensitive_access()
|
1703
|
-
|
1704
|
-
@instance.send(
|
1705
|
-
:from_patch_backend!,
|
1706
|
-
nature: 'remove',
|
1707
|
-
path: path,
|
1708
|
-
value: value,
|
1709
|
-
altering_hash: scim_hash
|
1710
|
-
)
|
1711
|
-
|
1712
|
-
expect(scim_hash).to eql({
|
1713
|
-
'emails' => [
|
1714
|
-
{
|
1715
|
-
'type' => 'home',
|
1716
|
-
'value' => 'home@test.com'
|
1717
|
-
}
|
1718
|
-
]
|
1719
|
-
})
|
1720
|
-
end
|
1721
|
-
|
1722
|
-
it 'handles simple values rather than object (Hash) values' do
|
1723
|
-
path = [ 'test' ]
|
1724
|
-
value = 42
|
1725
|
-
scim_hash = {
|
1726
|
-
'test' => [
|
1727
|
-
'21',
|
1728
|
-
'42',
|
1729
|
-
'15'
|
1730
|
-
]
|
1731
|
-
}.with_indifferent_case_insensitive_access()
|
1732
|
-
|
1733
|
-
@instance.send(
|
1734
|
-
:from_patch_backend!,
|
1735
|
-
nature: 'remove',
|
1736
|
-
path: path,
|
1737
|
-
value: value,
|
1738
|
-
altering_hash: scim_hash
|
1739
|
-
)
|
1740
|
-
|
1741
|
-
expect(scim_hash).to eql({
|
1742
|
-
'test' => [
|
1743
|
-
'21',
|
1744
|
-
'15'
|
1745
|
-
]
|
1746
|
-
})
|
1747
|
-
end
|
1748
|
-
end
|
1749
|
-
end # "context 'Microsoft-style payload' do"
|
1750
|
-
|
1751
|
-
# https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
|
1752
|
-
#
|
1753
|
-
context 'Salesforce-style payload' do
|
1754
|
-
it 'removes identified user' do
|
1755
|
-
path = [ 'members' ]
|
1756
|
-
value = { 'members' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
|
1757
|
-
scim_hash = {
|
1758
|
-
'displayname' => 'Mock group',
|
1759
|
-
'members' => [
|
1760
|
-
{
|
1761
|
-
'value' => '50ca93d04ab0c2de4772',
|
1762
|
-
'display' => 'Ingrid Smith',
|
1763
|
-
'type' => 'User'
|
1764
|
-
},
|
1765
|
-
{
|
1766
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1767
|
-
'display' => 'Fred Smith',
|
1768
|
-
'type' => 'User'
|
1769
|
-
}
|
1770
|
-
]
|
1771
|
-
}.with_indifferent_case_insensitive_access()
|
1772
|
-
|
1773
|
-
@instance.send(
|
1774
|
-
:from_patch_backend!,
|
1775
|
-
nature: 'remove',
|
1776
|
-
path: path,
|
1777
|
-
value: value,
|
1778
|
-
altering_hash: scim_hash
|
1779
|
-
)
|
1780
|
-
|
1781
|
-
expect(scim_hash).to eql({
|
1782
|
-
'displayname' => 'Mock group',
|
1783
|
-
'members' => [
|
1784
|
-
{
|
1785
|
-
'value' => '50ca93d04ab0c2de4772',
|
1786
|
-
'display' => 'Ingrid Smith',
|
1787
|
-
'type' => 'User'
|
1788
|
-
}
|
1789
|
-
]
|
1790
|
-
})
|
1791
|
-
end
|
1792
|
-
|
1793
|
-
it 'matches the "members" key case-insensitive' do
|
1794
|
-
path = [ 'members' ]
|
1795
|
-
value = { 'MEMBERS' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
|
1796
|
-
scim_hash = {
|
1797
|
-
'displayname' => 'Mock group',
|
1798
|
-
'members' => [
|
1799
|
-
{
|
1800
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1801
|
-
'display' => 'Fred Smith',
|
1802
|
-
'type' => 'User'
|
1803
|
-
},
|
1804
|
-
{
|
1805
|
-
'value' => 'a774d480e8112101375b',
|
1806
|
-
'display' => 'Taylor Smith',
|
1807
|
-
'type' => 'User'
|
1808
|
-
}
|
1809
|
-
]
|
1810
|
-
}.with_indifferent_case_insensitive_access()
|
1811
|
-
|
1812
|
-
@instance.send(
|
1813
|
-
:from_patch_backend!,
|
1814
|
-
nature: 'remove',
|
1815
|
-
path: path,
|
1816
|
-
value: value,
|
1817
|
-
altering_hash: scim_hash
|
1818
|
-
)
|
1819
|
-
|
1820
|
-
expect(scim_hash).to eql({
|
1821
|
-
'displayname' => 'Mock group',
|
1822
|
-
'members' => [
|
1823
|
-
{
|
1824
|
-
'value' => 'a774d480e8112101375b',
|
1825
|
-
'display' => 'Taylor Smith',
|
1826
|
-
'type' => 'User'
|
1827
|
-
}
|
1828
|
-
]
|
1829
|
-
})
|
1830
|
-
end
|
1831
|
-
|
1832
|
-
it 'ignores unrecognised users' do
|
1833
|
-
path = [ 'members' ]
|
1834
|
-
value = { 'members' => [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ] }
|
1835
|
-
scim_hash = {
|
1836
|
-
'displayname' => 'Mock group',
|
1837
|
-
'members' => [
|
1838
|
-
{
|
1839
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1840
|
-
'display' => 'Fred Smith',
|
1841
|
-
'type' => 'User'
|
1842
|
-
}
|
1843
|
-
]
|
1844
|
-
}.with_indifferent_case_insensitive_access()
|
1845
|
-
|
1846
|
-
@instance.send(
|
1847
|
-
:from_patch_backend!,
|
1848
|
-
nature: 'remove',
|
1849
|
-
path: path,
|
1850
|
-
value: value,
|
1851
|
-
altering_hash: scim_hash
|
1852
|
-
)
|
1853
|
-
|
1854
|
-
# The 'value' mismatched, so the user was not removed.
|
1855
|
-
#
|
1856
|
-
expect(scim_hash).to eql({
|
1857
|
-
'displayname' => 'Mock group',
|
1858
|
-
'members' => [
|
1859
|
-
{
|
1860
|
-
'value' => 'f648f8d5ea4e4cd38e9c',
|
1861
|
-
'display' => 'Fred Smith',
|
1862
|
-
'type' => 'User'
|
1863
|
-
}
|
1864
|
-
]
|
1865
|
-
})
|
1866
|
-
end
|
1867
|
-
end # "context 'Salesforce-style payload' do"
|
1868
|
-
end # "context 'special cases' do"
|
1869
1205
|
end # context 'when prior value already exists' do
|
1870
1206
|
|
1871
1207
|
context 'when value is not present' do
|
@@ -2411,24 +1747,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
2411
1747
|
expect(scim_hash['emails'][0]['type' ]).to eql('work')
|
2412
1748
|
expect(scim_hash['emails'][0]['value']).to eql('work@test.com')
|
2413
1749
|
end
|
2414
|
-
|
2415
|
-
context 'when prior value already exists, and no path' do
|
2416
|
-
it 'simple value: overwrites' do
|
2417
|
-
path = [ 'root' ]
|
2418
|
-
scim_hash = { 'root' => { 'userName' => 'bar', 'active' => true } }.with_indifferent_case_insensitive_access()
|
2419
|
-
|
2420
|
-
@instance.send(
|
2421
|
-
:from_patch_backend!,
|
2422
|
-
nature: 'replace',
|
2423
|
-
path: path,
|
2424
|
-
value: { 'active' => false }.with_indifferent_case_insensitive_access(),
|
2425
|
-
altering_hash: scim_hash
|
2426
|
-
)
|
2427
|
-
|
2428
|
-
expect(scim_hash['root']['userName']).to eql('bar')
|
2429
|
-
expect(scim_hash['root']['active']).to eql(false)
|
2430
|
-
end
|
2431
|
-
end
|
2432
1750
|
end # context 'when value is not present' do
|
2433
1751
|
end # "context 'replace' do"
|
2434
1752
|
|
@@ -2590,7 +1908,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
2590
1908
|
:from_patch_backend!,
|
2591
1909
|
nature: 'remove',
|
2592
1910
|
path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
|
2593
|
-
value:
|
1911
|
+
value: [{ 'deeper' => 'addition' }],
|
2594
1912
|
altering_hash: scim_hash
|
2595
1913
|
)
|
2596
1914
|
|
@@ -2727,38 +2045,6 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
2727
2045
|
expect(@instance.first_name).to eql('Baz')
|
2728
2046
|
end
|
2729
2047
|
|
2730
|
-
# Note odd ":" separating schema ID from first attribute, although
|
2731
|
-
# the nature of JSON rendering / other payloads might lead you to
|
2732
|
-
# expect a "." as with any other path component.
|
2733
|
-
#
|
2734
|
-
# Note the ":" separating the schema ID (URN) from the attribute.
|
2735
|
-
# The nature of JSON rendering / other payloads might lead you to
|
2736
|
-
# expect a "." as with any complex types, but that's not the case;
|
2737
|
-
# see https://tools.ietf.org/html/rfc7644#section-3.10, or
|
2738
|
-
# https://tools.ietf.org/html/rfc7644#section-3.5.2 of which in
|
2739
|
-
# particular, https://tools.ietf.org/html/rfc7644#page-35.
|
2740
|
-
#
|
2741
|
-
it 'which updates attributes defined by extension schema' do
|
2742
|
-
@instance.update!(department: 'SOMEDPT')
|
2743
|
-
|
2744
|
-
path = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department'
|
2745
|
-
path = path.upcase if force_upper_case
|
2746
|
-
|
2747
|
-
patch = {
|
2748
|
-
'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
2749
|
-
'Operations' => [
|
2750
|
-
{
|
2751
|
-
'op' => 'replace',
|
2752
|
-
'path' => path,
|
2753
|
-
'value' => 'OTHERDPT'
|
2754
|
-
}
|
2755
|
-
]
|
2756
|
-
}
|
2757
|
-
|
2758
|
-
@instance.from_scim_patch!(patch_hash: patch)
|
2759
|
-
expect(@instance.department).to eql('OTHERDPT')
|
2760
|
-
end
|
2761
|
-
|
2762
2048
|
it 'which updates with filter match' do
|
2763
2049
|
@instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
|
2764
2050
|
|