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
@@ -0,0 +1,91 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represents an attribute of a SCIM resource that is declared in its schema.
|
4
|
+
# Attributes can be simple or complex. A complex attribute needs to have its own schema that is passed to the initilize method when the attribute is instantiated.
|
5
|
+
# @example
|
6
|
+
# Attribute.new(name: 'userName', type: 'string', uniqueness: 'server')
|
7
|
+
# Attribute.new(name: 'name', complexType: ScimEngine::ComplexTypes::Name)
|
8
|
+
class Attribute
|
9
|
+
include ActiveModel::Model
|
10
|
+
include ScimEngine::Errors
|
11
|
+
attr_accessor :name, :type, :multiValued, :required, :caseExact, :mutability, :returned, :uniqueness, :subAttributes, :complexType, :canonicalValues
|
12
|
+
|
13
|
+
# @param options [Hash] a hash of values to be used for instantiating the attribute object. Some of the instance variables of the objects will have default values if this hash does not contain anything for them.
|
14
|
+
def initialize(options = {})
|
15
|
+
defaults = {
|
16
|
+
multiValued: false,
|
17
|
+
required: true,
|
18
|
+
caseExact: false,
|
19
|
+
mutability: 'readWrite',
|
20
|
+
uniqueness: 'none',
|
21
|
+
returned: 'default',
|
22
|
+
canonicalValues: []
|
23
|
+
}
|
24
|
+
|
25
|
+
if options[:complexType]
|
26
|
+
defaults.merge!(type: 'complex', subAttributes: options[:complexType].schema.scim_attributes)
|
27
|
+
end
|
28
|
+
|
29
|
+
super(defaults.merge(options || {}))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Validates a value against this attribute object. For simple attributes, it checks if blank is valid or not and if the type matches. For complex attributes, it delegates it to the valid? method of the complex type schema.
|
33
|
+
# If the value is not valid, validation message(s) are added to the errors attribute of this object.
|
34
|
+
# @return [Boolean] whether or not the value is valid for this attribute
|
35
|
+
def valid?(value)
|
36
|
+
return valid_blank? if value.blank? && !value.is_a?(FalseClass)
|
37
|
+
|
38
|
+
if type == 'complex'
|
39
|
+
return all_valid?(complexType, value) if multiValued
|
40
|
+
valid_complex_type?(value)
|
41
|
+
else
|
42
|
+
valid_simple_type?(value)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_blank?
|
47
|
+
return true unless self.required
|
48
|
+
errors.add(self.name, "is required")
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_complex_type?(value)
|
53
|
+
if !value.class.respond_to?(:schema) || value.class.schema != complexType.schema
|
54
|
+
errors.add(self.name, "has to follow the complexType format.")
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
value.class.schema.valid?(value)
|
58
|
+
return true if value.errors.empty?
|
59
|
+
add_errors_from_hash(value.errors.to_hash, prefix: self.name)
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid_simple_type?(value)
|
64
|
+
valid = (type == 'string' && value.is_a?(String)) ||
|
65
|
+
(type == 'boolean' && (value.is_a?(TrueClass) || value.is_a?(FalseClass))) ||
|
66
|
+
(type == 'integer' && (value.is_a?(Integer))) ||
|
67
|
+
(type == 'dateTime' && valid_date_time?(value))
|
68
|
+
errors.add(self.name, "has the wrong type. It has to be a(n) #{self.type}.") unless valid
|
69
|
+
valid
|
70
|
+
end
|
71
|
+
|
72
|
+
def valid_date_time?(value)
|
73
|
+
!!Time.iso8601(value)
|
74
|
+
rescue ArgumentError
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
def all_valid?(complex_type, value)
|
79
|
+
validations = value.map {|value_in_array| valid_complex_type?(value_in_array)}
|
80
|
+
validations.all?
|
81
|
+
end
|
82
|
+
|
83
|
+
def as_json(options = {})
|
84
|
+
options[:except] ||= ['complexType']
|
85
|
+
options[:except] << 'canonicalValues' if canonicalValues.empty?
|
86
|
+
super.except(options)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ScimEngine
|
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 = ScimEngine::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,for example if the type of the attribute matches the one defined in the schema.
|
22
|
+
# @param resource [Object] a resource object that uses this schema
|
23
|
+
def self.valid?(resource)
|
24
|
+
cloned_scim_attributes.each do |scim_attribute|
|
25
|
+
resource.add_errors_from_hash(scim_attribute.errors.to_hash) unless scim_attribute.valid?(resource.send(scim_attribute.name))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.cloned_scim_attributes
|
30
|
+
scim_attributes.map { |scim_attribute| scim_attribute.clone }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ScimEngine
|
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,15 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represnts the schema for the Email complex type
|
4
|
+
# @see ScimEngine::ComplexTypes::Email
|
5
|
+
class Email < Base
|
6
|
+
def self.scim_attributes
|
7
|
+
@scim_attributes ||= [
|
8
|
+
Attribute.new(name: 'value', type: 'string'),
|
9
|
+
Attribute.new(name: 'primary', type: 'boolean', required: false),
|
10
|
+
Attribute.new(name: 'type', type: 'string', required: false)
|
11
|
+
]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represnts the schema for the Group resource
|
4
|
+
# @see ScimEngine::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'),
|
21
|
+
Attribute.new(name: 'members', multiValued: true, complexType: ScimEngine::ComplexTypes::Reference, mutability: 'readOnly', required: false)
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represnts the schema for the Name complex type
|
4
|
+
# @see ScimEngine::ComplexTypes::Name
|
5
|
+
class Name < Base
|
6
|
+
|
7
|
+
def self.scim_attributes
|
8
|
+
@scim_attributes ||= [
|
9
|
+
Attribute.new(name: 'familyName', type: 'string'),
|
10
|
+
Attribute.new(name: 'givenName', type: 'string'),
|
11
|
+
Attribute.new(name: 'formatted', type: 'string', required: false)
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represnts the schema for the Reference complex type
|
4
|
+
# @see ScimEngine::ComplexTypes::Reference
|
5
|
+
class Reference < Base
|
6
|
+
def self.scim_attributes
|
7
|
+
@scim_attributes ||= [
|
8
|
+
Attribute.new(name: 'value', type: 'string', mutability: 'readOnly'),
|
9
|
+
Attribute.new(name: 'display', type: 'string', mutability: 'readOnly', required: false)
|
10
|
+
]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
# Represnts the schema for the User resource
|
4
|
+
# @see ScimEngine::Resources::User
|
5
|
+
class User < Base
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
super(name: 'User',
|
9
|
+
id: self.class.id,
|
10
|
+
description: 'Represents a User',
|
11
|
+
scim_attributes: self.class.scim_attributes)
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.id
|
16
|
+
'urn:ietf:params:scim:schemas:core:2.0:User'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.scim_attributes
|
20
|
+
[
|
21
|
+
Attribute.new(name: 'userName', type: 'string', uniqueness: 'server'),
|
22
|
+
Attribute.new(name: 'name', complexType: ScimEngine::ComplexTypes::Name),
|
23
|
+
Attribute.new(name: 'emails', multiValued: true, complexType: ScimEngine::ComplexTypes::Email),
|
24
|
+
Attribute.new(name: 'groups', multiValued: true, mutability: 'immutable', complexType: ScimEngine::ComplexTypes::Reference)
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
# Represnts the service provider info. Used by the /ServiceProviderConfig endpoint to privide specification compliance, authentication schemes, data models.
|
3
|
+
class ServiceProviderConfiguration
|
4
|
+
include ActiveModel::Model
|
5
|
+
|
6
|
+
attr_accessor :patch, :bulk, :filter, :changePassword,
|
7
|
+
:sort, :etag, :authenticationSchemes,
|
8
|
+
:schemas, :meta
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
defaults = {
|
12
|
+
bulk: Supportable.unsupported,
|
13
|
+
patch: Supportable.unsupported,
|
14
|
+
filter: Supportable.unsupported,
|
15
|
+
changePassword: Supportable.unsupported,
|
16
|
+
sort: Supportable.unsupported,
|
17
|
+
etag: Supportable.unsupported,
|
18
|
+
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
|
19
|
+
meta: Meta.new(
|
20
|
+
resourceType: 'ServiceProviderConfig',
|
21
|
+
created: Time.now,
|
22
|
+
lastModified: Time.now,
|
23
|
+
version: '1'
|
24
|
+
),
|
25
|
+
authenticationSchemes: [AuthenticationScheme.basic]
|
26
|
+
}
|
27
|
+
super(defaults.merge(attributes))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>ScimEngine</title>
|
5
|
+
<%= stylesheet_link_tag "scim_engine/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "scim_engine/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
ScimEngine::Engine.routes.draw do
|
2
|
+
get 'ServiceProviderConfig', to: 'service_provider_configurations#show'
|
3
|
+
get 'ResourceTypes', to: 'resource_types#index'
|
4
|
+
get 'ResourceTypes/:name', to: 'resource_types#show', as: :scim_resource_type
|
5
|
+
get 'Schemas', to: 'schemas#index', as: :scim_schemas
|
6
|
+
end
|
data/lib/scim_engine.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "scim_engine/engine"
|
2
|
+
|
3
|
+
module ScimEngine
|
4
|
+
def self.service_provider_configuration=(custom_configuration)
|
5
|
+
@service_provider_configuration = custom_configuration
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.service_provider_configuration(location:)
|
9
|
+
@service_provider_configuration ||= ServiceProviderConfiguration.new
|
10
|
+
@service_provider_configuration.meta.location = location
|
11
|
+
@service_provider_configuration
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace ScimEngine
|
4
|
+
|
5
|
+
Mime::Type.register "application/scim+json",:scim
|
6
|
+
|
7
|
+
ActionDispatch::Request.parameter_parsers[Mime::Type.lookup('application/scim+json').symbol] = lambda do |body|
|
8
|
+
JSON.parse(body)
|
9
|
+
end
|
10
|
+
|
11
|
+
mattr_accessor :username
|
12
|
+
mattr_accessor :password
|
13
|
+
|
14
|
+
def self.resources
|
15
|
+
default_resources + custom_resources
|
16
|
+
end
|
17
|
+
|
18
|
+
# Can be used to add a new resource type which is not provided by the gem.
|
19
|
+
# @example
|
20
|
+
# module Scim
|
21
|
+
# module Resources
|
22
|
+
# class ShinyResource < ScimEngine::Resources::Base
|
23
|
+
# set_schema Scim::Schema::Shiny
|
24
|
+
#
|
25
|
+
# def self.endpoint
|
26
|
+
# "/Shinies"
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# ScimEngine::Engine.add_custom_resource Scim::Resources::ShinyResource
|
32
|
+
def self.add_custom_resource(resource)
|
33
|
+
custom_resources << resource
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the list of custom resources, if any.
|
37
|
+
def self.custom_resources
|
38
|
+
@custom_resources ||= []
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the default resources added in this gem: User and Group.
|
42
|
+
def self.default_resources
|
43
|
+
[ Resources::User, Resources::Group ]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.schemas
|
47
|
+
resources.map(&:schemas).flatten.uniq.map(&:new)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ScimEngine::ApplicationController do
|
4
|
+
|
5
|
+
context 'custom authentication' do
|
6
|
+
before do
|
7
|
+
allow(ScimEngine::Engine).to receive(:username) { 'A' }
|
8
|
+
allow(ScimEngine::Engine).to receive(:password) { 'B' }
|
9
|
+
end
|
10
|
+
|
11
|
+
controller do
|
12
|
+
rescue_from StandardError, with: :handle_resource_not_found
|
13
|
+
|
14
|
+
def index
|
15
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'renders success when valid creds are given' do
|
20
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('A', 'B')
|
21
|
+
|
22
|
+
get :index, params: { format: :scim }
|
23
|
+
expect(response).to be_ok
|
24
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'renders failure with bad password' do
|
28
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('A', 'C')
|
29
|
+
|
30
|
+
get :index, params: { format: :scim }
|
31
|
+
expect(response).not_to be_ok
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'renders failure with bad user name' do
|
35
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('C', 'B')
|
36
|
+
|
37
|
+
get :index, params: { format: :scim }
|
38
|
+
expect(response).not_to be_ok
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it 'renders failure with bad user name and password' do
|
43
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('C', 'D')
|
44
|
+
|
45
|
+
get :index, params: { format: :scim }
|
46
|
+
expect(response).not_to be_ok
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
context 'authenticated' do
|
55
|
+
controller do
|
56
|
+
rescue_from StandardError, with: :handle_resource_not_found
|
57
|
+
|
58
|
+
def index
|
59
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
60
|
+
end
|
61
|
+
|
62
|
+
def authenticated?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'authenticate' do
|
68
|
+
it 'renders index if authenticated' do
|
69
|
+
get :index, params: { format: :scim }
|
70
|
+
expect(response).to be_ok
|
71
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'renders not authorized response if not authenticated' do
|
75
|
+
allow(controller).to receive(:authenticated?) { false }
|
76
|
+
get :index, params: { format: :scim }
|
77
|
+
expect(response).to have_http_status(:unauthorized)
|
78
|
+
parsed_body = JSON.parse(response.body)
|
79
|
+
expect(parsed_body).to include('schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'])
|
80
|
+
expect(parsed_body).to include('detail' => 'Requires authentication')
|
81
|
+
expect(parsed_body).to include('status' => '401')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'renders resource not found response when resource cannot be found for the given id' do
|
85
|
+
allow(controller).to receive(:index).and_raise(StandardError)
|
86
|
+
get :index, params: { id: 10, format: :scim }
|
87
|
+
expect(response).to have_http_status(:not_found)
|
88
|
+
parsed_body = JSON.parse(response.body)
|
89
|
+
expect(parsed_body).to include('schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'])
|
90
|
+
expect(parsed_body).to include('detail' => 'Resource 10 not found')
|
91
|
+
expect(parsed_body).to include('status' => '404')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'require_scim' do
|
96
|
+
it 'renders not acceptable if the request does not use scim type' do
|
97
|
+
get :index
|
98
|
+
expect(response).to have_http_status(:not_acceptable)
|
99
|
+
|
100
|
+
expect(JSON.parse(response.body)['detail']).to eql('Only application/scim+json type is accepted.')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|