scimitar 2.8.0 → 2.10.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +23 -18
  4. data/app/controllers/scimitar/application_controller.rb +4 -5
  5. data/app/controllers/scimitar/resource_types_controller.rb +7 -1
  6. data/app/controllers/scimitar/schemas_controller.rb +361 -1
  7. data/app/models/scimitar/engine_configuration.rb +3 -1
  8. data/app/models/scimitar/lists/query_parser.rb +10 -10
  9. data/app/models/scimitar/resource_type.rb +4 -6
  10. data/app/models/scimitar/resources/base.rb +37 -6
  11. data/app/models/scimitar/resources/mixin.rb +15 -10
  12. data/app/models/scimitar/schema/base.rb +1 -1
  13. data/config/initializers/scimitar.rb +41 -0
  14. data/lib/scimitar/engine.rb +50 -12
  15. data/lib/scimitar/support/utilities.rb +8 -3
  16. data/lib/scimitar/version.rb +2 -2
  17. data/spec/apps/dummy/app/models/mock_user.rb +11 -3
  18. data/spec/apps/dummy/config/initializers/scimitar.rb +29 -1
  19. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -0
  20. data/spec/apps/dummy/db/schema.rb +1 -0
  21. data/spec/controllers/scimitar/resource_types_controller_spec.rb +8 -4
  22. data/spec/controllers/scimitar/schemas_controller_spec.rb +342 -54
  23. data/spec/models/scimitar/lists/query_parser_spec.rb +5 -0
  24. data/spec/models/scimitar/resources/base_spec.rb +11 -11
  25. data/spec/models/scimitar/resources/base_validation_spec.rb +1 -1
  26. data/spec/models/scimitar/resources/mixin_spec.rb +31 -12
  27. data/spec/requests/active_record_backed_resources_controller_spec.rb +86 -2
  28. data/spec/requests/engine_spec.rb +75 -0
  29. data/spec/spec_helper.rb +1 -1
  30. metadata +21 -21
@@ -10,74 +10,362 @@ RSpec.describe Scimitar::SchemasController do
10
10
  super
11
11
  end
12
12
  end
13
+
13
14
  context '#index' do
14
- it 'returns a collection of supported schemas' do
15
- get :index, params: { format: :scim }
16
- expect(response).to be_ok
17
- parsed_body = JSON.parse(response.body)
18
- expect(parsed_body.length).to eql(3)
19
- schema_names = parsed_body.map {|schema| schema['name']}
20
- expect(schema_names).to match_array(['User', 'ExtendedUser', 'Group'])
21
- end
15
+ shared_examples 'a Schema list which' do
16
+ it 'returns a valid ListResponse' do
17
+ get :index, params: { format: :scim }
18
+ expect(response).to be_ok
22
19
 
23
- it 'returns only the User schema when its id is provided' do
24
- get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
25
- expect(response).to be_ok
26
- parsed_body = JSON.parse(response.body)
27
- expect(parsed_body['name']).to eql('User')
28
- end
20
+ parsed_body = JSON.parse(response.body)
21
+ schema_count = parsed_body['Resources']&.size
29
22
 
30
- it 'includes the controller customised schema location' do
31
- get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
32
- expect(response).to be_ok
33
- parsed_body = JSON.parse(response.body)
34
- expect(parsed_body.dig('meta', 'location')).to eq scim_schemas_url(name: Scimitar::Schema::User.id, test: 1)
35
- end
23
+ expect(parsed_body['schemas' ]).to match_array(['urn:ietf:params:scim:api:messages:2.0:ListResponse'])
24
+ expect(parsed_body['totalResults']).to eql(schema_count)
25
+ expect(parsed_body['itemsPerPage']).to eql(schema_count)
26
+ expect(parsed_body['startIndex' ]).to eql(1)
27
+ end
28
+
29
+ it 'returns a collection of supported schemas' do
30
+ get :index, params: { format: :scim }
31
+ expect(response).to be_ok
32
+
33
+ parsed_body = JSON.parse(response.body)
34
+ expect(parsed_body['Resources']&.size).to eql(4)
35
+
36
+ schema_names = parsed_body['Resources'].map {|schema| schema['name']}
37
+ expect(schema_names).to match_array(['User', 'EnterpriseExtendedUser', 'ManagementExtendedUser', 'Group'])
38
+ end
39
+
40
+ it 'returns only the User schema when its ID is provided' do
41
+ get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
42
+ expect(response).to be_ok
36
43
 
37
- it 'returns only the Group schema when its id is provided' do
38
- get :index, params: { name: Scimitar::Schema::Group.id, format: :scim }
39
- expect(response).to be_ok
40
- parsed_body = JSON.parse(response.body)
41
- expect(parsed_body['name']).to eql('Group')
44
+ parsed_body = JSON.parse(response.body)
45
+ expect(parsed_body.dig('Resources', 0, 'name')).to eql('User')
46
+ end
47
+
48
+ it 'includes the controller customised schema location' do
49
+ get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
50
+ expect(response).to be_ok
51
+
52
+ parsed_body = JSON.parse(response.body)
53
+ expect(parsed_body.dig('Resources', 0, 'meta', 'location')).to eq scim_schemas_url(name: Scimitar::Schema::User.id, test: 1)
54
+ end
55
+
56
+ it 'returns only the Group schema when its ID is provided' do
57
+ get :index, params: { name: Scimitar::Schema::Group.id, format: :scim }
58
+ expect(response).to be_ok
59
+
60
+ parsed_body = JSON.parse(response.body)
61
+
62
+ expect(parsed_body['Resources' ]&.size).to eql(1)
63
+ expect(parsed_body['totalResults'] ).to eql(1)
64
+ expect(parsed_body['itemsPerPage'] ).to eql(1)
65
+ expect(parsed_body['startIndex' ] ).to eql(1)
66
+
67
+ expect(parsed_body.dig('Resources', 0, 'name')).to eql('Group')
68
+ end
42
69
  end
43
70
 
44
- context 'with custom resource types' do
45
- around :each do | example |
46
- example.run()
47
- ensure
48
- Scimitar::Engine.reset_custom_resources
71
+ context 'with default engine configuration of schema_list_from_attribute_mappings undefined' do
72
+ it_behaves_like 'a Schema list which'
73
+
74
+ it 'returns all attributes' do
75
+ get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
76
+ expect(response).to be_ok
77
+
78
+ parsed_body = JSON.parse(response.body)
79
+ user_attrs = parsed_body['Resources'].find { | r | r['name'] == 'User' }
80
+
81
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'ims' }).to be_present
82
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'entitlements' }).to be_present
83
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'x509Certificates' }).to be_present
84
+
85
+ name_attr = user_attrs['attributes'].find { | a | a['name'] == 'name' }
86
+
87
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'honorificPrefix' }).to be_present
88
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'honorificSuffix' }).to be_present
49
89
  end
50
90
 
51
- it 'returns only the License schemas when its id is provided' do
52
- license_schema = Class.new(Scimitar::Schema::Base) do
53
- def initialize(options = {})
54
- super(name: 'License',
55
- id: self.class.id,
56
- description: 'Represents a License')
57
- end
58
- def self.id
59
- 'License'
91
+ context 'with custom resource types' do
92
+ around :each do | example |
93
+ example.run()
94
+ ensure
95
+ Scimitar::Engine.reset_custom_resources
96
+ end
97
+
98
+ it 'returns only the License schemas when its ID is provided' do
99
+ license_schema = Class.new(Scimitar::Schema::Base) do
100
+ def initialize(options = {})
101
+ super(name: 'License', id: self.class.id(), description: 'Represents a License')
102
+ end
103
+ def self.id; 'urn:ietf:params:scim:schemas:license'; end
104
+ def self.scim_attributes; []; end
60
105
  end
61
- def self.scim_attributes
62
- []
106
+
107
+ license_resource = Class.new(Scimitar::Resources::Base) do
108
+ set_schema(license_schema)
109
+ def self.endpoint; '/License'; end
63
110
  end
111
+
112
+ Scimitar::Engine.add_custom_resource(license_resource)
113
+
114
+ get :index, params: { name: license_schema.id, format: :scim }
115
+ expect(response).to be_ok
116
+
117
+ parsed_body = JSON.parse(response.body)
118
+ expect(parsed_body.dig('Resources', 0, 'name')).to eql('License')
64
119
  end
120
+ end # "context 'with custom resource types' do"
121
+ end # "context 'with default engine configuration of schema_list_from_attribute_mappings undefined' do"
65
122
 
66
- license_resource = Class.new(Scimitar::Resources::Base) do
67
- set_schema license_schema
68
- def self.endopint
69
- '/Gaga'
70
- end
123
+ context 'with engine configuration of schema_list_from_attribute_mappings set' do
124
+ context 'standard resources' do
125
+ around :each do | example |
126
+ old_config = Scimitar.engine_configuration.schema_list_from_attribute_mappings
127
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [
128
+ MockUser,
129
+ MockGroup
130
+ ]
131
+ example.run()
132
+ ensure
133
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = old_config
71
134
  end
72
135
 
73
- Scimitar::Engine.add_custom_resource(license_resource)
136
+ it_behaves_like 'a Schema list which'
74
137
 
75
- get :index, params: { name: license_schema.id, format: :scim }
76
- expect(response).to be_ok
77
- parsed_body = JSON.parse(response.body)
78
- expect(parsed_body['name']).to eql('License')
138
+ it 'returns only mapped attributes' do
139
+ get :index, params: { name: Scimitar::Schema::User.id, format: :scim }
140
+ expect(response).to be_ok
141
+
142
+ parsed_body = JSON.parse(response.body)
143
+ user_attrs = parsed_body['Resources'].find { | r | r['name'] == 'User' }
144
+ password_attr = user_attrs['attributes'].find { | a | a['name'] == 'password' }
145
+
146
+ expect(password_attr['mutability']).to eql('writeOnly')
147
+
148
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'ims' }).to_not be_present
149
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'entitlements' }).to_not be_present
150
+ expect(user_attrs['attributes'].find { | a | a['name'] == 'x509Certificates' }).to_not be_present
151
+
152
+ name_attr = user_attrs['attributes'].find { | a | a['name'] == 'name' }
153
+
154
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'givenName' }).to be_present
155
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'familyName' }).to be_present
156
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'honorificPrefix' }).to_not be_present
157
+ expect(name_attr['subAttributes'].find { | s | s['name'] == 'honorificSuffix' }).to_not be_present
158
+
159
+ emails_attr = user_attrs['attributes' ].find { | a | a['name'] == 'emails' }
160
+ value_attr = emails_attr['subAttributes'].find { | a | a['name'] == 'value' }
161
+ primary_attr = emails_attr['subAttributes'].find { | a | a['name'] == 'primary' }
162
+
163
+ expect( value_attr['mutability']).to eql('readWrite')
164
+ expect(primary_attr['mutability']).to eql('readOnly')
165
+
166
+ expect(emails_attr['subAttributes'].find { | s | s['name'] == 'type' }).to_not be_present
167
+ expect(emails_attr['subAttributes'].find { | s | s['name'] == 'display' }).to_not be_present
168
+
169
+ groups_attr = user_attrs['attributes' ].find { | a | a['name'] == 'groups' }
170
+ value_attr = groups_attr['subAttributes'].find { | a | a['name'] == 'value' }
171
+ display_attr = groups_attr['subAttributes'].find { | a | a['name'] == 'display' }
172
+
173
+ expect( value_attr['mutability']).to eql('readOnly')
174
+ expect(display_attr['mutability']).to eql('readOnly')
175
+ end
79
176
  end
80
- end
81
- end
82
- end
83
177
 
178
+ context 'with custom resource types' do
179
+ let(:license_schema) {
180
+ Class.new(Scimitar::Schema::Base) do
181
+ def initialize(options = {})
182
+ super(
183
+ id: self.class.id(),
184
+ name: 'License',
185
+ description: 'Represents a license',
186
+ scim_attributes: self.class.scim_attributes
187
+ )
188
+ end
189
+ def self.id; 'urn:ietf:params:scim:schemas:license'; end
190
+ def self.scim_attributes
191
+ [
192
+ Scimitar::Schema::Attribute.new(name: 'licenseNumber', type: 'string'),
193
+ Scimitar::Schema::Attribute.new(name: 'licenseExpired', type: 'boolean', mutability: 'readOnly'),
194
+ ]
195
+ end
196
+ end
197
+ }
198
+
199
+ let(:license_resource) {
200
+ local_var_license_schema = license_schema()
201
+
202
+ Class.new(Scimitar::Resources::Base) do
203
+ set_schema(local_var_license_schema)
204
+ def self.endpoint; '/License'; end
205
+ end
206
+ }
207
+
208
+ let(:license_model_base) {
209
+ local_var_license_resource = license_resource()
210
+
211
+ Class.new do
212
+ singleton_class.class_eval do
213
+ define_method(:scim_resource_type) { local_var_license_resource }
214
+ end
215
+ end
216
+ }
217
+
218
+ around :each do | example |
219
+ old_config = Scimitar.engine_configuration.schema_list_from_attribute_mappings
220
+ Scimitar::Engine.add_custom_resource(license_resource())
221
+ example.run()
222
+ ensure
223
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = old_config
224
+ Scimitar::Engine.reset_custom_resources
225
+ end
226
+
227
+ context 'with an empty attribute map' do
228
+ it 'returns no attributes' do
229
+ license_model = Class.new(license_model_base()) do
230
+ attr_accessor :license_number
231
+
232
+ def self.scim_mutable_attributes; nil; end
233
+ def self.scim_queryable_attributes; nil; end
234
+ def self.scim_attributes_map; {}; end # Empty map
235
+
236
+ include Scimitar::Resources::Mixin
237
+ end
238
+
239
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [license_model]
240
+
241
+ get :index, params: { format: :scim }
242
+ expect(response).to be_ok
243
+
244
+ parsed_body = JSON.parse(response.body)
245
+
246
+ expect(parsed_body.dig('Resources', 0, 'name' )).to eql('License')
247
+ expect(parsed_body.dig('Resources', 0, 'attributes')).to be_empty
248
+ end
249
+ end # "context 'with an empty attribute map' do"
250
+
251
+ context 'with a defined attribute map' do
252
+ it 'returns only the License schemas when its ID is provided' do
253
+ license_model = Class.new(license_model_base()) do
254
+ attr_accessor :license_number
255
+
256
+ def self.scim_mutable_attributes; nil; end
257
+ def self.scim_queryable_attributes; nil; end
258
+ def self.scim_attributes_map # Simple map
259
+ { licenseNumber: :license_number }
260
+ end
261
+
262
+ include Scimitar::Resources::Mixin
263
+ end
264
+
265
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [license_model]
266
+
267
+ get :index, params: { format: :scim }
268
+ expect(response).to be_ok
269
+
270
+ parsed_body = JSON.parse(response.body)
271
+
272
+ expect(parsed_body.dig('Resources', 0, 'name' )).to eql('License')
273
+ expect(parsed_body.dig('Resources', 0, 'attributes').size ).to eql(1)
274
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'name' )).to eql('licenseNumber')
275
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'mutability')).to eql('readWrite')
276
+ end
277
+ end # "context 'with a defined attribute map' do"
278
+
279
+ context 'with mutability overridden' do
280
+ it 'returns read-only when expected' do
281
+ license_model = Class.new(license_model_base()) do
282
+ attr_accessor :license_number
283
+
284
+ def self.scim_mutable_attributes; []; end # Note empty array, NOT "nil" - no mutable attributes
285
+ def self.scim_queryable_attributes; nil; end
286
+ def self.scim_attributes_map
287
+ { licenseNumber: :license_number }
288
+ end
289
+
290
+ include Scimitar::Resources::Mixin
291
+ end
292
+
293
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [license_model]
294
+
295
+ get :index, params: { format: :scim }
296
+ expect(response).to be_ok
297
+
298
+ parsed_body = JSON.parse(response.body)
299
+
300
+ expect(parsed_body.dig('Resources', 0, 'name' )).to eql('License')
301
+ expect(parsed_body.dig('Resources', 0, 'attributes').size ).to eql(1)
302
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'name' )).to eql('licenseNumber')
303
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'mutability')).to eql('readOnly')
304
+ end
305
+
306
+ it 'returns write-only when expected' do
307
+ license_model = Class.new(license_model_base()) do
308
+ attr_writer :license_number # Writer only, no reader
309
+
310
+ def self.scim_mutable_attributes; nil; end
311
+ def self.scim_queryable_attributes; nil; end
312
+ def self.scim_attributes_map
313
+ { licenseNumber: :license_number }
314
+ end
315
+
316
+ include Scimitar::Resources::Mixin
317
+ end
318
+
319
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [license_model]
320
+
321
+ get :index, params: { format: :scim }
322
+ expect(response).to be_ok
323
+
324
+ parsed_body = JSON.parse(response.body)
325
+
326
+ expect(parsed_body.dig('Resources', 0, 'name' )).to eql('License')
327
+ expect(parsed_body.dig('Resources', 0, 'attributes').size ).to eql(1)
328
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'name' )).to eql('licenseNumber')
329
+ expect(parsed_body.dig('Resources', 0, 'attributes', 0, 'mutability')).to eql('writeOnly')
330
+ end
331
+
332
+ it 'handles conflicts via reality-wins' do
333
+ license_model = Class.new(license_model_base()) do
334
+ def self.scim_mutable_attributes; [:licence_expired]; end
335
+ def self.scim_queryable_attributes; nil; end
336
+ def self.scim_attributes_map
337
+ { licenseNumber: :license_number, licenseExpired: :licence_expired }
338
+ end
339
+
340
+ include Scimitar::Resources::Mixin
341
+ end
342
+
343
+ Scimitar.engine_configuration.schema_list_from_attribute_mappings = [license_model]
344
+
345
+ get :index, params: { format: :scim }
346
+ expect(response).to be_ok
347
+
348
+ parsed_body = JSON.parse(response.body)
349
+ attributes = parsed_body.dig('Resources', 0, 'attributes')
350
+
351
+ expect(parsed_body.dig('Resources', 0, 'name')).to eql('License')
352
+ expect(attributes.size).to eql(2)
353
+
354
+ number_attr = attributes.find { | a | a['name'] == 'licenseNumber' }
355
+ expiry_attr = attributes.find { | a | a['name'] == 'licenseExpired' }
356
+
357
+ # Number attribute - no reader or writer, so code has to shrug and
358
+ # say "it's broken, so I'll quote the schema verbatim'.
359
+ #
360
+ # Expiry attribute - is read-only in schema, but we declare it as a
361
+ # writable attribute and provide no reader. This clashes badly; the
362
+ # schema read-only declaration is ignored in favour of reality.
363
+ #
364
+ expect(number_attr['mutability']).to eql('readWrite')
365
+ expect(expiry_attr['mutability']).to eql('writeOnly')
366
+ end
367
+ end # "context 'with mutability overridden' do"
368
+ end # "context 'with custom resource types' do"
369
+ end # "context 'with engine configuration of schema_list_from_attribute_mappings: true' do"
370
+ end # "context '#index' do
371
+ end
@@ -347,6 +347,11 @@ RSpec.describe Scimitar::Lists::QueryParser do
347
347
  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
348
  end
349
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
+
350
355
  # https://github.com/RIPAGlobal/scimitar/issues/116
351
356
  #
352
357
  context 'with schema IDs (GitHub issue #116)' 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
- 'custom-id'
7
+ 'urn:ietf:params:scim:schemas:custom-id'
8
8
  end
9
9
 
10
10
  def self.scim_attributes
@@ -125,7 +125,7 @@ RSpec.describe Scimitar::Resources::Base do
125
125
 
126
126
  result = resource.as_json
127
127
 
128
- expect(result['schemas'] ).to eql(['custom-id'])
128
+ expect(result['schemas'] ).to eql(['urn:ietf:params:scim:schemas:custom-id'])
129
129
  expect(result['meta']['resourceType']).to eql('CustomResourse')
130
130
  expect(result['errors'] ).to be_nil
131
131
  end
@@ -144,7 +144,7 @@ RSpec.describe Scimitar::Resources::Base do
144
144
 
145
145
  result = resource.as_json
146
146
 
147
- expect(result['schemas'] ).to eql(['custom-id'])
147
+ expect(result['schemas'] ).to eql(['urn:ietf:params:scim:schemas:custom-id'])
148
148
  expect(result['meta']['resourceType']).to eql('CustomResourse')
149
149
  expect(result['errors'] ).to be_nil
150
150
  expect(result['name'] ).to be_present
@@ -295,7 +295,7 @@ RSpec.describe Scimitar::Resources::Base do
295
295
  context 'of custom schema' do
296
296
  ThirdCustomSchema = Class.new(Scimitar::Schema::Base) do
297
297
  def self.id
298
- 'custom-id'
298
+ 'urn:ietf:params:scim:schemas:custom-id'
299
299
  end
300
300
 
301
301
  def self.scim_attributes
@@ -305,7 +305,7 @@ RSpec.describe Scimitar::Resources::Base do
305
305
 
306
306
  ExtensionSchema = Class.new(Scimitar::Schema::Base) do
307
307
  def self.id
308
- 'extension-id'
308
+ 'urn:ietf:params:scim:schemas:extension'
309
309
  end
310
310
 
311
311
  def self.scim_attributes
@@ -333,13 +333,13 @@ RSpec.describe Scimitar::Resources::Base do
333
333
 
334
334
  context '#initialize' do
335
335
  it 'allows setting extension attributes' do
336
- resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
336
+ resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {relationship: 'GAGA'})
337
337
  expect(resource.relationship).to eql('GAGA')
338
338
  end
339
339
 
340
340
  it 'allows setting complex extension attributes' do
341
341
  user_groups = [{ value: '123' }, { value: '456'}]
342
- resource = resource_class.new('extension-id' => {userGroups: user_groups})
342
+ resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {userGroups: user_groups})
343
343
  expect(resource.userGroups.map(&:value)).to eql(['123', '456'])
344
344
  end
345
345
  end # "context '#initialize' do"
@@ -348,8 +348,8 @@ RSpec.describe Scimitar::Resources::Base do
348
348
  it 'namespaces the extension attributes' do
349
349
  resource = resource_class.new(relationship: 'GAGA')
350
350
  hash = resource.as_json
351
- expect(hash["schemas"]).to eql(['custom-id', 'extension-id'])
352
- expect(hash["extension-id"]).to eql("relationship" => 'GAGA')
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
353
  end
354
354
  end # "context '#as_json' do"
355
355
 
@@ -362,10 +362,10 @@ RSpec.describe Scimitar::Resources::Base do
362
362
 
363
363
  context 'validation' do
364
364
  it 'validates into custom schema' do
365
- resource = resource_class.new('extension-id' => {})
365
+ resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {})
366
366
  expect(resource.valid?).to eql(false)
367
367
 
368
- resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
368
+ resource = resource_class.new('urn:ietf:params:scim:schemas:extension' => {relationship: 'GAGA'})
369
369
  expect(resource.relationship).to eql('GAGA')
370
370
  expect(resource.valid?).to eql(true)
371
371
  end
@@ -4,7 +4,7 @@ RSpec.describe Scimitar::Resources::Base do
4
4
  context '#valid?' do
5
5
  MyCustomSchema = Class.new(Scimitar::Schema::Base) do
6
6
  def self.id
7
- 'custom-id'
7
+ 'urn:ietf:params:scim:schemas:custom-id'
8
8
  end
9
9
 
10
10
  class NameWithRequirementsSchema < Scimitar::Schema::Base
@@ -288,10 +288,15 @@ RSpec.describe Scimitar::Resources::Mixin do
288
288
  'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
289
289
  'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
290
290
  'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
291
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
291
+ 'schemas' => [
292
+ 'urn:ietf:params:scim:schemas:core:2.0:User',
293
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User',
294
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User',
295
+ ],
292
296
  'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
293
297
  'organization' => 'SOMEORG',
294
298
  },
299
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User' => {},
295
300
  })
296
301
  end
297
302
  end # "context 'with list of requested attributes' do"
@@ -333,13 +338,19 @@ RSpec.describe Scimitar::Resources::Mixin do
333
338
  'externalId' => 'AA02984',
334
339
  'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
335
340
  'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
336
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
337
-
341
+ 'schemas' => [
342
+ 'urn:ietf:params:scim:schemas:core:2.0:User',
343
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User',
344
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User',
345
+ ],
338
346
  'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
339
347
  'organization' => 'SOMEORG',
340
348
  'department' => nil,
341
- 'primaryEmail' => instance.work_email_address
342
- }
349
+ 'primaryEmail' => instance.work_email_address,
350
+ },
351
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User' => {
352
+ 'manager' => nil
353
+ },
343
354
  })
344
355
  end
345
356
  end # "context 'with a UUID, renamed primary key column' do"
@@ -463,9 +474,13 @@ RSpec.describe Scimitar::Resources::Mixin do
463
474
  ],
464
475
 
465
476
  'meta' => {'location'=>'https://test.com/static_map_test', 'resourceType'=>'User'},
466
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
467
-
468
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
477
+ 'schemas' => [
478
+ 'urn:ietf:params:scim:schemas:core:2.0:User',
479
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User',
480
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User',
481
+ ],
482
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {},
483
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User' => {},
469
484
  })
470
485
  end
471
486
  end # "context 'using static mappings' do"
@@ -492,9 +507,13 @@ RSpec.describe Scimitar::Resources::Mixin do
492
507
  ],
493
508
 
494
509
  'meta' => {'location'=>'https://test.com/dynamic_map_test', 'resourceType'=>'User'},
495
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
496
-
497
- 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {}
510
+ 'schemas' => [
511
+ 'urn:ietf:params:scim:schemas:core:2.0:User',
512
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User',
513
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User',
514
+ ],
515
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {},
516
+ 'urn:ietf:params:scim:schemas:extension:manager:1.0:User' => {},
498
517
  })
499
518
  end
500
519
  end # "context 'using dynamic lists' do"
@@ -536,7 +555,7 @@ RSpec.describe Scimitar::Resources::Mixin do
536
555
 
537
556
  expect do
538
557
  scim = instance.to_scim(location: 'https://test.com/static_map_test')
539
- end.to raise_error(RuntimeError) { |e| expect(e.message).to include('Array contains someting other than mapping Hash(es)') }
558
+ end.to raise_error(RuntimeError) { |e| expect(e.message).to include('Array contains something other than mapping Hash(es)') }
540
559
  end
541
560
 
542
561
  it 'complains about bad Hash entries in mapping Arrays' do