scimitar 1.8.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scimitar/active_record_backed_resources_controller.rb +20 -94
  3. data/app/controllers/scimitar/application_controller.rb +13 -41
  4. data/app/controllers/scimitar/schemas_controller.rb +0 -5
  5. data/app/models/scimitar/complex_types/address.rb +6 -0
  6. data/app/models/scimitar/engine_configuration.rb +5 -13
  7. data/app/models/scimitar/error_response.rb +0 -12
  8. data/app/models/scimitar/lists/query_parser.rb +10 -25
  9. data/app/models/scimitar/resource_invalid_error.rb +1 -1
  10. data/app/models/scimitar/resources/base.rb +4 -17
  11. data/app/models/scimitar/resources/mixin.rb +42 -539
  12. data/app/models/scimitar/schema/address.rb +0 -1
  13. data/app/models/scimitar/schema/attribute.rb +5 -14
  14. data/app/models/scimitar/schema/base.rb +1 -1
  15. data/app/models/scimitar/schema/vdtp.rb +1 -1
  16. data/app/models/scimitar/service_provider_configuration.rb +3 -14
  17. data/config/initializers/scimitar.rb +3 -28
  18. data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +10 -140
  19. data/lib/scimitar/version.rb +2 -2
  20. data/lib/scimitar.rb +2 -7
  21. data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
  22. data/spec/apps/dummy/app/models/mock_group.rb +1 -1
  23. data/spec/apps/dummy/app/models/mock_user.rb +8 -36
  24. data/spec/apps/dummy/config/application.rb +1 -0
  25. data/spec/apps/dummy/config/environments/test.rb +28 -5
  26. data/spec/apps/dummy/config/initializers/scimitar.rb +10 -61
  27. data/spec/apps/dummy/config/routes.rb +7 -28
  28. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -10
  29. data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
  30. data/spec/apps/dummy/db/schema.rb +4 -11
  31. data/spec/controllers/scimitar/application_controller_spec.rb +3 -126
  32. data/spec/controllers/scimitar/resource_types_controller_spec.rb +2 -2
  33. data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -10
  34. data/spec/models/scimitar/complex_types/address_spec.rb +4 -3
  35. data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
  36. data/spec/models/scimitar/lists/query_parser_spec.rb +9 -76
  37. data/spec/models/scimitar/resources/base_spec.rb +70 -216
  38. data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
  39. data/spec/models/scimitar/resources/mixin_spec.rb +129 -1447
  40. data/spec/models/scimitar/schema/attribute_spec.rb +3 -22
  41. data/spec/models/scimitar/schema/base_spec.rb +1 -1
  42. data/spec/models/scimitar/schema/user_spec.rb +0 -10
  43. data/spec/requests/active_record_backed_resources_controller_spec.rb +68 -787
  44. data/spec/requests/application_controller_spec.rb +3 -16
  45. data/spec/spec_helper.rb +0 -8
  46. data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +0 -108
  47. metadata +14 -25
  48. data/LICENSE.txt +0 -21
  49. data/README.md +0 -710
  50. data/lib/scimitar/support/utilities.rb +0 -51
  51. data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +0 -25
  52. data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +0 -25
  53. data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +0 -24
  54. data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +0 -25
@@ -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: 'John',
104
+ givenName: 'John',
123
105
  familyName: 'Smith'
124
106
  })
125
107
 
126
108
  result = resource.as_json
127
-
128
- expect(result['schemas'] ).to eql(['custom-id'])
129
- expect(result['meta']['resourceType']).to eql('CustomResourse')
130
- expect(result['errors'] ).to be_nil
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(['custom-id'])
109
+ expect(result['schemas']).to eql(['custom-id'])
148
110
  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
- context 'of custom schema' do
296
- ThirdCustomSchema = Class.new(Scimitar::Schema::Base) do
297
- def self.id
298
- '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
- ExtensionSchema = Class.new(Scimitar::Schema::Base) do
307
- def self.id
308
- 'extension-id'
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
- let(:resource_class) {
320
- Class.new(Scimitar::Resources::Base) do
321
- set_schema ThirdCustomSchema
322
- extend_schema ExtensionSchema
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('extension-id' => {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('extension-id' => {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(['custom-id', 'extension-id'])
352
- expect(hash["extension-id"]).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('extension-id' => {})
366
- expect(resource.valid?).to eql(false)
367
-
368
- resource = resource_class.new('extension-id' => {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
- context '.find_attribute' do
376
- it 'finds in first schema' do
377
- found = resource_class().find_attribute('name') # Defined in ThirdCustomSchema
378
- expect(found).to be_present
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
- it 'finds across schemas' do
384
- found = resource_class().find_attribute('relationship') # Defined in ExtensionSchema
385
- expect(found).to be_present
386
- expect(found.name).to eql('relationship')
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
- context 'of core schema' do
393
- EnterpriseExtensionSchema = Class.new(Scimitar::Schema::Base) do
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.scim_attributes
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
- let(:resource_class) {
407
- Class.new(Scimitar::Resources::Base) do
408
- set_schema Scimitar::Schema::User
409
- extend_schema EnterpriseExtensionSchema
410
-
411
- def self.endpoint
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
- expect(resource.organization).to eql('SOMEORG')
426
- expect(resource.department ).to eql('SOMEDPT')
427
- end
428
- end # "context '#initialize' do"
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
- context '#as_json' do
431
- it 'namespaces the extension attributes' do
432
- resource = resource_class.new(organization: 'SOMEORG', department: 'SOMEDPT')
433
- hash = resource.as_json
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
- expect(hash['schemas']).to eql(['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'])
436
- expect(hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']).to eql('organization' => 'SOMEORG', 'department' => 'SOMEDPT')
437
- end
438
- end # "context '#as_json' do"
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
- context '.resource_type' do
441
- it 'appends the extension schemas' do
442
- resource_type = resource_class.resource_type('http://example.com')
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
- context 'validation' do
448
- it 'validates into custom schema' do
449
- resource = resource_class.new('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {})
450
- expect(resource.valid?).to eql(false)
451
-
452
- resource = resource_class.new(
453
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
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
- it 'finds across schemas' do
477
- found = resource_class().find_attribute('organization') # Defined in EnterpriseExtensionSchema
478
- expect(found).to be_present
479
- expect(found.name).to eql('organization')
480
- expect(found.type).to eql('string')
481
- end
482
- end # "context '.find_attribute' do"
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,6 +1,7 @@
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
@@ -20,9 +21,6 @@ RSpec.describe Scimitar::Resources::Base do
20
21
  ),
21
22
  Scimitar::Schema::Attribute.new(
22
23
  name: 'complexNames', complexType: Scimitar::ComplexTypes::Name, multiValued:true, required: false
23
- ),
24
- Scimitar::Schema::Attribute.new(
25
- name: 'vdtpTestByEmail', complexType: Scimitar::ComplexTypes::Email, required: false
26
24
  )
27
25
  ]
28
26
  end
@@ -59,28 +57,5 @@ RSpec.describe Scimitar::Resources::Base do
59
57
  expect(resource.valid?).to be(false)
60
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."])
61
59
  end
62
-
63
- context 'configuration of required values in VDTP schema' do
64
- around :each do | example |
65
- original_configuration = Scimitar.engine_configuration.optional_value_fields_required
66
- Scimitar::Schema::Email.instance_variable_set('@scim_attributes', nil)
67
- example.run()
68
- ensure
69
- Scimitar.engine_configuration.optional_value_fields_required = original_configuration
70
- Scimitar::Schema::Email.instance_variable_set('@scim_attributes', nil)
71
- end
72
-
73
- it 'requires a value by default' do
74
- resource = MyCustomResource.new(vdtpTestByEmail: { value: nil }, enforce: false)
75
- expect(resource.valid?).to be(false)
76
- expect(resource.errors.full_messages).to match_array(['Vdtptestbyemail value is required'])
77
- end
78
-
79
- it 'can be configured for optional values' do
80
- Scimitar.engine_configuration.optional_value_fields_required = false
81
- resource = MyCustomResource.new(vdtpTestByEmail: { value: nil }, enforce: false)
82
- expect(resource.valid?).to be(true)
83
- end
84
- end # "context 'configuration of required values in VDTP schema' do"
85
- end # "context '#valid?' do"
60
+ end
86
61
  end