scimitar 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +27 -6
- data/app/models/scimitar/lists/query_parser.rb +11 -1
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
- data/spec/apps/dummy/app/models/mock_group.rb +1 -1
- data/spec/apps/dummy/app/models/mock_user.rb +10 -7
- data/spec/apps/dummy/config/routes.rb +3 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +2 -2
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +8 -3
- data/spec/apps/dummy/db/schema.rb +8 -4
- data/spec/models/scimitar/lists/query_parser_spec.rb +9 -9
- data/spec/models/scimitar/resources/mixin_spec.rb +67 -39
- data/spec/models/scimitar/resources/user_spec.rb +2 -2
- data/spec/models/scimitar/schema/attribute_spec.rb +0 -3
- data/spec/requests/active_record_backed_resources_controller_spec.rb +172 -40
- metadata +35 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd0efbdecece6297062b46cf46bdeaccd8330cd58fc94fb2bcb0de7c7bb90806
|
4
|
+
data.tar.gz: 7f3528ee080f0f295d03c77fba1586484b5cadd6ef5e4de1fc00d7807ec7a0c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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().
|
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
|
data/lib/scimitar/version.rb
CHANGED
@@ -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.
|
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 = '
|
11
|
+
DATE = '2023-01-26'
|
12
12
|
|
13
13
|
end
|
@@ -5,7 +5,7 @@ class MockUser < ActiveRecord::Base
|
|
5
5
|
# ===========================================================================
|
6
6
|
|
7
7
|
READWRITE_ATTRS = %w{
|
8
|
-
|
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: :
|
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
|
-
'
|
96
|
-
'
|
97
|
-
'
|
98
|
-
'
|
99
|
-
'
|
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
|
-
|
4
|
-
t.
|
5
|
-
t.index
|
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.
|
28
|
-
t.index ["mock_group_id"
|
29
|
-
t.index ["mock_user_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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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, '
|
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
|
-
|
163
|
-
instance
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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.
|
409
|
-
{'type' => 'User', 'value' => u3.
|
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.
|
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.
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
33
|
-
|
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(
|
39
|
-
expect(result['Resources'].size).to eql(
|
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([@
|
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(['
|
99
|
+
expect(usernames).to match_array(['2'])
|
46
100
|
end
|
47
101
|
|
48
|
-
it 'applies a filter, with case-insensitive
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
128
|
-
|
244
|
+
expect(response.status).to eql(200)
|
245
|
+
result = JSON.parse(response.body)
|
129
246
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
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.
|
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/
|
269
|
-
- spec/
|
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
|