scimaenaga 0.6.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/app/controllers/concerns/scim_rails/exception_handler.rb +74 -22
- data/app/controllers/scim_rails/scim_groups_controller.rb +14 -3
- data/app/controllers/scim_rails/scim_schemas_controller.rb +42 -0
- data/app/controllers/scim_rails/scim_users_controller.rb +22 -4
- data/app/libraries/scim_patch.rb +15 -9
- data/app/libraries/scim_patch_operation.rb +50 -71
- data/app/libraries/scim_patch_operation_converter.rb +90 -0
- data/app/libraries/scim_patch_operation_group.rb +100 -0
- data/app/libraries/scim_patch_operation_user.rb +53 -0
- data/config/routes.rb +14 -11
- data/lib/generators/scim_rails/templates/initializer.rb +106 -0
- data/lib/scim_rails/config.rb +8 -5
- data/lib/scim_rails/version.rb +1 -1
- data/spec/controllers/scim_rails/scim_groups_controller_spec.rb +25 -7
- data/spec/controllers/scim_rails/scim_schemas_controller_spec.rb +238 -0
- data/spec/controllers/scim_rails/scim_schemas_request_spec.rb +39 -0
- data/spec/controllers/scim_rails/scim_users_controller_spec.rb +361 -220
- data/spec/dummy/app/models/user.rb +9 -0
- data/spec/dummy/config/initializers/scim_rails_config.rb +27 -24
- data/spec/dummy/db/migrate/20220131090107_add_deletable_to_users.rb +5 -0
- data/spec/dummy/db/schema.rb +2 -1
- data/spec/dummy/db/seeds.rb +10 -1
- data/spec/factories/user.rb +2 -0
- data/spec/libraries/scim_patch_operation_group_spec.rb +165 -0
- data/spec/libraries/scim_patch_operation_user_spec.rb +101 -0
- data/spec/libraries/scim_patch_spec.rb +135 -53
- metadata +80 -80
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +0 -377
- data/spec/dummy/put_group.http +0 -5
- data/spec/dummy/tmp/restart.txt +0 -0
- data/spec/libraries/scim_patch_operation_spec.rb +0 -96
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ScimPatchOperationGroup < ScimPatchOperation
|
4
|
+
|
5
|
+
def save(model)
|
6
|
+
if @path_scim[:attribute] == 'members'
|
7
|
+
save_members(model)
|
8
|
+
return
|
9
|
+
end
|
10
|
+
|
11
|
+
case @op
|
12
|
+
when 'add', 'replace'
|
13
|
+
model.attributes = { @path_sp => @value }
|
14
|
+
when 'remove'
|
15
|
+
model.attributes = { @path_sp => nil }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def save_members(model)
|
22
|
+
current_member_ids = model.public_send(member_relation_attribute).map(&:to_s)
|
23
|
+
|
24
|
+
case @op
|
25
|
+
when 'add'
|
26
|
+
member_ids = add_member_ids(current_member_ids)
|
27
|
+
when 'replace'
|
28
|
+
member_ids = replace_member_ids
|
29
|
+
when 'remove'
|
30
|
+
member_ids = remove_member_ids(current_member_ids)
|
31
|
+
end
|
32
|
+
|
33
|
+
model.public_send("#{member_relation_attribute}=", member_ids.uniq)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_member_ids(current_member_ids)
|
37
|
+
current_member_ids.concat(member_ids_from_value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_member_ids
|
41
|
+
member_ids_from_value
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_member_ids(current_member_ids)
|
45
|
+
removed_member_ids = if member_ids_from_value.present?
|
46
|
+
member_ids_from_value
|
47
|
+
else
|
48
|
+
[member_id_from_filter]
|
49
|
+
end
|
50
|
+
current_member_ids - removed_member_ids
|
51
|
+
end
|
52
|
+
|
53
|
+
def member_ids_from_value
|
54
|
+
@member_ids_from_value ||= @value&.map do |v|
|
55
|
+
v['value'].to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def member_id_from_filter
|
60
|
+
@path_scim.dig(:filter, :parameter)
|
61
|
+
end
|
62
|
+
|
63
|
+
def member_relation_attribute
|
64
|
+
ScimRails.config.group_member_relation_attribute
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate(_op, _path, _value)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
def path_scim_to_path_sp(path_scim)
|
72
|
+
# path_scim example1:
|
73
|
+
# {
|
74
|
+
# attribute: 'members',
|
75
|
+
# filter: {
|
76
|
+
# attribute: 'value',
|
77
|
+
# operator: 'eq',
|
78
|
+
# parameter: 'XXXX'
|
79
|
+
# },
|
80
|
+
# rest_path: []
|
81
|
+
# }
|
82
|
+
|
83
|
+
# path_scim example2:
|
84
|
+
# {
|
85
|
+
# attribute: 'displayName',
|
86
|
+
# filter: nil,
|
87
|
+
# rest_path: []
|
88
|
+
# }
|
89
|
+
if path_scim[:attribute] == 'members'
|
90
|
+
return ScimRails.config.group_member_relation_attribute
|
91
|
+
end
|
92
|
+
|
93
|
+
dig_keys = [path_scim[:attribute].to_sym]
|
94
|
+
dig_keys.concat(path_scim[:rest_path].map(&:to_sym))
|
95
|
+
|
96
|
+
# *dig_keys example: displayName
|
97
|
+
ScimRails.config.mutable_group_attributes_schema.dig(*dig_keys)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ScimPatchOperationUser < ScimPatchOperation
|
4
|
+
|
5
|
+
def save(model)
|
6
|
+
case @op
|
7
|
+
when 'add', 'replace'
|
8
|
+
model.attributes = { @path_sp => @value }
|
9
|
+
when 'remove'
|
10
|
+
model.attributes = { @path_sp => nil }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def validate(_op, _path, value)
|
17
|
+
if value.instance_of? Array
|
18
|
+
raise ScimRails::ExceptionHandler::UnsupportedPatchRequest
|
19
|
+
end
|
20
|
+
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
def path_scim_to_path_sp(path_scim)
|
25
|
+
# path_scim example1:
|
26
|
+
# {
|
27
|
+
# attribute: 'emails',
|
28
|
+
# filter: {
|
29
|
+
# attribute: 'type',
|
30
|
+
# operator: 'eq',
|
31
|
+
# parameter: 'work'
|
32
|
+
# },
|
33
|
+
# rest_path: ['value']
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# path_scim example2:
|
37
|
+
# {
|
38
|
+
# attribute: 'name',
|
39
|
+
# filter: nil,
|
40
|
+
# rest_path: ['givenName']
|
41
|
+
# }
|
42
|
+
dig_keys = [path_scim[:attribute].to_sym]
|
43
|
+
|
44
|
+
# Library ignores filter conditions ([type eq "work"])
|
45
|
+
dig_keys << 0 if path_scim[:attribute] == 'emails'
|
46
|
+
|
47
|
+
dig_keys.concat(path_scim[:rest_path].map(&:to_sym))
|
48
|
+
|
49
|
+
# *dig_keys example: emails, 0, value
|
50
|
+
ScimRails.config.mutable_user_attributes_schema.dig(*dig_keys)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
ScimRails::Engine.routes.draw do
|
2
|
-
get 'scim/v2/Users',
|
3
|
-
post 'scim/v2/Users',
|
4
|
-
get 'scim/v2/Users/:id',
|
5
|
-
put 'scim/v2/Users/:id',
|
6
|
-
patch 'scim/v2/Users/:id',
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
get 'scim/v2/Users', action: :index, controller: 'scim_users'
|
3
|
+
post 'scim/v2/Users', action: :create, controller: 'scim_users'
|
4
|
+
get 'scim/v2/Users/:id', action: :show, controller: 'scim_users'
|
5
|
+
put 'scim/v2/Users/:id', action: :put_update, controller: 'scim_users'
|
6
|
+
patch 'scim/v2/Users/:id', action: :patch_update, controller: 'scim_users'
|
7
|
+
delete 'scim/v2/Users/:id', action: :destroy, controller: 'scim_users'
|
8
|
+
get 'scim/v2/Groups', action: :index, controller: 'scim_groups'
|
9
|
+
post 'scim/v2/Groups', action: :create, controller: 'scim_groups'
|
10
|
+
get 'scim/v2/Groups/:id', action: :show, controller: 'scim_groups'
|
11
|
+
put 'scim/v2/Groups/:id', action: :put_update, controller: 'scim_groups'
|
12
|
+
patch 'scim/v2/Groups/:id', action: :patch_update, controller: 'scim_groups'
|
13
|
+
delete 'scim/v2/Groups/:id', action: :destroy, controller: 'scim_groups'
|
14
|
+
get 'scim/v2/Schemas', action: :index, controller: 'scim_schemas'
|
15
|
+
get 'scim/v2/Schemas/:id', action: :show, controller: 'scim_schemas'
|
13
16
|
end
|
@@ -157,4 +157,110 @@ ScimRails.configure do |config|
|
|
157
157
|
# Set group_destroy_method to a method on the Group model
|
158
158
|
# to be called on a destroy request
|
159
159
|
# config.group_destroy_method = :destroy!
|
160
|
+
|
161
|
+
# /Schemas settings.
|
162
|
+
# These settings are not used in /Users and /Groups for now.
|
163
|
+
# Configure this only when you need Schemas endpoint.
|
164
|
+
# Schemas endpoint returns the configured values as-is.
|
165
|
+
config.schemas = [
|
166
|
+
# Define User schemas
|
167
|
+
{
|
168
|
+
# Normally you don't have to change schemas/id/name/description
|
169
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:Schema'],
|
170
|
+
id: 'urn:ietf:params:scim:schemas:core:2.0:User',
|
171
|
+
name: 'User',
|
172
|
+
description: 'User Account',
|
173
|
+
|
174
|
+
# Configure 'attributes' as it corresponds with other configurations and your model
|
175
|
+
attributes: [
|
176
|
+
{
|
177
|
+
# Name of SCIM attribute. It must be configured in "user_schema"
|
178
|
+
name: 'userName',
|
179
|
+
|
180
|
+
# "type" must be string/boolan/decimal/integer/dateTime/reference
|
181
|
+
# "complex" value is not supported now
|
182
|
+
type: 'string',
|
183
|
+
|
184
|
+
# Multi value attribute is not supported, must be false
|
185
|
+
multiValued: false,
|
186
|
+
|
187
|
+
description: 'Unique identifier for the User. REQUIRED.',
|
188
|
+
|
189
|
+
# Specify true when you require this attribute
|
190
|
+
required: true,
|
191
|
+
|
192
|
+
# In this Library, String value is always handled as case exact
|
193
|
+
caseExact: true,
|
194
|
+
|
195
|
+
# "mutability" must be readOnly/readWrite/writeOnly
|
196
|
+
# "immutable" is not supported.
|
197
|
+
# readOnly: attribute is defined in queryable_user_attributes but not in mutable_user_attributes and user_schema
|
198
|
+
# readWrite: attribute is defined in queryable_user_attributes, mutable_user_attributes and user_schema
|
199
|
+
# writeOnly: attribute is defined in mutable_user_attributes, and user_schema but not in queryable_user_attributes
|
200
|
+
mutability: 'readWrite',
|
201
|
+
|
202
|
+
# "returned" must be always/never. default and request are not supported
|
203
|
+
# always: attribute is defined in user_schema
|
204
|
+
# never: attribute is not defined in user_schema
|
205
|
+
returned: 'always',
|
206
|
+
|
207
|
+
# "uniqueness" must be none/server/global. It's dependent on your service
|
208
|
+
uniqueness: 'server',
|
209
|
+
}
|
210
|
+
],
|
211
|
+
meta: {
|
212
|
+
resourceType: 'Schema',
|
213
|
+
location:
|
214
|
+
'/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User',
|
215
|
+
},
|
216
|
+
},
|
217
|
+
# define Group schemas
|
218
|
+
{
|
219
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:Schema'],
|
220
|
+
id: 'urn:ietf:params:scim:schemas:core:2.0:Group',
|
221
|
+
name: 'Group',
|
222
|
+
description: 'Group',
|
223
|
+
attributes: [
|
224
|
+
{
|
225
|
+
# Same as the User attributes
|
226
|
+
name: 'displayName',
|
227
|
+
type: 'string',
|
228
|
+
multiValued: false,
|
229
|
+
description: 'A human-readable name for the Group. REQUIRED.',
|
230
|
+
required: true,
|
231
|
+
caseExact: true,
|
232
|
+
mutability: 'readWrite',
|
233
|
+
returned: 'always',
|
234
|
+
uniqueness: 'none',
|
235
|
+
},
|
236
|
+
{
|
237
|
+
name: 'members',
|
238
|
+
|
239
|
+
# Only "members" can be configured as a complex and multivalued attribute
|
240
|
+
type: 'complex',
|
241
|
+
multiValued: true,
|
242
|
+
|
243
|
+
description: 'A list of members of the Group.',
|
244
|
+
required: false,
|
245
|
+
subAttributes: [
|
246
|
+
{
|
247
|
+
name: 'value',
|
248
|
+
type: 'string',
|
249
|
+
multiValued: false,
|
250
|
+
description: 'Identifier of the member of this Group.',
|
251
|
+
required: false,
|
252
|
+
caseExact: true,
|
253
|
+
mutability: 'immutable',
|
254
|
+
returned: 'default',
|
255
|
+
uniqueness: 'none',
|
256
|
+
}
|
257
|
+
],
|
258
|
+
}
|
259
|
+
],
|
260
|
+
meta: {
|
261
|
+
resourceType: 'Schema',
|
262
|
+
location: '/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group',
|
263
|
+
},
|
264
|
+
}
|
265
|
+
]
|
160
266
|
end
|
data/lib/scim_rails/config.rb
CHANGED
@@ -13,7 +13,7 @@ module ScimRails
|
|
13
13
|
|
14
14
|
# Class containing configuration of ScimRails
|
15
15
|
class Config
|
16
|
-
ALGO_NONE =
|
16
|
+
ALGO_NONE = 'none'
|
17
17
|
|
18
18
|
attr_writer \
|
19
19
|
:basic_auth_model,
|
@@ -44,20 +44,23 @@ module ScimRails
|
|
44
44
|
:user_attributes,
|
45
45
|
:user_schema,
|
46
46
|
:group_schema,
|
47
|
-
:
|
47
|
+
:user_destroy_method,
|
48
|
+
:group_destroy_method,
|
49
|
+
:schemas
|
48
50
|
|
49
51
|
def initialize
|
50
|
-
@basic_auth_model =
|
52
|
+
@basic_auth_model = 'Company'
|
51
53
|
@scim_users_list_order = :id
|
52
|
-
@scim_users_model =
|
54
|
+
@scim_users_model = 'User'
|
53
55
|
@scim_groups_list_order = :id
|
54
|
-
@scim_groups_model =
|
56
|
+
@scim_groups_model = 'Group'
|
55
57
|
@signing_algorithm = ALGO_NONE
|
56
58
|
@user_schema = {}
|
57
59
|
@user_attributes = []
|
58
60
|
@user_abbreviated_schema = {}
|
59
61
|
@group_schema = {}
|
60
62
|
@group_abbreviated_schema = {}
|
63
|
+
@schemas = []
|
61
64
|
end
|
62
65
|
|
63
66
|
def mutable_user_attributes_schema
|
data/lib/scim_rails/version.rb
CHANGED
@@ -505,12 +505,6 @@ RSpec.describe ScimRails::ScimGroupsController, type: :controller do
|
|
505
505
|
end
|
506
506
|
|
507
507
|
context 'when Group destroy method is configured' do
|
508
|
-
before do
|
509
|
-
allow(ScimRails.config).to(
|
510
|
-
receive(:group_destroy_method).and_return(:destroy!)
|
511
|
-
)
|
512
|
-
end
|
513
|
-
|
514
508
|
it 'returns empty response' do
|
515
509
|
delete :destroy, params: { id: 1 }, as: :json
|
516
510
|
|
@@ -557,7 +551,31 @@ RSpec.describe ScimRails::ScimGroupsController, type: :controller do
|
|
557
551
|
delete :destroy, params: { id: 1 }, as: :json
|
558
552
|
end.not_to change { company.groups.reload.count }.from(1)
|
559
553
|
|
560
|
-
expect(response.status).to eq
|
554
|
+
expect(response.status).to eq 500
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
context 'when Group destroy method is invalid' do
|
559
|
+
it 'does not delete Group' do
|
560
|
+
allow(ScimRails.config).to(
|
561
|
+
receive(:group_destroy_method).and_return('destory!')
|
562
|
+
)
|
563
|
+
|
564
|
+
expect do
|
565
|
+
delete :destroy, params: { id: 1 }, as: :json
|
566
|
+
end.not_to change { company.groups.reload.count }.from(1)
|
567
|
+
|
568
|
+
expect(response.status).to eq 500
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
context 'whenr target Group is not found' do
|
573
|
+
it 'return 404 not found' do
|
574
|
+
expect do
|
575
|
+
delete :destroy, params: { id: 999999 }, as: :json
|
576
|
+
end.not_to change { company.groups.reload.count }.from(1)
|
577
|
+
|
578
|
+
expect(response.status).to eq 404
|
561
579
|
end
|
562
580
|
end
|
563
581
|
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe ScimRails::ScimSchemasController, type: :controller do
|
6
|
+
include AuthHelper
|
7
|
+
|
8
|
+
routes { ScimRails::Engine.routes }
|
9
|
+
|
10
|
+
let(:schemas) do
|
11
|
+
[
|
12
|
+
{
|
13
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:Schema'],
|
14
|
+
id: 'urn:ietf:params:scim:schemas:core:2.0:User',
|
15
|
+
name: 'User',
|
16
|
+
description: 'User Account',
|
17
|
+
attributes: [
|
18
|
+
{
|
19
|
+
name: 'userName',
|
20
|
+
type: 'string',
|
21
|
+
multiValued: false,
|
22
|
+
description: 'Unique identifier for the User. REQUIRED.',
|
23
|
+
required: true,
|
24
|
+
caseExact: false,
|
25
|
+
mutability: 'readWrite',
|
26
|
+
returned: 'default',
|
27
|
+
uniqueness: 'server',
|
28
|
+
}
|
29
|
+
],
|
30
|
+
meta: {
|
31
|
+
resourceType: 'Schema',
|
32
|
+
location:
|
33
|
+
'/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User',
|
34
|
+
},
|
35
|
+
},
|
36
|
+
{
|
37
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:Schema'],
|
38
|
+
id: 'urn:ietf:params:scim:schemas:core:2.0:Group',
|
39
|
+
name: 'Group',
|
40
|
+
description: 'Group',
|
41
|
+
attributes: [
|
42
|
+
{
|
43
|
+
name: 'displayName',
|
44
|
+
type: 'string',
|
45
|
+
multiValued: false,
|
46
|
+
description: 'A human-readable name for the Group. REQUIRED.',
|
47
|
+
required: false,
|
48
|
+
caseExact: false,
|
49
|
+
mutability: 'readWrite',
|
50
|
+
returned: 'default',
|
51
|
+
uniqueness: 'none',
|
52
|
+
}
|
53
|
+
],
|
54
|
+
meta: {
|
55
|
+
resourceType: 'Schema',
|
56
|
+
location: '/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group',
|
57
|
+
},
|
58
|
+
}
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:schemas_110) do
|
63
|
+
[
|
64
|
+
{ id: 'dummy1' }, { id: 'dummy2' }, { id: 'dummy3' },
|
65
|
+
{ id: 'dummy4' }, { id: 'dummy5' }, { id: 'dummy6' },
|
66
|
+
{ id: 'dummy7' }, { id: 'dummy8' }, { id: 'dummy9' },
|
67
|
+
{ id: 'dummy10' }, { id: 'dummy11' }, { id: 'dummy12' },
|
68
|
+
{ id: 'dummy13' }, { id: 'dummy14' }, { id: 'dummy15' },
|
69
|
+
{ id: 'dummy16' }, { id: 'dummy17' }, { id: 'dummy18' },
|
70
|
+
{ id: 'dummy19' }, { id: 'dummy20' }, { id: 'dummy21' },
|
71
|
+
{ id: 'dummy22' }, { id: 'dummy23' }, { id: 'dummy24' },
|
72
|
+
{ id: 'dummy25' }, { id: 'dummy26' }, { id: 'dummy27' },
|
73
|
+
{ id: 'dummy28' }, { id: 'dummy29' }, { id: 'dummy30' },
|
74
|
+
{ id: 'dummy31' }, { id: 'dummy32' }, { id: 'dummy33' },
|
75
|
+
{ id: 'dummy34' }, { id: 'dummy35' }, { id: 'dummy36' },
|
76
|
+
{ id: 'dummy37' }, { id: 'dummy38' }, { id: 'dummy39' },
|
77
|
+
{ id: 'dummy40' }, { id: 'dummy41' }, { id: 'dummy42' },
|
78
|
+
{ id: 'dummy43' }, { id: 'dummy44' }, { id: 'dummy45' },
|
79
|
+
{ id: 'dummy46' }, { id: 'dummy47' }, { id: 'dummy48' },
|
80
|
+
{ id: 'dummy49' }, { id: 'dummy50' }, { id: 'dummy51' },
|
81
|
+
{ id: 'dummy52' }, { id: 'dummy53' }, { id: 'dummy54' },
|
82
|
+
{ id: 'dummy55' }, { id: 'dummy56' }, { id: 'dummy57' },
|
83
|
+
{ id: 'dummy58' }, { id: 'dummy59' }, { id: 'dummy60' },
|
84
|
+
{ id: 'dummy61' }, { id: 'dummy62' }, { id: 'dummy63' },
|
85
|
+
{ id: 'dummy64' }, { id: 'dummy65' }, { id: 'dummy66' },
|
86
|
+
{ id: 'dummy67' }, { id: 'dummy68' }, { id: 'dummy69' },
|
87
|
+
{ id: 'dummy70' }, { id: 'dummy71' }, { id: 'dummy72' },
|
88
|
+
{ id: 'dummy73' }, { id: 'dummy74' }, { id: 'dummy75' },
|
89
|
+
{ id: 'dummy76' }, { id: 'dummy77' }, { id: 'dummy78' },
|
90
|
+
{ id: 'dummy79' }, { id: 'dummy80' }, { id: 'dummy81' },
|
91
|
+
{ id: 'dummy82' }, { id: 'dummy83' }, { id: 'dummy84' },
|
92
|
+
{ id: 'dummy85' }, { id: 'dummy86' }, { id: 'dummy87' },
|
93
|
+
{ id: 'dummy88' }, { id: 'dummy89' }, { id: 'dummy90' },
|
94
|
+
{ id: 'dummy91' }, { id: 'dummy92' }, { id: 'dummy93' },
|
95
|
+
{ id: 'dummy94' }, { id: 'dummy95' }, { id: 'dummy96' },
|
96
|
+
{ id: 'dummy97' }, { id: 'dummy98' }, { id: 'dummy99' },
|
97
|
+
{ id: 'dummy100' }, { id: 'dummy101' }, { id: 'dummy102' },
|
98
|
+
{ id: 'dummy103' }, { id: 'dummy104' }, { id: 'dummy105' },
|
99
|
+
{ id: 'dummy106' }, { id: 'dummy107' }, { id: 'dummy108' },
|
100
|
+
{ id: 'dummy109' }, { id: 'dummy110' }
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'index' do
|
105
|
+
let(:company) { create(:company) }
|
106
|
+
|
107
|
+
context 'when unauthorized' do
|
108
|
+
it 'returns scim+json content type' do
|
109
|
+
get :index, as: :json
|
110
|
+
|
111
|
+
expect(response.media_type).to eq 'application/scim+json'
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'fails with no credentials' do
|
115
|
+
get :index, as: :json
|
116
|
+
|
117
|
+
expect(response.status).to eq 401
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'fails with invalid credentials' do
|
121
|
+
request.env['HTTP_AUTHORIZATION'] =
|
122
|
+
ActionController::HttpAuthentication::Basic
|
123
|
+
.encode_credentials('unauthorized', '123456')
|
124
|
+
|
125
|
+
get :index, as: :json
|
126
|
+
|
127
|
+
expect(response.status).to eq 401
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when authorized' do
|
132
|
+
before :each do
|
133
|
+
http_login(company)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns scim+json content type' do
|
137
|
+
get :index, as: :json
|
138
|
+
|
139
|
+
expect(response.media_type).to eq 'application/scim+json'
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'is successful with valid credentials' do
|
143
|
+
get :index, as: :json
|
144
|
+
|
145
|
+
expect(response.status).to eq 200
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'returns all results' do
|
149
|
+
allow(ScimRails.config).to(receive(:schemas).and_return(schemas))
|
150
|
+
get :index, as: :json
|
151
|
+
response_body = JSON.parse(response.body)
|
152
|
+
expect(response_body.dig('schemas', 0)).to(
|
153
|
+
eq 'urn:ietf:params:scim:api:messages:2.0:ListResponse'
|
154
|
+
)
|
155
|
+
expect(response_body['totalResults']).to eq 2
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'defaults to 100 results' do
|
159
|
+
allow(ScimRails.config).to(receive(:schemas).and_return(schemas_110))
|
160
|
+
|
161
|
+
get :index, as: :json
|
162
|
+
response_body = JSON.parse(response.body)
|
163
|
+
expect(response_body['totalResults']).to eq 110
|
164
|
+
expect(response_body['startIndex']).to eq 1
|
165
|
+
expect(response_body['Resources'].count).to eq 100
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'paginates results' do
|
169
|
+
allow(ScimRails.config).to(receive(:schemas).and_return(schemas_110))
|
170
|
+
get :index, params: {
|
171
|
+
startIndex: 101,
|
172
|
+
count: 5,
|
173
|
+
}, as: :json
|
174
|
+
response_body = JSON.parse(response.body)
|
175
|
+
expect(response_body['totalResults']).to eq 110
|
176
|
+
expect(response_body['startIndex']).to eq 101
|
177
|
+
expect(response_body['Resources'].count).to eq 5
|
178
|
+
expect(response_body.dig('Resources', 0, 'id')).to eq 'dummy101'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'show' do
|
184
|
+
let(:company) { create(:company) }
|
185
|
+
|
186
|
+
context 'when unauthorized' do
|
187
|
+
it 'returns scim+json content type' do
|
188
|
+
get :show, params: { id: 1 }, as: :json
|
189
|
+
|
190
|
+
expect(response.media_type).to eq 'application/scim+json'
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'fails with no credentials' do
|
194
|
+
get :show, params: { id: 1 }, as: :json
|
195
|
+
|
196
|
+
expect(response.status).to eq 401
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'fails with invalid credentials' do
|
200
|
+
request.env['HTTP_AUTHORIZATION'] =
|
201
|
+
ActionController::HttpAuthentication::Basic
|
202
|
+
.encode_credentials('unauthorized', '123456')
|
203
|
+
|
204
|
+
get :show, params: { id: 1 }, as: :json
|
205
|
+
|
206
|
+
expect(response.status).to eq 401
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'when authorized' do
|
211
|
+
before :each do
|
212
|
+
http_login(company)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'returns scim+json content type' do
|
216
|
+
allow(ScimRails.config).to(receive(:schemas).and_return(schemas))
|
217
|
+
get :show, params: { id: 'urn:ietf:params:scim:schemas:core:2.0:User' }, as: :json
|
218
|
+
|
219
|
+
expect(response.media_type).to eq 'application/scim+json'
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'is successful with valid credentials' do
|
223
|
+
allow(ScimRails.config).to(receive(:schemas).and_return(schemas))
|
224
|
+
get :show, params: { id: 'urn:ietf:params:scim:schemas:core:2.0:User' }, as: :json
|
225
|
+
|
226
|
+
response_body = JSON.parse(response.body)
|
227
|
+
expect(response.status).to eq 200
|
228
|
+
expect(response_body['name']).to eq 'User'
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'returns :not_found for id that cannot be found' do
|
232
|
+
get :show, params: { id: 'fake_id' }, as: :json
|
233
|
+
|
234
|
+
expect(response.status).to eq 404
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe ScimRails::ScimSchemasController, type: :request do
|
6
|
+
let(:company) { create(:company) }
|
7
|
+
let(:credentials) do
|
8
|
+
Base64.encode64("#{company.subdomain}:#{company.api_token}")
|
9
|
+
end
|
10
|
+
let(:authorization) { "Basic #{credentials}" }
|
11
|
+
|
12
|
+
def get_request(content_type = 'application/scim+json')
|
13
|
+
get '/scim/v2/Schemas',
|
14
|
+
headers: {
|
15
|
+
Authorization: authorization,
|
16
|
+
'Content-Type': content_type,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'OAuth Bearer Authorization' do
|
21
|
+
context 'with valid token' do
|
22
|
+
let(:authorization) { "Bearer #{company.api_token}" }
|
23
|
+
|
24
|
+
it 'supports OAuth bearer authorization and succeeds' do
|
25
|
+
get_request
|
26
|
+
expect(response.status).to eq 200
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with invalid token' do
|
31
|
+
let(:authorization) { "Bearer #{SecureRandom.hex}" }
|
32
|
+
|
33
|
+
it 'The request fails' do
|
34
|
+
get_request
|
35
|
+
expect(response.status).to eq 401
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|