scimitar 1.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 +7 -0
- data/Rakefile +16 -0
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +180 -0
- data/app/controllers/scimitar/application_controller.rb +129 -0
- data/app/controllers/scimitar/resource_types_controller.rb +28 -0
- data/app/controllers/scimitar/resources_controller.rb +203 -0
- data/app/controllers/scimitar/schemas_controller.rb +16 -0
- data/app/controllers/scimitar/service_provider_configurations_controller.rb +8 -0
- data/app/models/scimitar/authentication_error.rb +9 -0
- data/app/models/scimitar/authentication_scheme.rb +18 -0
- data/app/models/scimitar/bulk.rb +8 -0
- data/app/models/scimitar/complex_types/address.rb +18 -0
- data/app/models/scimitar/complex_types/base.rb +41 -0
- data/app/models/scimitar/complex_types/email.rb +12 -0
- data/app/models/scimitar/complex_types/entitlement.rb +12 -0
- data/app/models/scimitar/complex_types/ims.rb +12 -0
- data/app/models/scimitar/complex_types/name.rb +12 -0
- data/app/models/scimitar/complex_types/phone_number.rb +12 -0
- data/app/models/scimitar/complex_types/photo.rb +12 -0
- data/app/models/scimitar/complex_types/reference_group.rb +12 -0
- data/app/models/scimitar/complex_types/reference_member.rb +12 -0
- data/app/models/scimitar/complex_types/role.rb +12 -0
- data/app/models/scimitar/complex_types/x509_certificate.rb +12 -0
- data/app/models/scimitar/engine_configuration.rb +24 -0
- data/app/models/scimitar/error_response.rb +20 -0
- data/app/models/scimitar/errors.rb +14 -0
- data/app/models/scimitar/filter.rb +11 -0
- data/app/models/scimitar/filter_error.rb +22 -0
- data/app/models/scimitar/invalid_syntax_error.rb +9 -0
- data/app/models/scimitar/lists/count.rb +64 -0
- data/app/models/scimitar/lists/query_parser.rb +730 -0
- data/app/models/scimitar/meta.rb +7 -0
- data/app/models/scimitar/not_found_error.rb +10 -0
- data/app/models/scimitar/resource_invalid_error.rb +9 -0
- data/app/models/scimitar/resource_type.rb +29 -0
- data/app/models/scimitar/resources/base.rb +159 -0
- data/app/models/scimitar/resources/group.rb +13 -0
- data/app/models/scimitar/resources/mixin.rb +964 -0
- data/app/models/scimitar/resources/user.rb +13 -0
- data/app/models/scimitar/schema/address.rb +24 -0
- data/app/models/scimitar/schema/attribute.rb +123 -0
- data/app/models/scimitar/schema/base.rb +86 -0
- data/app/models/scimitar/schema/derived_attributes.rb +24 -0
- data/app/models/scimitar/schema/email.rb +10 -0
- data/app/models/scimitar/schema/entitlement.rb +10 -0
- data/app/models/scimitar/schema/group.rb +27 -0
- data/app/models/scimitar/schema/ims.rb +10 -0
- data/app/models/scimitar/schema/name.rb +20 -0
- data/app/models/scimitar/schema/phone_number.rb +10 -0
- data/app/models/scimitar/schema/photo.rb +10 -0
- data/app/models/scimitar/schema/reference_group.rb +23 -0
- data/app/models/scimitar/schema/reference_member.rb +21 -0
- data/app/models/scimitar/schema/role.rb +10 -0
- data/app/models/scimitar/schema/user.rb +52 -0
- data/app/models/scimitar/schema/vdtp.rb +18 -0
- data/app/models/scimitar/schema/x509_certificate.rb +22 -0
- data/app/models/scimitar/service_provider_configuration.rb +49 -0
- data/app/models/scimitar/supportable.rb +14 -0
- data/app/views/layouts/scimitar/application.html.erb +14 -0
- data/config/initializers/scimitar.rb +82 -0
- data/config/routes.rb +6 -0
- data/lib/scimitar.rb +23 -0
- data/lib/scimitar/engine.rb +63 -0
- data/lib/scimitar/version.rb +13 -0
- data/spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb +24 -0
- data/spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb +30 -0
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +13 -0
- data/spec/apps/dummy/app/controllers/mock_users_controller.rb +13 -0
- data/spec/apps/dummy/app/models/mock_group.rb +83 -0
- data/spec/apps/dummy/app/models/mock_user.rb +104 -0
- data/spec/apps/dummy/config/application.rb +17 -0
- data/spec/apps/dummy/config/boot.rb +2 -0
- data/spec/apps/dummy/config/environment.rb +2 -0
- data/spec/apps/dummy/config/environments/test.rb +15 -0
- data/spec/apps/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/apps/dummy/config/initializers/scimitar.rb +14 -0
- data/spec/apps/dummy/config/initializers/session_store.rb +3 -0
- data/spec/apps/dummy/config/routes.rb +24 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +15 -0
- data/spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb +10 -0
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +8 -0
- data/spec/apps/dummy/db/schema.rb +42 -0
- data/spec/controllers/scimitar/application_controller_spec.rb +173 -0
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +94 -0
- data/spec/controllers/scimitar/resources_controller_spec.rb +247 -0
- data/spec/controllers/scimitar/schemas_controller_spec.rb +75 -0
- data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +22 -0
- data/spec/models/scimitar/complex_types/address_spec.rb +19 -0
- data/spec/models/scimitar/complex_types/email_spec.rb +23 -0
- data/spec/models/scimitar/lists/count_spec.rb +147 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +763 -0
- data/spec/models/scimitar/resource_type_spec.rb +21 -0
- data/spec/models/scimitar/resources/base_spec.rb +289 -0
- data/spec/models/scimitar/resources/base_validation_spec.rb +61 -0
- data/spec/models/scimitar/resources/mixin_spec.rb +2127 -0
- data/spec/models/scimitar/resources/user_spec.rb +55 -0
- data/spec/models/scimitar/schema/attribute_spec.rb +80 -0
- data/spec/models/scimitar/schema/base_spec.rb +64 -0
- data/spec/models/scimitar/schema/group_spec.rb +87 -0
- data/spec/models/scimitar/schema/user_spec.rb +710 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +569 -0
- data/spec/requests/application_controller_spec.rb +49 -0
- data/spec/requests/controller_configuration_spec.rb +17 -0
- data/spec/requests/engine_spec.rb +20 -0
- data/spec/spec_helper.rb +66 -0
- metadata +315 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
|
|
4
|
+
# Represents the schema for the Address complex type.
|
|
5
|
+
#
|
|
6
|
+
# See also Scimitar::ComplexTypes::Address
|
|
7
|
+
#
|
|
8
|
+
class Address < Base
|
|
9
|
+
|
|
10
|
+
def self.scim_attributes
|
|
11
|
+
@scim_attributes ||= [
|
|
12
|
+
Attribute.new(name: 'type', type: 'string'),
|
|
13
|
+
Attribute.new(name: 'formatted', type: 'string'),
|
|
14
|
+
Attribute.new(name: 'streetAddress', type: 'string'),
|
|
15
|
+
Attribute.new(name: 'locality', type: 'string'),
|
|
16
|
+
Attribute.new(name: 'region', type: 'string'),
|
|
17
|
+
Attribute.new(name: 'postalCode', type: 'string'),
|
|
18
|
+
Attribute.new(name: 'country', type: 'string'),
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
|
|
4
|
+
# Represents an attribute of a SCIM resource that is declared in its
|
|
5
|
+
# schema.
|
|
6
|
+
#
|
|
7
|
+
# Attributes can be simple or complex. A complex attribute needs to have
|
|
8
|
+
# its own schema that is passed to the initialize method when the attribute
|
|
9
|
+
# is instantiated.
|
|
10
|
+
#
|
|
11
|
+
# Examples:
|
|
12
|
+
#
|
|
13
|
+
# Attribute.new(name: 'userName', type: 'string', uniqueness: 'server')
|
|
14
|
+
# Attribute.new(name: 'name', complexType: Scimitar::ComplexTypes::Name)
|
|
15
|
+
#
|
|
16
|
+
class Attribute
|
|
17
|
+
include ActiveModel::Model
|
|
18
|
+
include Scimitar::Errors
|
|
19
|
+
|
|
20
|
+
attr_accessor :name,
|
|
21
|
+
:type,
|
|
22
|
+
:multiValued,
|
|
23
|
+
:required,
|
|
24
|
+
:caseExact,
|
|
25
|
+
:mutability,
|
|
26
|
+
:returned,
|
|
27
|
+
:uniqueness,
|
|
28
|
+
:subAttributes,
|
|
29
|
+
:complexType,
|
|
30
|
+
:canonicalValues
|
|
31
|
+
|
|
32
|
+
# +options+:: Hash of values to be used for instantiating the attribute
|
|
33
|
+
# object. Some of the instance variables of the objects will
|
|
34
|
+
# have default values if this hash does not contain anything
|
|
35
|
+
# for them.
|
|
36
|
+
#
|
|
37
|
+
def initialize(options = {})
|
|
38
|
+
defaults = {
|
|
39
|
+
multiValued: false,
|
|
40
|
+
required: false,
|
|
41
|
+
caseExact: false,
|
|
42
|
+
mutability: 'readWrite',
|
|
43
|
+
uniqueness: 'none',
|
|
44
|
+
returned: 'default',
|
|
45
|
+
canonicalValues: []
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if options[:complexType]
|
|
49
|
+
defaults.merge!(type: 'complex', subAttributes: options[:complexType].schema.scim_attributes)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
super(defaults.merge(options || {}))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Validates a value against this attribute object. For simple attributes,
|
|
56
|
+
# it checks if blank is valid or not and if the type matches. For complex
|
|
57
|
+
# attributes, it delegates it to the valid? method of the complex type
|
|
58
|
+
# schema.
|
|
59
|
+
#
|
|
60
|
+
# If the value is not valid, validation message(s) are added to the
|
|
61
|
+
# #errors attribute of this object.
|
|
62
|
+
#
|
|
63
|
+
# +value+:: Value to check.
|
|
64
|
+
#
|
|
65
|
+
# Returns +true+ if value is valid for this attribute, else +false+.
|
|
66
|
+
#
|
|
67
|
+
def valid?(value)
|
|
68
|
+
return valid_blank? if value.blank? && !value.is_a?(FalseClass)
|
|
69
|
+
|
|
70
|
+
if type == 'complex'
|
|
71
|
+
return all_valid?(complexType, value) if multiValued
|
|
72
|
+
valid_complex_type?(value)
|
|
73
|
+
else
|
|
74
|
+
valid_simple_type?(value)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def valid_blank?
|
|
79
|
+
return true unless self.required
|
|
80
|
+
errors.add(self.name, 'is required')
|
|
81
|
+
false
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def valid_complex_type?(value)
|
|
85
|
+
if !value.class.respond_to?(:schema) || value.class.schema != complexType.schema
|
|
86
|
+
errors.add(self.name, 'has to follow the complexType format.')
|
|
87
|
+
return false
|
|
88
|
+
end
|
|
89
|
+
value.class.schema.valid?(value)
|
|
90
|
+
return true if value.errors.empty?
|
|
91
|
+
add_errors_from_hash(value.errors.to_hash, prefix: self.name)
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def valid_simple_type?(value)
|
|
96
|
+
valid = (type == 'string' && value.is_a?(String)) ||
|
|
97
|
+
(type == 'boolean' && (value.is_a?(TrueClass) || value.is_a?(FalseClass))) ||
|
|
98
|
+
(type == 'integer' && (value.is_a?(Integer))) ||
|
|
99
|
+
(type == 'dateTime' && valid_date_time?(value))
|
|
100
|
+
errors.add(self.name, "has the wrong type. It has to be a(n) #{self.type}.") unless valid
|
|
101
|
+
valid
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def valid_date_time?(value)
|
|
105
|
+
!!Time.iso8601(value)
|
|
106
|
+
rescue ArgumentError
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def all_valid?(complex_type, value)
|
|
111
|
+
validations = value.map {|value_in_array| valid_complex_type?(value_in_array)}
|
|
112
|
+
validations.all?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def as_json(options = {})
|
|
116
|
+
options[:except] ||= ['complexType']
|
|
117
|
+
options[:except] << 'canonicalValues' if canonicalValues.empty?
|
|
118
|
+
super.except(options)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
# The base class that each schema class must inherit from.
|
|
4
|
+
# These classes represent the schema of a SCIM resource or a complex type that could be used in a resource.
|
|
5
|
+
class Base
|
|
6
|
+
include ActiveModel::Model
|
|
7
|
+
attr_accessor :id, :name, :description, :scim_attributes, :meta
|
|
8
|
+
|
|
9
|
+
def initialize(options = {})
|
|
10
|
+
super
|
|
11
|
+
@meta = Meta.new(resourceType: 'Schema')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Converts the schema to its json representation that will be returned by /SCHEMAS end-point of a SCIM service provider.
|
|
15
|
+
def as_json(options = {})
|
|
16
|
+
@meta.location = Scimitar::Engine.routes.url_helpers.scim_schemas_path(name: id)
|
|
17
|
+
original = super
|
|
18
|
+
original.merge('attributes' => original.delete('scim_attributes'))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Validates the resource against specific validations of each attribute,
|
|
22
|
+
# for example if the type of the attribute matches the one defined in the
|
|
23
|
+
# schema.
|
|
24
|
+
#
|
|
25
|
+
# +resource+:: A resource object that uses this schema.
|
|
26
|
+
#
|
|
27
|
+
def self.valid?(resource)
|
|
28
|
+
cloned_scim_attributes.each do |scim_attribute|
|
|
29
|
+
resource.add_errors_from_hash(scim_attribute.errors.to_hash) unless scim_attribute.valid?(resource.send(scim_attribute.name))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.cloned_scim_attributes
|
|
34
|
+
scim_attributes.map { |scim_attribute| scim_attribute.clone }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Find a given attribute this schema, travelling down a path to any
|
|
38
|
+
# sub-attributes within. Given that callers might be dealing with paths
|
|
39
|
+
# into actual SCIM data, array indices for multi-value attributes are
|
|
40
|
+
# allowed (as integers) and simply skipped - only the names are of
|
|
41
|
+
# interest.
|
|
42
|
+
#
|
|
43
|
+
# This is typically used to access attribute properties such as intended
|
|
44
|
+
# mutability ('readOnly', 'readWrite', 'immutable', 'writeOnly').
|
|
45
|
+
#
|
|
46
|
+
# Returns the found Scimitar::Schema::Attribute or "nil".
|
|
47
|
+
#
|
|
48
|
+
# *path:: One or more attribute names as Strings, or Integer indices.
|
|
49
|
+
#
|
|
50
|
+
# For example, in a User schema, passing "name", "givenName" would find
|
|
51
|
+
# the "givenName" attribute. Passing "emails", 0, "value" would find the
|
|
52
|
+
# schema attribute for "value" under "emails", ignoring the array index
|
|
53
|
+
# (since the schema is identical for each item in an array of values).
|
|
54
|
+
#
|
|
55
|
+
# See also Scimitar::Resources::Base::find_attribute
|
|
56
|
+
#
|
|
57
|
+
def self.find_attribute(*path)
|
|
58
|
+
found_attribute = nil
|
|
59
|
+
current_attributes = self.scim_attributes()
|
|
60
|
+
|
|
61
|
+
until path.empty? do
|
|
62
|
+
current_path_entry = path.shift()
|
|
63
|
+
next if current_path_entry.is_a?(Integer) # Skip array indicies arising from multi-value attributes
|
|
64
|
+
|
|
65
|
+
found_attribute = current_attributes.find do | attribute_to_check |
|
|
66
|
+
attribute_to_check.name == current_path_entry
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if found_attribute && path.present? # Any sub-attributes to check?...
|
|
70
|
+
if found_attribute.subAttributes.present? # ...and are any defined?
|
|
71
|
+
current_attributes = found_attribute.subAttributes
|
|
72
|
+
else
|
|
73
|
+
found_attribute = nil
|
|
74
|
+
break # NOTE EARLY EXIT - tried to find a sub-attribute but there are none
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
break # NOTE EARLY EXIT - no found attribute, or found target item at end of path
|
|
78
|
+
end
|
|
79
|
+
end # "until path.empty() do"
|
|
80
|
+
|
|
81
|
+
return found_attribute
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
module DerivedAttributes
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
class_methods do
|
|
7
|
+
def set_schema(schema)
|
|
8
|
+
@schema = schema
|
|
9
|
+
derive_attributes_from_schema(schema)
|
|
10
|
+
schema
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def derive_attributes_from_schema(schema)
|
|
14
|
+
attr_accessor *schema.scim_attributes.map(&:name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def schema
|
|
18
|
+
@schema
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
# Represents the schema for the Group resource
|
|
4
|
+
# See also Scimitar::Resources::Group
|
|
5
|
+
class Group < Base
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super(name: 'Group',
|
|
9
|
+
id: self.class.id,
|
|
10
|
+
description: 'Represents a Group',
|
|
11
|
+
scim_attributes: self.class.scim_attributes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.id
|
|
15
|
+
'urn:ietf:params:scim:schemas:core:2.0:Group'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.scim_attributes
|
|
19
|
+
[
|
|
20
|
+
Attribute.new(name: 'displayName', type: 'string', required: true),
|
|
21
|
+
Attribute.new(name: 'members', multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceMember, mutability: 'readWrite')
|
|
22
|
+
]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
# Represents the schema for the Name complex type
|
|
4
|
+
# See also Scimitar::ComplexTypes::Name
|
|
5
|
+
class Name < Base
|
|
6
|
+
|
|
7
|
+
def self.scim_attributes
|
|
8
|
+
@scim_attributes ||= [
|
|
9
|
+
Attribute.new(name: 'familyName', type: 'string', required: true),
|
|
10
|
+
Attribute.new(name: 'givenName', type: 'string', required: true),
|
|
11
|
+
Attribute.new(name: 'middleName', type: 'string'),
|
|
12
|
+
Attribute.new(name: 'formatted', type: 'string'),
|
|
13
|
+
Attribute.new(name: 'honorificPrefix', type: 'string'),
|
|
14
|
+
Attribute.new(name: 'honorificSuffix', type: 'string'),
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
|
|
4
|
+
# Represents the schema for the ReferenceGroup complex type,
|
|
5
|
+
# referring to a group of which a user is a member - used in
|
|
6
|
+
# a User SCIM resource's "groups" array.
|
|
7
|
+
#
|
|
8
|
+
# These are always read-only, with no ability to change the
|
|
9
|
+
# membership list through a User. Change via Groups instead.
|
|
10
|
+
#
|
|
11
|
+
# See also Scimitar::ComplexTypes::ReferenceGroup
|
|
12
|
+
#
|
|
13
|
+
class ReferenceGroup < Base
|
|
14
|
+
def self.scim_attributes
|
|
15
|
+
@scim_attributes ||= [
|
|
16
|
+
Attribute.new(name: 'value', type: 'string', mutability: 'readOnly', required: true),
|
|
17
|
+
Attribute.new(name: 'display', type: 'string', mutability: 'readOnly'),
|
|
18
|
+
Attribute.new(name: 'type', type: 'string', mutability: 'readOnly'),
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Scimitar
|
|
2
|
+
module Schema
|
|
3
|
+
|
|
4
|
+
# Represents the schema for the ReferenceMember complex type,
|
|
5
|
+
# referring to a member of a group (where members can themselves
|
|
6
|
+
# be Users or Groups, identified by the "type" attribute). Used
|
|
7
|
+
# by the Group SCIM resource's "members" array.
|
|
8
|
+
#
|
|
9
|
+
# See also Scimitar::ComplexTypes::ReferenceMember
|
|
10
|
+
#
|
|
11
|
+
class ReferenceMember < Base
|
|
12
|
+
def self.scim_attributes
|
|
13
|
+
@scim_attributes ||= [
|
|
14
|
+
Attribute.new(name: 'value', type: 'string', mutability: 'immutable', required: true),
|
|
15
|
+
Attribute.new(name: 'type', type: 'string', mutability: 'immutable'),
|
|
16
|
+
Attribute.new(name: 'display', type: 'string', mutability: 'immutable'),
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|