powerhome-scimitar 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +708 -0
- data/Rakefile +16 -0
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +257 -0
- data/app/controllers/scimitar/application_controller.rb +157 -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 +21 -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 +12 -0
- data/app/models/scimitar/complex_types/base.rb +83 -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 +32 -0
- data/app/models/scimitar/error_response.rb +32 -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 +745 -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 +190 -0
- data/app/models/scimitar/resources/group.rb +13 -0
- data/app/models/scimitar/resources/mixin.rb +1524 -0
- data/app/models/scimitar/resources/user.rb +13 -0
- data/app/models/scimitar/schema/address.rb +25 -0
- data/app/models/scimitar/schema/attribute.rb +132 -0
- data/app/models/scimitar/schema/base.rb +90 -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 +60 -0
- data/app/models/scimitar/supportable.rb +14 -0
- data/app/views/layouts/scimitar/application.html.erb +14 -0
- data/config/initializers/scimitar.rb +111 -0
- data/config/routes.rb +6 -0
- data/lib/scimitar/engine.rb +63 -0
- data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +216 -0
- data/lib/scimitar/support/utilities.rb +51 -0
- data/lib/scimitar/version.rb +13 -0
- data/lib/scimitar.rb +29 -0
- data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +25 -0
- data/spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb +24 -0
- data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +25 -0
- data/spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb +30 -0
- data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +24 -0
- data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +25 -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 +132 -0
- data/spec/apps/dummy/config/application.rb +18 -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 +38 -0
- data/spec/apps/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/apps/dummy/config/initializers/scimitar.rb +61 -0
- data/spec/apps/dummy/config/initializers/session_store.rb +3 -0
- data/spec/apps/dummy/config/routes.rb +45 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +24 -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 +13 -0
- data/spec/apps/dummy/db/schema.rb +48 -0
- data/spec/controllers/scimitar/application_controller_spec.rb +296 -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 +83 -0
- data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +22 -0
- data/spec/models/scimitar/complex_types/address_spec.rb +18 -0
- data/spec/models/scimitar/complex_types/email_spec.rb +21 -0
- data/spec/models/scimitar/lists/count_spec.rb +147 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +830 -0
- data/spec/models/scimitar/resource_type_spec.rb +21 -0
- data/spec/models/scimitar/resources/base_spec.rb +485 -0
- data/spec/models/scimitar/resources/base_validation_spec.rb +86 -0
- data/spec/models/scimitar/resources/mixin_spec.rb +3562 -0
- data/spec/models/scimitar/resources/user_spec.rb +68 -0
- data/spec/models/scimitar/schema/attribute_spec.rb +99 -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 +720 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +1354 -0
- data/spec/requests/application_controller_spec.rb +61 -0
- data/spec/requests/controller_configuration_spec.rb +17 -0
- data/spec/requests/engine_spec.rb +45 -0
- data/spec/spec_helper.rb +101 -0
- data/spec/spec_helper_spec.rb +30 -0
- data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +169 -0
- metadata +321 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Scimitar
|
2
|
+
# Provides info about a resource type. Instances of this class are used to provide info through the /ResourceTypes endpoint of a SCIM service provider.
|
3
|
+
class ResourceType
|
4
|
+
include ActiveModel::Model
|
5
|
+
attr_accessor :meta, :endpoint, :schema, :schemas, :id, :name, :schemaExtensions
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
default_attributes = {
|
9
|
+
meta: Meta.new(
|
10
|
+
'resourceType': 'ResourceType'
|
11
|
+
),
|
12
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:ResourceType']
|
13
|
+
}
|
14
|
+
super(default_attributes.merge(attributes))
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def as_json(options = {})
|
19
|
+
without_extensions = super(except: 'schemaExtensions')
|
20
|
+
if schemaExtensions.present?
|
21
|
+
extensions = schemaExtensions.map{|extension| {"schema" => extension, "required" => false}}
|
22
|
+
without_extensions.merge('schemaExtensions' => extensions)
|
23
|
+
else
|
24
|
+
without_extensions
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Scimitar
|
2
|
+
module Resources
|
3
|
+
# The base class for all SCIM resources.
|
4
|
+
class Base
|
5
|
+
include ActiveModel::Model
|
6
|
+
include Scimitar::Schema::DerivedAttributes
|
7
|
+
include Scimitar::Errors
|
8
|
+
|
9
|
+
attr_accessor :id, :externalId, :meta
|
10
|
+
attr_reader :errors
|
11
|
+
validate :validate_resource
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
flattened_attributes = flatten_extension_attributes(options)
|
15
|
+
ci_all_attributes = Scimitar::Support::HashWithIndifferentCaseInsensitiveAccess.new
|
16
|
+
camel_attributes = {}
|
17
|
+
|
18
|
+
# Create a map where values are the schema-correct-case attribute names
|
19
|
+
# and the values are set the same, but since the ci_all_attributes data
|
20
|
+
# type is HashWithIndifferentCaseInsensitiveAccess, lookups in this are
|
21
|
+
# case insensitive. Thus, arbitrary case input data can be mapped to
|
22
|
+
# the case correctness required for ActiveModel's attribute accessors.
|
23
|
+
#
|
24
|
+
self.class.all_attributes.each { |attr| ci_all_attributes[attr] = attr }
|
25
|
+
|
26
|
+
flattened_attributes.each do | key, value |
|
27
|
+
if ci_all_attributes.key?(key)
|
28
|
+
camel_attributes[ci_all_attributes[key]] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
super(camel_attributes)
|
33
|
+
constantize_complex_types(camel_attributes)
|
34
|
+
|
35
|
+
@errors = ActiveModel::Errors.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def flatten_extension_attributes(options)
|
39
|
+
flattened = options.dup
|
40
|
+
self.class.extended_schemas.each do |extended_schema|
|
41
|
+
if extension_attrs = flattened.delete(extended_schema.id)
|
42
|
+
flattened.merge!(extension_attrs)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
flattened
|
46
|
+
end
|
47
|
+
|
48
|
+
# Can be used to extend an existing resource type's schema. For example:
|
49
|
+
#
|
50
|
+
# module Scim
|
51
|
+
# module Schema
|
52
|
+
# class MyExtension < Scimitar::Schema::Base
|
53
|
+
#
|
54
|
+
# def initialize(options = {})
|
55
|
+
# super(name: 'ExtendedGroup',
|
56
|
+
# id: self.class.id,
|
57
|
+
# description: 'Represents extra info about a group',
|
58
|
+
# scim_attributes: self.class.scim_attributes)
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# def self.id
|
62
|
+
# 'urn:ietf:params:scim:schemas:extension:extendedgroup:2.0:Group'
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# def self.scim_attributes
|
66
|
+
# [Scimitar::Schema::Attribute.new(name: 'someAddedAttribute',
|
67
|
+
# type: 'string',
|
68
|
+
# required: true,
|
69
|
+
# canonicalValues: ['FOO', 'BAR'])]
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# Scimitar::Resources::Group.extend_schema Scim::Schema::MyExtension
|
76
|
+
#
|
77
|
+
def self.extend_schema(schema)
|
78
|
+
derive_attributes_from_schema(schema)
|
79
|
+
extended_schemas << schema
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.extended_schemas
|
83
|
+
@extended_schemas ||= []
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.schemas
|
87
|
+
([schema] + extended_schemas).flatten
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.all_attributes
|
91
|
+
scim_attributes = schemas.map(&:scim_attributes).flatten.map(&:name)
|
92
|
+
scim_attributes + [:id, :externalId, :meta]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Calls to Scimitar::Schema::Base::find_attribute for each of the schemas
|
96
|
+
# in ::schemas, in order returned (so main schema would be first, then
|
97
|
+
# any extended schemas searched next). Returns the first match found, or
|
98
|
+
# +nil+.
|
99
|
+
#
|
100
|
+
# See Scimitar::Schema::Base::find_attribute for details on parameters,
|
101
|
+
# more about the return value and other general information.
|
102
|
+
#
|
103
|
+
def self.find_attribute(*path)
|
104
|
+
found_attribute = nil
|
105
|
+
|
106
|
+
self.schemas.each do | schema |
|
107
|
+
found_attribute = schema.find_attribute(*path)
|
108
|
+
break unless found_attribute.nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
return found_attribute
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.complex_scim_attributes
|
115
|
+
schemas.flat_map(&:scim_attributes).select(&:complexType).group_by(&:name)
|
116
|
+
end
|
117
|
+
|
118
|
+
def complex_type_from_hash(scim_attribute, attr_value)
|
119
|
+
if attr_value.is_a?(Hash)
|
120
|
+
scim_attribute.complexType.new(attr_value)
|
121
|
+
else
|
122
|
+
attr_value
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def constantize_complex_types(hash)
|
127
|
+
hash.with_indifferent_access.each_pair do |attr_name, attr_value|
|
128
|
+
scim_attribute = self.class.complex_scim_attributes[attr_name].try(:first)
|
129
|
+
|
130
|
+
if scim_attribute && scim_attribute.complexType
|
131
|
+
if scim_attribute.multiValued
|
132
|
+
self.send("#{attr_name}=", attr_value&.map {|attr_for_each_item| complex_type_from_hash(scim_attribute, attr_for_each_item)})
|
133
|
+
else
|
134
|
+
self.send("#{attr_name}=", complex_type_from_hash(scim_attribute, attr_value))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Renders *in full* as JSON; typically used for write-based operations...
|
141
|
+
#
|
142
|
+
# record = self.storage_class().new
|
143
|
+
# record.from_scim!(scim_hash: scim_resource.as_json())
|
144
|
+
# self.save!(record)
|
145
|
+
#
|
146
|
+
# ...so all fields, even those marked "returned: false", are included.
|
147
|
+
# Use Scimitar::Resources::Mixin::to_scim to obtain a SCIM object with
|
148
|
+
# non-returnable fields omitted, rendering *that* as JSON via #to_json.
|
149
|
+
#
|
150
|
+
def as_json(options = {})
|
151
|
+
self.meta = Meta.new unless self.meta && self.meta.is_a?(Meta)
|
152
|
+
self.meta.resourceType = self.class.resource_type_id
|
153
|
+
|
154
|
+
original_hash = super(options).except('errors')
|
155
|
+
original_hash.merge!('schemas' => self.class.schemas.map(&:id))
|
156
|
+
|
157
|
+
self.class.extended_schemas.each do |extension_schema|
|
158
|
+
extension_attributes = extension_schema.scim_attributes.map(&:name)
|
159
|
+
original_hash.merge!(extension_schema.id => original_hash.extract!(*extension_attributes))
|
160
|
+
end
|
161
|
+
|
162
|
+
original_hash
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.resource_type_id
|
166
|
+
name.demodulize
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.resource_type(location)
|
170
|
+
resource_type = ResourceType.new(
|
171
|
+
endpoint: endpoint,
|
172
|
+
schema: schema.id,
|
173
|
+
id: resource_type_id,
|
174
|
+
name: resource_type_id,
|
175
|
+
schemaExtensions: extended_schemas.map(&:id)
|
176
|
+
)
|
177
|
+
|
178
|
+
resource_type.meta.location = location
|
179
|
+
resource_type
|
180
|
+
end
|
181
|
+
|
182
|
+
def validate_resource
|
183
|
+
self.class.schema.valid?(self)
|
184
|
+
self.class.extended_schemas.each do |extended_schema|
|
185
|
+
extended_schema.valid?(self)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|