scimitar 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scimitar/active_record_backed_resources_controller.rb +6 -27
  3. data/app/controllers/scimitar/application_controller.rb +9 -29
  4. data/app/models/scimitar/engine_configuration.rb +3 -7
  5. data/app/models/scimitar/error_response.rb +0 -12
  6. data/app/models/scimitar/errors.rb +1 -1
  7. data/app/models/scimitar/lists/query_parser.rb +4 -14
  8. data/app/models/scimitar/resources/base.rb +1 -1
  9. data/app/models/scimitar/resources/mixin.rb +2 -78
  10. data/app/models/scimitar/schema/address.rb +0 -1
  11. data/app/models/scimitar/schema/attribute.rb +1 -1
  12. data/app/models/scimitar/schema/base.rb +3 -1
  13. data/app/models/scimitar/schema/vdtp.rb +1 -1
  14. data/config/initializers/scimitar.rb +70 -86
  15. data/lib/scimitar/version.rb +2 -2
  16. data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
  17. data/spec/apps/dummy/app/models/mock_group.rb +1 -1
  18. data/spec/apps/dummy/app/models/mock_user.rb +7 -10
  19. data/spec/apps/dummy/config/application.rb +1 -0
  20. data/spec/apps/dummy/config/environments/test.rb +28 -5
  21. data/spec/apps/dummy/config/initializers/scimitar.rb +10 -8
  22. data/spec/apps/dummy/config/routes.rb +0 -4
  23. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +2 -2
  24. data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
  25. data/spec/apps/dummy/db/schema.rb +4 -8
  26. data/spec/controllers/scimitar/application_controller_spec.rb +1 -70
  27. data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
  28. data/spec/models/scimitar/lists/query_parser_spec.rb +9 -9
  29. data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
  30. data/spec/models/scimitar/resources/mixin_spec.rb +40 -675
  31. data/spec/models/scimitar/resources/user_spec.rb +4 -4
  32. data/spec/models/scimitar/schema/attribute_spec.rb +3 -0
  33. data/spec/models/scimitar/schema/base_spec.rb +1 -1
  34. data/spec/models/scimitar/schema/user_spec.rb +0 -10
  35. data/spec/requests/active_record_backed_resources_controller_spec.rb +40 -309
  36. data/spec/requests/application_controller_spec.rb +3 -7
  37. metadata +7 -7
@@ -42,25 +42,25 @@ RSpec.describe Scimitar::Resources::User do
42
42
  let(:user) { described_class.new }
43
43
 
44
44
  it 'adds the error when the value is a string' do
45
- user.add_errors_from_hash({key: 'some error'})
45
+ user.add_errors_from_hash(errors_hash: {key: 'some error'})
46
46
  expect(user.errors.messages.to_h).to eql({key: ['some error']})
47
47
  expect(user.errors.full_messages).to eql(['Key some error'])
48
48
  end
49
49
 
50
50
  it 'adds the error when the value is an array' do
51
- user.add_errors_from_hash({key: ['error1', 'error2']})
51
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']})
52
52
  expect(user.errors.messages.to_h).to eql({key: ['error1', 'error2']})
53
53
  expect(user.errors.full_messages).to eql(['Key error1', 'Key error2'])
54
54
  end
55
55
 
56
56
  it 'adds the error with prefix when the value is a string' do
57
- user.add_errors_from_hash({key: 'some error'}, prefix: :pre)
57
+ user.add_errors_from_hash(errors_hash: {key: 'some error'}, prefix: :pre)
58
58
  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['some error']})
59
59
  expect(user.errors.full_messages).to eql(['Pre key some error'])
60
60
  end
61
61
 
62
62
  it 'adds the error wity prefix when the value is an array' do
63
- user.add_errors_from_hash({key: ['error1', 'error2']}, prefix: :pre)
63
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']}, prefix: :pre)
64
64
  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['error1', 'error2']})
65
65
  expect(user.errors.full_messages).to eql(['Pre key error1', 'Pre key error2'])
66
66
  end
@@ -21,8 +21,10 @@ RSpec.describe Scimitar::Schema::Attribute do
21
21
  expect(name.type).to eql('complex')
22
22
  expect(name.subAttributes).to eql(Scimitar::Schema::Name.scim_attributes)
23
23
  end
24
+
24
25
  end
25
26
 
27
+
26
28
  context '#valid?' do
27
29
  it 'is invalid if attribute is required but value is blank' do
28
30
  attribute = described_class.new(name: 'userName', type: 'string', required: true)
@@ -74,4 +76,5 @@ RSpec.describe Scimitar::Schema::Attribute do
74
76
  expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('gaga')).to be(false)
75
77
  end
76
78
  end
79
+
77
80
  end
@@ -11,7 +11,7 @@ RSpec.describe Scimitar::Schema::Base do
11
11
  end
12
12
 
13
13
  context '#initialize' do
14
- it 'creates "meta"' do
14
+ it 'creates a meta' do
15
15
  schema = described_class.new
16
16
  expect(schema.meta.resourceType).to eql('Schema')
17
17
  end
@@ -419,16 +419,6 @@ RSpec.describe Scimitar::Schema::User do
419
419
  "name": "type",
420
420
  "type": "string"
421
421
  },
422
- {
423
- "multiValued": false,
424
- "required": false,
425
- "caseExact": false,
426
- "mutability": "readWrite",
427
- "uniqueness": "none",
428
- "returned": "default",
429
- "name": "primary",
430
- "type": "boolean"
431
- },
432
422
  {
433
423
  "multiValued": false,
434
424
  "required": false,
@@ -1,27 +1,12 @@
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
- lmt = Time.parse("2023-01-09 14:25:00 +1300")
9
-
10
- # If a sort order is unspecified, the controller defaults to ID ascending.
11
- # With UUID based IDs, testing life is made easier by ensuring that the
12
- # creation order matches an ascending UUID sort order (which is what would
13
- # happen if we were using integer primary keys).
14
- #
15
- lmt = Time.parse("2023-01-09 14:25:00 +1300")
16
- ids = 3.times.map { SecureRandom.uuid }.sort()
17
-
18
- @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)
19
- @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)
20
- @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)
21
-
22
- @g1 = MockGroup.create!(display_name: 'Group 1')
23
- @g2 = MockGroup.create!(display_name: 'Group 2')
24
- @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')
25
10
  end
26
11
 
27
12
  # ===========================================================================
@@ -44,65 +29,26 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
44
29
  end # "context 'with no items' do"
45
30
 
46
31
  context 'with items' do
47
- context 'with a UUID, renamed primary key column' do
48
- it 'returns all items' do
49
- get '/Users', params: { format: :scim }
50
-
51
- expect(response.status).to eql(200)
52
- result = JSON.parse(response.body)
53
-
54
- expect(result['totalResults']).to eql(3)
55
- expect(result['Resources'].size).to eql(3)
56
-
57
- ids = result['Resources'].map { |resource| resource['id'] }
58
- expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s, @u3.primary_key.to_s])
59
-
60
- usernames = result['Resources'].map { |resource| resource['userName'] }
61
- expect(usernames).to match_array(['1', '2', '3'])
62
- end
63
- end # "context 'with a UUID, renamed primary key column' do"
64
-
65
- context 'with an integer, conventionally named primary key column' do
66
- it 'returns all items' do
67
- get '/Groups', params: { format: :scim }
68
-
69
- expect(response.status).to eql(200)
70
- result = JSON.parse(response.body)
71
-
72
- expect(result['totalResults']).to eql(3)
73
- expect(result['Resources'].size).to eql(3)
74
-
75
- ids = result['Resources'].map { |resource| resource['id'] }
76
- expect(ids).to match_array([@g1.id.to_s, @g2.id.to_s, @g3.id.to_s])
77
-
78
- usernames = result['Resources'].map { |resource| resource['displayName'] }
79
- expect(usernames).to match_array(['Group 1', 'Group 2', 'Group 3'])
80
- end
81
- end # "context 'with an integer, conventionally named primary key column' do"
82
-
83
- it 'applies a filter, with case-insensitive value comparison' do
84
- get '/Users', params: {
85
- format: :scim,
86
- filter: 'name.givenName eq "FOO" and name.familyName pr and emails ne "home_1@test.com"'
87
- }
32
+ it 'returns all items' do
33
+ get '/Users', params: { format: :scim }
88
34
 
89
35
  expect(response.status).to eql(200)
90
36
  result = JSON.parse(response.body)
91
37
 
92
- expect(result['totalResults']).to eql(1)
93
- expect(result['Resources'].size).to eql(1)
38
+ expect(result['totalResults']).to eql(3)
39
+ expect(result['Resources'].size).to eql(3)
94
40
 
95
41
  ids = result['Resources'].map { |resource| resource['id'] }
96
- expect(ids).to match_array([@u2.primary_key.to_s])
42
+ expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s, @u3.id.to_s])
97
43
 
98
44
  usernames = result['Resources'].map { |resource| resource['userName'] }
99
- expect(usernames).to match_array(['2'])
45
+ expect(usernames).to match_array(['1', '2', '3'])
100
46
  end
101
47
 
102
- it 'applies a filter, with case-insensitive attribute matching (GitHub issue #37)' do
48
+ it 'applies a filter, with case-insensitive value comparison' do
103
49
  get '/Users', params: {
104
50
  format: :scim,
105
- filter: 'name.GIVENNAME eq "Foo" and name.Familyname pr and emails ne "home_1@test.com"'
51
+ filter: 'name.givenName eq "Foo" and name.familyName pr and emails ne "home_1@TEST.COM"'
106
52
  }
107
53
 
108
54
  expect(response.status).to eql(200)
@@ -112,74 +58,12 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
112
58
  expect(result['Resources'].size).to eql(1)
113
59
 
114
60
  ids = result['Resources'].map { |resource| resource['id'] }
115
- expect(ids).to match_array([@u2.primary_key.to_s])
61
+ expect(ids).to match_array([@u2.id.to_s])
116
62
 
117
63
  usernames = result['Resources'].map { |resource| resource['userName'] }
118
64
  expect(usernames).to match_array(['2'])
119
65
  end
120
66
 
121
- # Strange attribute capitalisation in tests here builds on test coverage
122
- # for now-fixed GitHub issue #37.
123
- #
124
- context '"meta" / IDs (GitHub issue #36)' do
125
- it 'applies a filter on primary keys, using direct comparison (rather than e.g. case-insensitive operators)' do
126
- get '/Users', params: {
127
- format: :scim,
128
- filter: "id eq \"#{@u3.primary_key}\""
129
- }
130
-
131
- expect(response.status).to eql(200)
132
- result = JSON.parse(response.body)
133
-
134
- expect(result['totalResults']).to eql(1)
135
- expect(result['Resources'].size).to eql(1)
136
-
137
- ids = result['Resources'].map { |resource| resource['id'] }
138
- expect(ids).to match_array([@u3.primary_key.to_s])
139
-
140
- usernames = result['Resources'].map { |resource| resource['userName'] }
141
- expect(usernames).to match_array(['3'])
142
- end
143
-
144
- it 'applies a filter on external IDs, using direct comparison' do
145
- get '/Users', params: {
146
- format: :scim,
147
- filter: "externalID eq \"#{@u2.scim_uid}\""
148
- }
149
-
150
- expect(response.status).to eql(200)
151
- result = JSON.parse(response.body)
152
-
153
- expect(result['totalResults']).to eql(1)
154
- expect(result['Resources'].size).to eql(1)
155
-
156
- ids = result['Resources'].map { |resource| resource['id'] }
157
- expect(ids).to match_array([@u2.primary_key.to_s])
158
-
159
- usernames = result['Resources'].map { |resource| resource['userName'] }
160
- expect(usernames).to match_array(['2'])
161
- end
162
-
163
- it 'applies a filter on "meta" entries, using direct comparison' do
164
- get '/Users', params: {
165
- format: :scim,
166
- filter: "Meta.LastModified eq \"#{@u3.updated_at}\""
167
- }
168
-
169
- expect(response.status).to eql(200)
170
- result = JSON.parse(response.body)
171
-
172
- expect(result['totalResults']).to eql(1)
173
- expect(result['Resources'].size).to eql(1)
174
-
175
- ids = result['Resources'].map { |resource| resource['id'] }
176
- expect(ids).to match_array([@u3.primary_key.to_s])
177
-
178
- usernames = result['Resources'].map { |resource| resource['userName'] }
179
- expect(usernames).to match_array(['3'])
180
- end
181
- end # "context '"meta" / IDs (GitHub issue #36)' do"
182
-
183
67
  it 'obeys a page size' do
184
68
  get '/Users', params: {
185
69
  format: :scim,
@@ -193,7 +77,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
193
77
  expect(result['Resources'].size).to eql(2)
194
78
 
195
79
  ids = result['Resources'].map { |resource| resource['id'] }
196
- expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s])
80
+ expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s])
197
81
 
198
82
  usernames = result['Resources'].map { |resource| resource['userName'] }
199
83
  expect(usernames).to match_array(['1', '2'])
@@ -212,7 +96,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
212
96
  expect(result['Resources'].size).to eql(2)
213
97
 
214
98
  ids = result['Resources'].map { |resource| resource['id'] }
215
- expect(ids).to match_array([@u2.primary_key.to_s, @u3.primary_key.to_s])
99
+ expect(ids).to match_array([@u2.id.to_s, @u3.id.to_s])
216
100
 
217
101
  usernames = result['Resources'].map { |resource| resource['userName'] }
218
102
  expect(usernames).to match_array(['2', '3'])
@@ -236,34 +120,18 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
236
120
  # ===========================================================================
237
121
 
238
122
  context '#show' do
239
- context 'with a UUID, renamed primary key column' do
240
- it 'shows an item' do
241
- expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
242
- get "/Users/#{@u2.primary_key}", params: { format: :scim }
243
-
244
- expect(response.status).to eql(200)
245
- result = JSON.parse(response.body)
246
-
247
- expect(result['id']).to eql(@u2.primary_key.to_s)
248
- expect(result['userName']).to eql('2')
249
- expect(result['name']['familyName']).to eql('Bar')
250
- expect(result['meta']['resourceType']).to eql('User')
251
- end
252
- end # "context 'with a UUID, renamed primary key column' do"
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 }
253
126
 
254
- context 'with an integer, conventionally named primary key column' do
255
- it 'shows an item' do
256
- expect_any_instance_of(MockGroupsController).to receive(:show).once.and_call_original
257
- get "/Groups/#{@g2.id}", params: { format: :scim }
258
-
259
- expect(response.status).to eql(200)
260
- result = JSON.parse(response.body)
127
+ expect(response.status).to eql(200)
128
+ result = JSON.parse(response.body)
261
129
 
262
- expect(result['id']).to eql(@g2.id.to_s) # Note - ID was converted String; not Integer
263
- expect(result['displayName']).to eql('Group 2')
264
- expect(result['meta']['resourceType']).to eql('Group')
265
- end
266
- end # "context 'with an integer, conventionally named primary key column' do"
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
267
135
 
268
136
  it 'renders 404' do
269
137
  get '/Users/xyz', params: { format: :scim }
@@ -296,7 +164,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
296
164
  expect(response.status).to eql(201)
297
165
  result = JSON.parse(response.body)
298
166
 
299
- expect(result['id']).to eql(new_mock.primary_key.to_s)
167
+ expect(result['id']).to eql(new_mock.id.to_s)
300
168
  expect(result['meta']['resourceType']).to eql('User')
301
169
  expect(new_mock.username).to eql('4')
302
170
  end
@@ -312,7 +180,6 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
312
180
  givenName: 'Given',
313
181
  familyName: 'Family'
314
182
  },
315
- meta: { resourceType: 'User' },
316
183
  emails: [
317
184
  {
318
185
  type: 'work',
@@ -337,7 +204,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
337
204
  expect(response.status).to eql(201)
338
205
  result = JSON.parse(response.body)
339
206
 
340
- expect(result['id']).to eql(new_mock.primary_key.to_s)
207
+ expect(result['id']).to eql(new_mock.id.to_s)
341
208
  expect(result['meta']['resourceType']).to eql('User')
342
209
  expect(new_mock.username).to eql('4')
343
210
  expect(new_mock.first_name).to eql('Given')
@@ -411,13 +278,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
411
278
 
412
279
  expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
413
280
  expect {
414
- put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
281
+ put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
415
282
  }.to_not change { MockUser.count }
416
283
 
417
284
  expect(response.status).to eql(200)
418
285
  result = JSON.parse(response.body)
419
286
 
420
- expect(result['id']).to eql(@u2.primary_key.to_s)
287
+ expect(result['id']).to eql(@u2.id.to_s)
421
288
  expect(result['meta']['resourceType']).to eql('User')
422
289
 
423
290
  @u2.reload
@@ -439,7 +306,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
439
306
 
440
307
  it 'notes schema validation failures' do
441
308
  expect {
442
- put "/Users/#{@u2.primary_key}", params: {
309
+ put "/Users/#{@u2.id}", params: {
443
310
  format: :scim
444
311
  # userName parameter is required by schema, but missing
445
312
  }
@@ -518,13 +385,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
518
385
 
519
386
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
520
387
  expect {
521
- patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
388
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
522
389
  }.to_not change { MockUser.count }
523
390
 
524
391
  expect(response.status).to eql(200)
525
392
  result = JSON.parse(response.body)
526
393
 
527
- expect(result['id']).to eql(@u2.primary_key.to_s)
394
+ expect(result['id']).to eql(@u2.id.to_s)
528
395
  expect(result['meta']['resourceType']).to eql('User')
529
396
 
530
397
  @u2.reload
@@ -555,13 +422,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
555
422
 
556
423
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
557
424
  expect {
558
- patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
425
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
559
426
  }.to_not change { MockUser.count }
560
427
 
561
428
  expect(response.status).to eql(200)
562
429
  result = JSON.parse(response.body)
563
430
 
564
- expect(result['id']).to eql(@u2.primary_key.to_s)
431
+ expect(result['id']).to eql(@u2.id.to_s)
565
432
  expect(result['meta']['resourceType']).to eql('User')
566
433
 
567
434
  @u2.reload
@@ -587,13 +454,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
587
454
 
588
455
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
589
456
  expect {
590
- patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
457
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
591
458
  }.to_not change { MockUser.count }
592
459
 
593
460
  expect(response.status).to eql(200)
594
461
  result = JSON.parse(response.body)
595
462
 
596
- expect(result['id']).to eql(@u2.primary_key.to_s)
463
+ expect(result['id']).to eql(@u2.id.to_s)
597
464
  expect(result['meta']['resourceType']).to eql('User')
598
465
 
599
466
  @u2.reload
@@ -619,13 +486,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
619
486
 
620
487
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
621
488
  expect {
622
- patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
489
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
623
490
  }.to_not change { MockUser.count }
624
491
 
625
492
  expect(response.status).to eql(200)
626
493
  result = JSON.parse(response.body)
627
494
 
628
- expect(result['id']).to eql(@u2.primary_key.to_s)
495
+ expect(result['id']).to eql(@u2.id.to_s)
629
496
  expect(result['meta']['resourceType']).to eql('User')
630
497
 
631
498
  @u2.reload
@@ -649,7 +516,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
649
516
 
650
517
  it 'notes Rails validation failures' do
651
518
  expect {
652
- patch "/Users/#{@u2.primary_key}", params: {
519
+ patch "/Users/#{@u2.id}", params: {
653
520
  format: :scim,
654
521
  Operations: [
655
522
  {
@@ -693,142 +560,6 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
693
560
  result = JSON.parse(response.body)
694
561
  expect(result['status']).to eql('404')
695
562
  end
696
-
697
- context 'when removing users from groups' do
698
- before :each do
699
- @g1.mock_users << @u1
700
- @g1.mock_users << @u2
701
- @g1.mock_users << @u3
702
-
703
- # (Self-check) Verify group representation
704
- #
705
- get "/Groups/#{@g1.id}", params: { format: :scim }
706
-
707
- expect(response.status).to eql(200)
708
- result = JSON.parse(response.body)
709
-
710
- expect(result['members'].map { |m| m['value'] }.sort()).to eql(MockUser.pluck(:primary_key).sort())
711
- end
712
-
713
- it 'can remove all users' do
714
- expect {
715
- expect {
716
- patch "/Groups/#{@g1.id}", params: {
717
- format: :scim,
718
- Operations: [
719
- {
720
- op: 'remove',
721
- path: 'members'
722
- }
723
- ]
724
- }
725
- }.to_not change { MockUser.count }
726
- }.to_not change { MockGroup.count }
727
-
728
- get "/Groups/#{@g1.id}", params: { format: :scim }
729
-
730
- expect(response.status).to eql(200)
731
- result = JSON.parse(response.body)
732
-
733
- expect(result['members']).to be_empty
734
- expect(@g1.reload().mock_users).to be_empty
735
- end
736
-
737
- # Define via 'let':
738
- #
739
- # * Hash 'payload', to send via 'patch'
740
- # * MockUser 'removed_user', which is the user that should be removed
741
- #
742
- shared_examples 'a user remover' do
743
- it 'which removes the identified user' do
744
- expect {
745
- expect {
746
- patch "/Groups/#{@g1.id}", params: payload()
747
- }.to_not change { MockUser.count }
748
- }.to_not change { MockGroup.count }
749
-
750
- expected_remaining_user_ids = MockUser
751
- .where.not(primary_key: removed_user().id)
752
- .pluck(:primary_key)
753
- .sort()
754
-
755
- get "/Groups/#{@g1.id}", params: { format: :scim }
756
-
757
- expect(response.status).to eql(200)
758
- result = JSON.parse(response.body)
759
-
760
- expect(result['members'].map { |m| m['value'] }.sort()).to eql(expected_remaining_user_ids)
761
- expect(@g1.reload().mock_users.map(&:primary_key).sort()).to eql(expected_remaining_user_ids)
762
- end
763
- end
764
-
765
- # https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2.2
766
- #
767
- context 'and using an RFC-compliant payload' do
768
- let(:removed_user) { @u2 }
769
- let(:payload) do
770
- {
771
- format: :scim,
772
- Operations: [
773
- {
774
- op: 'remove',
775
- path: "members[value eq \"#{removed_user().primary_key}\"]",
776
- }
777
- ]
778
- }
779
- end
780
-
781
- it_behaves_like 'a user remover'
782
- end # context 'and using an RFC-compliant payload' do
783
-
784
- # https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
785
- #
786
- context 'and using a Microsoft variant payload' do
787
- let(:removed_user) { @u2 }
788
- let(:payload) do
789
- {
790
- format: :scim,
791
- Operations: [
792
- {
793
- op: 'remove',
794
- path: 'members',
795
- value: [{
796
- '$ref' => nil,
797
- 'value' => removed_user().primary_key
798
- }]
799
- }
800
- ]
801
- }
802
- end
803
-
804
- it_behaves_like 'a user remover'
805
- end # context 'and using a Microsoft variant payload' do
806
-
807
- # https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
808
- #
809
- context 'and using a Salesforce variant payload' do
810
- let(:removed_user) { @u2 }
811
- let(:payload) do
812
- {
813
- format: :scim,
814
- Operations: [
815
- {
816
- op: 'remove',
817
- path: 'members',
818
- value: {
819
- 'members' => [{
820
- '$ref' => nil,
821
- 'value' => removed_user().primary_key
822
- }]
823
- }
824
- }
825
- ]
826
- }
827
- end
828
-
829
- it_behaves_like 'a user remover'
830
- end # context 'and using a Salesforce variant payload' do
831
- end # "context 'when removing users from groups' do"
832
563
  end # "context '#update' do"
833
564
 
834
565
  # ===========================================================================
@@ -838,7 +569,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
838
569
  expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
839
570
  expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
840
571
  expect {
841
- delete "/Users/#{@u2.primary_key}", params: { format: :scim }
572
+ delete "/Users/#{@u2.id}", params: { format: :scim }
842
573
  }.to change { MockUser.count }.by(-1)
843
574
 
844
575
  expect(response.status).to eql(204)
@@ -850,7 +581,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
850
581
  expect_any_instance_of(MockUser).to_not receive(:destroy!)
851
582
 
852
583
  expect {
853
- delete "/CustomDestroyUsers/#{@u2.primary_key}", params: { format: :scim }
584
+ delete "/CustomDestroyUsers/#{@u2.id}", params: { format: :scim }
854
585
  }.to_not change { MockUser.count }
855
586
 
856
587
  expect(response.status).to eql(204)
@@ -11,14 +11,11 @@ RSpec.describe Scimitar::ApplicationController do
11
11
  end
12
12
 
13
13
  context 'format handling' do
14
- it 'renders "OK" if the request does not provide any Content-Type value' do
14
+ it 'renders "not acceptable" if the request does not use SCIM type' do
15
15
  get '/CustomRequestVerifiers', params: { format: :html }
16
16
 
17
- expect(response).to have_http_status(:ok)
18
- parsed_body = JSON.parse(response.body)
19
- expect(parsed_body['request']['is_scim' ]).to eql(true)
20
- expect(parsed_body['request']['format' ]).to eql('application/scim+json')
21
- expect(parsed_body['request']['content_type']).to eql('application/scim+json')
17
+ expect(response).to have_http_status(:not_acceptable)
18
+ expect(JSON.parse(response.body)['detail']).to eql('Only application/scim+json type is accepted.')
22
19
  end
23
20
 
24
21
  it 'renders 400 if given bad JSON' do
@@ -26,7 +23,6 @@ RSpec.describe Scimitar::ApplicationController do
26
23
 
27
24
  expect(response).to have_http_status(:bad_request)
28
25
  expect(JSON.parse(response.body)['detail']).to start_with('Invalid JSON - ')
29
- expect(JSON.parse(response.body)['detail']).to include("'not-json-12345'")
30
26
  end
31
27
 
32
28
  it 'translates Content-Type to Rails request format' do