scimitar 2.3.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/models/scimitar/resources/mixin.rb +108 -3
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/app/models/mock_user.rb +9 -1
- data/spec/apps/dummy/config/initializers/scimitar.rb +37 -0
- data/spec/apps/dummy/config/routes.rb +1 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +8 -0
- data/spec/apps/dummy/db/schema.rb +2 -0
- data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -2
- data/spec/models/scimitar/resources/base_spec.rb +161 -66
- data/spec/models/scimitar/resources/mixin_spec.rb +673 -5
- data/spec/requests/active_record_backed_resources_controller_spec.rb +136 -0
- metadata +10 -10
@@ -172,6 +172,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
172
172
|
instance.work_email_address = 'foo.bar@test.com'
|
173
173
|
instance.home_email_address = nil
|
174
174
|
instance.work_phone_number = '+642201234567'
|
175
|
+
instance.organization = 'SOMEORG'
|
175
176
|
|
176
177
|
g1 = MockGroup.create!(display_name: 'Group 1')
|
177
178
|
g2 = MockGroup.create!(display_name: 'Group 2')
|
@@ -194,7 +195,12 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
194
195
|
'externalId' => 'AA02984',
|
195
196
|
'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
|
196
197
|
'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
|
197
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0: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
|
+
}
|
198
204
|
})
|
199
205
|
end
|
200
206
|
end # "context 'with a UUID, renamed primary key column' do"
|
@@ -318,7 +324,9 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
318
324
|
],
|
319
325
|
|
320
326
|
'meta' => {'location'=>'https://test.com/static_map_test', 'resourceType'=>'User'},
|
321
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
327
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
|
328
|
+
|
329
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
|
322
330
|
})
|
323
331
|
end
|
324
332
|
end # "context 'using static mappings' do"
|
@@ -345,7 +353,9 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
345
353
|
],
|
346
354
|
|
347
355
|
'meta' => {'location'=>'https://test.com/dynamic_map_test', 'resourceType'=>'User'},
|
348
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
356
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
|
357
|
+
|
358
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
|
349
359
|
})
|
350
360
|
end
|
351
361
|
end # "context 'using dynamic lists' do"
|
@@ -402,7 +412,12 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
402
412
|
'id' => '42', # Note, String
|
403
413
|
'externalId' => 'AA02984',
|
404
414
|
'meta' => {'location' => 'https://test.com/mock_users/42', 'resourceType' => 'User'},
|
405
|
-
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
|
415
|
+
'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
|
416
|
+
|
417
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
|
418
|
+
'organization' => 'SOMEORG',
|
419
|
+
'DEPARTMENT' => 'SOMEDPT'
|
420
|
+
}
|
406
421
|
}
|
407
422
|
|
408
423
|
hash = spec_helper_hupcase(hash) if force_upper_case
|
@@ -418,6 +433,8 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
418
433
|
expect(instance.work_email_address).to eql('foo.bar@test.com')
|
419
434
|
expect(instance.home_email_address).to be_nil
|
420
435
|
expect(instance.work_phone_number ).to eql('+642201234567')
|
436
|
+
expect(instance.organization ).to eql('SOMEORG')
|
437
|
+
expect(instance.department ).to eql('SOMEDPT')
|
421
438
|
end
|
422
439
|
|
423
440
|
it 'honouring read-write lists' do
|
@@ -704,6 +721,21 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
704
721
|
expect(scim_hash['name']['familyName']).to eql('Bar')
|
705
722
|
end
|
706
723
|
|
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
|
+
|
707
739
|
# For 'add', filter at end-of-path is nonsensical and not
|
708
740
|
# supported by spec or Scimitar; we only test mid-path filters.
|
709
741
|
#
|
@@ -892,6 +924,21 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
892
924
|
expect(scim_hash['name']['givenName']).to eql('Baz')
|
893
925
|
end
|
894
926
|
|
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
|
+
|
895
942
|
context 'with filter mid-path: adds' do
|
896
943
|
it 'by string match' do
|
897
944
|
path = [ 'emails[type eq "work"]', 'value' ]
|
@@ -1230,6 +1277,595 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
1230
1277
|
|
1231
1278
|
expect(scim_hash).to_not have_key('emails')
|
1232
1279
|
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"
|
1233
1869
|
end # context 'when prior value already exists' do
|
1234
1870
|
|
1235
1871
|
context 'when value is not present' do
|
@@ -1954,7 +2590,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
1954
2590
|
:from_patch_backend!,
|
1955
2591
|
nature: 'remove',
|
1956
2592
|
path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
|
1957
|
-
value:
|
2593
|
+
value: nil,
|
1958
2594
|
altering_hash: scim_hash
|
1959
2595
|
)
|
1960
2596
|
|
@@ -2091,6 +2727,38 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
2091
2727
|
expect(@instance.first_name).to eql('Baz')
|
2092
2728
|
end
|
2093
2729
|
|
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
|
+
|
2094
2762
|
it 'which updates with filter match' do
|
2095
2763
|
@instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
|
2096
2764
|
|