scim_engine 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/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 +19 -0
- data/app/models/scim_engine/complex_types/email.rb +11 -0
- data/app/models/scim_engine/complex_types/name.rb +7 -0
- data/app/models/scim_engine/complex_types/reference.rb +7 -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 +28 -0
- data/app/models/scim_engine/resources/base.rb +110 -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 +82 -0
- data/app/models/scim_engine/schema/base.rb +30 -0
- data/app/models/scim_engine/schema/derived_attributes.rb +24 -0
- data/app/models/scim_engine/schema/email.rb +13 -0
- data/app/models/scim_engine/schema/group.rb +25 -0
- data/app/models/scim_engine/schema/name.rb +15 -0
- data/app/models/scim_engine/schema/reference.rb +12 -0
- data/app/models/scim_engine/schema/user.rb +28 -0
- data/app/models/scim_engine/service_provider_configuration.rb +30 -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/engine.rb +35 -0
- data/lib/scim_engine/version.rb +3 -0
- data/lib/scim_engine.rb +13 -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 +134 -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/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/config.ru +4 -0
- data/spec/dummy/log/test.log +863 -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,13 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
class Email < Base
|
4
|
+
def self.scim_attributes
|
5
|
+
@scim_attributes ||= [
|
6
|
+
Attribute.new(name: 'value', type: 'string'),
|
7
|
+
Attribute.new(name: 'primary', type: 'boolean', required: false),
|
8
|
+
Attribute.new(name: 'type', type: 'string', required: false)
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
class Group < Base
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
super(name: 'Group',
|
7
|
+
id: self.class.id,
|
8
|
+
description: 'Represents a Group',
|
9
|
+
scim_attributes: self.class.scim_attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.id
|
13
|
+
'urn:ietf:params:scim:schemas:core:2.0:Group'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.scim_attributes
|
17
|
+
[
|
18
|
+
Attribute.new(name: 'displayName', type: 'string'),
|
19
|
+
Attribute.new(name: 'members', multiValued: true, complexType: ScimEngine::ComplexTypes::Reference, mutability: 'readOnly', required: false)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
class Name < Base
|
4
|
+
|
5
|
+
def self.scim_attributes
|
6
|
+
@scim_attributes ||= [
|
7
|
+
Attribute.new(name: 'familyName', type: 'string'),
|
8
|
+
Attribute.new(name: 'givenName', type: 'string'),
|
9
|
+
Attribute.new(name: 'formatted', type: 'string', required: false)
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
class Reference < Base
|
4
|
+
def self.scim_attributes
|
5
|
+
@scim_attributes ||= [
|
6
|
+
Attribute.new(name: 'value', type: 'string', mutability: 'readOnly'),
|
7
|
+
Attribute.new(name: 'display', type: 'string', mutability: 'readOnly', required: false)
|
8
|
+
]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
module Schema
|
3
|
+
class User < Base
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
super(name: 'User',
|
7
|
+
id: self.class.id,
|
8
|
+
description: 'Represents a User',
|
9
|
+
scim_attributes: self.class.scim_attributes)
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.id
|
14
|
+
'urn:ietf:params:scim:schemas:core:2.0:User'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.scim_attributes
|
18
|
+
[
|
19
|
+
Attribute.new(name: 'userName', type: 'string', uniqueness: 'server'),
|
20
|
+
Attribute.new(name: 'name', complexType: ScimEngine::ComplexTypes::Name),
|
21
|
+
Attribute.new(name: 'emails', multiValued: true, complexType: ScimEngine::ComplexTypes::Email),
|
22
|
+
Attribute.new(name: 'groups', multiValued: true, mutability: 'immutable', complexType: ScimEngine::ComplexTypes::Reference)
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ScimEngine
|
2
|
+
class ServiceProviderConfiguration
|
3
|
+
include ActiveModel::Model
|
4
|
+
|
5
|
+
attr_accessor :patch, :bulk, :filter, :changePassword,
|
6
|
+
:sort, :etag, :authenticationSchemes,
|
7
|
+
:schemas, :meta
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
defaults = {
|
11
|
+
bulk: Supportable.unsupported,
|
12
|
+
patch: Supportable.unsupported,
|
13
|
+
filter: Supportable.unsupported,
|
14
|
+
changePassword: Supportable.unsupported,
|
15
|
+
sort: Supportable.unsupported,
|
16
|
+
etag: Supportable.unsupported,
|
17
|
+
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
|
18
|
+
meta: Meta.new(
|
19
|
+
resourceType: 'ServiceProviderConfig',
|
20
|
+
created: Time.now,
|
21
|
+
lastModified: Time.now,
|
22
|
+
version: '1'
|
23
|
+
),
|
24
|
+
authenticationSchemes: [AuthenticationScheme.basic]
|
25
|
+
}
|
26
|
+
super(defaults.merge(attributes))
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
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
|
@@ -0,0 +1,35 @@
|
|
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
|
+
def self.add_custom_resource(resource)
|
19
|
+
custom_resources << resource
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.custom_resources
|
23
|
+
@custom_resources ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.default_resources
|
27
|
+
[ Resources::User, Resources::Group ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.schemas
|
31
|
+
resources.map(&:schemas).flatten.uniq.map(&:new)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
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,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
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ScimEngine::ResourceTypesController do
|
4
|
+
routes { ScimEngine::Engine.routes }
|
5
|
+
|
6
|
+
before(:each) { allow(controller).to receive(:authenticated?).and_return(true) }
|
7
|
+
|
8
|
+
describe 'GET index' do
|
9
|
+
it 'renders the resource type for user' do
|
10
|
+
get :index, format: :scim
|
11
|
+
response_hash = JSON.parse(response.body)
|
12
|
+
expected_response = [ ScimEngine::Resources::User.resource_type(scim_resource_type_url(name: 'User')),
|
13
|
+
ScimEngine::Resources::Group.resource_type(scim_resource_type_url(name: 'Group'))
|
14
|
+
].to_json
|
15
|
+
|
16
|
+
response_hash = JSON.parse(response.body)
|
17
|
+
expect(response_hash).to eql(JSON.parse(expected_response))
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'renders custom resource types' do
|
21
|
+
custom_resource = Class.new(ScimEngine::Resources::Base) do
|
22
|
+
set_schema ScimEngine::Schema::User
|
23
|
+
|
24
|
+
def self.endpoint
|
25
|
+
"/Gaga"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.resource_type_id
|
29
|
+
'Gaga'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
allow(ScimEngine::Engine).to receive(:custom_resources) {[ custom_resource ]}
|
34
|
+
|
35
|
+
get :index, params: { format: :scim }
|
36
|
+
response_hash = JSON.parse(response.body)
|
37
|
+
expect(response_hash.size).to eql(3)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'GET show' do
|
42
|
+
it 'renders the resource type for user' do
|
43
|
+
get :show, params: { name: 'User', format: :scim }
|
44
|
+
response_hash = JSON.parse(response.body)
|
45
|
+
expected_response = ScimEngine::Resources::User.resource_type(scim_resource_type_url(name: 'User')).to_json
|
46
|
+
expect(response_hash).to eql(JSON.parse(expected_response))
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'renders the resource type for group' do
|
50
|
+
get :show, params: { name: 'Group', format: :scim }
|
51
|
+
response_hash = JSON.parse(response.body)
|
52
|
+
expected_response = ScimEngine::Resources::Group.resource_type(scim_resource_type_url(name: 'Group')).to_json
|
53
|
+
expect(response_hash).to eql(JSON.parse(expected_response))
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'renders custom resource type' do
|
57
|
+
custom_resource = Class.new(ScimEngine::Resources::Base) do
|
58
|
+
set_schema ScimEngine::Schema::User
|
59
|
+
|
60
|
+
def self.endpoint
|
61
|
+
"/Gaga"
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.resource_type_id
|
65
|
+
'Gaga'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
allow(ScimEngine::Engine).to receive(:custom_resources) {[ custom_resource ]}
|
70
|
+
|
71
|
+
|
72
|
+
get :show, params: { name: 'Gaga', format: :scim }
|
73
|
+
response_hash = JSON.parse(response.body)
|
74
|
+
expected_response = custom_resource.resource_type(scim_resource_type_url(name: 'Gaga')).to_json
|
75
|
+
expect(response_hash).to eql(JSON.parse(expected_response))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ScimEngine::ResourcesController do
|
4
|
+
|
5
|
+
before(:each) { allow(controller).to receive(:authenticated?).and_return(true) }
|
6
|
+
|
7
|
+
controller do
|
8
|
+
def show
|
9
|
+
super do |id|
|
10
|
+
ScimEngine::Resources::Group.new(id: id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
super(ScimEngine::Resources::Group) do |resource|
|
16
|
+
resource
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def update
|
21
|
+
super(ScimEngine::Resources::Group) do |resource|
|
22
|
+
resource
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy
|
27
|
+
super do |id|
|
28
|
+
successful_delete?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def successful_delete?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'GET show' do
|
38
|
+
it 'renders the resource' do
|
39
|
+
get :show, params: { id: '10', format: :scim }
|
40
|
+
|
41
|
+
expect(response).to be_ok
|
42
|
+
expect(JSON.parse(response.body)).to include('id' => '10')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'POST create' do
|
47
|
+
it 'returns error if body is missing' do
|
48
|
+
post :create, params: { format: :scim }
|
49
|
+
expect(response.status).to eql(400)
|
50
|
+
expect(JSON.parse(response.body)['detail']).to eql('must provide a request body')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'works if the request is valid' do
|
54
|
+
post :create, params: { displayName: 'Sauron biz', format: :scim }
|
55
|
+
expect(response).to have_http_status(:created)
|
56
|
+
expect(JSON.parse(response.body)['displayName']).to eql('Sauron biz')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'renders error if resource object cannot be built from the params' do
|
60
|
+
put :update, params: { id: 'group-id', name: {email: 'a@b.com'}, format: :scim }
|
61
|
+
expect(response.status).to eql(400)
|
62
|
+
expect(JSON.parse(response.body)['detail']).to match(/^Invalid/)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'renders application side error' do
|
66
|
+
allow_any_instance_of(ScimEngine::Resources::Group).to receive(:to_json).and_raise(ScimEngine::ErrorResponse.new(status: 400, detail: 'gaga'))
|
67
|
+
put :update, params: { id: 'group-id', displayName: 'invalid name', format: :scim }
|
68
|
+
expect(response.status).to eql(400)
|
69
|
+
expect(JSON.parse(response.body)['detail']).to eql('gaga')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'renders error if id is provided' do
|
73
|
+
post :create, params: { id: 'some-id', displayName: 'sauron', format: :scim }
|
74
|
+
|
75
|
+
expect(response).to have_http_status(:bad_request)
|
76
|
+
|
77
|
+
parsed_response = JSON.parse(response.body)
|
78
|
+
expect(parsed_response['detail']).to start_with('id and externalId are not valid parameters for create')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'renders error if externalId is provided' do
|
82
|
+
post :create, params: { externalId: 'some-id', displayName: 'sauron', format: :scim }
|
83
|
+
|
84
|
+
expect(response).to have_http_status(:bad_request)
|
85
|
+
|
86
|
+
parsed_response = JSON.parse(response.body)
|
87
|
+
expect(parsed_response['detail']).to start_with('id and externalId are not valid parameters for create')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'PUT update' do
|
92
|
+
it 'returns error if body is missing' do
|
93
|
+
put :update, params: { id: 'group-id', format: :scim }
|
94
|
+
expect(response.status).to eql(400)
|
95
|
+
expect(JSON.parse(response.body)['detail']).to eql('must provide a request body')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'works if the request is valid' do
|
99
|
+
put :update, params: { id: 'group-id', displayName: 'sauron', format: :scim }
|
100
|
+
expect(response.status).to eql(200)
|
101
|
+
expect(JSON.parse(response.body)['displayName']).to eql('sauron')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'renders error if resource object cannot be built from the params' do
|
105
|
+
put :update, params: { id: 'group-id', name: {email: 'a@b.com'}, format: :scim }
|
106
|
+
expect(response.status).to eql(400)
|
107
|
+
expect(JSON.parse(response.body)['detail']).to match(/^Invalid/)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'renders application side error' do
|
111
|
+
allow_any_instance_of(ScimEngine::Resources::Group).to receive(:to_json).and_raise(ScimEngine::ErrorResponse.new(status: 400, detail: 'gaga'))
|
112
|
+
put :update, params: { id: 'group-id', displayName: 'invalid name', format: :scim }
|
113
|
+
expect(response.status).to eql(400)
|
114
|
+
expect(JSON.parse(response.body)['detail']).to eql('gaga')
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'DELETE destroy' do
|
120
|
+
it 'returns an empty response with no content status if deletion is successful' do
|
121
|
+
delete :destroy, params: { id: 'group-id', format: :scim }
|
122
|
+
expect(response).to have_http_status(:no_content)
|
123
|
+
expect(response.body).to be_empty
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'renders error if deletion fails' do
|
127
|
+
allow(controller).to receive(:successful_delete?).and_return(false)
|
128
|
+
delete :destroy, params: { id: 'group-id', format: :scim }
|
129
|
+
expect(response).to have_http_status(:internal_server_error)
|
130
|
+
parsed_response = JSON.parse(response.body)
|
131
|
+
expect(parsed_response['detail']).to eql("Failed to delete the resource with id 'group-id'. Please try again later")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ScimEngine::SchemasController do
|
4
|
+
|
5
|
+
before(:each) { allow(controller).to receive(:authenticated?).and_return(true) }
|
6
|
+
|
7
|
+
controller do
|
8
|
+
def index
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
describe '#index' do
|
13
|
+
it 'returns a collection of supported schemas' do
|
14
|
+
get :index, params: { format: :scim }
|
15
|
+
expect(response).to be_ok
|
16
|
+
parsed_body = JSON.parse(response.body)
|
17
|
+
expect(parsed_body.length).to eql(2)
|
18
|
+
schema_names = parsed_body.map {|schema| schema['name']}
|
19
|
+
expect(schema_names).to match_array(['User', 'Group'])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns only the User schema when its id is provided' do
|
23
|
+
get :index, params: { name: ScimEngine::Schema::User.id, format: :scim }
|
24
|
+
expect(response).to be_ok
|
25
|
+
parsed_body = JSON.parse(response.body)
|
26
|
+
expect(parsed_body['name']).to eql('User')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns only the Group schema when its id is provided' do
|
30
|
+
get :index, params: { name: ScimEngine::Schema::Group.id, format: :scim }
|
31
|
+
expect(response).to be_ok
|
32
|
+
parsed_body = JSON.parse(response.body)
|
33
|
+
expect(parsed_body['name']).to eql('Group')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns only the License schemas when its id is provided' do
|
37
|
+
license_schema = Class.new(ScimEngine::Schema::Base) do
|
38
|
+
def initialize(options = {})
|
39
|
+
super(name: 'License',
|
40
|
+
id: self.class.id,
|
41
|
+
description: 'Represents a License')
|
42
|
+
end
|
43
|
+
def self.id
|
44
|
+
'License'
|
45
|
+
end
|
46
|
+
def self.scim_attributes
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
license_resource = Class.new(ScimEngine::Resources::Base) do
|
52
|
+
set_schema license_schema
|
53
|
+
def self.endopint
|
54
|
+
'/Gaga'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
allow(ScimEngine::Engine).to receive(:custom_resources) {[license_resource]}
|
59
|
+
get :index, params: { name: license_schema.id, format: :scim }
|
60
|
+
expect(response).to be_ok
|
61
|
+
parsed_body = JSON.parse(response.body)
|
62
|
+
expect(parsed_body['name']).to eql('License')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ScimEngine::ServiceProviderConfigurationsController do
|
4
|
+
|
5
|
+
before(:each) { allow(controller).to receive(:authenticated?).and_return(true) }
|
6
|
+
|
7
|
+
controller do
|
8
|
+
def show
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
describe '#show' do
|
13
|
+
it 'renders the servive provider configurations' do
|
14
|
+
get :show, params: { id: 'fake', format: :scim }
|
15
|
+
|
16
|
+
expect(response).to be_ok
|
17
|
+
expect(JSON.parse(response.body)).to include('patch' => {'supported' => false})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|