scimitar 2.2.0 → 2.4.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.
@@ -159,41 +159,67 @@ RSpec.describe Scimitar::Resources::Mixin do
159
159
  # =========================================================================
160
160
 
161
161
  context '#to_scim' 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
162
+ context 'with a UUID, renamed primary key column' do
163
+ it 'compiles instance attribute values into a SCIM representation' do
164
+ uuid = SecureRandom.uuid
165
+
166
+ instance = MockUser.new
167
+ instance.primary_key = uuid
168
+ instance.scim_uid = 'AA02984'
169
+ instance.username = 'foo'
170
+ instance.first_name = 'Foo'
171
+ instance.last_name = 'Bar'
172
+ instance.work_email_address = 'foo.bar@test.com'
173
+ instance.home_email_address = nil
174
+ instance.work_phone_number = '+642201234567'
175
+
176
+ g1 = MockGroup.create!(display_name: 'Group 1')
177
+ g2 = MockGroup.create!(display_name: 'Group 2')
178
+ g3 = MockGroup.create!(display_name: 'Group 3')
179
+
180
+ g1.mock_users << instance
181
+ g3.mock_users << instance
182
+
183
+ scim = instance.to_scim(location: "https://test.com/mock_users/#{uuid}")
184
+ json = scim.to_json()
185
+ hash = JSON.parse(json)
186
+
187
+ expect(hash).to eql({
188
+ 'userName' => 'foo',
189
+ 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
190
+ 'active' => true,
191
+ 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
192
+ 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
193
+ 'id' => uuid,
194
+ 'externalId' => 'AA02984',
195
+ 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
196
+ 'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
197
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
198
+ })
199
+ end
200
+ end # "context 'with a UUID, renamed primary key column' do"
201
+
202
+ context 'with an integer, conventionally named primary key column' do
203
+ it 'compiles instance attribute values into a SCIM representation' do
204
+ instance = MockGroup.new
205
+ instance.id = 42
206
+ instance.scim_uid = 'GG02984'
207
+ instance.display_name = 'Some group'
208
+
209
+ scim = instance.to_scim(location: 'https://test.com/mock_groups/42')
210
+ json = scim.to_json()
211
+ hash = JSON.parse(json)
212
+
213
+ expect(hash).to eql({
214
+ 'displayName' => 'Some group',
215
+ 'id' => '42', # Note, String
216
+ 'externalId' => 'GG02984',
217
+ 'members' => [],
218
+ 'meta' => {'location'=>'https://test.com/mock_groups/42', 'resourceType'=>'Group'},
219
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
220
+ })
221
+ end
222
+ end # "context 'with an integer, conventionally named primary key column' do"
197
223
 
198
224
  context 'with optional timestamps' do
199
225
  context 'creation only' do
@@ -405,8 +431,8 @@ RSpec.describe Scimitar::Resources::Mixin do
405
431
  'displayName' => 'Foo Group',
406
432
  'members' => [
407
433
  {'type' => 'Group', 'value' => g1.id.to_s},
408
- {'type' => 'User', 'value' => u1.id.to_s},
409
- {'type' => 'User', 'value' => u3.id.to_s}
434
+ {'type' => 'User', 'value' => u1.primary_key.to_s},
435
+ {'type' => 'User', 'value' => u3.primary_key.to_s}
410
436
  ],
411
437
  'externalId' => 'GG01536',
412
438
  'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
@@ -453,8 +479,10 @@ RSpec.describe Scimitar::Resources::Mixin do
453
479
  end # "context 'using upper case' do"
454
480
 
455
481
  it 'clears things not present in input' do
482
+ uuid = SecureRandom.uuid
483
+
456
484
  instance = MockUser.new
457
- instance.id = 42
485
+ instance.primary_key = uuid
458
486
  instance.scim_uid = 'AA02984'
459
487
  instance.username = 'foo'
460
488
  instance.first_name = 'Foo'
@@ -465,7 +493,7 @@ RSpec.describe Scimitar::Resources::Mixin do
465
493
 
466
494
  instance.from_scim!(scim_hash: {})
467
495
 
468
- expect(instance.id ).to eql(42)
496
+ expect(instance.primary_key ).to eql(uuid)
469
497
  expect(instance.scim_uid ).to be_nil
470
498
  expect(instance.username ).to be_nil
471
499
  expect(instance.first_name ).to be_nil
@@ -1202,6 +1230,595 @@ RSpec.describe Scimitar::Resources::Mixin do
1202
1230
 
1203
1231
  expect(scim_hash).to_not have_key('emails')
1204
1232
  end
1233
+
1234
+ # What we expect:
1235
+ #
1236
+ # https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2.2
1237
+ # https://docs.snowflake.com/en/user-guide/scim-intro.html#patch-scim-v2-groups-id
1238
+ #
1239
+ # ...vs accounting for the unusual payloads we sometimes get,
1240
+ # tested here.
1241
+ #
1242
+ context 'special cases' do
1243
+
1244
+ # https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
1245
+ #
1246
+ context 'Microsoft-style payload' do
1247
+ context 'removing a user from a group' do
1248
+ it 'removes identified user' do
1249
+ path = [ 'members' ]
1250
+ value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
1251
+ scim_hash = {
1252
+ 'displayname' => 'Mock group',
1253
+ 'members' => [
1254
+ {
1255
+ 'value' => '50ca93d04ab0c2de4772',
1256
+ 'display' => 'Ingrid Smith',
1257
+ 'type' => 'User'
1258
+ },
1259
+ {
1260
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1261
+ 'display' => 'Fred Smith',
1262
+ 'type' => 'User'
1263
+ },
1264
+ {
1265
+ 'value' => 'a774d480e8112101375b',
1266
+ 'display' => 'Taylor Smith',
1267
+ 'type' => 'User'
1268
+ }
1269
+ ]
1270
+ }.with_indifferent_case_insensitive_access()
1271
+
1272
+ @instance.send(
1273
+ :from_patch_backend!,
1274
+ nature: 'remove',
1275
+ path: path,
1276
+ value: value,
1277
+ altering_hash: scim_hash
1278
+ )
1279
+
1280
+ expect(scim_hash).to eql({
1281
+ 'displayname' => 'Mock group',
1282
+ 'members' => [
1283
+ {
1284
+ 'value' => '50ca93d04ab0c2de4772',
1285
+ 'display' => 'Ingrid Smith',
1286
+ 'type' => 'User'
1287
+ },
1288
+ {
1289
+ 'value' => 'a774d480e8112101375b',
1290
+ 'display' => 'Taylor Smith',
1291
+ 'type' => 'User'
1292
+ }
1293
+ ]
1294
+ })
1295
+ end
1296
+
1297
+ it 'removes multiple identified users' do
1298
+ path = [ 'members' ]
1299
+ value = [
1300
+ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' },
1301
+ { '$ref' => nil, 'value' => '50ca93d04ab0c2de4772' }
1302
+ ]
1303
+ scim_hash = {
1304
+ 'displayname' => 'Mock group',
1305
+ 'members' => [
1306
+ {
1307
+ 'value' => '50ca93d04ab0c2de4772',
1308
+ 'display' => 'Ingrid Smith',
1309
+ 'type' => 'User'
1310
+ },
1311
+ {
1312
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1313
+ 'display' => 'Fred Smith',
1314
+ 'type' => 'User'
1315
+ },
1316
+ {
1317
+ 'value' => 'a774d480e8112101375b',
1318
+ 'display' => 'Taylor Smith',
1319
+ 'type' => 'User'
1320
+ }
1321
+ ]
1322
+ }.with_indifferent_case_insensitive_access()
1323
+
1324
+ @instance.send(
1325
+ :from_patch_backend!,
1326
+ nature: 'remove',
1327
+ path: path,
1328
+ value: value,
1329
+ altering_hash: scim_hash
1330
+ )
1331
+
1332
+ expect(scim_hash).to eql({
1333
+ 'displayname' => 'Mock group',
1334
+ 'members' => [
1335
+ {
1336
+ 'value' => 'a774d480e8112101375b',
1337
+ 'display' => 'Taylor Smith',
1338
+ 'type' => 'User'
1339
+ }
1340
+ ]
1341
+ })
1342
+ end
1343
+
1344
+ it 'removes all users individually without error' do
1345
+ path = [ 'members' ]
1346
+ value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ]
1347
+ scim_hash = {
1348
+ 'displayname' => 'Mock group',
1349
+ 'members' => [
1350
+ {
1351
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1352
+ 'display' => 'Fred Smith',
1353
+ 'type' => 'User'
1354
+ }
1355
+ ]
1356
+ }.with_indifferent_case_insensitive_access()
1357
+
1358
+ @instance.send(
1359
+ :from_patch_backend!,
1360
+ nature: 'remove',
1361
+ path: path,
1362
+ value: value,
1363
+ altering_hash: scim_hash
1364
+ )
1365
+
1366
+ expect(scim_hash).to eql({
1367
+ 'displayname' => 'Mock group',
1368
+ 'members' => []
1369
+ })
1370
+ end
1371
+
1372
+ it 'can match on multiple attributes' do
1373
+ path = [ 'members' ]
1374
+ value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'User' } ]
1375
+ scim_hash = {
1376
+ 'displayname' => 'Mock group',
1377
+ 'members' => [
1378
+ {
1379
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1380
+ 'display' => 'Fred Smith',
1381
+ 'type' => 'User'
1382
+ }
1383
+ ]
1384
+ }.with_indifferent_case_insensitive_access()
1385
+
1386
+ @instance.send(
1387
+ :from_patch_backend!,
1388
+ nature: 'remove',
1389
+ path: path,
1390
+ value: value,
1391
+ altering_hash: scim_hash
1392
+ )
1393
+
1394
+ expect(scim_hash).to eql({
1395
+ 'displayname' => 'Mock group',
1396
+ 'members' => []
1397
+ })
1398
+ end
1399
+
1400
+ it 'ignores unrecognised users' do
1401
+ path = [ 'members' ]
1402
+ value = [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ]
1403
+ scim_hash = {
1404
+ 'displayname' => 'Mock group',
1405
+ 'members' => [
1406
+ {
1407
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1408
+ 'display' => 'Fred Smith',
1409
+ 'type' => 'User'
1410
+ }
1411
+ ]
1412
+ }.with_indifferent_case_insensitive_access()
1413
+
1414
+ @instance.send(
1415
+ :from_patch_backend!,
1416
+ nature: 'remove',
1417
+ path: path,
1418
+ value: value,
1419
+ altering_hash: scim_hash
1420
+ )
1421
+
1422
+ # The 'value' mismatched, so the user was not removed.
1423
+ #
1424
+ expect(scim_hash).to eql({
1425
+ 'displayname' => 'Mock group',
1426
+ 'members' => [
1427
+ {
1428
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1429
+ 'display' => 'Fred Smith',
1430
+ 'type' => 'User'
1431
+ }
1432
+ ]
1433
+ })
1434
+ end
1435
+
1436
+ it 'ignores a mismatch on (for example) "type"' do
1437
+ path = [ 'members' ]
1438
+ value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'Group' } ]
1439
+ scim_hash = {
1440
+ 'displayname' => 'Mock group',
1441
+ 'members' => [
1442
+ {
1443
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1444
+ 'display' => 'Fred Smith',
1445
+ 'type' => 'User'
1446
+ }
1447
+ ]
1448
+ }.with_indifferent_case_insensitive_access()
1449
+
1450
+ @instance.send(
1451
+ :from_patch_backend!,
1452
+ nature: 'remove',
1453
+ path: path,
1454
+ value: value,
1455
+ altering_hash: scim_hash
1456
+ )
1457
+
1458
+ # Type 'Group' mismatches 'User', so the user was not
1459
+ # removed.
1460
+ #
1461
+ expect(scim_hash).to eql({
1462
+ 'displayname' => 'Mock group',
1463
+ 'members' => [
1464
+ {
1465
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1466
+ 'display' => 'Fred Smith',
1467
+ 'type' => 'User'
1468
+ }
1469
+ ]
1470
+ })
1471
+ end
1472
+
1473
+ it 'matches keys case-insensitive' do
1474
+ path = [ 'members' ]
1475
+ value = [ { '$ref' => nil, 'VALUe' => 'f648f8d5ea4e4cd38e9c' } ]
1476
+ scim_hash = {
1477
+ 'displayname' => 'Mock group',
1478
+ 'memBERS' => [
1479
+ {
1480
+ 'vaLUe' => 'f648f8d5ea4e4cd38e9c',
1481
+ 'display' => 'Fred Smith',
1482
+ 'type' => 'User'
1483
+ }
1484
+ ]
1485
+ }.with_indifferent_case_insensitive_access()
1486
+
1487
+ @instance.send(
1488
+ :from_patch_backend!,
1489
+ nature: 'remove',
1490
+ path: path,
1491
+ value: value,
1492
+ altering_hash: scim_hash
1493
+ )
1494
+
1495
+ expect(scim_hash).to eql({
1496
+ 'displayname' => 'Mock group',
1497
+ 'members' => []
1498
+ })
1499
+ end
1500
+
1501
+ it 'matches values case-sensitive' do
1502
+ path = [ 'members' ]
1503
+ value = [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c', 'type' => 'USER' } ]
1504
+ scim_hash = {
1505
+ 'displayname' => 'Mock group',
1506
+ 'members' => [
1507
+ {
1508
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1509
+ 'display' => 'Fred Smith',
1510
+ 'type' => 'User'
1511
+ }
1512
+ ]
1513
+ }.with_indifferent_case_insensitive_access()
1514
+
1515
+ @instance.send(
1516
+ :from_patch_backend!,
1517
+ nature: 'remove',
1518
+ path: path,
1519
+ value: value,
1520
+ altering_hash: scim_hash
1521
+ )
1522
+
1523
+ # USER mismatchs User, so the user was not removed.
1524
+ #
1525
+ expect(scim_hash).to eql({
1526
+ 'displayname' => 'Mock group',
1527
+ 'members' => [
1528
+ {
1529
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1530
+ 'display' => 'Fred Smith',
1531
+ 'type' => 'User'
1532
+ }
1533
+ ]
1534
+ })
1535
+ end
1536
+ end # "context 'removing a user from a group' do"
1537
+
1538
+ context 'generic use' do
1539
+ it 'removes matched items' do
1540
+ path = [ 'emails' ]
1541
+ value = [ { 'type' => 'work' } ]
1542
+ scim_hash = {
1543
+ 'emails' => [
1544
+ {
1545
+ 'type' => 'home',
1546
+ 'value' => 'home@test.com'
1547
+ },
1548
+ {
1549
+ 'type' => 'work',
1550
+ 'value' => 'work@test.com'
1551
+ }
1552
+ ]
1553
+ }.with_indifferent_case_insensitive_access()
1554
+
1555
+ @instance.send(
1556
+ :from_patch_backend!,
1557
+ nature: 'remove',
1558
+ path: path,
1559
+ value: value,
1560
+ altering_hash: scim_hash
1561
+ )
1562
+
1563
+ expect(scim_hash).to eql({
1564
+ 'emails' => [
1565
+ {
1566
+ 'type' => 'home',
1567
+ 'value' => 'home@test.com'
1568
+ }
1569
+ ]
1570
+ })
1571
+ end
1572
+
1573
+ it 'ignores unmatched items' do
1574
+ path = [ 'emails' ]
1575
+ value = [ { 'type' => 'missing' } ]
1576
+ scim_hash = {
1577
+ 'emails' => [
1578
+ {
1579
+ 'type' => 'home',
1580
+ 'value' => 'home@test.com'
1581
+ },
1582
+ {
1583
+ 'type' => 'work',
1584
+ 'value' => 'work@test.com'
1585
+ }
1586
+ ]
1587
+ }.with_indifferent_case_insensitive_access()
1588
+
1589
+ @instance.send(
1590
+ :from_patch_backend!,
1591
+ nature: 'remove',
1592
+ path: path,
1593
+ value: value,
1594
+ altering_hash: scim_hash
1595
+ )
1596
+
1597
+ expect(scim_hash).to eql({
1598
+ 'emails' => [
1599
+ {
1600
+ 'type' => 'home',
1601
+ 'value' => 'home@test.com'
1602
+ },
1603
+ {
1604
+ 'type' => 'work',
1605
+ 'value' => 'work@test.com'
1606
+ }
1607
+ ]
1608
+ })
1609
+ end
1610
+
1611
+ it 'compares string forms' do
1612
+ path = [ 'test' ]
1613
+ value = [
1614
+ { 'active' => true, 'value' => '12' },
1615
+ { 'active' => 'false', 'value' => 42 }
1616
+ ]
1617
+ scim_hash = {
1618
+ 'test' => [
1619
+ {
1620
+ 'active' => 'true',
1621
+ 'value' => 12
1622
+ },
1623
+ {
1624
+ 'active' => false,
1625
+ 'value' => '42'
1626
+ }
1627
+ ]
1628
+ }.with_indifferent_case_insensitive_access()
1629
+
1630
+ @instance.send(
1631
+ :from_patch_backend!,
1632
+ nature: 'remove',
1633
+ path: path,
1634
+ value: value,
1635
+ altering_hash: scim_hash
1636
+ )
1637
+
1638
+ expect(scim_hash).to eql({'test' => []})
1639
+ end
1640
+
1641
+ it 'handles a singular to-remove value rather than an array' do
1642
+ path = [ 'emails' ]
1643
+ value = { 'type' => 'work' }
1644
+ scim_hash = {
1645
+ 'emails' => [
1646
+ {
1647
+ 'type' => 'home',
1648
+ 'value' => 'home@test.com'
1649
+ },
1650
+ {
1651
+ 'type' => 'work',
1652
+ 'value' => 'work@test.com'
1653
+ }
1654
+ ]
1655
+ }.with_indifferent_case_insensitive_access()
1656
+
1657
+ @instance.send(
1658
+ :from_patch_backend!,
1659
+ nature: 'remove',
1660
+ path: path,
1661
+ value: value,
1662
+ altering_hash: scim_hash
1663
+ )
1664
+
1665
+ expect(scim_hash).to eql({
1666
+ 'emails' => [
1667
+ {
1668
+ 'type' => 'home',
1669
+ 'value' => 'home@test.com'
1670
+ }
1671
+ ]
1672
+ })
1673
+ end
1674
+
1675
+ it 'handles simple values rather than object (Hash) values' do
1676
+ path = [ 'test' ]
1677
+ value = 42
1678
+ scim_hash = {
1679
+ 'test' => [
1680
+ '21',
1681
+ '42',
1682
+ '15'
1683
+ ]
1684
+ }.with_indifferent_case_insensitive_access()
1685
+
1686
+ @instance.send(
1687
+ :from_patch_backend!,
1688
+ nature: 'remove',
1689
+ path: path,
1690
+ value: value,
1691
+ altering_hash: scim_hash
1692
+ )
1693
+
1694
+ expect(scim_hash).to eql({
1695
+ 'test' => [
1696
+ '21',
1697
+ '15'
1698
+ ]
1699
+ })
1700
+ end
1701
+ end
1702
+ end # "context 'Microsoft-style payload' do"
1703
+
1704
+ # https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
1705
+ #
1706
+ context 'Salesforce-style payload' do
1707
+ it 'removes identified user' do
1708
+ path = [ 'members' ]
1709
+ value = { 'members' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
1710
+ scim_hash = {
1711
+ 'displayname' => 'Mock group',
1712
+ 'members' => [
1713
+ {
1714
+ 'value' => '50ca93d04ab0c2de4772',
1715
+ 'display' => 'Ingrid Smith',
1716
+ 'type' => 'User'
1717
+ },
1718
+ {
1719
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1720
+ 'display' => 'Fred Smith',
1721
+ 'type' => 'User'
1722
+ }
1723
+ ]
1724
+ }.with_indifferent_case_insensitive_access()
1725
+
1726
+ @instance.send(
1727
+ :from_patch_backend!,
1728
+ nature: 'remove',
1729
+ path: path,
1730
+ value: value,
1731
+ altering_hash: scim_hash
1732
+ )
1733
+
1734
+ expect(scim_hash).to eql({
1735
+ 'displayname' => 'Mock group',
1736
+ 'members' => [
1737
+ {
1738
+ 'value' => '50ca93d04ab0c2de4772',
1739
+ 'display' => 'Ingrid Smith',
1740
+ 'type' => 'User'
1741
+ }
1742
+ ]
1743
+ })
1744
+ end
1745
+
1746
+ it 'matches the "members" key case-insensitive' do
1747
+ path = [ 'members' ]
1748
+ value = { 'MEMBERS' => [ { '$ref' => nil, 'value' => 'f648f8d5ea4e4cd38e9c' } ] }
1749
+ scim_hash = {
1750
+ 'displayname' => 'Mock group',
1751
+ 'members' => [
1752
+ {
1753
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1754
+ 'display' => 'Fred Smith',
1755
+ 'type' => 'User'
1756
+ },
1757
+ {
1758
+ 'value' => 'a774d480e8112101375b',
1759
+ 'display' => 'Taylor Smith',
1760
+ 'type' => 'User'
1761
+ }
1762
+ ]
1763
+ }.with_indifferent_case_insensitive_access()
1764
+
1765
+ @instance.send(
1766
+ :from_patch_backend!,
1767
+ nature: 'remove',
1768
+ path: path,
1769
+ value: value,
1770
+ altering_hash: scim_hash
1771
+ )
1772
+
1773
+ expect(scim_hash).to eql({
1774
+ 'displayname' => 'Mock group',
1775
+ 'members' => [
1776
+ {
1777
+ 'value' => 'a774d480e8112101375b',
1778
+ 'display' => 'Taylor Smith',
1779
+ 'type' => 'User'
1780
+ }
1781
+ ]
1782
+ })
1783
+ end
1784
+
1785
+ it 'ignores unrecognised users' do
1786
+ path = [ 'members' ]
1787
+ value = { 'members' => [ { '$ref' => nil, 'value' => '11b054a9c85216ed9356' } ] }
1788
+ scim_hash = {
1789
+ 'displayname' => 'Mock group',
1790
+ 'members' => [
1791
+ {
1792
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1793
+ 'display' => 'Fred Smith',
1794
+ 'type' => 'User'
1795
+ }
1796
+ ]
1797
+ }.with_indifferent_case_insensitive_access()
1798
+
1799
+ @instance.send(
1800
+ :from_patch_backend!,
1801
+ nature: 'remove',
1802
+ path: path,
1803
+ value: value,
1804
+ altering_hash: scim_hash
1805
+ )
1806
+
1807
+ # The 'value' mismatched, so the user was not removed.
1808
+ #
1809
+ expect(scim_hash).to eql({
1810
+ 'displayname' => 'Mock group',
1811
+ 'members' => [
1812
+ {
1813
+ 'value' => 'f648f8d5ea4e4cd38e9c',
1814
+ 'display' => 'Fred Smith',
1815
+ 'type' => 'User'
1816
+ }
1817
+ ]
1818
+ })
1819
+ end
1820
+ end # "context 'Salesforce-style payload' do"
1821
+ end # "context 'special cases' do"
1205
1822
  end # context 'when prior value already exists' do
1206
1823
 
1207
1824
  context 'when value is not present' do
@@ -1926,7 +2543,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1926
2543
  :from_patch_backend!,
1927
2544
  nature: 'remove',
1928
2545
  path: ['complex[type eq "type1"]', 'data', 'nested[nature eq "nature2"]', 'info'],
1929
- value: [{ 'deeper' => 'addition' }],
2546
+ value: nil,
1930
2547
  altering_hash: scim_hash
1931
2548
  )
1932
2549