scim_engine 2.1.1
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/MIT-LICENSE +20 -0
- data/Rakefile +25 -0
- data/app/controllers/scim_engine/application_controller.rb +36 -0
- data/app/controllers/scim_engine/resource_types_controller.rb +22 -0
- data/app/controllers/scim_engine/resources_controller.rb +71 -0
- data/app/controllers/scim_engine/schemas_controller.rb +16 -0
- data/app/controllers/scim_engine/service_provider_configurations_controller.rb +8 -0
- data/app/models/scim_engine/authentication_error.rb +9 -0
- data/app/models/scim_engine/authentication_scheme.rb +12 -0
- data/app/models/scim_engine/bulk.rb +5 -0
- data/app/models/scim_engine/complex_types/base.rb +37 -0
- data/app/models/scim_engine/complex_types/email.rb +14 -0
- data/app/models/scim_engine/complex_types/name.rb +9 -0
- data/app/models/scim_engine/complex_types/reference.rb +9 -0
- data/app/models/scim_engine/error_response.rb +13 -0
- data/app/models/scim_engine/errors.rb +14 -0
- data/app/models/scim_engine/filter.rb +5 -0
- data/app/models/scim_engine/meta.rb +7 -0
- data/app/models/scim_engine/not_found_error.rb +10 -0
- data/app/models/scim_engine/resource_invalid_error.rb +9 -0
- data/app/models/scim_engine/resource_type.rb +29 -0
- data/app/models/scim_engine/resources/base.rb +139 -0
- data/app/models/scim_engine/resources/group.rb +13 -0
- data/app/models/scim_engine/resources/user.rb +13 -0
- data/app/models/scim_engine/schema/attribute.rb +91 -0
- data/app/models/scim_engine/schema/base.rb +35 -0
- data/app/models/scim_engine/schema/derived_attributes.rb +24 -0
- data/app/models/scim_engine/schema/email.rb +15 -0
- data/app/models/scim_engine/schema/group.rb +27 -0
- data/app/models/scim_engine/schema/name.rb +17 -0
- data/app/models/scim_engine/schema/reference.rb +14 -0
- data/app/models/scim_engine/schema/user.rb +30 -0
- data/app/models/scim_engine/service_provider_configuration.rb +31 -0
- data/app/models/scim_engine/supportable.rb +10 -0
- data/app/views/layouts/scim_engine/application.html.erb +14 -0
- data/config/routes.rb +6 -0
- data/lib/scim_engine.rb +13 -0
- data/lib/scim_engine/engine.rb +51 -0
- data/lib/scim_engine/version.rb +3 -0
- data/lib/tasks/scim_engine_tasks.rake +4 -0
- data/spec/controllers/scim_engine/application_controller_spec.rb +104 -0
- data/spec/controllers/scim_engine/resource_types_controller_spec.rb +78 -0
- data/spec/controllers/scim_engine/resources_controller_spec.rb +132 -0
- data/spec/controllers/scim_engine/schemas_controller_spec.rb +66 -0
- data/spec/controllers/scim_engine/service_provider_configurations_controller_spec.rb +21 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +16 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/log/test.log +175 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/models/scim_engine/complex_types/email_spec.rb +23 -0
- data/spec/models/scim_engine/resource_type_spec.rb +21 -0
- data/spec/models/scim_engine/resources/base_spec.rb +246 -0
- data/spec/models/scim_engine/resources/base_validation_spec.rb +61 -0
- data/spec/models/scim_engine/resources/user_spec.rb +55 -0
- data/spec/models/scim_engine/schema/attribute_spec.rb +80 -0
- data/spec/models/scim_engine/schema/base_spec.rb +19 -0
- data/spec/models/scim_engine/schema/group_spec.rb +66 -0
- data/spec/models/scim_engine/schema/user_spec.rb +140 -0
- data/spec/rails_helper.rb +57 -0
- data/spec/spec_helper.rb +99 -0
- metadata +246 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3cbd57ab672582d1708e7e6f534331bd523706c
|
4
|
+
data.tar.gz: 2e91e1a79a20c400ae2b43b01b1cef6b6fe774c4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc7d7d6064b5abe6fa107780c6b5243b5dc01ca87a56ab512b31d27707d43d15349d60a5f68e1dd954483eb9ff201ddb57bea15eda819f15e74305077b7844da
|
7
|
+
data.tar.gz: 7f433b3c515d42c72e16ec1a29eb897eb6b4c0dd59c7be8f5f9466dd0499a8b1dbfcce40bf55dbd14e1e68ec6b8183c8774e7f90a9f172e09bc809203be8f2ca
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ScimEngine'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
before_action :require_scim
|
4
|
+
before_action :authenticate
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def authenticated?
|
9
|
+
authenticate_with_http_basic do |name, password|
|
10
|
+
name == ScimEngine::Engine.username && password == ScimEngine::Engine.password
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate
|
15
|
+
handle_scim_error(ScimEngine::AuthenticationError.new) unless authenticated?
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_resource_not_found(exception)
|
19
|
+
handle_scim_error(NotFoundError.new(params[:id]))
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle_record_invalid(error_message)
|
23
|
+
handle_scim_error(ErrorResponse.new(status: 400, detail: "Operation failed since record has become invalid: #{error_message}"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_scim_error(error_response)
|
27
|
+
render json: error_response, status: error_response.status
|
28
|
+
end
|
29
|
+
|
30
|
+
def require_scim
|
31
|
+
unless request.format == :scim
|
32
|
+
handle_scim_error(ErrorResponse.new(status: 406, detail: "Only #{Mime::Type.lookup_by_extension(:scim)} type is accepted."))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_dependency "scim_engine/application_controller"
|
2
|
+
|
3
|
+
module ScimEngine
|
4
|
+
class ResourceTypesController < ApplicationController
|
5
|
+
def index
|
6
|
+
resource_types = ScimEngine::Engine.resources.map do |resource|
|
7
|
+
resource.resource_type(scim_resource_type_url(name: resource.resource_type_id))
|
8
|
+
end
|
9
|
+
|
10
|
+
render json: resource_types
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
resource_types = ScimEngine::Engine.resources.reduce({}) do |hash, resource|
|
15
|
+
hash[resource.resource_type_id] = resource.resource_type(scim_resource_type_url(name: resource.resource_type_id))
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
|
19
|
+
render json: resource_types[params[:name]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_dependency "scim_engine/application_controller"
|
2
|
+
|
3
|
+
module ScimEngine
|
4
|
+
class ResourcesController < ApplicationController
|
5
|
+
|
6
|
+
def show(&block)
|
7
|
+
scim_user = yield resource_params[:id]
|
8
|
+
render json: scim_user
|
9
|
+
rescue ErrorResponse => error
|
10
|
+
handle_scim_error(error)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(resource_type, &block)
|
14
|
+
if resource_params[:id].present?
|
15
|
+
handle_scim_error(ErrorResponse.new(status: 400, detail: 'id is not a valid parameter for create'))
|
16
|
+
return
|
17
|
+
end
|
18
|
+
with_scim_resource(resource_type) do |resource|
|
19
|
+
render json: yield(resource, is_create: true), status: :created
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(resource_type, &block)
|
24
|
+
with_scim_resource(resource_type) do |resource|
|
25
|
+
render json: yield(resource)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy
|
30
|
+
if yield(resource_params[:id]) != false
|
31
|
+
head :no_content
|
32
|
+
else
|
33
|
+
handle_scim_error(ErrorResponse.new(status: 500, detail: "Failed to delete the resource with id '#{params[:id]}'. Please try again later"))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def validate_request
|
40
|
+
if request.raw_post.blank?
|
41
|
+
raise ScimEngine::ErrorResponse.new(status: 400, detail: 'must provide a request body')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def with_scim_resource(resource_type)
|
46
|
+
validate_request
|
47
|
+
begin
|
48
|
+
resource = resource_type.new(resource_params.to_h)
|
49
|
+
unless resource.valid?
|
50
|
+
raise ScimEngine::ErrorResponse.new(status: 400,
|
51
|
+
detail: "Invalid resource: #{resource.errors.full_messages.join(', ')}.",
|
52
|
+
scimType: 'invalidValue')
|
53
|
+
end
|
54
|
+
|
55
|
+
yield(resource)
|
56
|
+
rescue NoMethodError => error
|
57
|
+
Rails.logger.error error
|
58
|
+
raise ScimEngine::ErrorResponse.new(status: 400, detail: 'invalid request')
|
59
|
+
end
|
60
|
+
rescue ScimEngine::ErrorResponse => error
|
61
|
+
handle_scim_error(error)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def resource_params
|
67
|
+
params.permit!
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_dependency "scim_engine/application_controller"
|
2
|
+
|
3
|
+
module ScimEngine
|
4
|
+
class SchemasController < ApplicationController
|
5
|
+
def index
|
6
|
+
schemas = ScimEngine::Engine.schemas
|
7
|
+
schemas_by_id = schemas.reduce({}) do |hash, schema|
|
8
|
+
hash[schema.id] = schema
|
9
|
+
hash
|
10
|
+
end
|
11
|
+
|
12
|
+
render json: schemas_by_id[params[:name]] || schemas
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
class AuthenticationScheme
|
3
|
+
include ActiveModel::Model
|
4
|
+
attr_accessor :type, :name, :description
|
5
|
+
|
6
|
+
def self.basic
|
7
|
+
new type: 'httpbasic',
|
8
|
+
name: 'HTTP Basic',
|
9
|
+
description: 'Authentication scheme using the HTTP Basic Standard'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module ComplexTypes
|
3
|
+
# This class represents complex types that could be used inside SCIM resources.
|
4
|
+
# Each complex type must inherit from this class. They also need to have their own schema defined.
|
5
|
+
# @example
|
6
|
+
# module ScimEngine
|
7
|
+
# module ComplexTypes
|
8
|
+
# class Email < Base
|
9
|
+
# set_schema ScimEngine::Schema::Email
|
10
|
+
#
|
11
|
+
# def as_json(options = {})
|
12
|
+
# {'type' => 'work', 'primary' => true}.merge(super(options))
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
class Base
|
19
|
+
include ActiveModel::Model
|
20
|
+
include ScimEngine::Schema::DerivedAttributes
|
21
|
+
include ScimEngine::Errors
|
22
|
+
|
23
|
+
def initialize(options={})
|
24
|
+
super
|
25
|
+
@errors = ActiveModel::Errors.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Converts the object to its SCIM representation which is always a json representation
|
29
|
+
# @param options [Hash] a hash that could provide default values for some of the attributes of this complex type object
|
30
|
+
#
|
31
|
+
def as_json(options={})
|
32
|
+
options[:except] ||= ['errors']
|
33
|
+
super.except(options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module ComplexTypes
|
3
|
+
# Represents the complex email type.
|
4
|
+
# @see ScimEngine::Schema::Email
|
5
|
+
class Email < Base
|
6
|
+
set_schema ScimEngine::Schema::Email
|
7
|
+
|
8
|
+
# Returns the json representation of an email.
|
9
|
+
def as_json(options = {})
|
10
|
+
{'type' => 'work', 'primary' => true}.merge(super(options))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
class ErrorResponse < StandardError
|
3
|
+
include ActiveModel::Model
|
4
|
+
|
5
|
+
attr_accessor :status, :detail, :scimType
|
6
|
+
|
7
|
+
def as_json(options = {})
|
8
|
+
{'schemas': ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
9
|
+
'detail': detail,
|
10
|
+
'status': "#{status}"}.merge(scimType ? {'scimType': scimType} : {})
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Errors
|
3
|
+
def add_errors_from_hash(errors_hash, prefix: nil)
|
4
|
+
errors_hash.each_pair do |key, value|
|
5
|
+
new_key = prefix.nil? ? key : "#{prefix}.#{key}".to_sym
|
6
|
+
if value.is_a?(Array)
|
7
|
+
value.each {|error| errors.add(new_key, error)}
|
8
|
+
else
|
9
|
+
errors.add(new_key, value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ScimEngine
|
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,139 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Resources
|
3
|
+
# The base class for all SCIM resources.
|
4
|
+
class Base
|
5
|
+
include ActiveModel::Model
|
6
|
+
include ScimEngine::Schema::DerivedAttributes
|
7
|
+
include ScimEngine::Errors
|
8
|
+
|
9
|
+
attr_accessor :id, :externalId, :meta
|
10
|
+
attr_reader :errors
|
11
|
+
validate :validate_resource
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
flattended_attributes = flatten_extension_attributes(options)
|
15
|
+
attributes = flattended_attributes.with_indifferent_access.slice(*self.class.all_attributes)
|
16
|
+
super(attributes)
|
17
|
+
constantize_complex_types(attributes)
|
18
|
+
@errors = ActiveModel::Errors.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def flatten_extension_attributes(options)
|
22
|
+
flattened = options.dup
|
23
|
+
self.class.extended_schemas.each do |extended_schema|
|
24
|
+
if extension_attrs = flattened.delete(extended_schema.id)
|
25
|
+
flattened.merge!(extension_attrs)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
flattened
|
29
|
+
end
|
30
|
+
|
31
|
+
# Can be used to extend an existing resource type's schema
|
32
|
+
# @example
|
33
|
+
# module Scim
|
34
|
+
# module Schema
|
35
|
+
# class MyExtension < ScimEngine::Schema::Base
|
36
|
+
#
|
37
|
+
# def initialize(options = {})
|
38
|
+
# super(name: 'ExtendedGroup',
|
39
|
+
# id: self.class.id,
|
40
|
+
# description: 'Represents extra info about a group',
|
41
|
+
# scim_attributes: self.class.scim_attributes)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def self.id
|
45
|
+
# 'urn:ietf:params:scim:schemas:extension:extendedgroup:2.0:Group'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def self.scim_attributes
|
49
|
+
# [ScimEngine::Schema::Attribute.new(name: 'someAddedAttribute',
|
50
|
+
# type: 'string',
|
51
|
+
# required: true,
|
52
|
+
# canonicalValues: ['FOO', 'BAR'])]
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# ScimEngine::Resources::Group.extend_schema Scim::Schema::MyExtention
|
59
|
+
def self.extend_schema(schema)
|
60
|
+
derive_attributes_from_schema(schema)
|
61
|
+
extended_schemas << schema
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.extended_schemas
|
65
|
+
@extended_schemas ||= []
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.schemas
|
69
|
+
([schema] + extended_schemas).flatten
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.all_attributes
|
73
|
+
scim_attributes = schemas.map(&:scim_attributes).flatten.map(&:name)
|
74
|
+
scim_attributes + [:id, :externalId, :meta]
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.complex_scim_attributes
|
78
|
+
schema.scim_attributes.select(&:complexType).group_by(&:name)
|
79
|
+
end
|
80
|
+
|
81
|
+
def complex_type_from_hash(scim_attribute, attr_value)
|
82
|
+
if attr_value.is_a?(Hash)
|
83
|
+
scim_attribute.complexType.new(attr_value)
|
84
|
+
else
|
85
|
+
attr_value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def constantize_complex_types(hash)
|
90
|
+
hash.with_indifferent_access.each_pair do |attr_name, attr_value|
|
91
|
+
scim_attribute = self.class.complex_scim_attributes[attr_name].try(:first)
|
92
|
+
if scim_attribute && scim_attribute.complexType
|
93
|
+
if scim_attribute.multiValued
|
94
|
+
self.send("#{attr_name}=", attr_value.map {|attr_for_each_item| complex_type_from_hash(scim_attribute, attr_for_each_item)})
|
95
|
+
else
|
96
|
+
self.send("#{attr_name}=", complex_type_from_hash(scim_attribute, attr_value))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def as_json(options = {})
|
103
|
+
self.meta = Meta.new unless self.meta
|
104
|
+
meta.resourceType = self.class.resource_type_id
|
105
|
+
original_hash = super(options).except('errors')
|
106
|
+
original_hash.merge!("schemas" => self.class.schemas.map(&:id))
|
107
|
+
self.class.extended_schemas.each do |extension_schema|
|
108
|
+
extension_attributes = extension_schema.scim_attributes.map(&:name)
|
109
|
+
original_hash.merge!(extension_schema.id => original_hash.extract!(*extension_attributes))
|
110
|
+
end
|
111
|
+
original_hash
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.resource_type_id
|
115
|
+
name.demodulize
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.resource_type(location)
|
119
|
+
resource_type = ResourceType.new(
|
120
|
+
endpoint: endpoint,
|
121
|
+
schema: schema.id,
|
122
|
+
id: resource_type_id,
|
123
|
+
name: resource_type_id,
|
124
|
+
schemaExtensions: extended_schemas.map(&:id)
|
125
|
+
)
|
126
|
+
|
127
|
+
resource_type.meta.location = location
|
128
|
+
resource_type
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate_resource
|
132
|
+
self.class.schema.valid?(self)
|
133
|
+
self.class.extended_schemas.each do |extended_schema|
|
134
|
+
extended_schema.valid?(self)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|