scimaenaga 0.5.0 → 0.7.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/MIT-LICENSE +1 -0
- data/README.md +2 -14
- data/app/controllers/concerns/scim_rails/exception_handler.rb +43 -1
- data/app/controllers/scim_rails/scim_groups_controller.rb +64 -40
- data/app/controllers/scim_rails/scim_users_controller.rb +39 -65
- data/app/libraries/scim_patch.rb +15 -10
- data/app/libraries/scim_patch_operation.rb +127 -24
- data/app/models/scim_rails/scim_query_parser.rb +5 -3
- data/config/routes.rb +2 -0
- data/lib/generators/scim_rails/templates/initializer.rb +0 -6
- data/lib/scim_rails/config.rb +1 -2
- data/lib/scim_rails/version.rb +1 -1
- data/spec/controllers/scim_rails/scim_groups_controller_spec.rb +249 -136
- data/spec/controllers/scim_rails/scim_users_controller_spec.rb +413 -203
- data/spec/dummy/app/models/user.rb +21 -0
- data/spec/dummy/bin/setup +2 -0
- data/spec/dummy/config/initializers/scim_rails_config.rb +6 -4
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20220117095407_add_country_to_users.rb +5 -0
- data/spec/dummy/db/migrate/20220131090107_add_deletable_to_users.rb +5 -0
- data/spec/dummy/db/schema.rb +7 -5
- data/spec/dummy/db/seeds.rb +15 -1
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +5770 -0
- data/spec/dummy/put_group.http +5 -0
- data/spec/dummy/tmp/restart.txt +0 -0
- data/spec/factories/user.rb +2 -0
- data/spec/libraries/scim_patch_operation_spec.rb +61 -31
- data/spec/libraries/scim_patch_spec.rb +38 -29
- data/spec/models/scim_query_parser_spec.rb +30 -0
- metadata +83 -67
- data/spec/support/scim_rails_config.rb +0 -59
@@ -1,32 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
6
6
|
include AuthHelper
|
7
7
|
|
8
8
|
routes { ScimRails::Engine.routes }
|
9
9
|
|
10
|
-
describe
|
10
|
+
describe 'index' do
|
11
11
|
let(:company) { create(:company) }
|
12
12
|
|
13
|
-
context
|
14
|
-
it
|
13
|
+
context 'when unauthorized' do
|
14
|
+
it 'returns scim+json content type' do
|
15
15
|
get :index, as: :json
|
16
16
|
|
17
|
-
expect(response.media_type).to eq
|
17
|
+
expect(response.media_type).to eq 'application/scim+json'
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
20
|
+
it 'fails with no credentials' do
|
21
21
|
get :index, as: :json
|
22
22
|
|
23
23
|
expect(response.status).to eq 401
|
24
24
|
end
|
25
25
|
|
26
|
-
it
|
27
|
-
request.env[
|
26
|
+
it 'fails with invalid credentials' do
|
27
|
+
request.env['HTTP_AUTHORIZATION'] =
|
28
28
|
ActionController::HttpAuthentication::Basic
|
29
|
-
.encode_credentials(
|
29
|
+
.encode_credentials('unauthorized', '123456')
|
30
30
|
|
31
31
|
get :index, as: :json
|
32
32
|
|
@@ -34,56 +34,57 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
context
|
37
|
+
context 'when when authorized' do
|
38
38
|
before :each do
|
39
39
|
http_login(company)
|
40
40
|
end
|
41
41
|
|
42
|
-
it
|
42
|
+
it 'returns scim+json content type' do
|
43
43
|
get :index, as: :json
|
44
44
|
|
45
|
-
expect(response.media_type).to eq
|
45
|
+
expect(response.media_type).to eq 'application/scim+json'
|
46
46
|
end
|
47
47
|
|
48
|
-
it
|
48
|
+
it 'is successful with valid credentials' do
|
49
49
|
get :index, as: :json
|
50
50
|
|
51
51
|
expect(response.status).to eq 200
|
52
52
|
end
|
53
53
|
|
54
|
-
it
|
54
|
+
it 'returns all results' do
|
55
55
|
create_list(:user, 10, company: company)
|
56
56
|
|
57
57
|
get :index, as: :json
|
58
58
|
response_body = JSON.parse(response.body)
|
59
|
-
expect(response_body.dig(
|
60
|
-
|
59
|
+
expect(response_body.dig('schemas',
|
60
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:ListResponse'
|
61
|
+
expect(response_body['totalResults']).to eq 10
|
61
62
|
end
|
62
63
|
|
63
|
-
it
|
64
|
+
it 'defaults to 100 results' do
|
64
65
|
create_list(:user, 300, company: company)
|
65
66
|
|
66
67
|
get :index, as: :json
|
67
68
|
response_body = JSON.parse(response.body)
|
68
|
-
expect(response_body[
|
69
|
-
expect(response_body[
|
69
|
+
expect(response_body['totalResults']).to eq 300
|
70
|
+
expect(response_body['Resources'].count).to eq 100
|
70
71
|
end
|
71
72
|
|
72
|
-
it
|
73
|
+
it 'paginates results' do
|
73
74
|
create_list(:user, 400, company: company)
|
74
75
|
expect(company.users.first.id).to eq 1
|
75
76
|
|
76
77
|
get :index, params: {
|
77
78
|
startIndex: 101,
|
78
|
-
count: 200
|
79
|
+
count: 200,
|
79
80
|
}, as: :json
|
80
81
|
response_body = JSON.parse(response.body)
|
81
|
-
expect(response_body[
|
82
|
-
expect(response_body[
|
83
|
-
expect(response_body.dig(
|
82
|
+
expect(response_body['totalResults']).to eq 400
|
83
|
+
expect(response_body['Resources'].count).to eq 200
|
84
|
+
expect(response_body.dig('Resources', 0, 'id')).to eq 101
|
84
85
|
end
|
85
86
|
|
86
|
-
it
|
87
|
+
it 'paginates results by configurable scim_users_list_order' do
|
87
88
|
allow(ScimRails.config).to receive(:scim_users_list_order).and_return({ created_at: :desc })
|
88
89
|
|
89
90
|
create_list(:user, 400, company: company)
|
@@ -91,78 +92,79 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
91
92
|
|
92
93
|
get :index, params: {
|
93
94
|
startIndex: 1,
|
94
|
-
count: 10
|
95
|
+
count: 10,
|
95
96
|
}, as: :json
|
96
97
|
response_body = JSON.parse(response.body)
|
97
|
-
expect(response_body[
|
98
|
-
expect(response_body[
|
99
|
-
expect(response_body.dig(
|
98
|
+
expect(response_body['totalResults']).to eq 400
|
99
|
+
expect(response_body['Resources'].count).to eq 10
|
100
|
+
expect(response_body.dig('Resources', 0, 'id')).to eq 400
|
100
101
|
end
|
101
102
|
|
102
|
-
it
|
103
|
-
create(:user, email:
|
104
|
-
create(:user, email:
|
103
|
+
it 'filters results by provided email filter' do
|
104
|
+
create(:user, email: 'test1@example.com', company: company)
|
105
|
+
create(:user, email: 'test2@example.com', company: company)
|
105
106
|
|
106
107
|
get :index, params: {
|
107
|
-
filter:
|
108
|
+
filter: 'email eq test1@example.com',
|
108
109
|
}, as: :json
|
109
110
|
response_body = JSON.parse(response.body)
|
110
|
-
expect(response_body[
|
111
|
-
expect(response_body[
|
111
|
+
expect(response_body['totalResults']).to eq 1
|
112
|
+
expect(response_body['Resources'].count).to eq 1
|
112
113
|
end
|
113
114
|
|
114
|
-
it
|
115
|
-
create(:user, first_name:
|
116
|
-
create(:user, first_name:
|
115
|
+
it 'filters results by provided name filter' do
|
116
|
+
create(:user, first_name: 'Chidi', last_name: 'Anagonye', company: company)
|
117
|
+
create(:user, first_name: 'Eleanor', last_name: 'Shellstrop', company: company)
|
117
118
|
|
118
119
|
get :index, params: {
|
119
|
-
filter:
|
120
|
+
filter: 'familyName eq Shellstrop',
|
120
121
|
}, as: :json
|
121
122
|
response_body = JSON.parse(response.body)
|
122
|
-
expect(response_body[
|
123
|
-
expect(response_body[
|
123
|
+
expect(response_body['totalResults']).to eq 1
|
124
|
+
expect(response_body['Resources'].count).to eq 1
|
124
125
|
end
|
125
126
|
|
126
|
-
it
|
127
|
+
it 'returns no results for unfound filter parameters' do
|
127
128
|
get :index, params: {
|
128
|
-
filter:
|
129
|
+
filter: 'familyName eq fake_not_there',
|
129
130
|
}, as: :json
|
130
131
|
response_body = JSON.parse(response.body)
|
131
|
-
expect(response_body[
|
132
|
-
expect(response_body[
|
132
|
+
expect(response_body['totalResults']).to eq 0
|
133
|
+
expect(response_body['Resources'].count).to eq 0
|
133
134
|
end
|
134
135
|
|
135
|
-
it
|
136
|
+
it 'returns no results for undefined filter queries' do
|
136
137
|
get :index, params: {
|
137
|
-
filter:
|
138
|
+
filter: 'address eq 101 Nowhere USA',
|
138
139
|
}, as: :json
|
139
140
|
expect(response.status).to eq 400
|
140
141
|
response_body = JSON.parse(response.body)
|
141
|
-
expect(response_body.dig(
|
142
|
+
expect(response_body.dig('schemas',
|
143
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:Error'
|
142
144
|
end
|
143
145
|
end
|
144
146
|
end
|
145
147
|
|
146
|
-
describe
|
148
|
+
describe 'show' do
|
147
149
|
let(:company) { create(:company) }
|
148
150
|
|
149
|
-
context
|
150
|
-
it
|
151
|
+
context 'when unauthorized' do
|
152
|
+
it 'returns scim+json content type' do
|
151
153
|
get :show, params: { id: 1 }, as: :json
|
152
154
|
|
153
|
-
expect(response.media_type).to eq
|
155
|
+
expect(response.media_type).to eq 'application/scim+json'
|
154
156
|
end
|
155
157
|
|
156
|
-
it
|
158
|
+
it 'fails with no credentials' do
|
157
159
|
get :show, params: { id: 1 }, as: :json
|
158
160
|
|
159
161
|
expect(response.status).to eq 401
|
160
162
|
end
|
161
163
|
|
162
|
-
it
|
163
|
-
request.env[
|
164
|
+
it 'fails with invalid credentials' do
|
165
|
+
request.env['HTTP_AUTHORIZATION'] =
|
164
166
|
ActionController::HttpAuthentication::Basic
|
165
|
-
.encode_credentials(
|
167
|
+
.encode_credentials('unauthorized', '123456')
|
166
168
|
|
167
169
|
get :show, params: { id: 1 }, as: :json
|
168
170
|
|
@@ -170,31 +172,31 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
170
172
|
end
|
171
173
|
end
|
172
174
|
|
173
|
-
context
|
175
|
+
context 'when authorized' do
|
174
176
|
before :each do
|
175
177
|
http_login(company)
|
176
178
|
end
|
177
179
|
|
178
|
-
it
|
180
|
+
it 'returns scim+json content type' do
|
179
181
|
get :show, params: { id: 1 }, as: :json
|
180
182
|
|
181
|
-
expect(response.media_type).to eq
|
183
|
+
expect(response.media_type).to eq 'application/scim+json'
|
182
184
|
end
|
183
185
|
|
184
|
-
it
|
186
|
+
it 'is successful with valid credentials' do
|
185
187
|
create(:user, id: 1, company: company)
|
186
188
|
get :show, params: { id: 1 }, as: :json
|
187
189
|
|
188
190
|
expect(response.status).to eq 200
|
189
191
|
end
|
190
192
|
|
191
|
-
it
|
192
|
-
get :show, params: { id:
|
193
|
+
it 'returns :not_found for id that cannot be found' do
|
194
|
+
get :show, params: { id: 'fake_id' }, as: :json
|
193
195
|
|
194
196
|
expect(response.status).to eq 404
|
195
197
|
end
|
196
198
|
|
197
|
-
it
|
199
|
+
it 'returns :not_found for a correct id but unauthorized company' do
|
198
200
|
new_company = create(:company)
|
199
201
|
create(:user, company: new_company, id: 1)
|
200
202
|
|
@@ -205,26 +207,26 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
205
207
|
end
|
206
208
|
end
|
207
209
|
|
208
|
-
describe
|
210
|
+
describe 'create' do
|
209
211
|
let(:company) { create(:company) }
|
210
212
|
|
211
|
-
context
|
212
|
-
it
|
213
|
+
context 'when unauthorized' do
|
214
|
+
it 'returns scim+json content type' do
|
213
215
|
post :create, as: :json
|
214
216
|
|
215
|
-
expect(response.media_type).to eq
|
217
|
+
expect(response.media_type).to eq 'application/scim+json'
|
216
218
|
end
|
217
219
|
|
218
|
-
it
|
220
|
+
it 'fails with no credentials' do
|
219
221
|
post :create, as: :json
|
220
222
|
|
221
223
|
expect(response.status).to eq 401
|
222
224
|
end
|
223
225
|
|
224
|
-
it
|
225
|
-
request.env[
|
226
|
+
it 'fails with invalid credentials' do
|
227
|
+
request.env['HTTP_AUTHORIZATION'] =
|
226
228
|
ActionController::HttpAuthentication::Basic
|
227
|
-
.encode_credentials(
|
229
|
+
.encode_credentials('unauthorized', '123456')
|
228
230
|
|
229
231
|
post :create, as: :json
|
230
232
|
|
@@ -232,139 +234,139 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
232
234
|
end
|
233
235
|
end
|
234
236
|
|
235
|
-
context
|
237
|
+
context 'when authorized' do
|
236
238
|
before :each do
|
237
239
|
http_login(company)
|
238
240
|
end
|
239
241
|
|
240
|
-
it
|
242
|
+
it 'returns scim+json content type' do
|
241
243
|
post :create, params: {
|
242
244
|
name: {
|
243
|
-
givenName:
|
244
|
-
familyName:
|
245
|
+
givenName: 'New',
|
246
|
+
familyName: 'User',
|
245
247
|
},
|
246
248
|
emails: [
|
247
249
|
{
|
248
|
-
value:
|
250
|
+
value: 'new@example.com',
|
249
251
|
}
|
250
|
-
]
|
252
|
+
],
|
251
253
|
}, as: :json
|
252
254
|
|
253
|
-
expect(response.media_type).to eq
|
255
|
+
expect(response.media_type).to eq 'application/scim+json'
|
254
256
|
end
|
255
257
|
|
256
|
-
it
|
258
|
+
it 'is successful with valid credentials' do
|
257
259
|
expect(company.users.count).to eq 0
|
258
260
|
|
259
261
|
post :create, params: {
|
260
262
|
name: {
|
261
|
-
givenName:
|
262
|
-
familyName:
|
263
|
+
givenName: 'New',
|
264
|
+
familyName: 'User',
|
263
265
|
},
|
264
266
|
emails: [
|
265
267
|
{
|
266
|
-
value:
|
268
|
+
value: 'new@example.com',
|
267
269
|
}
|
268
|
-
]
|
270
|
+
],
|
269
271
|
}, as: :json
|
270
272
|
|
271
273
|
expect(response.status).to eq 201
|
272
274
|
expect(company.users.count).to eq 1
|
273
275
|
user = company.users.first
|
274
276
|
expect(user.persisted?).to eq true
|
275
|
-
expect(user.first_name).to eq
|
276
|
-
expect(user.last_name).to eq
|
277
|
-
expect(user.email).to eq
|
277
|
+
expect(user.first_name).to eq 'New'
|
278
|
+
expect(user.last_name).to eq 'User'
|
279
|
+
expect(user.email).to eq 'new@example.com'
|
278
280
|
end
|
279
281
|
|
280
|
-
it
|
282
|
+
it 'ignores unconfigured params' do
|
281
283
|
post :create, params: {
|
282
284
|
name: {
|
283
|
-
formattedName:
|
284
|
-
givenName:
|
285
|
-
familyName:
|
285
|
+
formattedName: 'New User',
|
286
|
+
givenName: 'New',
|
287
|
+
familyName: 'User',
|
286
288
|
},
|
287
289
|
emails: [
|
288
290
|
{
|
289
|
-
value:
|
291
|
+
value: 'new@example.com',
|
290
292
|
}
|
291
|
-
]
|
293
|
+
],
|
292
294
|
}, as: :json
|
293
295
|
|
294
296
|
expect(response.status).to eq 201
|
295
297
|
expect(company.users.count).to eq 1
|
296
298
|
end
|
297
299
|
|
298
|
-
it
|
300
|
+
it 'returns 422 if required params are missing' do
|
299
301
|
post :create, params: {
|
300
302
|
name: {
|
301
|
-
familyName:
|
303
|
+
familyName: 'User',
|
302
304
|
},
|
303
305
|
emails: [
|
304
306
|
{
|
305
|
-
value:
|
307
|
+
value: 'new@example.com',
|
306
308
|
}
|
307
|
-
]
|
309
|
+
],
|
308
310
|
}, as: :json
|
309
311
|
|
310
312
|
expect(response.status).to eq 422
|
311
313
|
expect(company.users.count).to eq 0
|
312
314
|
end
|
313
315
|
|
314
|
-
it
|
315
|
-
create(:user, email:
|
316
|
+
it 'returns 201 if user already exists and updates user' do
|
317
|
+
create(:user, email: 'new@example.com', company: company)
|
316
318
|
|
317
319
|
post :create, params: {
|
318
320
|
name: {
|
319
|
-
givenName:
|
320
|
-
familyName:
|
321
|
+
givenName: 'Not New',
|
322
|
+
familyName: 'User',
|
321
323
|
},
|
322
324
|
emails: [
|
323
325
|
{
|
324
|
-
value:
|
326
|
+
value: 'new@example.com',
|
325
327
|
}
|
326
|
-
]
|
328
|
+
],
|
327
329
|
}, as: :json
|
328
330
|
|
329
331
|
expect(response.status).to eq 201
|
330
332
|
expect(company.users.count).to eq 1
|
331
|
-
expect(company.users.first.first_name).to eq
|
333
|
+
expect(company.users.first.first_name).to eq 'Not New'
|
332
334
|
end
|
333
335
|
|
334
|
-
it
|
336
|
+
it 'returns 409 if user already exists and config.scim_user_prevent_update_on_create is set to true' do
|
335
337
|
allow(ScimRails.config).to receive(:scim_user_prevent_update_on_create).and_return(true)
|
336
|
-
create(:user, email:
|
338
|
+
create(:user, email: 'new@example.com', company: company)
|
337
339
|
|
338
340
|
post :create, params: {
|
339
341
|
name: {
|
340
|
-
givenName:
|
341
|
-
familyName:
|
342
|
+
givenName: 'Not New',
|
343
|
+
familyName: 'User',
|
342
344
|
},
|
343
345
|
emails: [
|
344
346
|
{
|
345
|
-
value:
|
347
|
+
value: 'new@example.com',
|
346
348
|
}
|
347
|
-
]
|
349
|
+
],
|
348
350
|
}, as: :json
|
349
351
|
|
350
352
|
expect(response.status).to eq 409
|
351
353
|
expect(company.users.count).to eq 1
|
352
354
|
end
|
353
355
|
|
354
|
-
it
|
356
|
+
it 'creates and archives inactive user' do
|
355
357
|
post :create, params: {
|
356
358
|
id: 1,
|
357
|
-
userName:
|
359
|
+
userName: 'test@example.com',
|
358
360
|
name: {
|
359
|
-
givenName:
|
360
|
-
familyName:
|
361
|
+
givenName: 'Test',
|
362
|
+
familyName: 'User',
|
361
363
|
},
|
362
364
|
emails: [
|
363
365
|
{
|
364
|
-
value:
|
366
|
+
value: 'test@example.com',
|
365
367
|
}
|
366
368
|
],
|
367
|
-
active:
|
369
|
+
active: false,
|
368
370
|
}, as: :json
|
369
371
|
|
370
372
|
expect(response.status).to eq 201
|
@@ -375,26 +377,26 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
375
377
|
end
|
376
378
|
end
|
377
379
|
|
378
|
-
describe
|
380
|
+
describe 'put update' do
|
379
381
|
let(:company) { create(:company) }
|
380
382
|
|
381
|
-
context
|
382
|
-
it
|
383
|
+
context 'when unauthorized' do
|
384
|
+
it 'returns scim+json content type' do
|
383
385
|
put :put_update, params: { id: 1 }, as: :json
|
384
386
|
|
385
|
-
expect(response.media_type).to eq
|
387
|
+
expect(response.media_type).to eq 'application/scim+json'
|
386
388
|
end
|
387
389
|
|
388
|
-
it
|
390
|
+
it 'fails with no credentials' do
|
389
391
|
put :put_update, params: { id: 1 }, as: :json
|
390
392
|
|
391
393
|
expect(response.status).to eq 401
|
392
394
|
end
|
393
395
|
|
394
|
-
it
|
395
|
-
request.env[
|
396
|
+
it 'fails with invalid credentials' do
|
397
|
+
request.env['HTTP_AUTHORIZATION'] =
|
396
398
|
ActionController::HttpAuthentication::Basic
|
397
|
-
.encode_credentials(
|
399
|
+
.encode_credentials('unauthorized', '123456')
|
398
400
|
|
399
401
|
put :put_update, params: { id: 1 }, as: :json
|
400
402
|
|
@@ -402,50 +404,56 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
402
404
|
end
|
403
405
|
end
|
404
406
|
|
405
|
-
context
|
407
|
+
context 'when authorized' do
|
406
408
|
let!(:user) { create(:user, id: 1, company: company) }
|
407
409
|
|
408
410
|
before :each do
|
409
411
|
http_login(company)
|
410
412
|
end
|
411
413
|
|
412
|
-
it
|
414
|
+
it 'returns scim+json content type' do
|
413
415
|
put :put_update, params: put_params, as: :json
|
414
416
|
|
415
|
-
expect(response.media_type).to eq
|
417
|
+
expect(response.media_type).to eq 'application/scim+json'
|
416
418
|
end
|
417
419
|
|
418
|
-
it
|
420
|
+
it 'is successful with valid credentials' do
|
419
421
|
put :put_update, params: put_params, as: :json
|
420
422
|
|
421
423
|
expect(response.status).to eq 200
|
422
424
|
end
|
423
425
|
|
424
|
-
it
|
425
|
-
|
426
|
+
it 'successfully change user email' do
|
427
|
+
put :put_update, params: put_params(id: user.id), as: :json
|
428
|
+
|
429
|
+
expect(user.reload.email).to eq 'test@example.com'
|
430
|
+
end
|
431
|
+
|
432
|
+
it 'deprovisions an active record' do
|
433
|
+
request.content_type = 'application/scim+json'
|
426
434
|
put :put_update, params: put_params(active: false), as: :json
|
427
435
|
|
428
436
|
expect(response.status).to eq 200
|
429
437
|
expect(user.reload.active?).to eq false
|
430
438
|
end
|
431
439
|
|
432
|
-
it
|
440
|
+
it 'reprovisions an inactive record' do
|
433
441
|
user.archive!
|
434
442
|
expect(user.reload.active?).to eq false
|
435
|
-
request.content_type =
|
443
|
+
request.content_type = 'application/scim+json'
|
436
444
|
put :put_update, params: put_params(active: true), as: :json
|
437
445
|
|
438
446
|
expect(response.status).to eq 200
|
439
447
|
expect(user.reload.active?).to eq true
|
440
448
|
end
|
441
449
|
|
442
|
-
it
|
443
|
-
put :put_update, params: { id:
|
450
|
+
it 'returns :not_found for id that cannot be found' do
|
451
|
+
put :put_update, params: { id: 'fake_id' }, as: :json
|
444
452
|
|
445
453
|
expect(response.status).to eq 404
|
446
454
|
end
|
447
455
|
|
448
|
-
it
|
456
|
+
it 'returns :not_found for a correct id but unauthorized company' do
|
449
457
|
new_company = create(:company)
|
450
458
|
create(:user, company: new_company, id: 1000)
|
451
459
|
|
@@ -454,16 +462,16 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
454
462
|
expect(response.status).to eq 404
|
455
463
|
end
|
456
464
|
|
457
|
-
it
|
465
|
+
it 'is returns 422 with incomplete request' do
|
458
466
|
put :put_update, params: {
|
459
467
|
id: 1,
|
460
|
-
userName:
|
468
|
+
userName: 'test@example.com',
|
461
469
|
emails: [
|
462
470
|
{
|
463
|
-
value:
|
471
|
+
value: 'test@example.com',
|
464
472
|
}
|
465
473
|
],
|
466
|
-
active:
|
474
|
+
active: 'true',
|
467
475
|
}, as: :json
|
468
476
|
|
469
477
|
expect(response.status).to eq 422
|
@@ -471,26 +479,26 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
471
479
|
end
|
472
480
|
end
|
473
481
|
|
474
|
-
describe
|
482
|
+
describe 'patch update' do
|
475
483
|
let(:company) { create(:company) }
|
476
484
|
|
477
|
-
context
|
478
|
-
it
|
485
|
+
context 'when unauthorized' do
|
486
|
+
it 'returns scim+json content type' do
|
479
487
|
patch :patch_update, params: patch_params(id: 1), as: :json
|
480
488
|
|
481
|
-
expect(response.media_type).to eq
|
489
|
+
expect(response.media_type).to eq 'application/scim+json'
|
482
490
|
end
|
483
491
|
|
484
|
-
it
|
492
|
+
it 'fails with no credentials' do
|
485
493
|
patch :patch_update, params: patch_params(id: 1), as: :json
|
486
494
|
|
487
495
|
expect(response.status).to eq 401
|
488
496
|
end
|
489
497
|
|
490
|
-
it
|
491
|
-
request.env[
|
498
|
+
it 'fails with invalid credentials' do
|
499
|
+
request.env['HTTP_AUTHORIZATION'] =
|
492
500
|
ActionController::HttpAuthentication::Basic
|
493
|
-
.encode_credentials(
|
501
|
+
.encode_credentials('unauthorized', '123456')
|
494
502
|
|
495
503
|
patch :patch_update, params: patch_params(id: 1), as: :json
|
496
504
|
|
@@ -498,32 +506,55 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
498
506
|
end
|
499
507
|
end
|
500
508
|
|
501
|
-
context
|
509
|
+
context 'when authorized' do
|
502
510
|
let!(:user) { create(:user, id: 1, company: company) }
|
503
511
|
|
504
512
|
before :each do
|
505
513
|
http_login(company)
|
506
514
|
end
|
507
515
|
|
508
|
-
it
|
509
|
-
patch :patch_update, params: patch_params(id:
|
516
|
+
it 'returns scim+json content type' do
|
517
|
+
patch :patch_update, params: patch_params(id: user.id), as: :json
|
510
518
|
|
511
|
-
expect(response.media_type).to eq
|
519
|
+
expect(response.media_type).to eq 'application/scim+json'
|
512
520
|
end
|
513
521
|
|
514
|
-
it
|
515
|
-
patch :patch_update, params: patch_params(id:
|
522
|
+
it 'is successful with valid credentials' do
|
523
|
+
patch :patch_update, params: patch_params(id: user.id), as: :json
|
516
524
|
|
517
525
|
expect(response.status).to eq 200
|
518
526
|
end
|
519
527
|
|
520
|
-
it
|
521
|
-
|
528
|
+
it 'rollback all changes when contains any invalid operation' do
|
529
|
+
expect do
|
530
|
+
patch :patch_update, params: {
|
531
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
532
|
+
id: user.id,
|
533
|
+
Operations: [
|
534
|
+
{
|
535
|
+
op: 'Replace',
|
536
|
+
path: 'emails[type eq "work"].value',
|
537
|
+
value: 'change@example.com',
|
538
|
+
},
|
539
|
+
{
|
540
|
+
op: 'Replace',
|
541
|
+
value: 'hoge',
|
542
|
+
}
|
543
|
+
],
|
544
|
+
},
|
545
|
+
as: :json
|
546
|
+
end.to_not change { user.reload.email }
|
547
|
+
|
548
|
+
expect(response.status).to eq 422
|
549
|
+
end
|
550
|
+
|
551
|
+
it 'returns :not_found for id that cannot be found' do
|
552
|
+
get :patch_update, params: patch_params(id: 'fake_id'), as: :json
|
522
553
|
|
523
554
|
expect(response.status).to eq 404
|
524
555
|
end
|
525
556
|
|
526
|
-
it
|
557
|
+
it 'returns :not_found for a correct id but unauthorized company' do
|
527
558
|
new_company = create(:company)
|
528
559
|
create(:user, company: new_company, id: 1000)
|
529
560
|
|
@@ -532,12 +563,15 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
532
563
|
expect(response.status).to eq 404
|
533
564
|
end
|
534
565
|
|
535
|
-
|
566
|
+
it 'successfully archives user' do
|
536
567
|
expect(company.users.count).to eq 1
|
537
568
|
user = company.users.first
|
538
569
|
expect(user.archived?).to eq false
|
539
570
|
|
540
|
-
patch
|
571
|
+
patch \
|
572
|
+
:patch_update,
|
573
|
+
params: patch_active_params(id: user.id, active: false),
|
574
|
+
as: :json
|
541
575
|
|
542
576
|
expect(response.status).to eq 200
|
543
577
|
expect(company.users.count).to eq 1
|
@@ -545,14 +579,14 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
545
579
|
expect(user.archived?).to eq true
|
546
580
|
end
|
547
581
|
|
548
|
-
|
582
|
+
it 'successfully restores user' do
|
549
583
|
expect(company.users.count).to eq 1
|
550
584
|
user = company.users.first.tap(&:archive!)
|
551
585
|
expect(user.archived?).to eq true
|
552
586
|
|
553
587
|
patch \
|
554
588
|
:patch_update,
|
555
|
-
params:
|
589
|
+
params: patch_active_params(id: 1, active: true),
|
556
590
|
as: :json
|
557
591
|
|
558
592
|
expect(response.status).to eq 200
|
@@ -561,7 +595,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
561
595
|
expect(user.archived?).to eq false
|
562
596
|
end
|
563
597
|
|
564
|
-
it
|
598
|
+
it 'successfully change user email' do
|
565
599
|
expect(company.users.count).to eq 1
|
566
600
|
user = company.users.first
|
567
601
|
user.update(email: 'test@example.com')
|
@@ -569,7 +603,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
569
603
|
|
570
604
|
patch \
|
571
605
|
:patch_update,
|
572
|
-
params: patch_params(id: 1
|
606
|
+
params: patch_params(id: 1),
|
573
607
|
as: :json
|
574
608
|
|
575
609
|
expect(response.status).to eq 200
|
@@ -578,121 +612,297 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
578
612
|
expect(user.email).to eq 'change@example.com'
|
579
613
|
end
|
580
614
|
|
581
|
-
it
|
615
|
+
it 'is case insensetive for op value' do
|
582
616
|
# Note, this is for backward compatibility. op should always
|
583
617
|
# be lower case and support for case insensitivity will be removed
|
584
618
|
patch :patch_update, params: {
|
585
|
-
schemas: [
|
619
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
586
620
|
id: 1,
|
587
621
|
Operations: [
|
588
|
-
|
589
|
-
op:
|
590
|
-
path:
|
591
|
-
value:
|
622
|
+
{
|
623
|
+
op: 'Replace',
|
624
|
+
path: 'emails[type eq "work"].value',
|
625
|
+
value: 'test@example.com',
|
592
626
|
}
|
593
|
-
]
|
627
|
+
],
|
594
628
|
}, as: :json
|
595
629
|
|
596
630
|
expect(response.status).to eq 200
|
597
631
|
end
|
598
632
|
|
599
|
-
|
633
|
+
it "don't update if not included in mutable attributes" do
|
634
|
+
expect do
|
635
|
+
patch :patch_update, params: {
|
636
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
637
|
+
id: user.id,
|
638
|
+
Operations: [
|
639
|
+
{
|
640
|
+
op: 'Replace',
|
641
|
+
path: 'emails[type eq "work"].value',
|
642
|
+
value: 'change@example.com',
|
643
|
+
},
|
644
|
+
{
|
645
|
+
op: 'Replace',
|
646
|
+
path: 'country',
|
647
|
+
value: 'Japan',
|
648
|
+
}
|
649
|
+
],
|
650
|
+
}, as: :json
|
651
|
+
end.not_to change { user.reload.country }
|
652
|
+
|
653
|
+
expect(response.status).to eq 422
|
654
|
+
end
|
655
|
+
|
656
|
+
xit 'returns 422 when value is not an object' do
|
600
657
|
patch :patch_update, params: {
|
601
|
-
schemas: [
|
658
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
602
659
|
id: 1,
|
603
660
|
Operations: [
|
604
661
|
{
|
605
|
-
op:
|
606
|
-
path:
|
607
|
-
value:
|
662
|
+
op: 'replace',
|
663
|
+
path: 'emails[type eq "work"].value',
|
664
|
+
value: 'francis@example.com',
|
608
665
|
}
|
609
|
-
]
|
666
|
+
],
|
610
667
|
}
|
611
668
|
|
612
669
|
expect(response.status).to eq 422
|
613
670
|
response_body = JSON.parse(response.body)
|
614
|
-
expect(response_body.dig(
|
671
|
+
expect(response_body.dig('schemas',
|
672
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:Error'
|
615
673
|
end
|
616
674
|
|
617
|
-
it
|
675
|
+
it 'returns 422 when value is missing' do
|
618
676
|
patch :patch_update, params: {
|
619
|
-
schemas: [
|
677
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
620
678
|
id: 1,
|
621
679
|
Operations: [
|
622
680
|
{
|
623
|
-
op:
|
624
|
-
path:
|
681
|
+
op: 'replace',
|
682
|
+
path: 'emails[type eq "work"].value',
|
625
683
|
}
|
626
|
-
]
|
684
|
+
],
|
627
685
|
}, as: :json
|
628
686
|
|
629
687
|
expect(response.status).to eq 422
|
630
688
|
response_body = JSON.parse(response.body)
|
631
|
-
expect(response_body.dig(
|
689
|
+
expect(response_body.dig('schemas',
|
690
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:Error'
|
632
691
|
end
|
633
692
|
|
634
|
-
it
|
693
|
+
it 'returns 422 when path is missing' do
|
635
694
|
patch :patch_update, params: {
|
636
|
-
schemas: [
|
695
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
637
696
|
id: 1,
|
638
697
|
Operations: [
|
639
698
|
{
|
640
|
-
op:
|
699
|
+
op: 'replace',
|
641
700
|
}
|
642
|
-
]
|
701
|
+
],
|
643
702
|
}, as: :json
|
644
703
|
|
645
704
|
expect(response.status).to eq 422
|
646
705
|
response_body = JSON.parse(response.body)
|
647
|
-
expect(response_body.dig(
|
706
|
+
expect(response_body.dig('schemas',
|
707
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:Error'
|
648
708
|
end
|
649
709
|
|
650
|
-
it
|
710
|
+
it 'returns 422 operations key is missing' do
|
651
711
|
patch :patch_update, params: {
|
652
|
-
schemas: [
|
712
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
653
713
|
id: 1,
|
654
714
|
Foobars: [
|
655
715
|
{
|
656
|
-
op:
|
716
|
+
op: 'replace',
|
657
717
|
}
|
658
|
-
]
|
718
|
+
],
|
659
719
|
}, as: :json
|
660
720
|
|
661
721
|
expect(response.status).to eq 422
|
662
722
|
response_body = JSON.parse(response.body)
|
663
|
-
expect(response_body.dig(
|
723
|
+
expect(response_body.dig('schemas',
|
724
|
+
0)).to eq 'urn:ietf:params:scim:api:messages:2.0:Error'
|
725
|
+
end
|
726
|
+
|
727
|
+
it 'successfully change multiple attributes' do
|
728
|
+
patch :patch_update, params: {
|
729
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
730
|
+
id: 1,
|
731
|
+
Operations: [
|
732
|
+
{
|
733
|
+
op: 'Replace',
|
734
|
+
path: 'emails[type eq "work"].value',
|
735
|
+
value: 'changed@example.com',
|
736
|
+
},
|
737
|
+
{
|
738
|
+
op: 'Replace',
|
739
|
+
value: {
|
740
|
+
'name.givenName': 'changedGivenName',
|
741
|
+
'name.familyName': 'changedFamilyName',
|
742
|
+
},
|
743
|
+
}
|
744
|
+
],
|
745
|
+
}, as: :json
|
746
|
+
|
747
|
+
user.reload
|
748
|
+
expect(user.email).to eq 'changed@example.com'
|
749
|
+
expect(user.first_name).to eq 'changedGivenName'
|
750
|
+
expect(user.last_name).to eq 'changedFamilyName'
|
751
|
+
end
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
describe 'destroy' do
|
756
|
+
let(:company) { create(:company) }
|
757
|
+
|
758
|
+
context "when unauthorized" do
|
759
|
+
it "returns scim+json content type" do
|
760
|
+
delete :destroy, params: { id: 1 }, as: :json
|
761
|
+
|
762
|
+
expect(response.media_type).to eq "application/scim+json"
|
763
|
+
end
|
764
|
+
|
765
|
+
it "fails with no credentials" do
|
766
|
+
delete :destroy, params: { id: 1 }, as: :json
|
767
|
+
|
768
|
+
expect(response.status).to eq 401
|
769
|
+
end
|
770
|
+
|
771
|
+
it "fails with invalid credentials" do
|
772
|
+
request.env["HTTP_AUTHORIZATION"] =
|
773
|
+
ActionController::HttpAuthentication::Basic
|
774
|
+
.encode_credentials("unauthorized", "123456")
|
775
|
+
|
776
|
+
delete :destroy, params: { id: 1 }, as: :json
|
777
|
+
|
778
|
+
expect(response.status).to eq 401
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
context 'when authorized' do
|
783
|
+
let!(:user) { create(:user, id: 1, company: company) }
|
784
|
+
|
785
|
+
before :each do
|
786
|
+
http_login(company)
|
787
|
+
end
|
788
|
+
|
789
|
+
context 'when User destroy method is configured' do
|
790
|
+
|
791
|
+
it 'is sucessful with valid credentials' do
|
792
|
+
delete :destroy, params: { id: 1 }, as: :json
|
793
|
+
|
794
|
+
expect(response.status).to eq 204
|
795
|
+
end
|
796
|
+
|
797
|
+
it 'returns empty response' do
|
798
|
+
delete :destroy, params: { id: 1 }, as: :json
|
799
|
+
|
800
|
+
expect(response.body).to be_empty
|
801
|
+
end
|
802
|
+
|
803
|
+
it 'successfully deletes User' do
|
804
|
+
expect do
|
805
|
+
delete :destroy, params: { id: 1 }, as: :json
|
806
|
+
end.to change { company.users.reload.count }.from(1).to(0)
|
807
|
+
|
808
|
+
expect(response.status).to eq 204
|
809
|
+
end
|
810
|
+
end
|
811
|
+
|
812
|
+
context 'when User destroy method is not configured' do
|
813
|
+
it 'does not delete User' do
|
814
|
+
allow(ScimRails.config).to(
|
815
|
+
receive(:user_destroy_method).and_return(nil)
|
816
|
+
)
|
817
|
+
|
818
|
+
expect do
|
819
|
+
delete :destroy, params: { id: 1 }, as: :json
|
820
|
+
end.not_to change { company.users.reload.count }.from(1)
|
821
|
+
|
822
|
+
expect(response.status).to eq 500
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
context 'when User destroy method is invalid' do
|
827
|
+
it 'does not delete User' do
|
828
|
+
allow(ScimRails.config).to(
|
829
|
+
receive(:user_destroy_method).and_return('destory!')
|
830
|
+
)
|
831
|
+
|
832
|
+
expect do
|
833
|
+
delete :destroy, params: { id: 1 }, as: :json
|
834
|
+
end.not_to change { company.users.reload.count }.from(1)
|
835
|
+
|
836
|
+
expect(response.status).to eq 500
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
context 'when target User is not found' do
|
841
|
+
it 'return 404 not found' do
|
842
|
+
expect do
|
843
|
+
delete :destroy, params: { id: 999999 }, as: :json
|
844
|
+
end.not_to change { company.users.reload.count }.from(1)
|
845
|
+
|
846
|
+
expect(response.status).to eq 404
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
context 'when target User are not allowed to delete' do
|
851
|
+
let!(:user) { create(:user, id: 1, company: company, deletable: false) }
|
852
|
+
|
853
|
+
it 'does not delete user' do
|
854
|
+
expect do
|
855
|
+
delete :destroy, params: { id: 1 }, as: :json
|
856
|
+
end.not_to change { company.users.reload.count }.from(1)
|
857
|
+
|
858
|
+
expect(response.status).to eq 400
|
859
|
+
end
|
664
860
|
end
|
665
861
|
end
|
666
862
|
end
|
667
863
|
|
668
864
|
def patch_params(id:, active: false)
|
669
865
|
{
|
670
|
-
schemas: [
|
866
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
671
867
|
id: id,
|
672
868
|
Operations: [
|
673
869
|
{
|
674
|
-
op:
|
675
|
-
path:
|
676
|
-
value:
|
870
|
+
op: 'Replace',
|
871
|
+
path: 'emails[type eq "work"].value',
|
872
|
+
value: 'change@example.com',
|
677
873
|
}
|
678
|
-
]
|
874
|
+
],
|
679
875
|
}
|
680
876
|
end
|
681
877
|
|
682
|
-
def
|
878
|
+
def patch_active_params(id:, active: false)
|
683
879
|
{
|
684
|
-
|
685
|
-
|
880
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
881
|
+
id: id,
|
882
|
+
Operations: [
|
883
|
+
{
|
884
|
+
op: 'Replace',
|
885
|
+
path: 'active',
|
886
|
+
value: active,
|
887
|
+
}
|
888
|
+
],
|
889
|
+
}
|
890
|
+
end
|
891
|
+
|
892
|
+
def put_params(id: 1, active: true)
|
893
|
+
{
|
894
|
+
id: id,
|
895
|
+
userName: 'test@example.com',
|
686
896
|
name: {
|
687
|
-
givenName:
|
688
|
-
familyName:
|
897
|
+
givenName: 'Test',
|
898
|
+
familyName: 'User',
|
689
899
|
},
|
690
900
|
emails: [
|
691
901
|
{
|
692
|
-
value:
|
902
|
+
value: 'test@example.com',
|
693
903
|
}
|
694
904
|
],
|
695
|
-
active: active
|
905
|
+
active: active,
|
696
906
|
}
|
697
907
|
end
|
698
908
|
end
|