scimitar 1.10.0 → 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 +23 -98
- data/app/controllers/scimitar/application_controller.rb +13 -41
- data/app/controllers/scimitar/resource_types_controller.rb +2 -0
- data/app/controllers/scimitar/resources_controller.rb +2 -0
- data/app/controllers/scimitar/schemas_controller.rb +3 -366
- data/app/controllers/scimitar/service_provider_configurations_controller.rb +1 -0
- data/app/models/scimitar/complex_types/address.rb +6 -0
- data/app/models/scimitar/engine_configuration.rb +5 -15
- data/app/models/scimitar/error_response.rb +0 -12
- data/app/models/scimitar/lists/query_parser.rb +13 -113
- data/app/models/scimitar/resource_invalid_error.rb +1 -1
- data/app/models/scimitar/resources/base.rb +9 -53
- data/app/models/scimitar/resources/mixin.rb +59 -646
- data/app/models/scimitar/schema/address.rb +0 -1
- data/app/models/scimitar/schema/attribute.rb +5 -14
- data/app/models/scimitar/schema/base.rb +1 -1
- data/app/models/scimitar/schema/name.rb +2 -2
- data/app/models/scimitar/schema/user.rb +10 -10
- data/app/models/scimitar/schema/vdtp.rb +1 -1
- data/app/models/scimitar/service_provider_configuration.rb +3 -14
- data/config/initializers/scimitar.rb +3 -69
- data/lib/scimitar/engine.rb +12 -57
- data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +10 -140
- data/lib/scimitar/version.rb +2 -2
- data/lib/scimitar.rb +2 -7
- 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 +9 -52
- 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 +10 -90
- data/spec/apps/dummy/config/routes.rb +7 -28
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -11
- 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 -12
- data/spec/controllers/scimitar/application_controller_spec.rb +3 -126
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +2 -2
- data/spec/controllers/scimitar/schemas_controller_spec.rb +48 -344
- data/spec/models/scimitar/complex_types/address_spec.rb +4 -3
- data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +9 -146
- data/spec/models/scimitar/resources/base_spec.rb +71 -217
- data/spec/models/scimitar/resources/base_validation_spec.rb +5 -43
- data/spec/models/scimitar/resources/mixin_spec.rb +129 -1508
- data/spec/models/scimitar/schema/attribute_spec.rb +3 -22
- data/spec/models/scimitar/schema/base_spec.rb +1 -1
- data/spec/models/scimitar/schema/user_spec.rb +2 -12
- data/spec/requests/active_record_backed_resources_controller_spec.rb +66 -1016
- data/spec/requests/application_controller_spec.rb +3 -16
- data/spec/requests/engine_spec.rb +0 -75
- data/spec/spec_helper.rb +1 -9
- data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +0 -108
- metadata +26 -37
- data/LICENSE.txt +0 -21
- data/README.md +0 -717
- data/lib/scimitar/support/utilities.rb +0 -111
- data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +0 -24
- data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +0 -25
@@ -60,15 +60,6 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
60
60
|
expect(%Q("O'Malley")).to eql(tree[2])
|
61
61
|
end
|
62
62
|
|
63
|
-
it "extended attribute equals" do
|
64
|
-
@instance.parse(%Q(primaryEmail eq "foo@bar.com"))
|
65
|
-
|
66
|
-
rpn = @instance.rpn
|
67
|
-
expect('primaryEmail').to eql(rpn[0])
|
68
|
-
expect(%Q("foo@bar.com")).to eql(rpn[1])
|
69
|
-
expect('eq').to eql(rpn[2])
|
70
|
-
end
|
71
|
-
|
72
63
|
it "user name starts with" do
|
73
64
|
@instance.parse(%Q(userName sw "J"))
|
74
65
|
|
@@ -346,67 +337,6 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
346
337
|
result = @instance.send(:flatten_filter, 'emails[type eq "work" and value co "@example.com" ] or userType eq "Admin" or ims[type eq "xmpp" and value co "@foo.com"]')
|
347
338
|
expect(result).to eql('emails.type eq "work" and emails.value co "@example.com" or userType eq "Admin" or ims.type eq "xmpp" and ims.value co "@foo.com"')
|
348
339
|
end
|
349
|
-
|
350
|
-
it 'handles an example previously described as unsupported in README.md' do
|
351
|
-
result = @instance.send(:flatten_filter, 'filter=userType eq "Employee" and emails[type eq "work" and value co "@example.com"]')
|
352
|
-
expect(result).to eql('filter=userType eq "Employee" and emails.type eq "work" and emails.value co "@example.com"')
|
353
|
-
end
|
354
|
-
|
355
|
-
# https://github.com/RIPAGlobal/scimitar/issues/116
|
356
|
-
#
|
357
|
-
context 'with schema IDs (GitHub issue #116)' do
|
358
|
-
it 'handles simple attributes' do
|
359
|
-
result = @instance.send(:flatten_filter, 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeId eq "gsar"')
|
360
|
-
expect(result).to eql('employeeId eq "gsar"')
|
361
|
-
end
|
362
|
-
|
363
|
-
it 'handles dotted attribute paths' do
|
364
|
-
result = @instance.send(:flatten_filter, 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:imaginary.path eq "gsar"')
|
365
|
-
expect(result).to eql('imaginary.path eq "gsar"')
|
366
|
-
end
|
367
|
-
|
368
|
-
it 'replaces all examples' do
|
369
|
-
result = @instance.send(:flatten_filter, 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeId eq "gsar" or urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:imaginary.path eq "gsar"')
|
370
|
-
expect(result).to eql('employeeId eq "gsar" or imaginary.path eq "gsar"')
|
371
|
-
end
|
372
|
-
|
373
|
-
it 'handles the square bracket form with schema ID at the root' do
|
374
|
-
result = @instance.send(:flatten_filter, 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User[employeeId eq "gsar"')
|
375
|
-
expect(result).to eql('employeeId eq "gsar"')
|
376
|
-
end
|
377
|
-
|
378
|
-
it 'handles the square bracket form with schema ID and attribute at the root' do
|
379
|
-
result = @instance.send(:flatten_filter, 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:imaginary[path eq "gsar"')
|
380
|
-
expect(result).to eql('imaginary.path eq "gsar"')
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
# https://github.com/RIPAGlobal/scimitar/issues/115
|
385
|
-
#
|
386
|
-
context 'broken filters from Microsoft (GitHub issue #115)' do
|
387
|
-
it 'work with "eq"' do
|
388
|
-
result = @instance.send(:flatten_filter, 'emails[type eq "work"].value eq "foo@bar.com"')
|
389
|
-
expect(result).to eql('emails.type eq "work" and emails.value eq "foo@bar.com"')
|
390
|
-
end
|
391
|
-
|
392
|
-
it 'work with "ne"' do # (just check a couple of operators, not all!)
|
393
|
-
result = @instance.send(:flatten_filter, 'emails[type eq "work"].value ne "foo@bar.com"')
|
394
|
-
expect(result).to eql('emails.type eq "work" and emails.value ne "foo@bar.com"')
|
395
|
-
end
|
396
|
-
|
397
|
-
it 'preserve input case' do
|
398
|
-
result = @instance.send(:flatten_filter, 'emaiLs[TYPE eq "work"].valUE eq "FOO@bar.com"')
|
399
|
-
expect(result).to eql('emaiLs.TYPE eq "work" and emaiLs.valUE eq "FOO@bar.com"')
|
400
|
-
end
|
401
|
-
|
402
|
-
# At the time of writing, this was used in a "belt and braces" request
|
403
|
-
# spec in 'active_record_backed_resources_controller_spec.rb'.
|
404
|
-
#
|
405
|
-
it 'handles more complex, hypothetical cases' do
|
406
|
-
result = @instance.send(:flatten_filter, 'name[givenName eq "FOO"].familyName pr and emails ne "home_1@test.com"')
|
407
|
-
expect(result).to eql('name.givenName eq "FOO" and name.familyName pr and emails ne "home_1@test.com"')
|
408
|
-
end
|
409
|
-
end # "context 'broken filters from Microsoft' do"
|
410
340
|
end # "context 'when flattening is needed' do"
|
411
341
|
|
412
342
|
context 'with bad filters' do
|
@@ -475,19 +405,19 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
475
405
|
query = @instance.to_activerecord_query(MockUser.all)
|
476
406
|
|
477
407
|
expect(query.count).to eql(1)
|
478
|
-
expect(query.pluck(:
|
408
|
+
expect(query.pluck(:id)).to eql([user_1.id])
|
479
409
|
|
480
410
|
@instance.parse('name.givenName sw J') # First name starts with 'J'
|
481
411
|
query = @instance.to_activerecord_query(MockUser.all)
|
482
412
|
|
483
413
|
expect(query.count).to eql(2)
|
484
|
-
expect(query.pluck(:
|
414
|
+
expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
|
485
415
|
|
486
416
|
@instance.parse('name.familyName ew he') # Last name ends with 'he'
|
487
417
|
query = @instance.to_activerecord_query(MockUser.all)
|
488
418
|
|
489
419
|
expect(query.count).to eql(1)
|
490
|
-
expect(query.pluck(:
|
420
|
+
expect(query.pluck(:id)).to eql([user_2.id])
|
491
421
|
|
492
422
|
# Test presence
|
493
423
|
|
@@ -495,7 +425,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
495
425
|
query = @instance.to_activerecord_query(MockUser.all)
|
496
426
|
|
497
427
|
expect(query.count).to eql(2)
|
498
|
-
expect(query.pluck(:
|
428
|
+
expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
|
499
429
|
|
500
430
|
# Test a simple not-equals, but use a custom starting scope. Note that
|
501
431
|
# the query would find "user_3" *except* there is no first name defined
|
@@ -505,7 +435,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
505
435
|
query = @instance.to_activerecord_query(MockUser.where.not('first_name' => 'John'))
|
506
436
|
|
507
437
|
expect(query.count).to eql(1)
|
508
|
-
expect(query.pluck(:
|
438
|
+
expect(query.pluck(:id)).to match_array([user_1.id])
|
509
439
|
end
|
510
440
|
|
511
441
|
context 'when mapped to multiple columns' do
|
@@ -551,66 +481,6 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
551
481
|
end
|
552
482
|
end # "context 'when instructed to ignore an attribute' do"
|
553
483
|
|
554
|
-
context 'when an arel column is mapped' do
|
555
|
-
let(:scope_with_groups) { MockUser.left_joins(:mock_groups) }
|
556
|
-
|
557
|
-
context 'with binary operators' do
|
558
|
-
it 'reads across all using OR' do
|
559
|
-
@instance.parse('groups eq "12345"')
|
560
|
-
query = @instance.to_activerecord_query(scope_with_groups)
|
561
|
-
|
562
|
-
expect(query.to_sql).to eql(<<~SQL.squish)
|
563
|
-
SELECT "mock_users".*
|
564
|
-
FROM "mock_users"
|
565
|
-
LEFT OUTER JOIN "mock_groups_users" ON "mock_groups_users"."mock_user_id" = "mock_users"."primary_key"
|
566
|
-
LEFT OUTER JOIN "mock_groups" ON "mock_groups"."id" = "mock_groups_users"."mock_group_id"
|
567
|
-
WHERE "mock_groups"."id" ILIKE 12345
|
568
|
-
SQL
|
569
|
-
end
|
570
|
-
|
571
|
-
it 'works with other query elements using correct precedence' do
|
572
|
-
@instance.parse('groups eq "12345" and emails eq "any@test.com"')
|
573
|
-
query = @instance.to_activerecord_query(scope_with_groups)
|
574
|
-
|
575
|
-
expect(query.to_sql).to eql(<<~SQL.squish)
|
576
|
-
SELECT "mock_users".*
|
577
|
-
FROM "mock_users"
|
578
|
-
LEFT OUTER JOIN "mock_groups_users" ON "mock_groups_users"."mock_user_id" = "mock_users"."primary_key"
|
579
|
-
LEFT OUTER JOIN "mock_groups" ON "mock_groups"."id" = "mock_groups_users"."mock_group_id"
|
580
|
-
WHERE "mock_groups"."id" ILIKE 12345 AND ("mock_users"."work_email_address" ILIKE 'any@test.com' OR "mock_users"."home_email_address" ILIKE 'any@test.com')
|
581
|
-
SQL
|
582
|
-
end
|
583
|
-
end # "context 'with binary operators' do"
|
584
|
-
|
585
|
-
context 'with unary operators' do
|
586
|
-
it 'reads across all using OR' do
|
587
|
-
@instance.parse('groups pr')
|
588
|
-
query = @instance.to_activerecord_query(scope_with_groups)
|
589
|
-
|
590
|
-
expect(query.to_sql).to eql(<<~SQL.squish)
|
591
|
-
SELECT "mock_users".*
|
592
|
-
FROM "mock_users"
|
593
|
-
LEFT OUTER JOIN "mock_groups_users" ON "mock_groups_users"."mock_user_id" = "mock_users"."primary_key"
|
594
|
-
LEFT OUTER JOIN "mock_groups" ON "mock_groups"."id" = "mock_groups_users"."mock_group_id"
|
595
|
-
WHERE ("mock_groups"."id" != NULL AND "mock_groups"."id" IS NOT NULL)
|
596
|
-
SQL
|
597
|
-
end
|
598
|
-
|
599
|
-
it 'works with other query elements using correct precedence' do
|
600
|
-
@instance.parse('name.familyName eq "John" and groups pr')
|
601
|
-
query = @instance.to_activerecord_query(scope_with_groups)
|
602
|
-
|
603
|
-
expect(query.to_sql).to eql(<<~SQL.squish)
|
604
|
-
SELECT "mock_users".*
|
605
|
-
FROM "mock_users"
|
606
|
-
LEFT OUTER JOIN "mock_groups_users" ON "mock_groups_users"."mock_user_id" = "mock_users"."primary_key"
|
607
|
-
LEFT OUTER JOIN "mock_groups" ON "mock_groups"."id" = "mock_groups_users"."mock_group_id"
|
608
|
-
WHERE "mock_users"."last_name" ILIKE 'John' AND ("mock_groups"."id" != NULL AND "mock_groups"."id" IS NOT NULL)
|
609
|
-
SQL
|
610
|
-
end
|
611
|
-
end # "context 'with unary operators' do
|
612
|
-
end # "context 'when an arel column is mapped' do"
|
613
|
-
|
614
484
|
context 'with complex cases' do
|
615
485
|
context 'using AND' do
|
616
486
|
it 'generates expected SQL' do
|
@@ -629,7 +499,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
629
499
|
query = @instance.to_activerecord_query(MockUser.all)
|
630
500
|
|
631
501
|
expect(query.count).to eql(1)
|
632
|
-
expect(query.pluck(:
|
502
|
+
expect(query.pluck(:id)).to match_array([user_2.id])
|
633
503
|
end
|
634
504
|
end # "context 'simple AND' do"
|
635
505
|
|
@@ -650,7 +520,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
650
520
|
query = @instance.to_activerecord_query(MockUser.all)
|
651
521
|
|
652
522
|
expect(query.count).to eql(2)
|
653
|
-
expect(query.pluck(:
|
523
|
+
expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
|
654
524
|
end
|
655
525
|
end # "context 'simple OR' do"
|
656
526
|
|
@@ -662,13 +532,6 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
662
532
|
expect(query.to_sql).to eql(%q{SELECT "mock_users".* FROM "mock_users" WHERE "mock_users"."first_name" ILIKE 'Jane' AND ("mock_users"."last_name" ILIKE '%avi%' OR "mock_users"."last_name" ILIKE '%ith')})
|
663
533
|
end
|
664
534
|
|
665
|
-
it 'combined parentheses generates expected SQL' do
|
666
|
-
@instance.parse('(name.givenName eq "Jane" OR name.givenName eq "Jaden") and (name.familyName co "avi" or name.familyName ew "ith")')
|
667
|
-
query = @instance.to_activerecord_query(MockUser.all)
|
668
|
-
|
669
|
-
expect(query.to_sql).to eql(%q{SELECT "mock_users".* FROM "mock_users" WHERE ("mock_users"."first_name" ILIKE 'Jane' OR "mock_users"."first_name" ILIKE 'Jaden') AND ("mock_users"."last_name" ILIKE '%avi%' OR "mock_users"."last_name" ILIKE '%ith')})
|
670
|
-
end
|
671
|
-
|
672
535
|
it 'finds expected items' do
|
673
536
|
user_1 = MockUser.create(username: '1', first_name: 'Jane', last_name: 'Davis') # Match
|
674
537
|
user_2 = MockUser.create(username: '2', first_name: 'Jane', last_name: 'Smith') # Match
|
@@ -683,7 +546,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
683
546
|
query = @instance.to_activerecord_query(MockUser.all)
|
684
547
|
|
685
548
|
expect(query.count).to eql(3)
|
686
|
-
expect(query.pluck(:
|
549
|
+
expect(query.pluck(:id)).to match_array([user_1.id, user_2.id, user_3.id])
|
687
550
|
end
|
688
551
|
end # "context 'combined AND and OR' do"
|
689
552
|
|
@@ -727,7 +590,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
727
590
|
end
|
728
591
|
|
729
592
|
it 'complains if there is no column mapping available' do
|
730
|
-
expect { @instance.send(:activerecord_columns, '
|
593
|
+
expect { @instance.send(:activerecord_columns, 'externalId') }.to raise_error(Scimitar::FilterError)
|
731
594
|
end
|
732
595
|
|
733
596
|
it 'complains about malformed declarations' do
|
@@ -4,7 +4,7 @@ RSpec.describe Scimitar::Resources::Base do
|
|
4
4
|
context 'basic operation' do
|
5
5
|
FirstCustomSchema = Class.new(Scimitar::Schema::Base) do
|
6
6
|
def self.id
|
7
|
-
'
|
7
|
+
'custom-id'
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.scim_attributes
|
@@ -14,10 +14,7 @@ RSpec.describe Scimitar::Resources::Base do
|
|
14
14
|
),
|
15
15
|
Scimitar::Schema::Attribute.new(
|
16
16
|
name: 'names', multiValued: true, complexType: Scimitar::ComplexTypes::Name, required: false
|
17
|
-
)
|
18
|
-
Scimitar::Schema::Attribute.new(
|
19
|
-
name: 'privateName', complexType: Scimitar::ComplexTypes::Name, required: false, returned: 'never'
|
20
|
-
),
|
17
|
+
)
|
21
18
|
]
|
22
19
|
end
|
23
20
|
end
|
@@ -27,24 +24,12 @@ RSpec.describe Scimitar::Resources::Base do
|
|
27
24
|
end
|
28
25
|
|
29
26
|
context '#initialize' do
|
30
|
-
it 'accepts nil for non-required attributes' do
|
31
|
-
resource = CustomResourse.new(name: nil, names: nil, privateName: nil)
|
32
|
-
|
33
|
-
expect(resource.name).to be_nil
|
34
|
-
expect(resource.names).to be_nil
|
35
|
-
expect(resource.privateName).to be_nil
|
36
|
-
end
|
37
|
-
|
38
27
|
shared_examples 'an initializer' do | force_upper_case: |
|
39
28
|
it 'which builds the nested type' do
|
40
29
|
attributes = {
|
41
30
|
name: {
|
42
31
|
givenName: 'John',
|
43
32
|
familyName: 'Smith'
|
44
|
-
},
|
45
|
-
privateName: {
|
46
|
-
givenName: 'Alt John',
|
47
|
-
familyName: 'Alt Smith'
|
48
33
|
}
|
49
34
|
}
|
50
35
|
|
@@ -54,9 +39,6 @@ RSpec.describe Scimitar::Resources::Base do
|
|
54
39
|
expect(resource.name.is_a?(Scimitar::ComplexTypes::Name)).to be(true)
|
55
40
|
expect(resource.name.givenName).to eql('John')
|
56
41
|
expect(resource.name.familyName).to eql('Smith')
|
57
|
-
expect(resource.privateName.is_a?(Scimitar::ComplexTypes::Name)).to be(true)
|
58
|
-
expect(resource.privateName.givenName).to eql('Alt John')
|
59
|
-
expect(resource.privateName.familyName).to eql('Alt Smith')
|
60
42
|
end
|
61
43
|
|
62
44
|
it 'which builds an array of nested resources' do
|
@@ -119,38 +101,14 @@ RSpec.describe Scimitar::Resources::Base do
|
|
119
101
|
context '#as_json' do
|
120
102
|
it 'renders the json with the resourceType' do
|
121
103
|
resource = CustomResourse.new(name: {
|
122
|
-
givenName:
|
104
|
+
givenName: 'John',
|
123
105
|
familyName: 'Smith'
|
124
106
|
})
|
125
107
|
|
126
108
|
result = resource.as_json
|
127
|
-
|
128
|
-
expect(result['schemas'] ).to eql(['urn:ietf:params:scim:schemas:custom-id'])
|
109
|
+
expect(result['schemas']).to eql(['custom-id'])
|
129
110
|
expect(result['meta']['resourceType']).to eql('CustomResourse')
|
130
|
-
expect(result['errors']
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'excludes attributes that are flagged as do-not-return' do
|
134
|
-
resource = CustomResourse.new(
|
135
|
-
name: {
|
136
|
-
givenName: 'John',
|
137
|
-
familyName: 'Smith'
|
138
|
-
},
|
139
|
-
privateName: {
|
140
|
-
givenName: 'Alt John',
|
141
|
-
familyName: 'Alt Smith'
|
142
|
-
}
|
143
|
-
)
|
144
|
-
|
145
|
-
result = resource.as_json
|
146
|
-
|
147
|
-
expect(result['schemas'] ).to eql(['urn:ietf:params:scim:schemas:custom-id'])
|
148
|
-
expect(result['meta']['resourceType']).to eql('CustomResourse')
|
149
|
-
expect(result['errors'] ).to be_nil
|
150
|
-
expect(result['name'] ).to be_present
|
151
|
-
expect(result['name']['givenName'] ).to eql('John')
|
152
|
-
expect(result['name']['familyName'] ).to eql('Smith')
|
153
|
-
expect(result['privateName'] ).to be_present
|
111
|
+
expect(result['errors']).to be_nil
|
154
112
|
end
|
155
113
|
end # "context '#as_json' do"
|
156
114
|
|
@@ -292,194 +250,90 @@ RSpec.describe Scimitar::Resources::Base do
|
|
292
250
|
end # "context 'dynamic setters based on schema' do"
|
293
251
|
|
294
252
|
context 'schema extension' do
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
'urn:ietf:params:scim:schemas:custom-id'
|
299
|
-
end
|
300
|
-
|
301
|
-
def self.scim_attributes
|
302
|
-
[ Scimitar::Schema::Attribute.new(name: 'name', type: 'string') ]
|
303
|
-
end
|
253
|
+
ThirdCustomSchema = Class.new(Scimitar::Schema::Base) do
|
254
|
+
def self.id
|
255
|
+
'custom-id'
|
304
256
|
end
|
305
257
|
|
306
|
-
|
307
|
-
|
308
|
-
'urn:ietf:params:scim:schemas:extension'
|
309
|
-
end
|
310
|
-
|
311
|
-
def self.scim_attributes
|
312
|
-
[
|
313
|
-
Scimitar::Schema::Attribute.new(name: 'relationship', type: 'string', required: true),
|
314
|
-
Scimitar::Schema::Attribute.new(name: "userGroups", multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceGroup, mutability: "writeOnly")
|
315
|
-
]
|
316
|
-
end
|
258
|
+
def self.scim_attributes
|
259
|
+
[ Scimitar::Schema::Attribute.new(name: 'name', type: 'string') ]
|
317
260
|
end
|
261
|
+
end
|
318
262
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
def self.endpoint
|
325
|
-
'/gaga'
|
326
|
-
end
|
327
|
-
|
328
|
-
def self.resource_type_id
|
329
|
-
'CustomResource'
|
330
|
-
end
|
331
|
-
end
|
332
|
-
}
|
333
|
-
|
334
|
-
context '#initialize' do
|
335
|
-
it 'allows setting extension attributes' do
|
336
|
-
resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {relationship: 'GAGA'})
|
337
|
-
expect(resource.relationship).to eql('GAGA')
|
338
|
-
end
|
339
|
-
|
340
|
-
it 'allows setting complex extension attributes' do
|
341
|
-
user_groups = [{ value: '123' }, { value: '456'}]
|
342
|
-
resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {userGroups: user_groups})
|
343
|
-
expect(resource.userGroups.map(&:value)).to eql(['123', '456'])
|
344
|
-
end
|
345
|
-
end # "context '#initialize' do"
|
346
|
-
|
347
|
-
context '#as_json' do
|
348
|
-
it 'namespaces the extension attributes' do
|
349
|
-
resource = resource_class.new(relationship: 'GAGA')
|
350
|
-
hash = resource.as_json
|
351
|
-
expect(hash["schemas"]).to eql(['urn:ietf:params:scim:schemas:custom-id', 'urn:ietf:params:scim:schemas:extension'])
|
352
|
-
expect(hash["urn:ietf:params:scim:schemas:extension"]).to eql("relationship" => 'GAGA')
|
353
|
-
end
|
354
|
-
end # "context '#as_json' do"
|
355
|
-
|
356
|
-
context '.resource_type' do
|
357
|
-
it 'appends the extension schemas' do
|
358
|
-
resource_type = resource_class.resource_type('http://gaga')
|
359
|
-
expect(resource_type.meta.location).to eql('http://gaga')
|
360
|
-
expect(resource_type.schemaExtensions.count).to eql(1)
|
361
|
-
end
|
362
|
-
|
363
|
-
context 'validation' do
|
364
|
-
it 'validates into custom schema' do
|
365
|
-
resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {})
|
366
|
-
expect(resource.valid?).to eql(false)
|
367
|
-
|
368
|
-
resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {relationship: 'GAGA'})
|
369
|
-
expect(resource.relationship).to eql('GAGA')
|
370
|
-
expect(resource.valid?).to eql(true)
|
371
|
-
end
|
372
|
-
end # context 'validation'
|
373
|
-
end # "context '.resource_type' do"
|
263
|
+
ExtensionSchema = Class.new(Scimitar::Schema::Base) do
|
264
|
+
def self.id
|
265
|
+
'extension-id'
|
266
|
+
end
|
374
267
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
expect(found.name).to eql('name')
|
380
|
-
expect(found.type).to eql('string')
|
381
|
-
end
|
268
|
+
def self.scim_attributes
|
269
|
+
[ Scimitar::Schema::Attribute.new(name: 'relationship', type: 'string', required: true) ]
|
270
|
+
end
|
271
|
+
end
|
382
272
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
expect(found.type).to eql('string')
|
388
|
-
end
|
389
|
-
end # "context '.find_attribute' do"
|
390
|
-
end # "context 'of custom schema' do"
|
273
|
+
let(:resource_class) {
|
274
|
+
Class.new(Scimitar::Resources::Base) do
|
275
|
+
set_schema ThirdCustomSchema
|
276
|
+
extend_schema ExtensionSchema
|
391
277
|
|
392
|
-
|
393
|
-
|
394
|
-
def self.id
|
395
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
278
|
+
def self.endpoint
|
279
|
+
'/gaga'
|
396
280
|
end
|
397
281
|
|
398
|
-
def self.
|
399
|
-
|
400
|
-
Scimitar::Schema::Attribute.new(name: 'organization', type: 'string'),
|
401
|
-
Scimitar::Schema::Attribute.new(name: 'department', type: 'string')
|
402
|
-
]
|
282
|
+
def self.resource_type_id
|
283
|
+
'CustomResource'
|
403
284
|
end
|
404
285
|
end
|
286
|
+
}
|
405
287
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
'/Users'
|
413
|
-
end
|
414
|
-
|
415
|
-
def self.resource_type_id
|
416
|
-
'User'
|
417
|
-
end
|
418
|
-
end
|
419
|
-
}
|
420
|
-
|
421
|
-
context '#initialize' do
|
422
|
-
it 'allows setting extension attributes' do
|
423
|
-
resource = resource_class.new('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {organization: 'SOMEORG', department: 'SOMEDPT'})
|
288
|
+
context '#initialize' do
|
289
|
+
it 'allows setting extension attributes' do
|
290
|
+
resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
|
291
|
+
expect(resource.relationship).to eql('GAGA')
|
292
|
+
end
|
293
|
+
end # "context '#initialize' do"
|
424
294
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
295
|
+
context '#as_json' do
|
296
|
+
it 'namespaces the extension attributes' do
|
297
|
+
resource = resource_class.new(relationship: 'GAGA')
|
298
|
+
hash = resource.as_json
|
299
|
+
expect(hash["schemas"]).to eql(['custom-id', 'extension-id'])
|
300
|
+
expect(hash["extension-id"]).to eql("relationship" => 'GAGA')
|
301
|
+
end
|
302
|
+
end # "context '#as_json' do"
|
429
303
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
304
|
+
context '.resource_type' do
|
305
|
+
it 'appends the extension schemas' do
|
306
|
+
resource_type = resource_class.resource_type('http://gaga')
|
307
|
+
expect(resource_type.meta.location).to eql('http://gaga')
|
308
|
+
expect(resource_type.schemaExtensions.count).to eql(1)
|
309
|
+
end
|
434
310
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
311
|
+
context 'validation' do
|
312
|
+
it 'validates into custom schema' do
|
313
|
+
resource = resource_class.new('extension-id' => {})
|
314
|
+
expect(resource.valid?).to eql(false)
|
439
315
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
expect(resource_type.meta.location).to eql('http://example.com')
|
444
|
-
expect(resource_type.schemaExtensions.count).to eql(1)
|
316
|
+
resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
|
317
|
+
expect(resource.relationship).to eql('GAGA')
|
318
|
+
expect(resource.valid?).to eql(true)
|
445
319
|
end
|
320
|
+
end # context 'validation'
|
321
|
+
end # "context '.resource_type' do"
|
446
322
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
userName: 'SOMEUSR',
|
455
|
-
organization: 'SOMEORG',
|
456
|
-
department: 'SOMEDPT'
|
457
|
-
}
|
458
|
-
)
|
459
|
-
|
460
|
-
expect(resource.organization).to eql('SOMEORG')
|
461
|
-
expect(resource.department ).to eql('SOMEDPT')
|
462
|
-
expect(resource.valid? ).to eql(true)
|
463
|
-
end
|
464
|
-
end # context 'validation'
|
465
|
-
end # "context '.resource_type' do"
|
466
|
-
|
467
|
-
context '.find_attribute' do
|
468
|
-
it 'finds in first schema' do
|
469
|
-
found = resource_class().find_attribute('userName') # Defined in Scimitar::Schema::User
|
470
|
-
|
471
|
-
expect(found).to be_present
|
472
|
-
expect(found.name).to eql('userName')
|
473
|
-
expect(found.type).to eql('string')
|
474
|
-
end
|
323
|
+
context '.find_attribute' do
|
324
|
+
it 'finds in first schema' do
|
325
|
+
found = resource_class().find_attribute('name') # Defined in ThirdCustomSchema
|
326
|
+
expect(found).to be_present
|
327
|
+
expect(found.name).to eql('name')
|
328
|
+
expect(found.type).to eql('string')
|
329
|
+
end
|
475
330
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
end # "context 'of core schema' do"
|
331
|
+
it 'finds across schemas' do
|
332
|
+
found = resource_class().find_attribute('relationship') # Defined in ExtensionSchema
|
333
|
+
expect(found).to be_present
|
334
|
+
expect(found.name).to eql('relationship')
|
335
|
+
expect(found.type).to eql('string')
|
336
|
+
end
|
337
|
+
end # "context '.find_attribute' do"
|
484
338
|
end # "context 'schema extension' do"
|
485
339
|
end
|
@@ -1,23 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Scimitar::Resources::Base do
|
4
|
+
|
4
5
|
context '#valid?' do
|
5
6
|
MyCustomSchema = Class.new(Scimitar::Schema::Base) do
|
6
7
|
def self.id
|
7
|
-
'
|
8
|
-
end
|
9
|
-
|
10
|
-
class NameWithRequirementsSchema < Scimitar::Schema::Base
|
11
|
-
def self.scim_attributes
|
12
|
-
@scim_attributes ||= [
|
13
|
-
Scimitar::Schema::Attribute.new(name: 'familyName', type: 'string', required: true),
|
14
|
-
Scimitar::Schema::Attribute.new(name: 'givenName', type: 'string', required: true),
|
15
|
-
]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class NameWithRequirementsComplexType < Scimitar::ComplexTypes::Base
|
20
|
-
set_schema NameWithRequirementsSchema
|
8
|
+
'custom-id'
|
21
9
|
end
|
22
10
|
|
23
11
|
def self.scim_attributes
|
@@ -29,13 +17,10 @@ RSpec.describe Scimitar::Resources::Base do
|
|
29
17
|
name: 'enforce', type: 'boolean', required: true
|
30
18
|
),
|
31
19
|
Scimitar::Schema::Attribute.new(
|
32
|
-
name: 'complexName', complexType:
|
33
|
-
),
|
34
|
-
Scimitar::Schema::Attribute.new(
|
35
|
-
name: 'complexNames', complexType: Scimitar::ComplexTypes::Name, multiValued: true, required: false
|
20
|
+
name: 'complexName', complexType: Scimitar::ComplexTypes::Name, required: false
|
36
21
|
),
|
37
22
|
Scimitar::Schema::Attribute.new(
|
38
|
-
name: '
|
23
|
+
name: 'complexNames', complexType: Scimitar::ComplexTypes::Name, multiValued:true, required: false
|
39
24
|
)
|
40
25
|
]
|
41
26
|
end
|
@@ -72,28 +57,5 @@ RSpec.describe Scimitar::Resources::Base do
|
|
72
57
|
expect(resource.valid?).to be(false)
|
73
58
|
expect(resource.errors.full_messages).to match_array(["Complexnames has to follow the complexType format.", "Complexnames familyname has the wrong type. It has to be a(n) string."])
|
74
59
|
end
|
75
|
-
|
76
|
-
context 'configuration of required values in VDTP schema' do
|
77
|
-
around :each do | example |
|
78
|
-
original_configuration = Scimitar.engine_configuration.optional_value_fields_required
|
79
|
-
Scimitar::Schema::Email.instance_variable_set('@scim_attributes', nil)
|
80
|
-
example.run()
|
81
|
-
ensure
|
82
|
-
Scimitar.engine_configuration.optional_value_fields_required = original_configuration
|
83
|
-
Scimitar::Schema::Email.instance_variable_set('@scim_attributes', nil)
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'requires a value by default' do
|
87
|
-
resource = MyCustomResource.new(vdtpTestByEmail: { value: nil }, enforce: false)
|
88
|
-
expect(resource.valid?).to be(false)
|
89
|
-
expect(resource.errors.full_messages).to match_array(['Vdtptestbyemail value is required'])
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'can be configured for optional values' do
|
93
|
-
Scimitar.engine_configuration.optional_value_fields_required = false
|
94
|
-
resource = MyCustomResource.new(vdtpTestByEmail: { value: nil }, enforce: false)
|
95
|
-
expect(resource.valid?).to be(true)
|
96
|
-
end
|
97
|
-
end # "context 'configuration of required values in VDTP schema' do"
|
98
|
-
end # "context '#valid?' do"
|
60
|
+
end
|
99
61
|
end
|