scimaenaga 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -0
  3. data/README.md +2 -14
  4. data/app/controllers/concerns/scim_rails/exception_handler.rb +43 -1
  5. data/app/controllers/scim_rails/scim_groups_controller.rb +64 -40
  6. data/app/controllers/scim_rails/scim_users_controller.rb +39 -65
  7. data/app/libraries/scim_patch.rb +15 -10
  8. data/app/libraries/scim_patch_operation.rb +127 -24
  9. data/app/models/scim_rails/scim_query_parser.rb +5 -3
  10. data/config/routes.rb +2 -0
  11. data/lib/generators/scim_rails/templates/initializer.rb +0 -6
  12. data/lib/scim_rails/config.rb +1 -2
  13. data/lib/scim_rails/version.rb +1 -1
  14. data/spec/controllers/scim_rails/scim_groups_controller_spec.rb +249 -136
  15. data/spec/controllers/scim_rails/scim_users_controller_spec.rb +413 -203
  16. data/spec/dummy/app/models/user.rb +21 -0
  17. data/spec/dummy/bin/setup +2 -0
  18. data/spec/dummy/config/initializers/scim_rails_config.rb +6 -4
  19. data/spec/dummy/db/development.sqlite3 +0 -0
  20. data/spec/dummy/db/migrate/20220117095407_add_country_to_users.rb +5 -0
  21. data/spec/dummy/db/migrate/20220131090107_add_deletable_to_users.rb +5 -0
  22. data/spec/dummy/db/schema.rb +7 -5
  23. data/spec/dummy/db/seeds.rb +15 -1
  24. data/spec/dummy/db/test.sqlite3 +0 -0
  25. data/spec/dummy/log/development.log +0 -0
  26. data/spec/dummy/log/test.log +5770 -0
  27. data/spec/dummy/put_group.http +5 -0
  28. data/spec/dummy/tmp/restart.txt +0 -0
  29. data/spec/factories/user.rb +2 -0
  30. data/spec/libraries/scim_patch_operation_spec.rb +61 -31
  31. data/spec/libraries/scim_patch_spec.rb +38 -29
  32. data/spec/models/scim_query_parser_spec.rb +30 -0
  33. metadata +83 -67
  34. data/spec/support/scim_rails_config.rb +0 -59
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spec_helper"
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 "index" do
10
+ describe 'index' do
11
11
  let(:company) { create(:company) }
12
12
 
13
- context "when unauthorized" do
14
- it "returns scim+json content type" do
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 "application/scim+json"
17
+ expect(response.media_type).to eq 'application/scim+json'
18
18
  end
19
19
 
20
- it "fails with no credentials" do
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 "fails with invalid credentials" do
27
- request.env["HTTP_AUTHORIZATION"] =
26
+ it 'fails with invalid credentials' do
27
+ request.env['HTTP_AUTHORIZATION'] =
28
28
  ActionController::HttpAuthentication::Basic
29
- .encode_credentials("unauthorized", "123456")
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 "when when authorized" do
37
+ context 'when when authorized' do
38
38
  before :each do
39
39
  http_login(company)
40
40
  end
41
41
 
42
- it "returns scim+json content type" do
42
+ it 'returns scim+json content type' do
43
43
  get :index, as: :json
44
44
 
45
- expect(response.media_type).to eq "application/scim+json"
45
+ expect(response.media_type).to eq 'application/scim+json'
46
46
  end
47
47
 
48
- it "is successful with valid credentials" do
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 "returns all results" do
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:ListResponse"
60
- expect(response_body["totalResults"]).to eq 10
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 "defaults to 100 results" do
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["totalResults"]).to eq 300
69
- expect(response_body["Resources"].count).to eq 100
69
+ expect(response_body['totalResults']).to eq 300
70
+ expect(response_body['Resources'].count).to eq 100
70
71
  end
71
72
 
72
- it "paginates results" do
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["totalResults"]).to eq 400
82
- expect(response_body["Resources"].count).to eq 200
83
- expect(response_body.dig("Resources", 0, "id")).to eq 101
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 "paginates results by configurable scim_users_list_order" do
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["totalResults"]).to eq 400
98
- expect(response_body["Resources"].count).to eq 10
99
- expect(response_body.dig("Resources", 0, "id")).to eq 400
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 "filters results by provided email filter" do
103
- create(:user, email: "test1@example.com", company: company)
104
- create(:user, email: "test2@example.com", company: company)
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: "email eq test1@example.com"
108
+ filter: 'email eq test1@example.com',
108
109
  }, as: :json
109
110
  response_body = JSON.parse(response.body)
110
- expect(response_body["totalResults"]).to eq 1
111
- expect(response_body["Resources"].count).to eq 1
111
+ expect(response_body['totalResults']).to eq 1
112
+ expect(response_body['Resources'].count).to eq 1
112
113
  end
113
114
 
114
- it "filters results by provided name filter" do
115
- create(:user, first_name: "Chidi", last_name: "Anagonye", company: company)
116
- create(:user, first_name: "Eleanor", last_name: "Shellstrop", company: company)
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: "familyName eq Shellstrop"
120
+ filter: 'familyName eq Shellstrop',
120
121
  }, as: :json
121
122
  response_body = JSON.parse(response.body)
122
- expect(response_body["totalResults"]).to eq 1
123
- expect(response_body["Resources"].count).to eq 1
123
+ expect(response_body['totalResults']).to eq 1
124
+ expect(response_body['Resources'].count).to eq 1
124
125
  end
125
126
 
126
- it "returns no results for unfound filter parameters" do
127
+ it 'returns no results for unfound filter parameters' do
127
128
  get :index, params: {
128
- filter: "familyName eq fake_not_there"
129
+ filter: 'familyName eq fake_not_there',
129
130
  }, as: :json
130
131
  response_body = JSON.parse(response.body)
131
- expect(response_body["totalResults"]).to eq 0
132
- expect(response_body["Resources"].count).to eq 0
132
+ expect(response_body['totalResults']).to eq 0
133
+ expect(response_body['Resources'].count).to eq 0
133
134
  end
134
135
 
135
- it "returns no results for undefined filter queries" do
136
+ it 'returns no results for undefined filter queries' do
136
137
  get :index, params: {
137
- filter: "address eq 101 Nowhere USA"
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
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 "show" do
148
+ describe 'show' do
147
149
  let(:company) { create(:company) }
148
150
 
149
- context "when unauthorized" do
150
- it "returns scim+json content type" do
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 "application/scim+json"
155
+ expect(response.media_type).to eq 'application/scim+json'
154
156
  end
155
157
 
156
- it "fails with no credentials" do
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 "fails with invalid credentials" do
163
- request.env["HTTP_AUTHORIZATION"] =
164
+ it 'fails with invalid credentials' do
165
+ request.env['HTTP_AUTHORIZATION'] =
164
166
  ActionController::HttpAuthentication::Basic
165
- .encode_credentials("unauthorized", "123456")
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 "when authorized" do
175
+ context 'when authorized' do
174
176
  before :each do
175
177
  http_login(company)
176
178
  end
177
179
 
178
- it "returns scim+json content type" do
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 "application/scim+json"
183
+ expect(response.media_type).to eq 'application/scim+json'
182
184
  end
183
185
 
184
- it "is successful with valid credentials" do
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 "returns :not_found for id that cannot be found" do
192
- get :show, params: { id: "fake_id" }, as: :json
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 "returns :not_found for a correct id but unauthorized company" do
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 "create" do
210
+ describe 'create' do
209
211
  let(:company) { create(:company) }
210
212
 
211
- context "when unauthorized" do
212
- it "returns scim+json content type" do
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 "application/scim+json"
217
+ expect(response.media_type).to eq 'application/scim+json'
216
218
  end
217
219
 
218
- it "fails with no credentials" do
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 "fails with invalid credentials" do
225
- request.env["HTTP_AUTHORIZATION"] =
226
+ it 'fails with invalid credentials' do
227
+ request.env['HTTP_AUTHORIZATION'] =
226
228
  ActionController::HttpAuthentication::Basic
227
- .encode_credentials("unauthorized", "123456")
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 "when authorized" do
237
+ context 'when authorized' do
236
238
  before :each do
237
239
  http_login(company)
238
240
  end
239
241
 
240
- it "returns scim+json content type" do
242
+ it 'returns scim+json content type' do
241
243
  post :create, params: {
242
244
  name: {
243
- givenName: "New",
244
- familyName: "User"
245
+ givenName: 'New',
246
+ familyName: 'User',
245
247
  },
246
248
  emails: [
247
249
  {
248
- value: "new@example.com"
250
+ value: 'new@example.com',
249
251
  }
250
- ]
252
+ ],
251
253
  }, as: :json
252
254
 
253
- expect(response.media_type).to eq "application/scim+json"
255
+ expect(response.media_type).to eq 'application/scim+json'
254
256
  end
255
257
 
256
- it "is successful with valid credentials" do
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: "New",
262
- familyName: "User"
263
+ givenName: 'New',
264
+ familyName: 'User',
263
265
  },
264
266
  emails: [
265
267
  {
266
- value: "new@example.com"
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 "New"
276
- expect(user.last_name).to eq "User"
277
- expect(user.email).to eq "new@example.com"
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 "ignores unconfigured params" do
282
+ it 'ignores unconfigured params' do
281
283
  post :create, params: {
282
284
  name: {
283
- formattedName: "New User",
284
- givenName: "New",
285
- familyName: "User"
285
+ formattedName: 'New User',
286
+ givenName: 'New',
287
+ familyName: 'User',
286
288
  },
287
289
  emails: [
288
290
  {
289
- value: "new@example.com"
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 "returns 422 if required params are missing" do
300
+ it 'returns 422 if required params are missing' do
299
301
  post :create, params: {
300
302
  name: {
301
- familyName: "User"
303
+ familyName: 'User',
302
304
  },
303
305
  emails: [
304
306
  {
305
- value: "new@example.com"
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 "returns 201 if user already exists and updates user" do
315
- create(:user, email: "new@example.com", company: company)
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: "Not New",
320
- familyName: "User"
321
+ givenName: 'Not New',
322
+ familyName: 'User',
321
323
  },
322
324
  emails: [
323
325
  {
324
- value: "new@example.com"
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 "Not New"
333
+ expect(company.users.first.first_name).to eq 'Not New'
332
334
  end
333
335
 
334
- it "returns 409 if user already exists and config.scim_user_prevent_update_on_create is set to true" do
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: "new@example.com", company: company)
338
+ create(:user, email: 'new@example.com', company: company)
337
339
 
338
340
  post :create, params: {
339
341
  name: {
340
- givenName: "Not New",
341
- familyName: "User"
342
+ givenName: 'Not New',
343
+ familyName: 'User',
342
344
  },
343
345
  emails: [
344
346
  {
345
- value: "new@example.com"
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 "creates and archives inactive user" do
356
+ it 'creates and archives inactive user' do
355
357
  post :create, params: {
356
358
  id: 1,
357
- userName: "test@example.com",
359
+ userName: 'test@example.com',
358
360
  name: {
359
- givenName: "Test",
360
- familyName: "User"
361
+ givenName: 'Test',
362
+ familyName: 'User',
361
363
  },
362
364
  emails: [
363
365
  {
364
- value: "test@example.com"
366
+ value: 'test@example.com',
365
367
  }
366
368
  ],
367
- active: "false"
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 "put update" do
380
+ describe 'put update' do
379
381
  let(:company) { create(:company) }
380
382
 
381
- context "when unauthorized" do
382
- it "returns scim+json content type" do
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 "application/scim+json"
387
+ expect(response.media_type).to eq 'application/scim+json'
386
388
  end
387
389
 
388
- it "fails with no credentials" do
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 "fails with invalid credentials" do
395
- request.env["HTTP_AUTHORIZATION"] =
396
+ it 'fails with invalid credentials' do
397
+ request.env['HTTP_AUTHORIZATION'] =
396
398
  ActionController::HttpAuthentication::Basic
397
- .encode_credentials("unauthorized", "123456")
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 "when authorized" do
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 "returns scim+json content type" do
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 "application/scim+json"
417
+ expect(response.media_type).to eq 'application/scim+json'
416
418
  end
417
419
 
418
- it "is successful with with valid credentials" do
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 "deprovisions an active record" do
425
- request.content_type = "application/scim+json"
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 "reprovisions an inactive record" do
440
+ it 'reprovisions an inactive record' do
433
441
  user.archive!
434
442
  expect(user.reload.active?).to eq false
435
- request.content_type = "application/scim+json"
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 "returns :not_found for id that cannot be found" do
443
- put :put_update, params: { id: "fake_id" }, as: :json
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 "returns :not_found for a correct id but unauthorized company" do
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 "is returns 422 with incomplete request" do
465
+ it 'is returns 422 with incomplete request' do
458
466
  put :put_update, params: {
459
467
  id: 1,
460
- userName: "test@example.com",
468
+ userName: 'test@example.com',
461
469
  emails: [
462
470
  {
463
- value: "test@example.com"
471
+ value: 'test@example.com',
464
472
  }
465
473
  ],
466
- active: "true"
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 "patch update" do
482
+ describe 'patch update' do
475
483
  let(:company) { create(:company) }
476
484
 
477
- context "when unauthorized" do
478
- it "returns scim+json content type" do
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 "application/scim+json"
489
+ expect(response.media_type).to eq 'application/scim+json'
482
490
  end
483
491
 
484
- it "fails with no credentials" do
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 "fails with invalid credentials" do
491
- request.env["HTTP_AUTHORIZATION"] =
498
+ it 'fails with invalid credentials' do
499
+ request.env['HTTP_AUTHORIZATION'] =
492
500
  ActionController::HttpAuthentication::Basic
493
- .encode_credentials("unauthorized", "123456")
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 "when authorized" do
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 "returns scim+json content type" do
509
- patch :patch_update, params: patch_params(id: 1), as: :json
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 "application/scim+json"
519
+ expect(response.media_type).to eq 'application/scim+json'
512
520
  end
513
521
 
514
- it "is successful with valid credentials" do
515
- patch :patch_update, params: patch_params(id: 1), as: :json
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 "returns :not_found for id that cannot be found" do
521
- get :patch_update, params: patch_params(id: "fake_id"), as: :json
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 "returns :not_found for a correct id but unauthorized company" do
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
- xit "successfully archives user" do
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 :patch_update, params: patch_params(id: 1), as: :json
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
- xit "successfully restores user" do
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: patch_params(id: 1, active: true),
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 "successfully change user email" do
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, active: true),
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 "is case insensetive for op value" do
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: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
619
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
586
620
  id: 1,
587
621
  Operations: [
588
- {
589
- op: "Replace",
590
- path: "emails[type eq \"work\"].value",
591
- value: "test@example.com"
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
- xit "returns 422 when value is not an object" do
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: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
658
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
602
659
  id: 1,
603
660
  Operations: [
604
661
  {
605
- op: "replace",
606
- path: "emails[type eq \"work\"].value",
607
- value: "francis@example.com"
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
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 "returns 422 when value is missing" do
675
+ it 'returns 422 when value is missing' do
618
676
  patch :patch_update, params: {
619
- schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
677
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
620
678
  id: 1,
621
679
  Operations: [
622
680
  {
623
- op: "replace",
624
- path: "emails[type eq \"work\"].value"
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
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 "returns 422 when path is missing" do
693
+ it 'returns 422 when path is missing' do
635
694
  patch :patch_update, params: {
636
- schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
695
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
637
696
  id: 1,
638
697
  Operations: [
639
698
  {
640
- op: "replace"
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
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 "returns 422 operations key is missing" do
710
+ it 'returns 422 operations key is missing' do
651
711
  patch :patch_update, params: {
652
- schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
712
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
653
713
  id: 1,
654
714
  Foobars: [
655
715
  {
656
- op: "replace"
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("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
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: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
866
+ schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
671
867
  id: id,
672
868
  Operations: [
673
869
  {
674
- op: "Replace",
675
- path: "emails[type eq \"work\"].value",
676
- value: "change@example.com"
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 put_params(active: true)
878
+ def patch_active_params(id:, active: false)
683
879
  {
684
- id: 1,
685
- userName: "test@example.com",
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: "Test",
688
- familyName: "User"
897
+ givenName: 'Test',
898
+ familyName: 'User',
689
899
  },
690
900
  emails: [
691
901
  {
692
- value: "test@example.com"
902
+ value: 'test@example.com',
693
903
  }
694
904
  ],
695
- active: active
905
+ active: active,
696
906
  }
697
907
  end
698
908
  end