scim_engine 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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +25 -0
  4. data/app/controllers/scim_engine/application_controller.rb +36 -0
  5. data/app/controllers/scim_engine/resource_types_controller.rb +22 -0
  6. data/app/controllers/scim_engine/resources_controller.rb +71 -0
  7. data/app/controllers/scim_engine/schemas_controller.rb +16 -0
  8. data/app/controllers/scim_engine/service_provider_configurations_controller.rb +8 -0
  9. data/app/models/scim_engine/authentication_error.rb +9 -0
  10. data/app/models/scim_engine/authentication_scheme.rb +12 -0
  11. data/app/models/scim_engine/bulk.rb +5 -0
  12. data/app/models/scim_engine/complex_types/base.rb +19 -0
  13. data/app/models/scim_engine/complex_types/email.rb +11 -0
  14. data/app/models/scim_engine/complex_types/name.rb +7 -0
  15. data/app/models/scim_engine/complex_types/reference.rb +7 -0
  16. data/app/models/scim_engine/error_response.rb +13 -0
  17. data/app/models/scim_engine/errors.rb +14 -0
  18. data/app/models/scim_engine/filter.rb +5 -0
  19. data/app/models/scim_engine/meta.rb +7 -0
  20. data/app/models/scim_engine/not_found_error.rb +10 -0
  21. data/app/models/scim_engine/resource_invalid_error.rb +9 -0
  22. data/app/models/scim_engine/resource_type.rb +28 -0
  23. data/app/models/scim_engine/resources/base.rb +110 -0
  24. data/app/models/scim_engine/resources/group.rb +13 -0
  25. data/app/models/scim_engine/resources/user.rb +13 -0
  26. data/app/models/scim_engine/schema/attribute.rb +82 -0
  27. data/app/models/scim_engine/schema/base.rb +30 -0
  28. data/app/models/scim_engine/schema/derived_attributes.rb +24 -0
  29. data/app/models/scim_engine/schema/email.rb +13 -0
  30. data/app/models/scim_engine/schema/group.rb +25 -0
  31. data/app/models/scim_engine/schema/name.rb +15 -0
  32. data/app/models/scim_engine/schema/reference.rb +12 -0
  33. data/app/models/scim_engine/schema/user.rb +28 -0
  34. data/app/models/scim_engine/service_provider_configuration.rb +30 -0
  35. data/app/models/scim_engine/supportable.rb +10 -0
  36. data/app/views/layouts/scim_engine/application.html.erb +14 -0
  37. data/config/routes.rb +6 -0
  38. data/lib/scim_engine/engine.rb +35 -0
  39. data/lib/scim_engine/version.rb +3 -0
  40. data/lib/scim_engine.rb +13 -0
  41. data/lib/tasks/scim_engine_tasks.rake +4 -0
  42. data/spec/controllers/scim_engine/application_controller_spec.rb +104 -0
  43. data/spec/controllers/scim_engine/resource_types_controller_spec.rb +78 -0
  44. data/spec/controllers/scim_engine/resources_controller_spec.rb +134 -0
  45. data/spec/controllers/scim_engine/schemas_controller_spec.rb +66 -0
  46. data/spec/controllers/scim_engine/service_provider_configurations_controller_spec.rb +21 -0
  47. data/spec/dummy/README.rdoc +28 -0
  48. data/spec/dummy/Rakefile +6 -0
  49. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  50. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  51. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  52. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  53. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  54. data/spec/dummy/bin/bundle +3 -0
  55. data/spec/dummy/bin/rails +4 -0
  56. data/spec/dummy/bin/rake +4 -0
  57. data/spec/dummy/bin/setup +29 -0
  58. data/spec/dummy/config/application.rb +16 -0
  59. data/spec/dummy/config/boot.rb +5 -0
  60. data/spec/dummy/config/environment.rb +5 -0
  61. data/spec/dummy/config/environments/development.rb +41 -0
  62. data/spec/dummy/config/environments/production.rb +79 -0
  63. data/spec/dummy/config/environments/test.rb +42 -0
  64. data/spec/dummy/config/initializers/assets.rb +11 -0
  65. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  66. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  67. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  68. data/spec/dummy/config/initializers/inflections.rb +16 -0
  69. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  70. data/spec/dummy/config/initializers/session_store.rb +3 -0
  71. data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  72. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  73. data/spec/dummy/config/locales/en.yml +23 -0
  74. data/spec/dummy/config/routes.rb +4 -0
  75. data/spec/dummy/config/secrets.yml +22 -0
  76. data/spec/dummy/config.ru +4 -0
  77. data/spec/dummy/log/test.log +863 -0
  78. data/spec/dummy/public/404.html +67 -0
  79. data/spec/dummy/public/422.html +67 -0
  80. data/spec/dummy/public/500.html +66 -0
  81. data/spec/dummy/public/favicon.ico +0 -0
  82. data/spec/models/scim_engine/complex_types/email_spec.rb +23 -0
  83. data/spec/models/scim_engine/resource_type_spec.rb +21 -0
  84. data/spec/models/scim_engine/resources/base_spec.rb +246 -0
  85. data/spec/models/scim_engine/resources/base_validation_spec.rb +61 -0
  86. data/spec/models/scim_engine/resources/user_spec.rb +55 -0
  87. data/spec/models/scim_engine/schema/attribute_spec.rb +80 -0
  88. data/spec/models/scim_engine/schema/base_spec.rb +19 -0
  89. data/spec/models/scim_engine/schema/group_spec.rb +66 -0
  90. data/spec/models/scim_engine/schema/user_spec.rb +140 -0
  91. data/spec/rails_helper.rb +57 -0
  92. data/spec/spec_helper.rb +99 -0
  93. 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,10 @@
1
+ module ScimEngine
2
+ class Supportable
3
+ include ActiveModel::Model
4
+ attr_accessor :supported
5
+
6
+ def self.unsupported
7
+ new(supported: false)
8
+ end
9
+ end
10
+ 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
@@ -0,0 +1,3 @@
1
+ module ScimEngine
2
+ VERSION = "1.0.0"
3
+ end
@@ -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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :scim_engine do
3
+ # # Task goes here
4
+ # 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>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -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 .