scimitar 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9faabfad9cae931ab19f765393d9f307fa5d44773f943d2286fbd92302c2e9e1
4
- data.tar.gz: 2bc23d4e3b123103efd0fa87375547dcdf9e3684ea272a1e4941c43e7845937b
3
+ metadata.gz: cd0efbdecece6297062b46cf46bdeaccd8330cd58fc94fb2bcb0de7c7bb90806
4
+ data.tar.gz: 7f3528ee080f0f295d03c77fba1586484b5cadd6ef5e4de1fc00d7807ec7a0c1
5
5
  SHA512:
6
- metadata.gz: bb74810f72d7b806b7e6aef1a02d363be1135dbf190e2ae0e88a727f94ec9b90e643a13883cd64951d4879fe542fb5936621ec96c44d0f192050f9f6453b58ef
7
- data.tar.gz: b8ea496f490696379236a971070f6e04188fbfcff27f0dd484e18c01416193c625173c72618e123cbc39b1521ef4e6137553396d981db6366861f98f172cee2c
6
+ metadata.gz: 8044cc09d7b964401b09e6fe8ccfa272b6c67ab97687c79a151010a447109f9e671bb864094905b2495100591e2472f673ce2b977fc07f2a13213a8d4fbc75b1
7
+ data.tar.gz: 51a5522fcf948d3b9f19c6bc5b28732f0fb4537034106d47dca83d7d043d5749d9adf1c31acf10fc7eb79a63739787a0ec42f0a261eb293072634aabca0ec92d
@@ -21,6 +21,8 @@ module Scimitar
21
21
 
22
22
  rescue_from ActiveRecord::RecordNotFound, with: :handle_resource_not_found # See Scimitar::ApplicationController
23
23
 
24
+ before_action :obtain_id_column_name_from_attribute_map
25
+
24
26
  # GET (list)
25
27
  #
26
28
  def index
@@ -37,12 +39,13 @@ module Scimitar
37
39
  pagination_info = scim_pagination_info(query.count())
38
40
 
39
41
  page_of_results = query
42
+ .order(@id_column => :asc)
40
43
  .offset(pagination_info.offset)
41
44
  .limit(pagination_info.limit)
42
45
  .to_a()
43
46
 
44
47
  super(pagination_info, page_of_results) do | record |
45
- record.to_scim(location: url_for(action: :show, id: record.id))
48
+ record_to_scim(record)
46
49
  end
47
50
  end
48
51
 
@@ -51,7 +54,7 @@ module Scimitar
51
54
  def show
52
55
  super do |record_id|
53
56
  record = self.find_record(record_id)
54
- record.to_scim(location: url_for(action: :show, id: record_id))
57
+ record_to_scim(record)
55
58
  end
56
59
  end
57
60
 
@@ -63,7 +66,7 @@ module Scimitar
63
66
  record = self.storage_class().new
64
67
  record.from_scim!(scim_hash: scim_resource.as_json())
65
68
  self.save!(record)
66
- record.to_scim(location: url_for(action: :show, id: record.id))
69
+ record_to_scim(record)
67
70
  end
68
71
  end
69
72
  end
@@ -76,7 +79,7 @@ module Scimitar
76
79
  record = self.find_record(record_id)
77
80
  record.from_scim!(scim_hash: scim_resource.as_json())
78
81
  self.save!(record)
79
- record.to_scim(location: url_for(action: :show, id: record.id))
82
+ record_to_scim(record)
80
83
  end
81
84
  end
82
85
  end
@@ -89,7 +92,7 @@ module Scimitar
89
92
  record = self.find_record(record_id)
90
93
  record.from_scim_patch!(patch_hash: patch_hash)
91
94
  self.save!(record)
92
- record.to_scim(location: url_for(action: :show, id: record.id))
95
+ record_to_scim(record)
93
96
  end
94
97
  end
95
98
  end
@@ -137,7 +140,14 @@ module Scimitar
137
140
  # +record_id+:: Record ID (SCIM schema 'id' value - "our" ID).
138
141
  #
139
142
  def find_record(record_id)
140
- self.storage_scope().find(record_id)
143
+ self.storage_scope().find_by!(@id_column => record_id)
144
+ end
145
+
146
+ # DRY up controller actions - pass a record; returns the SCIM
147
+ # representation, with a "show" location specified via #url_for.
148
+ #
149
+ def record_to_scim(record)
150
+ record.to_scim(location: url_for(action: :show, id: record.send(@id_column)))
141
151
  end
142
152
 
143
153
  # Save a record, dealing with validation exceptions by raising SCIM
@@ -176,5 +186,16 @@ module Scimitar
176
186
  end
177
187
  end
178
188
 
189
+ # Called via +before_action+ - stores in @id_column the name of whatever
190
+ # model column is used to store the record ID, via
191
+ # Scimitar::Resources::Mixin::scim_attributes_map.
192
+ #
193
+ # Default is <tt>:id</tt>.
194
+ #
195
+ def obtain_id_column_name_from_attribute_map
196
+ attrs = storage_class().scim_attributes_map() || {}
197
+ @id_column = attrs[:id] || :id
198
+ end
199
+
179
200
  end
180
201
  end
@@ -78,7 +78,7 @@ module Scimitar
78
78
  # method's return value here.
79
79
  #
80
80
  def initialize(attribute_map)
81
- @attribute_map = attribute_map
81
+ @attribute_map = attribute_map.with_indifferent_case_insensitive_access()
82
82
  end
83
83
 
84
84
  # Parse SCIM filter query into RPN stack
@@ -605,6 +605,16 @@ module Scimitar
605
605
 
606
606
  raise Scimitar::FilterError unless all_supported
607
607
 
608
+ unless case_sensitive
609
+ lc_scim_attribute = scim_attribute.downcase()
610
+
611
+ case_sensitive = (
612
+ lc_scim_attribute == 'id' ||
613
+ lc_scim_attribute == 'externalid' ||
614
+ lc_scim_attribute.start_with?('meta.')
615
+ )
616
+ end
617
+
608
618
  column_names.each.with_index do | column_name, index |
609
619
  arel_column = arel_table[column_name]
610
620
  arel_operation = case scim_operator
@@ -3,11 +3,11 @@ module Scimitar
3
3
  # Gem version. If this changes, be sure to re-run "bundle install" or
4
4
  # "bundle update".
5
5
  #
6
- VERSION = '1.3.1'
6
+ VERSION = '1.4.0'
7
7
 
8
8
  # Date for VERSION. If this changes, be sure to re-run "bundle install"
9
9
  # or "bundle update".
10
10
  #
11
- DATE = '2022-11-04'
11
+ DATE = '2023-01-26'
12
12
 
13
13
  end
@@ -1,4 +1,4 @@
1
- class MockUsersController < Scimitar::ActiveRecordBackedResourcesController
1
+ class MockGroupsController < Scimitar::ActiveRecordBackedResourcesController
2
2
 
3
3
  protected
4
4
 
@@ -58,7 +58,7 @@ class MockGroup < ActiveRecord::Base
58
58
 
59
59
  case type.downcase
60
60
  when 'user'
61
- MockUser.find_by_id(id)
61
+ MockUser.find_by_primary_key(id)
62
62
  when 'group'
63
63
  MockGroup.find_by_id(id)
64
64
  else
@@ -5,7 +5,7 @@ class MockUser < ActiveRecord::Base
5
5
  # ===========================================================================
6
6
 
7
7
  READWRITE_ATTRS = %w{
8
- id
8
+ primary_key
9
9
  scim_uid
10
10
  username
11
11
  first_name
@@ -38,7 +38,7 @@ class MockUser < ActiveRecord::Base
38
38
 
39
39
  def self.scim_attributes_map
40
40
  return {
41
- id: :id,
41
+ id: :primary_key,
42
42
  externalId: :scim_uid,
43
43
  userName: :username,
44
44
  name: {
@@ -92,11 +92,14 @@ class MockUser < ActiveRecord::Base
92
92
 
93
93
  def self.scim_queryable_attributes
94
94
  return {
95
- 'name.givenName' => { column: :first_name },
96
- 'name.familyName' => { column: :last_name },
97
- 'emails' => { columns: [ :work_email_address, :home_email_address ] },
98
- 'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
99
- 'emails.type' => { ignore: true } # We can't filter on that; it'll just search all e-mails
95
+ 'id' => { column: :primary_key },
96
+ 'externalId' => { column: :scim_uid },
97
+ 'meta.lastModified' => { column: :updated_at },
98
+ 'name.givenName' => { column: :first_name },
99
+ 'name.familyName' => { column: :last_name },
100
+ 'emails' => { columns: [ :work_email_address, :home_email_address ] },
101
+ 'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
102
+ 'emails.type' => { ignore: true } # We can't filter on that; it'll just search all e-mails
100
103
  }
101
104
  end
102
105
 
@@ -13,6 +13,9 @@ Rails.application.routes.draw do
13
13
  patch 'Users/:id', to: 'mock_users#update'
14
14
  delete 'Users/:id', to: 'mock_users#destroy'
15
15
 
16
+ get 'Groups', to: 'mock_groups#index'
17
+ get 'Groups/:id', to: 'mock_groups#show'
18
+
16
19
  # For testing blocks passed to ActiveRecordBackedResourcesController#destroy
17
20
  #
18
21
  delete 'CustomDestroyUsers/:id', to: 'custom_destroy_mock_users#destroy'
@@ -1,6 +1,7 @@
1
1
  class CreateMockUsers < ActiveRecord::Migration[6.1]
2
2
  def change
3
- create_table :mock_users do |t|
3
+ create_table :mock_users, id: :uuid, primary_key: :primary_key do |t|
4
+ t.timestamps
4
5
 
5
6
  t.text :scim_uid
6
7
  t.text :username
@@ -9,7 +10,6 @@ class CreateMockUsers < ActiveRecord::Migration[6.1]
9
10
  t.text :work_email_address
10
11
  t.text :home_email_address
11
12
  t.text :work_phone_number
12
-
13
13
  end
14
14
  end
15
15
  end
@@ -1,8 +1,13 @@
1
1
  class CreateJoinTableMockGroupsMockUsers < ActiveRecord::Migration[6.1]
2
2
  def change
3
- create_join_table :mock_groups, :mock_users do |t|
4
- t.index [:mock_group_id, :mock_user_id]
5
- t.index [:mock_user_id, :mock_group_id]
3
+ create_table :mock_groups_users, id: false do | t |
4
+ t.references :mock_group, foreign_key: true, type: :int8, index: true, null: false
5
+ t.references :mock_user, type: :uuid, index: true, null: false, primary_key: :primary_key
6
+
7
+ # The 'foreign_key:' option (used above) only works for 'id' column names
8
+ # but the test data has a column named 'primary_key' for 'mock_users'.
9
+ #
10
+ t.foreign_key :mock_users, primary_key: :primary_key
6
11
  end
7
12
  end
8
13
  end
@@ -24,12 +24,14 @@ ActiveRecord::Schema.define(version: 2021_03_08_044214) do
24
24
 
25
25
  create_table "mock_groups_users", id: false, force: :cascade do |t|
26
26
  t.bigint "mock_group_id", null: false
27
- t.bigint "mock_user_id", null: false
28
- t.index ["mock_group_id", "mock_user_id"], name: "index_mock_groups_users_on_mock_group_id_and_mock_user_id"
29
- t.index ["mock_user_id", "mock_group_id"], name: "index_mock_groups_users_on_mock_user_id_and_mock_group_id"
27
+ t.uuid "mock_user_id", null: false
28
+ t.index ["mock_group_id"], name: "index_mock_groups_users_on_mock_group_id"
29
+ t.index ["mock_user_id"], name: "index_mock_groups_users_on_mock_user_id"
30
30
  end
31
31
 
32
- create_table "mock_users", force: :cascade do |t|
32
+ create_table "mock_users", primary_key: "primary_key", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
33
+ t.datetime "created_at", precision: 6, null: false
34
+ t.datetime "updated_at", precision: 6, null: false
33
35
  t.text "scim_uid"
34
36
  t.text "username"
35
37
  t.text "first_name"
@@ -39,4 +41,6 @@ ActiveRecord::Schema.define(version: 2021_03_08_044214) do
39
41
  t.text "work_phone_number"
40
42
  end
41
43
 
44
+ add_foreign_key "mock_groups_users", "mock_groups"
45
+ add_foreign_key "mock_groups_users", "mock_users", primary_key: "primary_key"
42
46
  end
@@ -405,19 +405,19 @@ RSpec.describe Scimitar::Lists::QueryParser do
405
405
  query = @instance.to_activerecord_query(MockUser.all)
406
406
 
407
407
  expect(query.count).to eql(1)
408
- expect(query.pluck(:id)).to eql([user_1.id])
408
+ expect(query.pluck(:primary_key)).to eql([user_1.id])
409
409
 
410
410
  @instance.parse('name.givenName sw J') # First name starts with 'J'
411
411
  query = @instance.to_activerecord_query(MockUser.all)
412
412
 
413
413
  expect(query.count).to eql(2)
414
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
414
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
415
415
 
416
416
  @instance.parse('name.familyName ew he') # Last name ends with 'he'
417
417
  query = @instance.to_activerecord_query(MockUser.all)
418
418
 
419
419
  expect(query.count).to eql(1)
420
- expect(query.pluck(:id)).to eql([user_2.id])
420
+ expect(query.pluck(:primary_key)).to eql([user_2.primary_key])
421
421
 
422
422
  # Test presence
423
423
 
@@ -425,7 +425,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
425
425
  query = @instance.to_activerecord_query(MockUser.all)
426
426
 
427
427
  expect(query.count).to eql(2)
428
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
428
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
429
429
 
430
430
  # Test a simple not-equals, but use a custom starting scope. Note that
431
431
  # the query would find "user_3" *except* there is no first name defined
@@ -435,7 +435,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
435
435
  query = @instance.to_activerecord_query(MockUser.where.not('first_name' => 'John'))
436
436
 
437
437
  expect(query.count).to eql(1)
438
- expect(query.pluck(:id)).to match_array([user_1.id])
438
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key])
439
439
  end
440
440
 
441
441
  context 'when mapped to multiple columns' do
@@ -499,7 +499,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
499
499
  query = @instance.to_activerecord_query(MockUser.all)
500
500
 
501
501
  expect(query.count).to eql(1)
502
- expect(query.pluck(:id)).to match_array([user_2.id])
502
+ expect(query.pluck(:primary_key)).to match_array([user_2.primary_key])
503
503
  end
504
504
  end # "context 'simple AND' do"
505
505
 
@@ -520,7 +520,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
520
520
  query = @instance.to_activerecord_query(MockUser.all)
521
521
 
522
522
  expect(query.count).to eql(2)
523
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id])
523
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key])
524
524
  end
525
525
  end # "context 'simple OR' do"
526
526
 
@@ -546,7 +546,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
546
546
  query = @instance.to_activerecord_query(MockUser.all)
547
547
 
548
548
  expect(query.count).to eql(3)
549
- expect(query.pluck(:id)).to match_array([user_1.id, user_2.id, user_3.id])
549
+ expect(query.pluck(:primary_key)).to match_array([user_1.primary_key, user_2.primary_key, user_3.primary_key])
550
550
  end
551
551
  end # "context 'combined AND and OR' do"
552
552
 
@@ -590,7 +590,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
590
590
  end
591
591
 
592
592
  it 'complains if there is no column mapping available' do
593
- expect { @instance.send(:activerecord_columns, 'externalId') }.to raise_error(Scimitar::FilterError)
593
+ expect { @instance.send(:activerecord_columns, 'userName') }.to raise_error(Scimitar::FilterError)
594
594
  end
595
595
 
596
596
  it 'complains about malformed declarations' do
@@ -159,41 +159,67 @@ RSpec.describe Scimitar::Resources::Mixin do
159
159
  # =========================================================================
160
160
 
161
161
  context '#to_scim' do
162
- it 'compiles instance attribute values into a SCIM representation' do
163
- instance = MockUser.new
164
- instance.id = 42
165
- instance.scim_uid = 'AA02984'
166
- instance.username = 'foo'
167
- instance.first_name = 'Foo'
168
- instance.last_name = 'Bar'
169
- instance.work_email_address = 'foo.bar@test.com'
170
- instance.home_email_address = nil
171
- instance.work_phone_number = '+642201234567'
172
-
173
- g1 = MockGroup.create!(display_name: 'Group 1')
174
- g2 = MockGroup.create!(display_name: 'Group 2')
175
- g3 = MockGroup.create!(display_name: 'Group 3')
176
-
177
- g1.mock_users << instance
178
- g3.mock_users << instance
179
-
180
- scim = instance.to_scim(location: 'https://test.com/mock_users/42')
181
- json = scim.to_json()
182
- hash = JSON.parse(json)
183
-
184
- expect(hash).to eql({
185
- 'userName' => 'foo',
186
- 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
187
- 'active' => true,
188
- 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
189
- 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
190
- 'id' => '42', # Note, String
191
- 'externalId' => 'AA02984',
192
- 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
193
- 'meta' => {'location'=>'https://test.com/mock_users/42', 'resourceType'=>'User'},
194
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
195
- })
196
- end
162
+ context 'with a UUID, renamed primary key column' do
163
+ it 'compiles instance attribute values into a SCIM representation' do
164
+ uuid = SecureRandom.uuid
165
+
166
+ instance = MockUser.new
167
+ instance.primary_key = uuid
168
+ instance.scim_uid = 'AA02984'
169
+ instance.username = 'foo'
170
+ instance.first_name = 'Foo'
171
+ instance.last_name = 'Bar'
172
+ instance.work_email_address = 'foo.bar@test.com'
173
+ instance.home_email_address = nil
174
+ instance.work_phone_number = '+642201234567'
175
+
176
+ g1 = MockGroup.create!(display_name: 'Group 1')
177
+ g2 = MockGroup.create!(display_name: 'Group 2')
178
+ g3 = MockGroup.create!(display_name: 'Group 3')
179
+
180
+ g1.mock_users << instance
181
+ g3.mock_users << instance
182
+
183
+ scim = instance.to_scim(location: "https://test.com/mock_users/#{uuid}")
184
+ json = scim.to_json()
185
+ hash = JSON.parse(json)
186
+
187
+ expect(hash).to eql({
188
+ 'userName' => 'foo',
189
+ 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
190
+ 'active' => true,
191
+ 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}, {"primary"=>false, "type"=>"home", "value"=>nil}],
192
+ 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}],
193
+ 'id' => uuid,
194
+ 'externalId' => 'AA02984',
195
+ 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}],
196
+ 'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'},
197
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
198
+ })
199
+ end
200
+ end # "context 'with a UUID, renamed primary key column' do"
201
+
202
+ context 'with an integer, conventionally named primary key column' do
203
+ it 'compiles instance attribute values into a SCIM representation' do
204
+ instance = MockGroup.new
205
+ instance.id = 42
206
+ instance.scim_uid = 'GG02984'
207
+ instance.display_name = 'Some group'
208
+
209
+ scim = instance.to_scim(location: 'https://test.com/mock_groups/42')
210
+ json = scim.to_json()
211
+ hash = JSON.parse(json)
212
+
213
+ expect(hash).to eql({
214
+ 'displayName' => 'Some group',
215
+ 'id' => '42', # Note, String
216
+ 'externalId' => 'GG02984',
217
+ 'members' => [],
218
+ 'meta' => {'location'=>'https://test.com/mock_groups/42', 'resourceType'=>'Group'},
219
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
220
+ })
221
+ end
222
+ end # "context 'with an integer, conventionally named primary key column' do"
197
223
 
198
224
  context 'with optional timestamps' do
199
225
  context 'creation only' do
@@ -405,8 +431,8 @@ RSpec.describe Scimitar::Resources::Mixin do
405
431
  'displayName' => 'Foo Group',
406
432
  'members' => [
407
433
  {'type' => 'Group', 'value' => g1.id.to_s},
408
- {'type' => 'User', 'value' => u1.id.to_s},
409
- {'type' => 'User', 'value' => u3.id.to_s}
434
+ {'type' => 'User', 'value' => u1.primary_key.to_s},
435
+ {'type' => 'User', 'value' => u3.primary_key.to_s}
410
436
  ],
411
437
  'externalId' => 'GG01536',
412
438
  'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
@@ -453,8 +479,10 @@ RSpec.describe Scimitar::Resources::Mixin do
453
479
  end # "context 'using upper case' do"
454
480
 
455
481
  it 'clears things not present in input' do
482
+ uuid = SecureRandom.uuid
483
+
456
484
  instance = MockUser.new
457
- instance.id = 42
485
+ instance.primary_key = uuid
458
486
  instance.scim_uid = 'AA02984'
459
487
  instance.username = 'foo'
460
488
  instance.first_name = 'Foo'
@@ -465,7 +493,7 @@ RSpec.describe Scimitar::Resources::Mixin do
465
493
 
466
494
  instance.from_scim!(scim_hash: {})
467
495
 
468
- expect(instance.id ).to eql(42)
496
+ expect(instance.primary_key ).to eql(uuid)
469
497
  expect(instance.scim_uid ).to be_nil
470
498
  expect(instance.username ).to be_nil
471
499
  expect(instance.first_name ).to be_nil
@@ -42,13 +42,13 @@ 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({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({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
@@ -21,10 +21,8 @@ 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
-
25
24
  end
26
25
 
27
-
28
26
  context '#valid?' do
29
27
  it 'is invalid if attribute is required but value is blank' do
30
28
  attribute = described_class.new(name: 'userName', type: 'string', required: true)
@@ -76,5 +74,4 @@ RSpec.describe Scimitar::Schema::Attribute do
76
74
  expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('gaga')).to be(false)
77
75
  end
78
76
  end
79
-
80
77
  end
@@ -1,12 +1,27 @@
1
1
  require 'spec_helper'
2
+ require 'time'
2
3
 
3
4
  RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
4
5
  before :each do
5
6
  allow_any_instance_of(Scimitar::ApplicationController).to receive(:authenticated?).and_return(true)
6
7
 
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')
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')
10
25
  end
11
26
 
12
27
  # ===========================================================================
@@ -29,26 +44,65 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
29
44
  end # "context 'with no items' do"
30
45
 
31
46
  context 'with items' do
32
- it 'returns all items' do
33
- get '/Users', params: { format: :scim }
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
+ }
34
88
 
35
89
  expect(response.status).to eql(200)
36
90
  result = JSON.parse(response.body)
37
91
 
38
- expect(result['totalResults']).to eql(3)
39
- expect(result['Resources'].size).to eql(3)
92
+ expect(result['totalResults']).to eql(1)
93
+ expect(result['Resources'].size).to eql(1)
40
94
 
41
95
  ids = result['Resources'].map { |resource| resource['id'] }
42
- expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s, @u3.id.to_s])
96
+ expect(ids).to match_array([@u2.primary_key.to_s])
43
97
 
44
98
  usernames = result['Resources'].map { |resource| resource['userName'] }
45
- expect(usernames).to match_array(['1', '2', '3'])
99
+ expect(usernames).to match_array(['2'])
46
100
  end
47
101
 
48
- it 'applies a filter, with case-insensitive value comparison' do
102
+ it 'applies a filter, with case-insensitive attribute matching (GitHub issue #37)' do
49
103
  get '/Users', params: {
50
104
  format: :scim,
51
- filter: 'name.givenName eq "Foo" and name.familyName pr and emails ne "home_1@TEST.COM"'
105
+ filter: 'name.GIVENNAME eq "Foo" and name.Familyname pr and emails ne "home_1@test.com"'
52
106
  }
53
107
 
54
108
  expect(response.status).to eql(200)
@@ -58,12 +112,74 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
58
112
  expect(result['Resources'].size).to eql(1)
59
113
 
60
114
  ids = result['Resources'].map { |resource| resource['id'] }
61
- expect(ids).to match_array([@u2.id.to_s])
115
+ expect(ids).to match_array([@u2.primary_key.to_s])
62
116
 
63
117
  usernames = result['Resources'].map { |resource| resource['userName'] }
64
118
  expect(usernames).to match_array(['2'])
65
119
  end
66
120
 
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
+
67
183
  it 'obeys a page size' do
68
184
  get '/Users', params: {
69
185
  format: :scim,
@@ -77,7 +193,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
77
193
  expect(result['Resources'].size).to eql(2)
78
194
 
79
195
  ids = result['Resources'].map { |resource| resource['id'] }
80
- expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s])
196
+ expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s])
81
197
 
82
198
  usernames = result['Resources'].map { |resource| resource['userName'] }
83
199
  expect(usernames).to match_array(['1', '2'])
@@ -96,7 +212,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
96
212
  expect(result['Resources'].size).to eql(2)
97
213
 
98
214
  ids = result['Resources'].map { |resource| resource['id'] }
99
- expect(ids).to match_array([@u2.id.to_s, @u3.id.to_s])
215
+ expect(ids).to match_array([@u2.primary_key.to_s, @u3.primary_key.to_s])
100
216
 
101
217
  usernames = result['Resources'].map { |resource| resource['userName'] }
102
218
  expect(usernames).to match_array(['2', '3'])
@@ -120,18 +236,34 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
120
236
  # ===========================================================================
121
237
 
122
238
  context '#show' 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 }
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 }
126
243
 
127
- expect(response.status).to eql(200)
128
- result = JSON.parse(response.body)
244
+ expect(response.status).to eql(200)
245
+ result = JSON.parse(response.body)
129
246
 
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
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"
253
+
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)
261
+
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"
135
267
 
136
268
  it 'renders 404' do
137
269
  get '/Users/xyz', params: { format: :scim }
@@ -164,7 +296,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
164
296
  expect(response.status).to eql(201)
165
297
  result = JSON.parse(response.body)
166
298
 
167
- expect(result['id']).to eql(new_mock.id.to_s)
299
+ expect(result['id']).to eql(new_mock.primary_key.to_s)
168
300
  expect(result['meta']['resourceType']).to eql('User')
169
301
  expect(new_mock.username).to eql('4')
170
302
  end
@@ -205,7 +337,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
205
337
  expect(response.status).to eql(201)
206
338
  result = JSON.parse(response.body)
207
339
 
208
- expect(result['id']).to eql(new_mock.id.to_s)
340
+ expect(result['id']).to eql(new_mock.primary_key.to_s)
209
341
  expect(result['meta']['resourceType']).to eql('User')
210
342
  expect(new_mock.username).to eql('4')
211
343
  expect(new_mock.first_name).to eql('Given')
@@ -279,13 +411,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
279
411
 
280
412
  expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
281
413
  expect {
282
- put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
414
+ put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
283
415
  }.to_not change { MockUser.count }
284
416
 
285
417
  expect(response.status).to eql(200)
286
418
  result = JSON.parse(response.body)
287
419
 
288
- expect(result['id']).to eql(@u2.id.to_s)
420
+ expect(result['id']).to eql(@u2.primary_key.to_s)
289
421
  expect(result['meta']['resourceType']).to eql('User')
290
422
 
291
423
  @u2.reload
@@ -307,7 +439,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
307
439
 
308
440
  it 'notes schema validation failures' do
309
441
  expect {
310
- put "/Users/#{@u2.id}", params: {
442
+ put "/Users/#{@u2.primary_key}", params: {
311
443
  format: :scim
312
444
  # userName parameter is required by schema, but missing
313
445
  }
@@ -386,13 +518,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
386
518
 
387
519
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
388
520
  expect {
389
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
521
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
390
522
  }.to_not change { MockUser.count }
391
523
 
392
524
  expect(response.status).to eql(200)
393
525
  result = JSON.parse(response.body)
394
526
 
395
- expect(result['id']).to eql(@u2.id.to_s)
527
+ expect(result['id']).to eql(@u2.primary_key.to_s)
396
528
  expect(result['meta']['resourceType']).to eql('User')
397
529
 
398
530
  @u2.reload
@@ -423,13 +555,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
423
555
 
424
556
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
425
557
  expect {
426
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
558
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
427
559
  }.to_not change { MockUser.count }
428
560
 
429
561
  expect(response.status).to eql(200)
430
562
  result = JSON.parse(response.body)
431
563
 
432
- expect(result['id']).to eql(@u2.id.to_s)
564
+ expect(result['id']).to eql(@u2.primary_key.to_s)
433
565
  expect(result['meta']['resourceType']).to eql('User')
434
566
 
435
567
  @u2.reload
@@ -455,13 +587,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
455
587
 
456
588
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
457
589
  expect {
458
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
590
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
459
591
  }.to_not change { MockUser.count }
460
592
 
461
593
  expect(response.status).to eql(200)
462
594
  result = JSON.parse(response.body)
463
595
 
464
- expect(result['id']).to eql(@u2.id.to_s)
596
+ expect(result['id']).to eql(@u2.primary_key.to_s)
465
597
  expect(result['meta']['resourceType']).to eql('User')
466
598
 
467
599
  @u2.reload
@@ -487,13 +619,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
487
619
 
488
620
  expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
489
621
  expect {
490
- patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
622
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
491
623
  }.to_not change { MockUser.count }
492
624
 
493
625
  expect(response.status).to eql(200)
494
626
  result = JSON.parse(response.body)
495
627
 
496
- expect(result['id']).to eql(@u2.id.to_s)
628
+ expect(result['id']).to eql(@u2.primary_key.to_s)
497
629
  expect(result['meta']['resourceType']).to eql('User')
498
630
 
499
631
  @u2.reload
@@ -517,7 +649,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
517
649
 
518
650
  it 'notes Rails validation failures' do
519
651
  expect {
520
- patch "/Users/#{@u2.id}", params: {
652
+ patch "/Users/#{@u2.primary_key}", params: {
521
653
  format: :scim,
522
654
  Operations: [
523
655
  {
@@ -570,7 +702,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
570
702
  expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
571
703
  expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
572
704
  expect {
573
- delete "/Users/#{@u2.id}", params: { format: :scim }
705
+ delete "/Users/#{@u2.primary_key}", params: { format: :scim }
574
706
  }.to change { MockUser.count }.by(-1)
575
707
 
576
708
  expect(response.status).to eql(204)
@@ -582,7 +714,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
582
714
  expect_any_instance_of(MockUser).to_not receive(:destroy!)
583
715
 
584
716
  expect {
585
- delete "/CustomDestroyUsers/#{@u2.id}", params: { format: :scim }
717
+ delete "/CustomDestroyUsers/#{@u2.primary_key}", params: { format: :scim }
586
718
  }.to_not change { MockUser.count }
587
719
 
588
720
  expect(response.status).to eql(204)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scimitar
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RIPA Global
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-11-04 00:00:00.000000000 Z
12
+ date: 2023-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -260,51 +260,51 @@ required_rubygems_version: !ruby/object:Gem::Requirement
260
260
  - !ruby/object:Gem::Version
261
261
  version: '0'
262
262
  requirements: []
263
- rubygems_version: 3.1.6
263
+ rubygems_version: 3.4.4
264
264
  signing_key:
265
265
  specification_version: 4
266
266
  summary: SCIM v2 for Rails
267
267
  test_files:
268
- - spec/spec_helper.rb
269
- - spec/models/scimitar/lists/query_parser_spec.rb
268
+ - spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb
269
+ - spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
270
+ - spec/apps/dummy/app/controllers/mock_groups_controller.rb
271
+ - spec/apps/dummy/app/controllers/mock_users_controller.rb
272
+ - spec/apps/dummy/app/models/mock_group.rb
273
+ - spec/apps/dummy/app/models/mock_user.rb
274
+ - spec/apps/dummy/config/application.rb
275
+ - spec/apps/dummy/config/boot.rb
276
+ - spec/apps/dummy/config/environment.rb
277
+ - spec/apps/dummy/config/environments/test.rb
278
+ - spec/apps/dummy/config/initializers/cookies_serializer.rb
279
+ - spec/apps/dummy/config/initializers/scimitar.rb
280
+ - spec/apps/dummy/config/initializers/session_store.rb
281
+ - spec/apps/dummy/config/routes.rb
282
+ - spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb
283
+ - spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
284
+ - spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
285
+ - spec/apps/dummy/db/schema.rb
286
+ - spec/controllers/scimitar/application_controller_spec.rb
287
+ - spec/controllers/scimitar/resource_types_controller_spec.rb
288
+ - spec/controllers/scimitar/resources_controller_spec.rb
289
+ - spec/controllers/scimitar/schemas_controller_spec.rb
290
+ - spec/controllers/scimitar/service_provider_configurations_controller_spec.rb
291
+ - spec/models/scimitar/complex_types/address_spec.rb
292
+ - spec/models/scimitar/complex_types/email_spec.rb
270
293
  - spec/models/scimitar/lists/count_spec.rb
294
+ - spec/models/scimitar/lists/query_parser_spec.rb
295
+ - spec/models/scimitar/resource_type_spec.rb
296
+ - spec/models/scimitar/resources/base_spec.rb
271
297
  - spec/models/scimitar/resources/base_validation_spec.rb
272
298
  - spec/models/scimitar/resources/mixin_spec.rb
273
299
  - spec/models/scimitar/resources/user_spec.rb
274
- - spec/models/scimitar/resources/base_spec.rb
275
300
  - spec/models/scimitar/schema/attribute_spec.rb
301
+ - spec/models/scimitar/schema/base_spec.rb
276
302
  - spec/models/scimitar/schema/group_spec.rb
277
303
  - spec/models/scimitar/schema/user_spec.rb
278
- - spec/models/scimitar/schema/base_spec.rb
279
- - spec/models/scimitar/resource_type_spec.rb
280
- - spec/models/scimitar/complex_types/email_spec.rb
281
- - spec/models/scimitar/complex_types/address_spec.rb
282
- - spec/requests/controller_configuration_spec.rb
283
- - spec/requests/engine_spec.rb
284
304
  - spec/requests/active_record_backed_resources_controller_spec.rb
285
305
  - spec/requests/application_controller_spec.rb
306
+ - spec/requests/controller_configuration_spec.rb
307
+ - spec/requests/engine_spec.rb
308
+ - spec/spec_helper.rb
286
309
  - spec/spec_helper_spec.rb
287
310
  - spec/support/hash_with_indifferent_case_insensitive_access_spec.rb
288
- - spec/controllers/scimitar/schemas_controller_spec.rb
289
- - spec/controllers/scimitar/resource_types_controller_spec.rb
290
- - spec/controllers/scimitar/resources_controller_spec.rb
291
- - spec/controllers/scimitar/service_provider_configurations_controller_spec.rb
292
- - spec/controllers/scimitar/application_controller_spec.rb
293
- - spec/apps/dummy/app/models/mock_group.rb
294
- - spec/apps/dummy/app/models/mock_user.rb
295
- - spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
296
- - spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb
297
- - spec/apps/dummy/app/controllers/mock_groups_controller.rb
298
- - spec/apps/dummy/app/controllers/mock_users_controller.rb
299
- - spec/apps/dummy/config/routes.rb
300
- - spec/apps/dummy/config/environments/test.rb
301
- - spec/apps/dummy/config/environment.rb
302
- - spec/apps/dummy/config/application.rb
303
- - spec/apps/dummy/config/boot.rb
304
- - spec/apps/dummy/config/initializers/session_store.rb
305
- - spec/apps/dummy/config/initializers/cookies_serializer.rb
306
- - spec/apps/dummy/config/initializers/scimitar.rb
307
- - spec/apps/dummy/db/schema.rb
308
- - spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
309
- - spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
310
- - spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb