scimitar 1.4.0 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/scimitar/application_controller.rb +2 -2
- data/app/models/scimitar/resources/mixin.rb +108 -3
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/app/models/mock_user.rb +9 -1
- data/spec/apps/dummy/config/initializers/scimitar.rb +37 -0
- data/spec/apps/dummy/config/routes.rb +1 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +8 -0
- data/spec/apps/dummy/db/schema.rb +2 -0
- data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -2
- data/spec/models/scimitar/resources/base_spec.rb +161 -66
- data/spec/models/scimitar/resources/mixin_spec.rb +673 -5
- data/spec/requests/active_record_backed_resources_controller_spec.rb +136 -0
- data/spec/requests/application_controller_spec.rb +10 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b55b1f0a0a700b57690cb05fc02241cf47f60a4ad8c92946a32366c90ee56d8c
|
4
|
+
data.tar.gz: a7143dcd7d1381250e66529c70288659a2018fb26fc16639c14ba796acc805d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb740b528770a918fc3237cabdc953affcde23fa7787cc9e9383a6d71b104e2d6325c2829545d367983a3bb64435179a0523b6d785d8e6d3eb7b2bfdb6c82748
|
7
|
+
data.tar.gz: 9f73bcafefbd7de57f258d1dc1c18e020b8dd7bca6330f7b288e308168daf7cb698051a9f2d4eaee9717139978df4e7a9fe854ba269ab94b2b46f1030d2ab9db
|
@@ -99,10 +99,10 @@ module Scimitar
|
|
99
99
|
def require_scim
|
100
100
|
scim_mime_type = Mime::Type.lookup_by_extension(:scim).to_s
|
101
101
|
|
102
|
-
if request.
|
102
|
+
if request.media_type.nil? || request.media_type.empty?
|
103
103
|
request.format = :scim
|
104
104
|
request.headers['CONTENT_TYPE'] = scim_mime_type
|
105
|
-
elsif request.
|
105
|
+
elsif request.media_type.downcase == scim_mime_type
|
106
106
|
request.format = :scim
|
107
107
|
elsif request.format == :scim
|
108
108
|
request.headers['CONTENT_TYPE'] = scim_mime_type
|
@@ -443,9 +443,30 @@ module Scimitar
|
|
443
443
|
ci_scim_hash = { 'root' => ci_scim_hash }.with_indifferent_case_insensitive_access()
|
444
444
|
end
|
445
445
|
|
446
|
+
# Handle extension schema. Contributed by @bettysteger and
|
447
|
+
# @MorrisFreeman via:
|
448
|
+
#
|
449
|
+
# https://github.com/RIPAGlobal/scimitar/issues/48
|
450
|
+
# https://github.com/RIPAGlobal/scimitar/pull/49
|
451
|
+
#
|
452
|
+
# Note the ":" separating the schema ID (URN) from the attribute.
|
453
|
+
# The nature of JSON rendering / other payloads might lead you to
|
454
|
+
# expect a "." as with any complex types, but that's not the case;
|
455
|
+
# see https://tools.ietf.org/html/rfc7644#section-3.10, or
|
456
|
+
# https://tools.ietf.org/html/rfc7644#section-3.5.2 of which in
|
457
|
+
# particular, https://tools.ietf.org/html/rfc7644#page-35.
|
458
|
+
#
|
459
|
+
paths = []
|
460
|
+
self.class.scim_resource_type.extended_schemas.each do |schema|
|
461
|
+
path_str.downcase.split(schema.id.downcase + ':').drop(1).each do |path|
|
462
|
+
paths += [schema.id] + path.split('.')
|
463
|
+
end
|
464
|
+
end
|
465
|
+
paths = path_str.split('.') if paths.empty?
|
466
|
+
|
446
467
|
self.from_patch_backend!(
|
447
468
|
nature: nature,
|
448
|
-
path:
|
469
|
+
path: paths,
|
449
470
|
value: value,
|
450
471
|
altering_hash: ci_scim_hash
|
451
472
|
)
|
@@ -616,7 +637,19 @@ module Scimitar
|
|
616
637
|
attrs_map_or_leaf_value.each do | scim_attribute, sub_attrs_map_or_leaf_value |
|
617
638
|
next if scim_attribute&.to_s&.downcase == 'id' && path.empty?
|
618
639
|
|
619
|
-
|
640
|
+
# Handle extension schema. Contributed by @bettysteger and
|
641
|
+
# @MorrisFreeman via:
|
642
|
+
#
|
643
|
+
# https://github.com/RIPAGlobal/scimitar/issues/48
|
644
|
+
# https://github.com/RIPAGlobal/scimitar/pull/49
|
645
|
+
#
|
646
|
+
attribute_tree = []
|
647
|
+
resource_class.extended_schemas.each do |schema|
|
648
|
+
attribute_tree << schema.id and break if schema.scim_attributes.any? { |attribute| attribute.name == scim_attribute.to_s }
|
649
|
+
end
|
650
|
+
attribute_tree << scim_attribute.to_s
|
651
|
+
|
652
|
+
sub_scim_hash_or_leaf_value = scim_hash_or_leaf_value&.dig(*attribute_tree)
|
620
653
|
|
621
654
|
self.from_scim_backend!(
|
622
655
|
attrs_map_or_leaf_value: sub_attrs_map_or_leaf_value,
|
@@ -901,14 +934,86 @@ module Scimitar
|
|
901
934
|
else
|
902
935
|
altering_hash[path_component] = value
|
903
936
|
end
|
937
|
+
|
904
938
|
when 'replace'
|
905
939
|
if path_component == 'root'
|
906
940
|
altering_hash[path_component].merge!(value)
|
907
941
|
else
|
908
942
|
altering_hash[path_component] = value
|
909
943
|
end
|
944
|
+
|
945
|
+
# The array check handles payloads seen from e.g. Microsoft for
|
946
|
+
# remove-user-from-group, where contrary to examples in the RFC
|
947
|
+
# which would imply "payload removes all users", there is the
|
948
|
+
# clear intent to remove just one.
|
949
|
+
#
|
950
|
+
# https://tools.ietf.org/html/rfc7644#section-3.5.2.2
|
951
|
+
# https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
|
952
|
+
#
|
953
|
+
# Since remove-all in the face of remove-one is destructive, we
|
954
|
+
# do a special check here to see if there's an array value for
|
955
|
+
# the array path that the payload yielded. If so, we can match
|
956
|
+
# each value against array items and remove just those items.
|
957
|
+
#
|
958
|
+
# There is an additional special case to handle a bad example
|
959
|
+
# from Salesforce:
|
960
|
+
#
|
961
|
+
# https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
|
962
|
+
#
|
910
963
|
when 'remove'
|
911
|
-
altering_hash.
|
964
|
+
if altering_hash[path_component].is_a?(Array) && value.present?
|
965
|
+
|
966
|
+
# Handle bad Salesforce example. That might be simply a
|
967
|
+
# documentation error, but just in case...
|
968
|
+
#
|
969
|
+
value = value.values.first if (
|
970
|
+
path_component&.downcase == 'members' &&
|
971
|
+
value.is_a?(Hash) &&
|
972
|
+
value.keys.size == 1 &&
|
973
|
+
value.keys.first&.downcase == 'members'
|
974
|
+
)
|
975
|
+
|
976
|
+
# The Microsoft example provides an array of values, but we
|
977
|
+
# may as well cope with a value specified 'flat'. Promote
|
978
|
+
# such a thing to an Array to simplify the following code.
|
979
|
+
#
|
980
|
+
value = [value] unless value.is_a?(Array)
|
981
|
+
|
982
|
+
# For each value item, delete matching array entries. The
|
983
|
+
# concept of "matching" is:
|
984
|
+
#
|
985
|
+
# * For simple non-Hash values (if possible) just delete on
|
986
|
+
# an exact match
|
987
|
+
#
|
988
|
+
# * For Hash-based values, only delete if all 'patch' keys
|
989
|
+
# are present in the resource and all values thus match.
|
990
|
+
#
|
991
|
+
# Special case to ignore '$ref' from the Microsoft payload.
|
992
|
+
#
|
993
|
+
# Note coercion to strings to account for SCIM vs the usual
|
994
|
+
# tricky case of underlying implementations with (say)
|
995
|
+
# integer primary keys, which all end up as strings anyway.
|
996
|
+
#
|
997
|
+
value.each do | value_item |
|
998
|
+
altering_hash[path_component].delete_if do | item |
|
999
|
+
if item.is_a?(Hash) && value_item.is_a?(Hash)
|
1000
|
+
matched_all = true
|
1001
|
+
value_item.each do | value_key, value_value |
|
1002
|
+
next if value_key == '$ref'
|
1003
|
+
if ! item.key?(value_key) || item[value_key]&.to_s != value_value&.to_s
|
1004
|
+
matched_all = false
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
matched_all
|
1008
|
+
else
|
1009
|
+
item&.to_s == value_item&.to_s
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
else
|
1014
|
+
altering_hash.delete(path_component)
|
1015
|
+
end
|
1016
|
+
|
912
1017
|
end
|
913
1018
|
end
|
914
1019
|
end
|
data/lib/scimitar/version.rb
CHANGED
@@ -3,11 +3,11 @@ module Scimitar
|
|
3
3
|
# Gem version. If this changes, be sure to re-run "bundle install" or
|
4
4
|
# "bundle update".
|
5
5
|
#
|
6
|
-
VERSION = '1.
|
6
|
+
VERSION = '1.5.2'
|
7
7
|
|
8
8
|
# Date for VERSION. If this changes, be sure to re-run "bundle install"
|
9
9
|
# or "bundle update".
|
10
10
|
#
|
11
|
-
DATE = '2023-
|
11
|
+
DATE = '2023-03-21'
|
12
12
|
|
13
13
|
end
|
@@ -13,6 +13,8 @@ class MockUser < ActiveRecord::Base
|
|
13
13
|
work_email_address
|
14
14
|
home_email_address
|
15
15
|
work_phone_number
|
16
|
+
organization
|
17
|
+
department
|
16
18
|
}
|
17
19
|
|
18
20
|
has_and_belongs_to_many :mock_groups
|
@@ -82,7 +84,13 @@ class MockUser < ActiveRecord::Base
|
|
82
84
|
}
|
83
85
|
}
|
84
86
|
],
|
85
|
-
active: :is_active
|
87
|
+
active: :is_active,
|
88
|
+
|
89
|
+
# Custom extension schema - see configuration in
|
90
|
+
# "spec/apps/dummy/config/initializers/scimitar.rb".
|
91
|
+
#
|
92
|
+
organization: :organization,
|
93
|
+
department: :department
|
86
94
|
}
|
87
95
|
end
|
88
96
|
|
@@ -1,5 +1,14 @@
|
|
1
1
|
# Test app configuration.
|
2
2
|
#
|
3
|
+
# Note that as a result of https://github.com/RIPAGlobal/scimitar/issues/48,
|
4
|
+
# tests include a custom extension of the core User schema. A shortcoming of
|
5
|
+
# some of the code from which Scimitar was originally built is that those
|
6
|
+
# extensions are done with class-level ivars, so it is largely impossible (or
|
7
|
+
# at least, impractical in tests) to avoid polluting the core class itself
|
8
|
+
# with the extension.
|
9
|
+
#
|
10
|
+
# All related schema tests are written with this in mind.
|
11
|
+
#
|
3
12
|
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
4
13
|
|
5
14
|
application_controller_mixin: Module.new do
|
@@ -12,3 +21,31 @@ Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
|
12
21
|
end
|
13
22
|
|
14
23
|
})
|
24
|
+
|
25
|
+
module ScimSchemaExtensions
|
26
|
+
module User
|
27
|
+
class Enterprise < Scimitar::Schema::Base
|
28
|
+
def initialize(options = {})
|
29
|
+
super(
|
30
|
+
name: 'ExtendedUser',
|
31
|
+
description: 'Enterprise extension for a User',
|
32
|
+
id: self.class.id,
|
33
|
+
scim_attributes: self.class.scim_attributes
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.id
|
38
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.scim_attributes
|
42
|
+
[
|
43
|
+
Scimitar::Schema::Attribute.new(name: 'organization', type: 'string'),
|
44
|
+
Scimitar::Schema::Attribute.new(name: 'department', type: 'string')
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Scimitar::Resources::User.extend_schema ScimSchemaExtensions::User::Enterprise
|
@@ -3,6 +3,8 @@ class CreateMockUsers < ActiveRecord::Migration[6.1]
|
|
3
3
|
create_table :mock_users, id: :uuid, primary_key: :primary_key do |t|
|
4
4
|
t.timestamps
|
5
5
|
|
6
|
+
# Support part of the core schema
|
7
|
+
#
|
6
8
|
t.text :scim_uid
|
7
9
|
t.text :username
|
8
10
|
t.text :first_name
|
@@ -10,6 +12,12 @@ class CreateMockUsers < ActiveRecord::Migration[6.1]
|
|
10
12
|
t.text :work_email_address
|
11
13
|
t.text :home_email_address
|
12
14
|
t.text :work_phone_number
|
15
|
+
|
16
|
+
# Support the custom extension schema - see configuration in
|
17
|
+
# "spec/apps/dummy/config/initializers/scimitar.rb".
|
18
|
+
#
|
19
|
+
t.text :organization
|
20
|
+
t.text :department
|
13
21
|
end
|
14
22
|
end
|
15
23
|
end
|
@@ -39,6 +39,8 @@ ActiveRecord::Schema.define(version: 2021_03_08_044214) do
|
|
39
39
|
t.text "work_email_address"
|
40
40
|
t.text "home_email_address"
|
41
41
|
t.text "work_phone_number"
|
42
|
+
t.text "organization"
|
43
|
+
t.text "department"
|
42
44
|
end
|
43
45
|
|
44
46
|
add_foreign_key "mock_groups_users", "mock_groups"
|
@@ -14,9 +14,9 @@ RSpec.describe Scimitar::SchemasController do
|
|
14
14
|
get :index, params: { format: :scim }
|
15
15
|
expect(response).to be_ok
|
16
16
|
parsed_body = JSON.parse(response.body)
|
17
|
-
expect(parsed_body.length).to eql(
|
17
|
+
expect(parsed_body.length).to eql(3)
|
18
18
|
schema_names = parsed_body.map {|schema| schema['name']}
|
19
|
-
expect(schema_names).to match_array(['User', 'Group'])
|
19
|
+
expect(schema_names).to match_array(['User', 'ExtendedUser', 'Group'])
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'returns only the User schema when its id is provided' do
|
@@ -250,90 +250,185 @@ RSpec.describe Scimitar::Resources::Base do
|
|
250
250
|
end # "context 'dynamic setters based on schema' do"
|
251
251
|
|
252
252
|
context 'schema extension' do
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
253
|
+
context 'of custom schema' do
|
254
|
+
ThirdCustomSchema = Class.new(Scimitar::Schema::Base) do
|
255
|
+
def self.id
|
256
|
+
'custom-id'
|
257
|
+
end
|
257
258
|
|
258
|
-
|
259
|
-
|
259
|
+
def self.scim_attributes
|
260
|
+
[ Scimitar::Schema::Attribute.new(name: 'name', type: 'string') ]
|
261
|
+
end
|
260
262
|
end
|
261
|
-
end
|
262
263
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
264
|
+
ExtensionSchema = Class.new(Scimitar::Schema::Base) do
|
265
|
+
def self.id
|
266
|
+
'extension-id'
|
267
|
+
end
|
267
268
|
|
268
|
-
|
269
|
-
|
269
|
+
def self.scim_attributes
|
270
|
+
[ Scimitar::Schema::Attribute.new(name: 'relationship', type: 'string', required: true) ]
|
271
|
+
end
|
270
272
|
end
|
271
|
-
end
|
272
273
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
274
|
+
let(:resource_class) {
|
275
|
+
Class.new(Scimitar::Resources::Base) do
|
276
|
+
set_schema ThirdCustomSchema
|
277
|
+
extend_schema ExtensionSchema
|
277
278
|
|
278
|
-
|
279
|
-
|
279
|
+
def self.endpoint
|
280
|
+
'/gaga'
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.resource_type_id
|
284
|
+
'CustomResource'
|
285
|
+
end
|
280
286
|
end
|
287
|
+
}
|
281
288
|
|
282
|
-
|
283
|
-
|
289
|
+
context '#initialize' do
|
290
|
+
it 'allows setting extension attributes' do
|
291
|
+
resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
|
292
|
+
expect(resource.relationship).to eql('GAGA')
|
284
293
|
end
|
285
|
-
end
|
286
|
-
|
294
|
+
end # "context '#initialize' do"
|
295
|
+
|
296
|
+
context '#as_json' do
|
297
|
+
it 'namespaces the extension attributes' do
|
298
|
+
resource = resource_class.new(relationship: 'GAGA')
|
299
|
+
hash = resource.as_json
|
300
|
+
expect(hash["schemas"]).to eql(['custom-id', 'extension-id'])
|
301
|
+
expect(hash["extension-id"]).to eql("relationship" => 'GAGA')
|
302
|
+
end
|
303
|
+
end # "context '#as_json' do"
|
287
304
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
305
|
+
context '.resource_type' do
|
306
|
+
it 'appends the extension schemas' do
|
307
|
+
resource_type = resource_class.resource_type('http://gaga')
|
308
|
+
expect(resource_type.meta.location).to eql('http://gaga')
|
309
|
+
expect(resource_type.schemaExtensions.count).to eql(1)
|
310
|
+
end
|
294
311
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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"
|
312
|
+
context 'validation' do
|
313
|
+
it 'validates into custom schema' do
|
314
|
+
resource = resource_class.new('extension-id' => {})
|
315
|
+
expect(resource.valid?).to eql(false)
|
303
316
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
end
|
317
|
+
resource = resource_class.new('extension-id' => {relationship: 'GAGA'})
|
318
|
+
expect(resource.relationship).to eql('GAGA')
|
319
|
+
expect(resource.valid?).to eql(true)
|
320
|
+
end
|
321
|
+
end # context 'validation'
|
322
|
+
end # "context '.resource_type' do"
|
310
323
|
|
311
|
-
context '
|
312
|
-
it '
|
313
|
-
|
314
|
-
expect(
|
324
|
+
context '.find_attribute' do
|
325
|
+
it 'finds in first schema' do
|
326
|
+
found = resource_class().find_attribute('name') # Defined in ThirdCustomSchema
|
327
|
+
expect(found).to be_present
|
328
|
+
expect(found.name).to eql('name')
|
329
|
+
expect(found.type).to eql('string')
|
330
|
+
end
|
315
331
|
|
316
|
-
|
317
|
-
|
318
|
-
expect(
|
332
|
+
it 'finds across schemas' do
|
333
|
+
found = resource_class().find_attribute('relationship') # Defined in ExtensionSchema
|
334
|
+
expect(found).to be_present
|
335
|
+
expect(found.name).to eql('relationship')
|
336
|
+
expect(found.type).to eql('string')
|
319
337
|
end
|
320
|
-
end # context '
|
321
|
-
end # "context '
|
338
|
+
end # "context '.find_attribute' do"
|
339
|
+
end # "context 'of custom schema' do"
|
322
340
|
|
323
|
-
context '
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
expect(found.type).to eql('string')
|
329
|
-
end
|
341
|
+
context 'of core schema' do
|
342
|
+
EnterpriseExtensionSchema = Class.new(Scimitar::Schema::Base) do
|
343
|
+
def self.id
|
344
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
345
|
+
end
|
330
346
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
347
|
+
def self.scim_attributes
|
348
|
+
[
|
349
|
+
Scimitar::Schema::Attribute.new(name: 'organization', type: 'string'),
|
350
|
+
Scimitar::Schema::Attribute.new(name: 'department', type: 'string')
|
351
|
+
]
|
352
|
+
end
|
336
353
|
end
|
337
|
-
|
354
|
+
|
355
|
+
let(:resource_class) {
|
356
|
+
Class.new(Scimitar::Resources::Base) do
|
357
|
+
set_schema Scimitar::Schema::User
|
358
|
+
extend_schema EnterpriseExtensionSchema
|
359
|
+
|
360
|
+
def self.endpoint
|
361
|
+
'/Users'
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.resource_type_id
|
365
|
+
'User'
|
366
|
+
end
|
367
|
+
end
|
368
|
+
}
|
369
|
+
|
370
|
+
context '#initialize' do
|
371
|
+
it 'allows setting extension attributes' do
|
372
|
+
resource = resource_class.new('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {organization: 'SOMEORG', department: 'SOMEDPT'})
|
373
|
+
|
374
|
+
expect(resource.organization).to eql('SOMEORG')
|
375
|
+
expect(resource.department ).to eql('SOMEDPT')
|
376
|
+
end
|
377
|
+
end # "context '#initialize' do"
|
378
|
+
|
379
|
+
context '#as_json' do
|
380
|
+
it 'namespaces the extension attributes' do
|
381
|
+
resource = resource_class.new(organization: 'SOMEORG', department: 'SOMEDPT')
|
382
|
+
hash = resource.as_json
|
383
|
+
|
384
|
+
expect(hash['schemas']).to eql(['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'])
|
385
|
+
expect(hash['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']).to eql('organization' => 'SOMEORG', 'department' => 'SOMEDPT')
|
386
|
+
end
|
387
|
+
end # "context '#as_json' do"
|
388
|
+
|
389
|
+
context '.resource_type' do
|
390
|
+
it 'appends the extension schemas' do
|
391
|
+
resource_type = resource_class.resource_type('http://example.com')
|
392
|
+
expect(resource_type.meta.location).to eql('http://example.com')
|
393
|
+
expect(resource_type.schemaExtensions.count).to eql(1)
|
394
|
+
end
|
395
|
+
|
396
|
+
context 'validation' do
|
397
|
+
it 'validates into custom schema' do
|
398
|
+
resource = resource_class.new('urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {})
|
399
|
+
expect(resource.valid?).to eql(false)
|
400
|
+
|
401
|
+
resource = resource_class.new(
|
402
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => {
|
403
|
+
userName: 'SOMEUSR',
|
404
|
+
organization: 'SOMEORG',
|
405
|
+
department: 'SOMEDPT'
|
406
|
+
}
|
407
|
+
)
|
408
|
+
|
409
|
+
expect(resource.organization).to eql('SOMEORG')
|
410
|
+
expect(resource.department ).to eql('SOMEDPT')
|
411
|
+
expect(resource.valid? ).to eql(true)
|
412
|
+
end
|
413
|
+
end # context 'validation'
|
414
|
+
end # "context '.resource_type' do"
|
415
|
+
|
416
|
+
context '.find_attribute' do
|
417
|
+
it 'finds in first schema' do
|
418
|
+
found = resource_class().find_attribute('userName') # Defined in Scimitar::Schema::User
|
419
|
+
|
420
|
+
expect(found).to be_present
|
421
|
+
expect(found.name).to eql('userName')
|
422
|
+
expect(found.type).to eql('string')
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'finds across schemas' do
|
426
|
+
found = resource_class().find_attribute('organization') # Defined in EnterpriseExtensionSchema
|
427
|
+
expect(found).to be_present
|
428
|
+
expect(found.name).to eql('organization')
|
429
|
+
expect(found.type).to eql('string')
|
430
|
+
end
|
431
|
+
end # "context '.find_attribute' do"
|
432
|
+
end # "context 'of core schema' do"
|
338
433
|
end # "context 'schema extension' do"
|
339
434
|
end
|