scimitar 1.10.0 → 2.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.
- checksums.yaml +4 -4
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +23 -98
- data/app/controllers/scimitar/application_controller.rb +13 -41
- data/app/controllers/scimitar/resource_types_controller.rb +2 -0
- data/app/controllers/scimitar/resources_controller.rb +2 -0
- data/app/controllers/scimitar/schemas_controller.rb +3 -366
- data/app/controllers/scimitar/service_provider_configurations_controller.rb +1 -0
- data/app/models/scimitar/complex_types/address.rb +6 -0
- data/app/models/scimitar/engine_configuration.rb +5 -15
- data/app/models/scimitar/error_response.rb +0 -12
- data/app/models/scimitar/lists/query_parser.rb +13 -113
- data/app/models/scimitar/resource_invalid_error.rb +1 -1
- data/app/models/scimitar/resources/base.rb +9 -53
- data/app/models/scimitar/resources/mixin.rb +59 -646
- data/app/models/scimitar/schema/address.rb +0 -1
- data/app/models/scimitar/schema/attribute.rb +5 -14
- data/app/models/scimitar/schema/base.rb +1 -1
- data/app/models/scimitar/schema/name.rb +2 -2
- data/app/models/scimitar/schema/user.rb +10 -10
- data/app/models/scimitar/schema/vdtp.rb +1 -1
- data/app/models/scimitar/service_provider_configuration.rb +3 -14
- data/config/initializers/scimitar.rb +3 -69
- data/lib/scimitar/engine.rb +12 -57
- data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +10 -140
- data/lib/scimitar/version.rb +2 -2
- data/lib/scimitar.rb +2 -7
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
- data/spec/apps/dummy/app/models/mock_group.rb +1 -1
- data/spec/apps/dummy/app/models/mock_user.rb +9 -52
- data/spec/apps/dummy/config/application.rb +1 -0
- data/spec/apps/dummy/config/environments/test.rb +28 -5
- data/spec/apps/dummy/config/initializers/scimitar.rb +10 -90
- data/spec/apps/dummy/config/routes.rb +7 -28
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -11
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
- data/spec/apps/dummy/db/schema.rb +4 -12
- data/spec/controllers/scimitar/application_controller_spec.rb +3 -126
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +2 -2
- data/spec/controllers/scimitar/schemas_controller_spec.rb +48 -344
- data/spec/models/scimitar/complex_types/address_spec.rb +4 -3
- data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +9 -146
- data/spec/models/scimitar/resources/base_spec.rb +71 -217
- data/spec/models/scimitar/resources/base_validation_spec.rb +5 -43
- data/spec/models/scimitar/resources/mixin_spec.rb +129 -1508
- data/spec/models/scimitar/schema/attribute_spec.rb +3 -22
- data/spec/models/scimitar/schema/base_spec.rb +1 -1
- data/spec/models/scimitar/schema/user_spec.rb +2 -12
- data/spec/requests/active_record_backed_resources_controller_spec.rb +66 -1016
- data/spec/requests/application_controller_spec.rb +3 -16
- data/spec/requests/engine_spec.rb +0 -75
- data/spec/spec_helper.rb +1 -9
- data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +0 -108
- metadata +26 -37
- data/LICENSE.txt +0 -21
- data/README.md +0 -717
- data/lib/scimitar/support/utilities.rb +0 -111
- data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +0 -24
- data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +0 -25
@@ -1,42 +1,25 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'time'
|
3
2
|
|
4
3
|
RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
5
4
|
before :each do
|
6
5
|
allow_any_instance_of(Scimitar::ApplicationController).to receive(:authenticated?).and_return(true)
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# happen if we were using integer primary keys).
|
12
|
-
#
|
13
|
-
lmt = Time.parse("2023-01-09 14:25:00 +1300")
|
14
|
-
ids = 3.times.map { SecureRandom.uuid }.sort()
|
15
|
-
|
16
|
-
@u1 = MockUser.create!(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
|
17
|
-
@u2 = MockUser.create!(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2, password: 'oldpassword')
|
18
|
-
@u3 = MockUser.create!(primary_key: ids.shift(), username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
|
19
|
-
|
20
|
-
@g1 = MockGroup.create!(display_name: 'Group 1')
|
21
|
-
@g2 = MockGroup.create!(display_name: 'Group 2')
|
22
|
-
@g3 = MockGroup.create!(display_name: 'Group 3')
|
7
|
+
@u1 = MockUser.create(username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com')
|
8
|
+
@u2 = MockUser.create(username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com')
|
9
|
+
@u3 = MockUser.create(username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com')
|
23
10
|
end
|
24
11
|
|
25
12
|
# ===========================================================================
|
26
13
|
|
27
14
|
context '#index' do
|
28
15
|
context 'with no items' do
|
29
|
-
|
16
|
+
it 'returns empty list' do
|
30
17
|
MockUser.delete_all
|
31
|
-
end
|
32
18
|
|
33
|
-
it 'returns empty list' do
|
34
19
|
expect_any_instance_of(MockUsersController).to receive(:index).once.and_call_original
|
35
20
|
get '/Users', params: { format: :scim }
|
36
21
|
|
37
|
-
expect(response.status
|
38
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
39
|
-
|
22
|
+
expect(response.status).to eql(200)
|
40
23
|
result = JSON.parse(response.body)
|
41
24
|
|
42
25
|
expect(result['totalResults']).to eql(0)
|
@@ -46,227 +29,55 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
46
29
|
end # "context 'with no items' do"
|
47
30
|
|
48
31
|
context 'with items' do
|
49
|
-
|
50
|
-
|
51
|
-
get '/Users', params: { format: :scim }
|
52
|
-
|
53
|
-
expect(response.status ).to eql(200)
|
54
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
55
|
-
|
56
|
-
result = JSON.parse(response.body)
|
57
|
-
|
58
|
-
expect(result['totalResults']).to eql(3)
|
59
|
-
expect(result['Resources'].size).to eql(3)
|
60
|
-
|
61
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
62
|
-
expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s, @u3.primary_key.to_s])
|
63
|
-
|
64
|
-
usernames = result['Resources'].map { |resource| resource['userName'] }
|
65
|
-
expect(usernames).to match_array(['1', '2', '3'])
|
66
|
-
end
|
67
|
-
end # "context 'with a UUID, renamed primary key column' do"
|
68
|
-
|
69
|
-
context 'with an integer, conventionally named primary key column' do
|
70
|
-
it 'returns all items' do
|
71
|
-
get '/Groups', params: { format: :scim }
|
72
|
-
|
73
|
-
expect(response.status ).to eql(200)
|
74
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
75
|
-
|
76
|
-
result = JSON.parse(response.body)
|
77
|
-
|
78
|
-
expect(result['totalResults']).to eql(3)
|
79
|
-
expect(result['Resources'].size).to eql(3)
|
80
|
-
|
81
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
82
|
-
expect(ids).to match_array([@g1.id.to_s, @g2.id.to_s, @g3.id.to_s])
|
83
|
-
|
84
|
-
usernames = result['Resources'].map { |resource| resource['displayName'] }
|
85
|
-
expect(usernames).to match_array(['Group 1', 'Group 2', 'Group 3'])
|
86
|
-
end
|
87
|
-
end # "context 'with an integer, conventionally named primary key column' do"
|
88
|
-
|
89
|
-
it 'applies a filter, with case-insensitive value comparison' do
|
90
|
-
get '/Users', params: {
|
91
|
-
format: :scim,
|
92
|
-
filter: 'name.givenName eq "FOO" and name.familyName pr and emails ne "home_1@test.com"'
|
93
|
-
}
|
94
|
-
|
95
|
-
expect(response.status ).to eql(200)
|
96
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
97
|
-
|
98
|
-
result = JSON.parse(response.body)
|
99
|
-
|
100
|
-
expect(result['totalResults']).to eql(1)
|
101
|
-
expect(result['Resources'].size).to eql(1)
|
102
|
-
|
103
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
104
|
-
expect(ids).to match_array([@u2.primary_key.to_s])
|
105
|
-
|
106
|
-
usernames = result['Resources'].map { |resource| resource['userName'] }
|
107
|
-
expect(usernames).to match_array(['2'])
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'returns only the requested attributes' do
|
111
|
-
get '/Users', params: {
|
112
|
-
format: :scim,
|
113
|
-
attributes: "id,name"
|
114
|
-
}
|
115
|
-
|
116
|
-
expect(response.status ).to eql(200)
|
117
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
32
|
+
it 'returns all items' do
|
33
|
+
get '/Users', params: { format: :scim }
|
118
34
|
|
35
|
+
expect(response.status).to eql(200)
|
119
36
|
result = JSON.parse(response.body)
|
120
37
|
|
121
38
|
expect(result['totalResults']).to eql(3)
|
122
39
|
expect(result['Resources'].size).to eql(3)
|
123
40
|
|
124
|
-
keys = result['Resources'].map { |resource| resource.keys }.flatten.uniq
|
125
|
-
|
126
|
-
expect(keys).to match_array(%w[
|
127
|
-
id
|
128
|
-
meta
|
129
|
-
name
|
130
|
-
schemas
|
131
|
-
urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
|
132
|
-
urn:ietf:params:scim:schemas:extension:manager:1.0:User
|
133
|
-
])
|
134
|
-
expect(result.dig('Resources', 0, 'id')).to eql @u1.primary_key.to_s
|
135
|
-
expect(result.dig('Resources', 0, 'name', 'givenName')).to eql 'Foo'
|
136
|
-
expect(result.dig('Resources', 0, 'name', 'familyName')).to eql 'Ark'
|
137
|
-
end
|
138
|
-
|
139
|
-
# https://github.com/RIPAGlobal/scimitar/issues/37
|
140
|
-
#
|
141
|
-
it 'applies a filter, with case-insensitive attribute matching (GitHub issue #37)' do
|
142
|
-
get '/Users', params: {
|
143
|
-
format: :scim,
|
144
|
-
filter: 'name.GIVENNAME eq "Foo" and name.Familyname pr and emails ne "home_1@test.com"'
|
145
|
-
}
|
146
|
-
|
147
|
-
expect(response.status ).to eql(200)
|
148
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
149
|
-
|
150
|
-
result = JSON.parse(response.body)
|
151
|
-
|
152
|
-
expect(result['totalResults']).to eql(1)
|
153
|
-
expect(result['Resources'].size).to eql(1)
|
154
|
-
|
155
41
|
ids = result['Resources'].map { |resource| resource['id'] }
|
156
|
-
expect(ids).to match_array([@u2.
|
42
|
+
expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s, @u3.id.to_s])
|
157
43
|
|
158
44
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
159
|
-
expect(usernames).to match_array(['2'])
|
45
|
+
expect(usernames).to match_array(['1', '2', '3'])
|
160
46
|
end
|
161
47
|
|
162
|
-
|
163
|
-
#
|
164
|
-
it 'handles broken Microsoft filters (GitHub issue #115)' do
|
48
|
+
it 'applies a filter, with case-insensitive value comparison' do
|
165
49
|
get '/Users', params: {
|
166
50
|
format: :scim,
|
167
|
-
filter: 'name
|
51
|
+
filter: 'name.givenName eq "Foo" and name.familyName pr and emails ne "home_1@TEST.COM"'
|
168
52
|
}
|
169
53
|
|
170
|
-
expect(response.status
|
171
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
172
|
-
|
54
|
+
expect(response.status).to eql(200)
|
173
55
|
result = JSON.parse(response.body)
|
174
56
|
|
175
57
|
expect(result['totalResults']).to eql(1)
|
176
58
|
expect(result['Resources'].size).to eql(1)
|
177
59
|
|
178
60
|
ids = result['Resources'].map { |resource| resource['id'] }
|
179
|
-
expect(ids).to match_array([@u2.
|
61
|
+
expect(ids).to match_array([@u2.id.to_s])
|
180
62
|
|
181
63
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
182
64
|
expect(usernames).to match_array(['2'])
|
183
65
|
end
|
184
66
|
|
185
|
-
|
186
|
-
# Strange attribute capitalisation in tests here builds on test coverage
|
187
|
-
# for now-fixed GitHub issue #37.
|
188
|
-
#
|
189
|
-
context '"meta" / IDs (GitHub issue #36)' do
|
190
|
-
it 'applies a filter on primary keys, using direct comparison (rather than e.g. case-insensitive operators)' do
|
191
|
-
get '/Users', params: {
|
192
|
-
format: :scim,
|
193
|
-
filter: "id eq \"#{@u3.primary_key}\""
|
194
|
-
}
|
195
|
-
|
196
|
-
expect(response.status ).to eql(200)
|
197
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
198
|
-
|
199
|
-
result = JSON.parse(response.body)
|
200
|
-
|
201
|
-
expect(result['totalResults']).to eql(1)
|
202
|
-
expect(result['Resources'].size).to eql(1)
|
203
|
-
|
204
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
205
|
-
expect(ids).to match_array([@u3.primary_key.to_s])
|
206
|
-
|
207
|
-
usernames = result['Resources'].map { |resource| resource['userName'] }
|
208
|
-
expect(usernames).to match_array(['3'])
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'applies a filter on external IDs, using direct comparison' do
|
212
|
-
get '/Users', params: {
|
213
|
-
format: :scim,
|
214
|
-
filter: "externalID eq \"#{@u2.scim_uid}\""
|
215
|
-
}
|
216
|
-
|
217
|
-
expect(response.status ).to eql(200)
|
218
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
219
|
-
|
220
|
-
result = JSON.parse(response.body)
|
221
|
-
|
222
|
-
expect(result['totalResults']).to eql(1)
|
223
|
-
expect(result['Resources'].size).to eql(1)
|
224
|
-
|
225
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
226
|
-
expect(ids).to match_array([@u2.primary_key.to_s])
|
227
|
-
|
228
|
-
usernames = result['Resources'].map { |resource| resource['userName'] }
|
229
|
-
expect(usernames).to match_array(['2'])
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'applies a filter on "meta" entries, using direct comparison' do
|
233
|
-
get '/Users', params: {
|
234
|
-
format: :scim,
|
235
|
-
filter: "Meta.LastModified eq \"#{@u3.updated_at}\""
|
236
|
-
}
|
237
|
-
|
238
|
-
expect(response.status ).to eql(200)
|
239
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
240
|
-
|
241
|
-
result = JSON.parse(response.body)
|
242
|
-
|
243
|
-
expect(result['totalResults']).to eql(1)
|
244
|
-
expect(result['Resources'].size).to eql(1)
|
245
|
-
|
246
|
-
ids = result['Resources'].map { |resource| resource['id'] }
|
247
|
-
expect(ids).to match_array([@u3.primary_key.to_s])
|
248
|
-
|
249
|
-
usernames = result['Resources'].map { |resource| resource['userName'] }
|
250
|
-
expect(usernames).to match_array(['3'])
|
251
|
-
end
|
252
|
-
end # "context '"meta" / IDs (GitHub issue #36)' do"
|
253
|
-
|
254
67
|
it 'obeys a page size' do
|
255
68
|
get '/Users', params: {
|
256
69
|
format: :scim,
|
257
70
|
count: 2
|
258
71
|
}
|
259
72
|
|
260
|
-
expect(response.status
|
261
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
262
|
-
|
73
|
+
expect(response.status).to eql(200)
|
263
74
|
result = JSON.parse(response.body)
|
264
75
|
|
265
76
|
expect(result['totalResults']).to eql(3)
|
266
77
|
expect(result['Resources'].size).to eql(2)
|
267
78
|
|
268
79
|
ids = result['Resources'].map { |resource| resource['id'] }
|
269
|
-
expect(ids).to match_array([@u1.
|
80
|
+
expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s])
|
270
81
|
|
271
82
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
272
83
|
expect(usernames).to match_array(['1', '2'])
|
@@ -278,16 +89,14 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
278
89
|
startIndex: 2
|
279
90
|
}
|
280
91
|
|
281
|
-
expect(response.status
|
282
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
283
|
-
|
92
|
+
expect(response.status).to eql(200)
|
284
93
|
result = JSON.parse(response.body)
|
285
94
|
|
286
95
|
expect(result['totalResults']).to eql(3)
|
287
96
|
expect(result['Resources'].size).to eql(2)
|
288
97
|
|
289
98
|
ids = result['Resources'].map { |resource| resource['id'] }
|
290
|
-
expect(ids).to match_array([@u2.
|
99
|
+
expect(ids).to match_array([@u2.id.to_s, @u3.id.to_s])
|
291
100
|
|
292
101
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
293
102
|
expect(usernames).to match_array(['2', '3'])
|
@@ -301,11 +110,8 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
301
110
|
filter: 'name.givenName'
|
302
111
|
}
|
303
112
|
|
304
|
-
expect(response.status
|
305
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
306
|
-
|
113
|
+
expect(response.status).to eql(400)
|
307
114
|
result = JSON.parse(response.body)
|
308
|
-
|
309
115
|
expect(result['scimType']).to eql('invalidFilter')
|
310
116
|
end
|
311
117
|
end # "context 'with bad calls' do"
|
@@ -314,47 +120,24 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
314
120
|
# ===========================================================================
|
315
121
|
|
316
122
|
context '#show' do
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
get "/Users/#{@u2.primary_key}", params: { format: :scim }
|
321
|
-
|
322
|
-
expect(response.status ).to eql(200)
|
323
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
324
|
-
|
325
|
-
result = JSON.parse(response.body)
|
326
|
-
|
327
|
-
expect(result['id']).to eql(@u2.primary_key.to_s)
|
328
|
-
expect(result['userName']).to eql('2')
|
329
|
-
expect(result['name']['familyName']).to eql('Bar')
|
330
|
-
expect(result['meta']['resourceType']).to eql('User')
|
331
|
-
end
|
332
|
-
end # "context 'with a UUID, renamed primary key column' do"
|
333
|
-
|
334
|
-
context 'with an integer, conventionally named primary key column' do
|
335
|
-
it 'shows an item' do
|
336
|
-
expect_any_instance_of(MockGroupsController).to receive(:show).once.and_call_original
|
337
|
-
get "/Groups/#{@g2.id}", params: { format: :scim }
|
338
|
-
|
339
|
-
expect(response.status ).to eql(200)
|
340
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
123
|
+
it 'shows an item' do
|
124
|
+
expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
|
125
|
+
get "/Users/#{@u2.id}", params: { format: :scim }
|
341
126
|
|
342
|
-
|
127
|
+
expect(response.status).to eql(200)
|
128
|
+
result = JSON.parse(response.body)
|
343
129
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
end
|
130
|
+
expect(result['id']).to eql(@u2.id.to_s) # Note - ID was converted String; not Integer
|
131
|
+
expect(result['userName']).to eql('2')
|
132
|
+
expect(result['name']['familyName']).to eql('Bar')
|
133
|
+
expect(result['meta']['resourceType']).to eql('User')
|
134
|
+
end
|
349
135
|
|
350
136
|
it 'renders 404' do
|
351
137
|
get '/Users/xyz', params: { format: :scim }
|
352
138
|
|
353
|
-
expect(response.status
|
354
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
355
|
-
|
139
|
+
expect(response.status).to eql(404)
|
356
140
|
result = JSON.parse(response.body)
|
357
|
-
|
358
141
|
expect(result['status']).to eql('404')
|
359
142
|
end
|
360
143
|
end # "context '#show' do"
|
@@ -367,15 +150,10 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
367
150
|
it 'with minimal parameters' do
|
368
151
|
mock_before = MockUser.all.to_a
|
369
152
|
|
370
|
-
attributes = { userName: '4' }
|
153
|
+
attributes = { userName: '4' } # Minimum required by schema
|
371
154
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
372
155
|
|
373
|
-
# Prove that certain known pathways are called; can then unit test
|
374
|
-
# those if need be and be sure that this covers #create actions.
|
375
|
-
#
|
376
156
|
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
377
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
378
|
-
|
379
157
|
expect {
|
380
158
|
post "/Users", params: attributes.merge(format: :scim)
|
381
159
|
}.to change { MockUser.count }.by(1)
|
@@ -383,12 +161,10 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
383
161
|
mock_after = MockUser.all.to_a
|
384
162
|
new_mock = (mock_after - mock_before).first
|
385
163
|
|
386
|
-
expect(response.status
|
387
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
388
|
-
|
164
|
+
expect(response.status).to eql(201)
|
389
165
|
result = JSON.parse(response.body)
|
390
166
|
|
391
|
-
expect(result['id']).to eql(new_mock.
|
167
|
+
expect(result['id']).to eql(new_mock.id.to_s)
|
392
168
|
expect(result['meta']['resourceType']).to eql('User')
|
393
169
|
expect(new_mock.username).to eql('4')
|
394
170
|
end
|
@@ -400,12 +176,10 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
400
176
|
|
401
177
|
attributes = {
|
402
178
|
userName: '4',
|
403
|
-
password: 'correcthorsebatterystaple',
|
404
179
|
name: {
|
405
180
|
givenName: 'Given',
|
406
181
|
familyName: 'Family'
|
407
182
|
},
|
408
|
-
meta: { resourceType: 'User' },
|
409
183
|
emails: [
|
410
184
|
{
|
411
185
|
type: 'work',
|
@@ -427,90 +201,17 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
427
201
|
mock_after = MockUser.all.to_a
|
428
202
|
new_mock = (mock_after - mock_before).first
|
429
203
|
|
430
|
-
expect(response.status
|
431
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
432
|
-
|
204
|
+
expect(response.status).to eql(201)
|
433
205
|
result = JSON.parse(response.body)
|
434
206
|
|
435
207
|
expect(result['id']).to eql(new_mock.id.to_s)
|
436
208
|
expect(result['meta']['resourceType']).to eql('User')
|
437
209
|
expect(new_mock.username).to eql('4')
|
438
|
-
expect(new_mock.password).to eql('correcthorsebatterystaple')
|
439
210
|
expect(new_mock.first_name).to eql('Given')
|
440
211
|
expect(new_mock.last_name).to eql('Family')
|
441
212
|
expect(new_mock.home_email_address).to eql('home_4@test.com')
|
442
213
|
expect(new_mock.work_email_address).to eql('work_4@test.com')
|
443
214
|
end
|
444
|
-
|
445
|
-
it 'with schema ID value keys without inline attributes' do
|
446
|
-
mock_before = MockUser.all.to_a
|
447
|
-
|
448
|
-
attributes = {
|
449
|
-
userName: '4',
|
450
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User': {
|
451
|
-
organization: 'Foo Bar!',
|
452
|
-
department: 'Bar Foo!'
|
453
|
-
},
|
454
|
-
'urn:ietf:params:scim:schemas:extension:manager:1.0:User': {
|
455
|
-
manager: 'Foo Baz!'
|
456
|
-
}
|
457
|
-
}
|
458
|
-
|
459
|
-
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
460
|
-
|
461
|
-
expect {
|
462
|
-
post "/Users", params: attributes.merge(format: :scim)
|
463
|
-
}.to change { MockUser.count }.by(1)
|
464
|
-
|
465
|
-
mock_after = MockUser.all.to_a
|
466
|
-
new_mock = (mock_after - mock_before).first
|
467
|
-
|
468
|
-
expect(response.status ).to eql(201)
|
469
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
470
|
-
|
471
|
-
result = JSON.parse(response.body)
|
472
|
-
|
473
|
-
expect(new_mock.organization).to eql('Foo Bar!')
|
474
|
-
expect(new_mock.department ).to eql('Bar Foo!')
|
475
|
-
expect(new_mock.manager ).to eql('Foo Baz!')
|
476
|
-
|
477
|
-
expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization']).to eql(new_mock.organization)
|
478
|
-
expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['department' ]).to eql(new_mock.department )
|
479
|
-
expect(result['urn:ietf:params:scim:schemas:extension:manager:1.0:User' ]['manager' ]).to eql(new_mock.manager )
|
480
|
-
end
|
481
|
-
|
482
|
-
it 'with schema ID value keys that have inline attributes' do
|
483
|
-
mock_before = MockUser.all.to_a
|
484
|
-
|
485
|
-
attributes = {
|
486
|
-
userName: '4',
|
487
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization': 'Foo Bar!',
|
488
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department': 'Bar Foo!',
|
489
|
-
'urn:ietf:params:scim:schemas:extension:manager:1.0:User:manager': 'Foo Baz!'
|
490
|
-
}
|
491
|
-
|
492
|
-
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
493
|
-
|
494
|
-
expect {
|
495
|
-
post "/Users", params: attributes.merge(format: :scim)
|
496
|
-
}.to change { MockUser.count }.by(1)
|
497
|
-
|
498
|
-
mock_after = MockUser.all.to_a
|
499
|
-
new_mock = (mock_after - mock_before).first
|
500
|
-
|
501
|
-
expect(response.status ).to eql(201)
|
502
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
503
|
-
|
504
|
-
result = JSON.parse(response.body)
|
505
|
-
|
506
|
-
expect(new_mock.organization).to eql('Foo Bar!')
|
507
|
-
expect(new_mock.department ).to eql('Bar Foo!')
|
508
|
-
expect(new_mock.manager ).to eql('Foo Baz!')
|
509
|
-
|
510
|
-
expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization']).to eql(new_mock.organization)
|
511
|
-
expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['department' ]).to eql(new_mock.department )
|
512
|
-
expect(result['urn:ietf:params:scim:schemas:extension:manager:1.0:User' ]['manager' ]).to eql(new_mock.manager )
|
513
|
-
end
|
514
215
|
end # "shared_examples 'a creator' do | force_upper_case: |"
|
515
216
|
|
516
217
|
context 'using schema-matched case' do
|
@@ -531,11 +232,8 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
531
232
|
}
|
532
233
|
}.to_not change { MockUser.count }
|
533
234
|
|
534
|
-
expect(response.status
|
535
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
536
|
-
|
235
|
+
expect(response.status).to eql(409)
|
537
236
|
result = JSON.parse(response.body)
|
538
|
-
|
539
237
|
expect(result['scimType']).to eql('uniqueness')
|
540
238
|
expect(result['detail']).to include('already been taken')
|
541
239
|
end
|
@@ -548,11 +246,8 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
548
246
|
}
|
549
247
|
}.to_not change { MockUser.count }
|
550
248
|
|
551
|
-
expect(response.status
|
552
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
553
|
-
|
249
|
+
expect(response.status).to eql(400)
|
554
250
|
result = JSON.parse(response.body)
|
555
|
-
|
556
251
|
expect(result['scimType']).to eql('invalidValue')
|
557
252
|
expect(result['detail']).to include('is required')
|
558
253
|
end
|
@@ -565,84 +260,12 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
565
260
|
}
|
566
261
|
}.to_not change { MockUser.count }
|
567
262
|
|
568
|
-
expect(response.status
|
569
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
570
|
-
|
263
|
+
expect(response.status).to eql(400)
|
571
264
|
result = JSON.parse(response.body)
|
572
265
|
|
573
266
|
expect(result['scimType']).to eql('invalidValue')
|
574
267
|
expect(result['detail']).to include('is reserved')
|
575
268
|
end
|
576
|
-
|
577
|
-
context 'with a block' do
|
578
|
-
it 'invokes the block' do
|
579
|
-
mock_before = MockUser.all.to_a
|
580
|
-
|
581
|
-
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
582
|
-
expect {
|
583
|
-
post "/CustomCreateUsers", params: {
|
584
|
-
format: :scim,
|
585
|
-
userName: '4' # Minimum required by schema
|
586
|
-
}
|
587
|
-
}.to change { MockUser.count }.by(1)
|
588
|
-
|
589
|
-
mock_after = MockUser.all.to_a
|
590
|
-
new_mock = (mock_after - mock_before).first
|
591
|
-
|
592
|
-
expect(response.status ).to eql(201)
|
593
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
594
|
-
|
595
|
-
result = JSON.parse(response.body)
|
596
|
-
|
597
|
-
expect(result['id']).to eql(new_mock.id.to_s)
|
598
|
-
expect(result['meta']['resourceType']).to eql('User')
|
599
|
-
expect(new_mock.first_name).to eql(CustomCreateMockUsersController::OVERRIDDEN_NAME)
|
600
|
-
end
|
601
|
-
|
602
|
-
it 'returns 409 for duplicates (by Rails validation)' do
|
603
|
-
existing_user = MockUser.create!(
|
604
|
-
username: '4',
|
605
|
-
first_name: 'Will Be Overridden',
|
606
|
-
last_name: 'Baz',
|
607
|
-
home_email_address: 'random@test.com',
|
608
|
-
scim_uid: '999'
|
609
|
-
)
|
610
|
-
|
611
|
-
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
612
|
-
expect {
|
613
|
-
post "/CustomCreateUsers", params: {
|
614
|
-
format: :scim,
|
615
|
-
userName: '4' # Already exists
|
616
|
-
}
|
617
|
-
}.to_not change { MockUser.count }
|
618
|
-
|
619
|
-
expect(response.status ).to eql(409)
|
620
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
621
|
-
|
622
|
-
result = JSON.parse(response.body)
|
623
|
-
|
624
|
-
expect(result['scimType']).to eql('uniqueness')
|
625
|
-
expect(result['detail']).to include('already been taken')
|
626
|
-
end
|
627
|
-
|
628
|
-
it 'notes Rails validation failures' do
|
629
|
-
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
630
|
-
expect {
|
631
|
-
post "/CustomCreateUsers", params: {
|
632
|
-
format: :scim,
|
633
|
-
userName: MockUser::INVALID_USERNAME
|
634
|
-
}
|
635
|
-
}.to_not change { MockUser.count }
|
636
|
-
|
637
|
-
expect(response.status ).to eql(400)
|
638
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
639
|
-
|
640
|
-
result = JSON.parse(response.body)
|
641
|
-
|
642
|
-
expect(result['scimType']).to eql('invalidValue')
|
643
|
-
expect(result['detail']).to include('is reserved')
|
644
|
-
end
|
645
|
-
end # "context 'with a block' do"
|
646
269
|
end # "context '#create' do"
|
647
270
|
|
648
271
|
# ===========================================================================
|
@@ -650,64 +273,26 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
650
273
|
context '#replace' do
|
651
274
|
shared_examples 'a replacer' do | force_upper_case: |
|
652
275
|
it 'which replaces all attributes in an instance' do
|
653
|
-
attributes = { userName: '4' }
|
276
|
+
attributes = { userName: '4' } # Minimum required by schema
|
654
277
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
655
278
|
|
656
|
-
# Prove that certain known pathways are called; can then unit test
|
657
|
-
# those if need be and be sure that this covers #replace actions.
|
658
|
-
#
|
659
279
|
expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
|
660
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
661
|
-
expect {
|
662
|
-
put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
|
663
|
-
}.to_not change { MockUser.count }
|
664
|
-
|
665
|
-
expect(response.status ).to eql(200)
|
666
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
667
|
-
|
668
|
-
result = JSON.parse(response.body)
|
669
|
-
|
670
|
-
expect(result['id']).to eql(@u2.primary_key.to_s)
|
671
|
-
expect(result['meta']['resourceType']).to eql('User')
|
672
|
-
|
673
|
-
expect(result).to have_key('name')
|
674
|
-
expect(result).to_not have_key('password')
|
675
|
-
|
676
|
-
@u2.reload
|
677
|
-
|
678
|
-
expect(@u2.username).to eql('4')
|
679
|
-
expect(@u2.first_name).to be_nil
|
680
|
-
expect(@u2.last_name).to be_nil
|
681
|
-
expect(@u2.home_email_address).to be_nil
|
682
|
-
expect(@u2.password).to be_nil
|
683
|
-
end
|
684
|
-
|
685
|
-
it 'can replace passwords' do
|
686
|
-
attributes = { userName: '4', password: 'correcthorsebatterystaple' }
|
687
|
-
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
688
|
-
|
689
280
|
expect {
|
690
|
-
put "/Users/#{@u2.
|
281
|
+
put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
|
691
282
|
}.to_not change { MockUser.count }
|
692
283
|
|
693
|
-
expect(response.status
|
694
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
695
|
-
|
284
|
+
expect(response.status).to eql(200)
|
696
285
|
result = JSON.parse(response.body)
|
697
286
|
|
698
|
-
expect(result['id']).to eql(@u2.
|
287
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
699
288
|
expect(result['meta']['resourceType']).to eql('User')
|
700
289
|
|
701
|
-
expect(result).to have_key('name')
|
702
|
-
expect(result).to_not have_key('password')
|
703
|
-
|
704
290
|
@u2.reload
|
705
291
|
|
706
292
|
expect(@u2.username).to eql('4')
|
707
293
|
expect(@u2.first_name).to be_nil
|
708
294
|
expect(@u2.last_name).to be_nil
|
709
295
|
expect(@u2.home_email_address).to be_nil
|
710
|
-
expect(@u2.password).to eql('correcthorsebatterystaple')
|
711
296
|
end
|
712
297
|
end # "shared_examples 'a replacer' do | force_upper_case: |"
|
713
298
|
|
@@ -721,17 +306,14 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
721
306
|
|
722
307
|
it 'notes schema validation failures' do
|
723
308
|
expect {
|
724
|
-
put "/Users/#{@u2.
|
309
|
+
put "/Users/#{@u2.id}", params: {
|
725
310
|
format: :scim
|
726
311
|
# userName parameter is required by schema, but missing
|
727
312
|
}
|
728
313
|
}.to_not change { MockUser.count }
|
729
314
|
|
730
|
-
expect(response.status
|
731
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
732
|
-
|
315
|
+
expect(response.status).to eql(400)
|
733
316
|
result = JSON.parse(response.body)
|
734
|
-
|
735
317
|
expect(result['scimType']).to eql('invalidValue')
|
736
318
|
expect(result['detail']).to include('is required')
|
737
319
|
|
@@ -745,15 +327,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
745
327
|
|
746
328
|
it 'notes Rails validation failures' do
|
747
329
|
expect {
|
748
|
-
|
330
|
+
post "/Users", params: {
|
749
331
|
format: :scim,
|
750
332
|
userName: MockUser::INVALID_USERNAME
|
751
333
|
}
|
752
334
|
}.to_not change { MockUser.count }
|
753
335
|
|
754
|
-
expect(response.status
|
755
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
756
|
-
|
336
|
+
expect(response.status).to eql(400)
|
757
337
|
result = JSON.parse(response.body)
|
758
338
|
|
759
339
|
expect(result['scimType']).to eql('invalidValue')
|
@@ -775,72 +355,17 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
775
355
|
}
|
776
356
|
}.to_not change { MockUser.count }
|
777
357
|
|
778
|
-
expect(response.status
|
779
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
780
|
-
|
358
|
+
expect(response.status).to eql(404)
|
781
359
|
result = JSON.parse(response.body)
|
782
|
-
|
783
360
|
expect(result['status']).to eql('404')
|
784
361
|
end
|
785
|
-
|
786
|
-
context 'with a block' do
|
787
|
-
it 'invokes the block' do
|
788
|
-
attributes = { userName: '4' } # Minimum required by schema
|
789
|
-
|
790
|
-
expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
|
791
|
-
expect {
|
792
|
-
put "/CustomReplaceUsers/#{@u2.primary_key}", params: {
|
793
|
-
format: :scim,
|
794
|
-
userName: '4'
|
795
|
-
}
|
796
|
-
}.to_not change { MockUser.count }
|
797
|
-
|
798
|
-
expect(response.status ).to eql(200)
|
799
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
800
|
-
|
801
|
-
result = JSON.parse(response.body)
|
802
|
-
|
803
|
-
expect(result['id']).to eql(@u2.primary_key.to_s)
|
804
|
-
expect(result['meta']['resourceType']).to eql('User')
|
805
|
-
|
806
|
-
@u2.reload
|
807
|
-
|
808
|
-
expect(@u2.username ).to eql('4')
|
809
|
-
expect(@u2.first_name).to eql(CustomReplaceMockUsersController::OVERRIDDEN_NAME)
|
810
|
-
end
|
811
|
-
|
812
|
-
it 'notes Rails validation failures' do
|
813
|
-
expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
|
814
|
-
expect {
|
815
|
-
put "/CustomReplaceUsers/#{@u2.primary_key}", params: {
|
816
|
-
format: :scim,
|
817
|
-
userName: MockUser::INVALID_USERNAME
|
818
|
-
}
|
819
|
-
}.to_not change { MockUser.count }
|
820
|
-
|
821
|
-
expect(response.status ).to eql(400)
|
822
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
823
|
-
|
824
|
-
result = JSON.parse(response.body)
|
825
|
-
|
826
|
-
expect(result['scimType']).to eql('invalidValue')
|
827
|
-
expect(result['detail']).to include('is reserved')
|
828
|
-
|
829
|
-
@u2.reload
|
830
|
-
|
831
|
-
expect(@u2.username).to eql('2')
|
832
|
-
expect(@u2.first_name).to eql('Foo')
|
833
|
-
expect(@u2.last_name).to eql('Bar')
|
834
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
835
|
-
end
|
836
|
-
end # "context 'with a block' do"
|
837
362
|
end # "context '#replace' do"
|
838
363
|
|
839
364
|
# ===========================================================================
|
840
365
|
|
841
366
|
context '#update' do
|
842
367
|
shared_examples 'an updater' do | force_upper_case: |
|
843
|
-
it 'which patches
|
368
|
+
it 'which patches specific attributes' do
|
844
369
|
payload = {
|
845
370
|
Operations: [
|
846
371
|
{
|
@@ -858,27 +383,17 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
858
383
|
|
859
384
|
payload = spec_helper_hupcase(payload) if force_upper_case
|
860
385
|
|
861
|
-
# Prove that certain known pathways are called; can then unit test
|
862
|
-
# those if need be and be sure that this covers #update actions.
|
863
|
-
#
|
864
386
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
865
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
866
|
-
|
867
387
|
expect {
|
868
|
-
patch "/Users/#{@u2.
|
388
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
869
389
|
}.to_not change { MockUser.count }
|
870
390
|
|
871
|
-
expect(response.status
|
872
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
873
|
-
|
391
|
+
expect(response.status).to eql(200)
|
874
392
|
result = JSON.parse(response.body)
|
875
393
|
|
876
|
-
expect(result['id']).to eql(@u2.
|
394
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
877
395
|
expect(result['meta']['resourceType']).to eql('User')
|
878
396
|
|
879
|
-
expect(result).to have_key('name')
|
880
|
-
expect(result).to_not have_key('password')
|
881
|
-
|
882
397
|
@u2.reload
|
883
398
|
|
884
399
|
expect(@u2.username).to eql('4')
|
@@ -886,151 +401,6 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
886
401
|
expect(@u2.last_name).to eql('Bar')
|
887
402
|
expect(@u2.home_email_address).to eql('home_2@test.com')
|
888
403
|
expect(@u2.work_email_address).to eql('work_4@test.com')
|
889
|
-
expect(@u2.password).to eql('oldpassword')
|
890
|
-
end
|
891
|
-
|
892
|
-
context 'which' do
|
893
|
-
shared_examples 'it handles not-to-spec in-value Azure/Entra dotted attribute paths' do | operation |
|
894
|
-
it "and performs operation" do
|
895
|
-
payload = {
|
896
|
-
Operations: [
|
897
|
-
{
|
898
|
-
op: 'add',
|
899
|
-
value: {
|
900
|
-
'name.givenName' => 'Foo!',
|
901
|
-
'name.familyName' => 'Bar!',
|
902
|
-
'name.formatted' => 'Foo! Bar!' # Unrecognised; should be ignored
|
903
|
-
},
|
904
|
-
},
|
905
|
-
]
|
906
|
-
}
|
907
|
-
|
908
|
-
payload = spec_helper_hupcase(payload) if force_upper_case
|
909
|
-
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
910
|
-
|
911
|
-
expect(response.status ).to eql(200)
|
912
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
913
|
-
|
914
|
-
@u2.reload
|
915
|
-
result = JSON.parse(response.body)
|
916
|
-
|
917
|
-
expect(@u2.first_name).to eql('Foo!')
|
918
|
-
expect(@u2.last_name ).to eql('Bar!')
|
919
|
-
end
|
920
|
-
end
|
921
|
-
|
922
|
-
it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'add'
|
923
|
-
it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'replace'
|
924
|
-
|
925
|
-
shared_examples 'it handles schema ID value keys without inline attributes' do | operation |
|
926
|
-
it "and performs operation" do
|
927
|
-
payload = {
|
928
|
-
Operations: [
|
929
|
-
{
|
930
|
-
op: operation,
|
931
|
-
value: {
|
932
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User': {
|
933
|
-
'organization' => 'Foo Bar!',
|
934
|
-
'department' => 'Bar Foo!'
|
935
|
-
},
|
936
|
-
'urn:ietf:params:scim:schemas:extension:manager:1.0:User': {
|
937
|
-
'manager' => 'Foo Baz!'
|
938
|
-
}
|
939
|
-
},
|
940
|
-
},
|
941
|
-
]
|
942
|
-
}
|
943
|
-
|
944
|
-
@u2.update!(organization: 'Old org')
|
945
|
-
payload = spec_helper_hupcase(payload) if force_upper_case
|
946
|
-
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
947
|
-
|
948
|
-
expect(response.status ).to eql(200)
|
949
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
950
|
-
|
951
|
-
@u2.reload
|
952
|
-
result = JSON.parse(response.body)
|
953
|
-
|
954
|
-
expect(@u2.organization).to eql('Foo Bar!')
|
955
|
-
expect(@u2.department ).to eql('Bar Foo!')
|
956
|
-
expect(@u2.manager ).to eql('Foo Baz!')
|
957
|
-
end
|
958
|
-
end
|
959
|
-
|
960
|
-
it_behaves_like 'it handles schema ID value keys without inline attributes', 'add'
|
961
|
-
it_behaves_like 'it handles schema ID value keys without inline attributes', 'replace'
|
962
|
-
|
963
|
-
shared_examples 'it handles schema ID value keys with inline attributes' do
|
964
|
-
it "and performs operation" do
|
965
|
-
payload = {
|
966
|
-
Operations: [
|
967
|
-
{
|
968
|
-
op: 'add',
|
969
|
-
value: {
|
970
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization' => 'Foo Bar!',
|
971
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department' => 'Bar Foo!',
|
972
|
-
'urn:ietf:params:scim:schemas:extension:manager:1.0:User:manager' => 'Foo Baz!'
|
973
|
-
},
|
974
|
-
},
|
975
|
-
]
|
976
|
-
}
|
977
|
-
|
978
|
-
@u2.update!(organization: 'Old org')
|
979
|
-
payload = spec_helper_hupcase(payload) if force_upper_case
|
980
|
-
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
981
|
-
|
982
|
-
expect(response.status ).to eql(200)
|
983
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
984
|
-
|
985
|
-
@u2.reload
|
986
|
-
result = JSON.parse(response.body)
|
987
|
-
|
988
|
-
expect(@u2.organization).to eql('Foo Bar!')
|
989
|
-
expect(@u2.department ).to eql('Bar Foo!')
|
990
|
-
expect(@u2.manager ).to eql('Foo Baz!')
|
991
|
-
end
|
992
|
-
end
|
993
|
-
|
994
|
-
it_behaves_like 'it handles schema ID value keys with inline attributes', 'add'
|
995
|
-
it_behaves_like 'it handles schema ID value keys with inline attributes', 'replace'
|
996
|
-
end
|
997
|
-
|
998
|
-
it 'which patches "returned: \'never\'" fields' do
|
999
|
-
payload = {
|
1000
|
-
Operations: [
|
1001
|
-
{
|
1002
|
-
op: 'replace',
|
1003
|
-
path: 'password',
|
1004
|
-
value: 'correcthorsebatterystaple'
|
1005
|
-
}
|
1006
|
-
]
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
payload = spec_helper_hupcase(payload) if force_upper_case
|
1010
|
-
|
1011
|
-
expect {
|
1012
|
-
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
1013
|
-
}.to_not change { MockUser.count }
|
1014
|
-
|
1015
|
-
expect(response.status ).to eql(200)
|
1016
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1017
|
-
|
1018
|
-
result = JSON.parse(response.body)
|
1019
|
-
|
1020
|
-
expect(result['id']).to eql(@u2.primary_key.to_s)
|
1021
|
-
expect(result['meta']['resourceType']).to eql('User')
|
1022
|
-
|
1023
|
-
expect(result).to have_key('name')
|
1024
|
-
expect(result).to_not have_key('password')
|
1025
|
-
|
1026
|
-
@u2.reload
|
1027
|
-
|
1028
|
-
expect(@u2.username).to eql('2')
|
1029
|
-
expect(@u2.first_name).to eql('Foo')
|
1030
|
-
expect(@u2.last_name).to eql('Bar')
|
1031
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
1032
|
-
expect(@u2.work_email_address).to be_nil
|
1033
|
-
expect(@u2.password).to eql('correcthorsebatterystaple')
|
1034
404
|
end
|
1035
405
|
|
1036
406
|
context 'which clears attributes' do
|
@@ -1052,15 +422,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1052
422
|
|
1053
423
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
1054
424
|
expect {
|
1055
|
-
patch "/Users/#{@u2.
|
425
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
1056
426
|
}.to_not change { MockUser.count }
|
1057
427
|
|
1058
|
-
expect(response.status
|
1059
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1060
|
-
|
428
|
+
expect(response.status).to eql(200)
|
1061
429
|
result = JSON.parse(response.body)
|
1062
430
|
|
1063
|
-
expect(result['id']).to eql(@u2.
|
431
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
1064
432
|
expect(result['meta']['resourceType']).to eql('User')
|
1065
433
|
|
1066
434
|
@u2.reload
|
@@ -1086,15 +454,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1086
454
|
|
1087
455
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
1088
456
|
expect {
|
1089
|
-
patch "/Users/#{@u2.
|
457
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
1090
458
|
}.to_not change { MockUser.count }
|
1091
459
|
|
1092
|
-
expect(response.status
|
1093
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1094
|
-
|
460
|
+
expect(response.status).to eql(200)
|
1095
461
|
result = JSON.parse(response.body)
|
1096
462
|
|
1097
|
-
expect(result['id']).to eql(@u2.
|
463
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
1098
464
|
expect(result['meta']['resourceType']).to eql('User')
|
1099
465
|
|
1100
466
|
@u2.reload
|
@@ -1120,15 +486,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1120
486
|
|
1121
487
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
1122
488
|
expect {
|
1123
|
-
patch "/Users/#{@u2.
|
489
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
1124
490
|
}.to_not change { MockUser.count }
|
1125
491
|
|
1126
|
-
expect(response.status
|
1127
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1128
|
-
|
492
|
+
expect(response.status).to eql(200)
|
1129
493
|
result = JSON.parse(response.body)
|
1130
494
|
|
1131
|
-
expect(result['id']).to eql(@u2.
|
495
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
1132
496
|
expect(result['meta']['resourceType']).to eql('User')
|
1133
497
|
|
1134
498
|
@u2.reload
|
@@ -1152,7 +516,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1152
516
|
|
1153
517
|
it 'notes Rails validation failures' do
|
1154
518
|
expect {
|
1155
|
-
patch "/Users/#{@u2.
|
519
|
+
patch "/Users/#{@u2.id}", params: {
|
1156
520
|
format: :scim,
|
1157
521
|
Operations: [
|
1158
522
|
{
|
@@ -1164,9 +528,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1164
528
|
}
|
1165
529
|
}.to_not change { MockUser.count }
|
1166
530
|
|
1167
|
-
expect(response.status
|
1168
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1169
|
-
|
531
|
+
expect(response.status).to eql(400)
|
1170
532
|
result = JSON.parse(response.body)
|
1171
533
|
|
1172
534
|
expect(result['scimType']).to eql('invalidValue')
|
@@ -1194,329 +556,20 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1194
556
|
}
|
1195
557
|
}.to_not change { MockUser.count }
|
1196
558
|
|
1197
|
-
expect(response.status
|
1198
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1199
|
-
|
559
|
+
expect(response.status).to eql(404)
|
1200
560
|
result = JSON.parse(response.body)
|
1201
|
-
|
1202
561
|
expect(result['status']).to eql('404')
|
1203
562
|
end
|
1204
|
-
|
1205
|
-
context 'when removing users from groups' do
|
1206
|
-
before :each do
|
1207
|
-
@g1.mock_users << @u1
|
1208
|
-
@g1.mock_users << @u2
|
1209
|
-
@g1.mock_users << @u3
|
1210
|
-
|
1211
|
-
# (Self-check) Verify group representation
|
1212
|
-
#
|
1213
|
-
get "/Groups/#{@g1.id}", params: { format: :scim }
|
1214
|
-
|
1215
|
-
expect(response.status).to eql(200)
|
1216
|
-
result = JSON.parse(response.body)
|
1217
|
-
|
1218
|
-
expect(result['members'].map { |m| m['value'] }.sort()).to eql(MockUser.pluck(:primary_key).sort())
|
1219
|
-
end
|
1220
|
-
|
1221
|
-
it 'can remove all users' do
|
1222
|
-
expect {
|
1223
|
-
expect {
|
1224
|
-
patch "/Groups/#{@g1.id}", params: {
|
1225
|
-
format: :scim,
|
1226
|
-
Operations: [
|
1227
|
-
{
|
1228
|
-
op: 'remove',
|
1229
|
-
path: 'members'
|
1230
|
-
}
|
1231
|
-
]
|
1232
|
-
}
|
1233
|
-
}.to_not change { MockUser.count }
|
1234
|
-
}.to_not change { MockGroup.count }
|
1235
|
-
|
1236
|
-
get "/Groups/#{@g1.id}", params: { format: :scim }
|
1237
|
-
|
1238
|
-
expect(response.status ).to eql(200)
|
1239
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1240
|
-
|
1241
|
-
result = JSON.parse(response.body)
|
1242
|
-
|
1243
|
-
expect(result['members']).to be_empty
|
1244
|
-
expect(@g1.reload().mock_users).to be_empty
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
# Define via 'let':
|
1248
|
-
#
|
1249
|
-
# * Hash 'payload', to send via 'patch'
|
1250
|
-
# * MockUser 'removed_user', which is the user that should be removed
|
1251
|
-
#
|
1252
|
-
shared_examples 'a user remover' do
|
1253
|
-
it 'which removes the identified user' do
|
1254
|
-
expect {
|
1255
|
-
expect {
|
1256
|
-
patch "/Groups/#{@g1.id}", params: payload()
|
1257
|
-
}.to_not change { MockUser.count }
|
1258
|
-
}.to_not change { MockGroup.count }
|
1259
|
-
|
1260
|
-
expected_remaining_user_ids = MockUser
|
1261
|
-
.where.not(primary_key: removed_user().id)
|
1262
|
-
.pluck(:primary_key)
|
1263
|
-
.sort()
|
1264
|
-
|
1265
|
-
get "/Groups/#{@g1.id}", params: { format: :scim }
|
1266
|
-
|
1267
|
-
expect(response.status ).to eql(200)
|
1268
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1269
|
-
|
1270
|
-
result = JSON.parse(response.body)
|
1271
|
-
|
1272
|
-
expect(result['members'].map { |m| m['value'] }.sort()).to eql(expected_remaining_user_ids)
|
1273
|
-
expect(@g1.reload().mock_users.map(&:primary_key).sort()).to eql(expected_remaining_user_ids)
|
1274
|
-
end
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
# https://tools.ietf.org/html/rfc7644#section-3.5.2.2
|
1278
|
-
#
|
1279
|
-
context 'and using an RFC-compliant payload' do
|
1280
|
-
let(:removed_user) { @u2 }
|
1281
|
-
let(:payload) do
|
1282
|
-
{
|
1283
|
-
format: :scim,
|
1284
|
-
Operations: [
|
1285
|
-
{
|
1286
|
-
op: 'remove',
|
1287
|
-
path: "members[value eq \"#{removed_user().primary_key}\"]",
|
1288
|
-
}
|
1289
|
-
]
|
1290
|
-
}
|
1291
|
-
end
|
1292
|
-
|
1293
|
-
it_behaves_like 'a user remover'
|
1294
|
-
end # context 'and using an RFC-compliant payload' do
|
1295
|
-
|
1296
|
-
# https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
|
1297
|
-
#
|
1298
|
-
context 'and using a Microsoft variant payload' do
|
1299
|
-
let(:removed_user) { @u2 }
|
1300
|
-
let(:payload) do
|
1301
|
-
{
|
1302
|
-
format: :scim,
|
1303
|
-
Operations: [
|
1304
|
-
{
|
1305
|
-
op: 'remove',
|
1306
|
-
path: 'members',
|
1307
|
-
value: [{
|
1308
|
-
'$ref' => nil,
|
1309
|
-
'value' => removed_user().primary_key
|
1310
|
-
}]
|
1311
|
-
}
|
1312
|
-
]
|
1313
|
-
}
|
1314
|
-
end
|
1315
|
-
|
1316
|
-
it_behaves_like 'a user remover'
|
1317
|
-
end # context 'and using a Microsoft variant payload' do
|
1318
|
-
|
1319
|
-
# https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
|
1320
|
-
#
|
1321
|
-
context 'and using a Salesforce variant payload' do
|
1322
|
-
let(:removed_user) { @u2 }
|
1323
|
-
let(:payload) do
|
1324
|
-
{
|
1325
|
-
format: :scim,
|
1326
|
-
Operations: [
|
1327
|
-
{
|
1328
|
-
op: 'remove',
|
1329
|
-
path: 'members',
|
1330
|
-
value: {
|
1331
|
-
'members' => [{
|
1332
|
-
'$ref' => nil,
|
1333
|
-
'value' => removed_user().primary_key
|
1334
|
-
}]
|
1335
|
-
}
|
1336
|
-
}
|
1337
|
-
]
|
1338
|
-
}
|
1339
|
-
end
|
1340
|
-
|
1341
|
-
it_behaves_like 'a user remover'
|
1342
|
-
end # context 'and using a Salesforce variant payload' do
|
1343
|
-
end # "context 'when removing users from groups' do"
|
1344
|
-
|
1345
|
-
context 'with a block' do
|
1346
|
-
it 'invokes the block' do
|
1347
|
-
payload = {
|
1348
|
-
format: :scim,
|
1349
|
-
Operations: [
|
1350
|
-
{
|
1351
|
-
op: 'add',
|
1352
|
-
path: 'userName',
|
1353
|
-
value: '4'
|
1354
|
-
},
|
1355
|
-
{
|
1356
|
-
op: 'replace',
|
1357
|
-
path: 'emails[type eq "work"]',
|
1358
|
-
value: { type: 'work', value: 'work_4@test.com' }
|
1359
|
-
}
|
1360
|
-
]
|
1361
|
-
}
|
1362
|
-
|
1363
|
-
expect_any_instance_of(CustomUpdateMockUsersController).to receive(:update).once.and_call_original
|
1364
|
-
expect {
|
1365
|
-
patch "/CustomUpdateUsers/#{@u2.primary_key}", params: payload
|
1366
|
-
}.to_not change { MockUser.count }
|
1367
|
-
|
1368
|
-
expect(response.status ).to eql(200)
|
1369
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1370
|
-
|
1371
|
-
result = JSON.parse(response.body)
|
1372
|
-
|
1373
|
-
expect(result['id']).to eql(@u2.primary_key.to_s)
|
1374
|
-
expect(result['meta']['resourceType']).to eql('User')
|
1375
|
-
|
1376
|
-
@u2.reload
|
1377
|
-
|
1378
|
-
expect(@u2.username ).to eql('4')
|
1379
|
-
expect(@u2.first_name ).to eql(CustomUpdateMockUsersController::OVERRIDDEN_NAME)
|
1380
|
-
expect(@u2.work_email_address).to eql('work_4@test.com')
|
1381
|
-
end
|
1382
|
-
|
1383
|
-
it 'notes Rails validation failures' do
|
1384
|
-
expect_any_instance_of(CustomUpdateMockUsersController).to receive(:update).once.and_call_original
|
1385
|
-
expect {
|
1386
|
-
patch "/CustomUpdateUsers/#{@u2.primary_key}", params: {
|
1387
|
-
format: :scim,
|
1388
|
-
Operations: [
|
1389
|
-
{
|
1390
|
-
op: 'add',
|
1391
|
-
path: 'userName',
|
1392
|
-
value: MockUser::INVALID_USERNAME
|
1393
|
-
}
|
1394
|
-
]
|
1395
|
-
}
|
1396
|
-
}.to_not change { MockUser.count }
|
1397
|
-
|
1398
|
-
expect(response.status ).to eql(400)
|
1399
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1400
|
-
|
1401
|
-
result = JSON.parse(response.body)
|
1402
|
-
|
1403
|
-
expect(result['scimType']).to eql('invalidValue')
|
1404
|
-
expect(result['detail']).to include('is reserved')
|
1405
|
-
|
1406
|
-
@u2.reload
|
1407
|
-
|
1408
|
-
expect(@u2.username).to eql('2')
|
1409
|
-
expect(@u2.first_name).to eql('Foo')
|
1410
|
-
expect(@u2.last_name).to eql('Bar')
|
1411
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
1412
|
-
end
|
1413
|
-
end # "context 'with a block' do"
|
1414
563
|
end # "context '#update' do"
|
1415
564
|
|
1416
|
-
# ===========================================================================
|
1417
|
-
# In-passing parts of tests above show that #create, #replace and #update all
|
1418
|
-
# route through #save!, so now add some unit tests for that and for exception
|
1419
|
-
# handling overrides invoked via #save!.
|
1420
|
-
# ===========================================================================
|
1421
|
-
|
1422
|
-
context 'overriding #save!' do
|
1423
|
-
it 'invokes a block if given one' do
|
1424
|
-
mock_before = MockUser.all.to_a
|
1425
|
-
attributes = { userName: '5' } # Minimum required by schema
|
1426
|
-
|
1427
|
-
expect_any_instance_of(CustomSaveMockUsersController).to receive(:create).once.and_call_original
|
1428
|
-
expect {
|
1429
|
-
post "/CustomSaveUsers", params: attributes.merge(format: :scim)
|
1430
|
-
}.to change { MockUser.count }.by(1)
|
1431
|
-
|
1432
|
-
mock_after = MockUser.all.to_a
|
1433
|
-
new_mock = (mock_after - mock_before).first
|
1434
|
-
|
1435
|
-
expect(response.status ).to eql(201)
|
1436
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1437
|
-
|
1438
|
-
expect(new_mock.username).to eql(CustomSaveMockUsersController::CUSTOM_SAVE_BLOCK_USERNAME_INDICATOR)
|
1439
|
-
end
|
1440
|
-
end # "context 'overriding #save!' do
|
1441
|
-
|
1442
|
-
context 'custom on-save exceptions' do
|
1443
|
-
MockUsersController.new.send(:scimitar_rescuable_exceptions).each do | exception_class |
|
1444
|
-
it "handles out-of-box exception #{exception_class}" do
|
1445
|
-
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1446
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1447
|
-
|
1448
|
-
expect_any_instance_of(MockUser).to receive(:save!).once { raise exception_class }
|
1449
|
-
|
1450
|
-
expect {
|
1451
|
-
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1452
|
-
}.to_not change { MockUser.count }
|
1453
|
-
|
1454
|
-
expected_status, expected_prefix = if exception_class == ActiveRecord::RecordNotUnique
|
1455
|
-
[409, 'Operation failed due to a uniqueness constraint: ']
|
1456
|
-
else
|
1457
|
-
[400, 'Operation failed since record has become invalid: ']
|
1458
|
-
end
|
1459
|
-
|
1460
|
-
expect(response.status ).to eql(expected_status)
|
1461
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1462
|
-
|
1463
|
-
result = JSON.parse(response.body)
|
1464
|
-
|
1465
|
-
# Check basic SCIM error rendering - good enough given other tests
|
1466
|
-
# elsewhere. Exact message varies by exception.
|
1467
|
-
#
|
1468
|
-
expect(result['detail']).to start_with(expected_prefix)
|
1469
|
-
end
|
1470
|
-
end
|
1471
|
-
|
1472
|
-
it 'handles custom exceptions' do
|
1473
|
-
exception_class = RuntimeError # (for testing only; usually, this would provoke a 500 response)
|
1474
|
-
|
1475
|
-
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1476
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1477
|
-
|
1478
|
-
expect_any_instance_of(MockUsersController).to receive(:scimitar_rescuable_exceptions).once { [ exception_class ] }
|
1479
|
-
expect_any_instance_of(MockUser ).to receive(:save! ).once { raise exception_class }
|
1480
|
-
|
1481
|
-
expect {
|
1482
|
-
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1483
|
-
}.to_not change { MockUser.count }
|
1484
|
-
|
1485
|
-
expect(response.status ).to eql(400)
|
1486
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1487
|
-
|
1488
|
-
result = JSON.parse(response.body)
|
1489
|
-
|
1490
|
-
expect(result['detail']).to start_with('Operation failed since record has become invalid: ')
|
1491
|
-
end
|
1492
|
-
|
1493
|
-
it 'reports other exceptions as 500s' do
|
1494
|
-
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1495
|
-
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1496
|
-
|
1497
|
-
expect_any_instance_of(MockUser).to receive(:save!).once { raise RuntimeError }
|
1498
|
-
|
1499
|
-
expect {
|
1500
|
-
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1501
|
-
}.to_not change { MockUser.count }
|
1502
|
-
|
1503
|
-
expect(response.status ).to eql(500)
|
1504
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1505
|
-
|
1506
|
-
result = JSON.parse(response.body)
|
1507
|
-
|
1508
|
-
expect(result['detail']).to eql('RuntimeError')
|
1509
|
-
end
|
1510
|
-
end
|
1511
|
-
|
1512
565
|
# ===========================================================================
|
1513
566
|
|
1514
567
|
context '#destroy' do
|
1515
|
-
it 'deletes an item if given no
|
568
|
+
it 'deletes an item if given no blok' do
|
1516
569
|
expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
|
1517
570
|
expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
|
1518
571
|
expect {
|
1519
|
-
delete "/Users/#{@u2.
|
572
|
+
delete "/Users/#{@u2.id}", params: { format: :scim }
|
1520
573
|
}.to change { MockUser.count }.by(-1)
|
1521
574
|
|
1522
575
|
expect(response.status).to eql(204)
|
@@ -1528,7 +581,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1528
581
|
expect_any_instance_of(MockUser).to_not receive(:destroy!)
|
1529
582
|
|
1530
583
|
expect {
|
1531
|
-
delete "/CustomDestroyUsers/#{@u2.
|
584
|
+
delete "/CustomDestroyUsers/#{@u2.id}", params: { format: :scim }
|
1532
585
|
}.to_not change { MockUser.count }
|
1533
586
|
|
1534
587
|
expect(response.status).to eql(204)
|
@@ -1543,11 +596,8 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1543
596
|
delete '/Users/xyz', params: { format: :scim }
|
1544
597
|
}.to_not change { MockUser.count }
|
1545
598
|
|
1546
|
-
expect(response.status
|
1547
|
-
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1548
|
-
|
599
|
+
expect(response.status).to eql(404)
|
1549
600
|
result = JSON.parse(response.body)
|
1550
|
-
|
1551
601
|
expect(result['status']).to eql('404')
|
1552
602
|
end
|
1553
603
|
end # "context '#destroy' do"
|