scimitar 2.2.0 → 2.3.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 -7
- 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 +5 -3
- data/spec/apps/dummy/config/routes.rb +9 -6
- 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 +9 -7
- data/spec/models/scimitar/lists/query_parser_spec.rb +8 -8
- data/spec/models/scimitar/resources/mixin_spec.rb +67 -39
- data/spec/requests/active_record_backed_resources_controller_spec.rb +94 -48
- metadata +3 -5
- data/spec/apps/dummy/db/migrate/20230109012729_add_timestamps_to_mock_user.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3904e389b3b00b4c49df6d9267cc0b5413613ed56b544ee888dd4b3bfa9d7489
|
4
|
+
data.tar.gz: 93b14676bda6d86d4add6c0bcbf8ae6415ff49177b384f2b75571a304485d87f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1fad29d6ba090ad00b55bdf59f1050aacd1858d4e511e726d3fa501a37389e57777cd2cb2253c20972106e567a3f4b5e07ecd53a17723dcab2517f0c8ab99cf
|
7
|
+
data.tar.gz: 30a1c3ad4c6653b347cf00228cad99b88a3dc4a2da45fe311b61d0a0f2d08db723fabf2041e3d72f1d1cea28d633c540322394841723bdd8db7d805e49819eb4
|
@@ -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,13 +39,13 @@ module Scimitar
|
|
37
39
|
pagination_info = scim_pagination_info(query.count())
|
38
40
|
|
39
41
|
page_of_results = query
|
40
|
-
.order(
|
42
|
+
.order(@id_column => :asc)
|
41
43
|
.offset(pagination_info.offset)
|
42
44
|
.limit(pagination_info.limit)
|
43
45
|
.to_a()
|
44
46
|
|
45
47
|
super(pagination_info, page_of_results) do | record |
|
46
|
-
|
48
|
+
record_to_scim(record)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
@@ -52,7 +54,7 @@ module Scimitar
|
|
52
54
|
def show
|
53
55
|
super do |record_id|
|
54
56
|
record = self.find_record(record_id)
|
55
|
-
record
|
57
|
+
record_to_scim(record)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
@@ -64,7 +66,7 @@ module Scimitar
|
|
64
66
|
record = self.storage_class().new
|
65
67
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
66
68
|
self.save!(record)
|
67
|
-
|
69
|
+
record_to_scim(record)
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -77,7 +79,7 @@ module Scimitar
|
|
77
79
|
record = self.find_record(record_id)
|
78
80
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
79
81
|
self.save!(record)
|
80
|
-
|
82
|
+
record_to_scim(record)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
@@ -90,7 +92,7 @@ module Scimitar
|
|
90
92
|
record = self.find_record(record_id)
|
91
93
|
record.from_scim_patch!(patch_hash: patch_hash)
|
92
94
|
self.save!(record)
|
93
|
-
|
95
|
+
record_to_scim(record)
|
94
96
|
end
|
95
97
|
end
|
96
98
|
end
|
@@ -138,7 +140,14 @@ module Scimitar
|
|
138
140
|
# +record_id+:: Record ID (SCIM schema 'id' value - "our" ID).
|
139
141
|
#
|
140
142
|
def find_record(record_id)
|
141
|
-
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)))
|
142
151
|
end
|
143
152
|
|
144
153
|
# Save a record, dealing with validation exceptions by raising SCIM
|
@@ -177,5 +186,16 @@ module Scimitar
|
|
177
186
|
end
|
178
187
|
end
|
179
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
|
+
|
180
200
|
end
|
181
201
|
end
|
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 = '2.
|
6
|
+
VERSION = '2.3.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 = '2023-01-
|
11
|
+
DATE = '2023-01-17'
|
12
12
|
|
13
13
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
class MockUser < ActiveRecord::Base
|
2
2
|
|
3
|
+
self.primary_key = :primary_key
|
4
|
+
|
3
5
|
# ===========================================================================
|
4
6
|
# TEST ATTRIBUTES - see db/migrate/20210304014602_create_mock_users.rb etc.
|
5
7
|
# ===========================================================================
|
6
8
|
|
7
9
|
READWRITE_ATTRS = %w{
|
8
|
-
|
10
|
+
primary_key
|
9
11
|
scim_uid
|
10
12
|
username
|
11
13
|
first_name
|
@@ -38,7 +40,7 @@ class MockUser < ActiveRecord::Base
|
|
38
40
|
|
39
41
|
def self.scim_attributes_map
|
40
42
|
return {
|
41
|
-
id: :
|
43
|
+
id: :primary_key,
|
42
44
|
externalId: :scim_uid,
|
43
45
|
userName: :username,
|
44
46
|
name: {
|
@@ -92,7 +94,7 @@ class MockUser < ActiveRecord::Base
|
|
92
94
|
|
93
95
|
def self.scim_queryable_attributes
|
94
96
|
return {
|
95
|
-
'id' => { column: :
|
97
|
+
'id' => { column: :primary_key },
|
96
98
|
'externalId' => { column: :scim_uid },
|
97
99
|
'meta.lastModified' => { column: :updated_at },
|
98
100
|
'name.givenName' => { column: :first_name },
|
@@ -6,12 +6,15 @@
|
|
6
6
|
Rails.application.routes.draw do
|
7
7
|
mount Scimitar::Engine, at: '/'
|
8
8
|
|
9
|
-
get 'Users',
|
10
|
-
get 'Users/:id',
|
11
|
-
post 'Users',
|
12
|
-
put 'Users/:id',
|
13
|
-
patch 'Users/:id',
|
14
|
-
delete 'Users/:id',
|
9
|
+
get 'Users', to: 'mock_users#index'
|
10
|
+
get 'Users/:id', to: 'mock_users#show'
|
11
|
+
post 'Users', to: 'mock_users#create'
|
12
|
+
put 'Users/:id', to: 'mock_users#replace'
|
13
|
+
patch 'Users/:id', to: 'mock_users#update'
|
14
|
+
delete 'Users/:id', to: 'mock_users#destroy'
|
15
|
+
|
16
|
+
get 'Groups', to: 'mock_groups#index'
|
17
|
+
get 'Groups/:id', to: 'mock_groups#show'
|
15
18
|
|
16
19
|
# For testing blocks passed to ActiveRecordBackedResourcesController#destroy
|
17
20
|
#
|
@@ -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
|
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema[7.0].define(version:
|
13
|
+
ActiveRecord::Schema[7.0].define(version: 2021_03_08_044214) do
|
14
14
|
# These are extensions that must be enabled in order to support this database
|
15
15
|
enable_extension "plpgsql"
|
16
16
|
|
@@ -23,12 +23,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_09_012729) do
|
|
23
23
|
|
24
24
|
create_table "mock_groups_users", id: false, force: :cascade do |t|
|
25
25
|
t.bigint "mock_group_id", null: false
|
26
|
-
t.
|
27
|
-
t.index ["mock_group_id"
|
28
|
-
t.index ["mock_user_id"
|
26
|
+
t.uuid "mock_user_id", null: false
|
27
|
+
t.index ["mock_group_id"], name: "index_mock_groups_users_on_mock_group_id"
|
28
|
+
t.index ["mock_user_id"], name: "index_mock_groups_users_on_mock_user_id"
|
29
29
|
end
|
30
30
|
|
31
|
-
create_table "mock_users", force: :cascade do |t|
|
31
|
+
create_table "mock_users", primary_key: "primary_key", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
32
|
+
t.datetime "created_at", null: false
|
33
|
+
t.datetime "updated_at", null: false
|
32
34
|
t.text "scim_uid"
|
33
35
|
t.text "username"
|
34
36
|
t.text "first_name"
|
@@ -36,8 +38,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_09_012729) do
|
|
36
38
|
t.text "work_email_address"
|
37
39
|
t.text "home_email_address"
|
38
40
|
t.text "work_phone_number"
|
39
|
-
t.datetime "created_at", null: false
|
40
|
-
t.datetime "updated_at", null: false
|
41
41
|
end
|
42
42
|
|
43
|
+
add_foreign_key "mock_groups_users", "mock_groups"
|
44
|
+
add_foreign_key "mock_groups_users", "mock_users", primary_key: "primary_key"
|
43
45
|
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.primary_key])
|
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
|
|
@@ -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
|
@@ -5,11 +5,21 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
5
5
|
before :each do
|
6
6
|
allow_any_instance_of(Scimitar::ApplicationController).to receive(:authenticated?).and_return(true)
|
7
7
|
|
8
|
+
# If a sort order is unspecified, the controller defaults to ID ascending.
|
9
|
+
# With UUID based IDs, testing life is made easier by ensuring that the
|
10
|
+
# creation order matches an ascending UUID sort order (which is what would
|
11
|
+
# happen if we were using integer primary keys).
|
12
|
+
#
|
8
13
|
lmt = Time.parse("2023-01-09 14:25:00 +1300")
|
14
|
+
ids = 3.times.map { SecureRandom.uuid }.sort()
|
9
15
|
|
10
|
-
@u1 = MockUser.create(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)
|
11
|
-
@u2 = MockUser.create(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)
|
12
|
-
@u3 = MockUser.create(username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
|
16
|
+
@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)
|
17
|
+
@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)
|
18
|
+
@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)
|
19
|
+
|
20
|
+
@g1 = MockGroup.create!(display_name: 'Group 1')
|
21
|
+
@g2 = MockGroup.create!(display_name: 'Group 2')
|
22
|
+
@g3 = MockGroup.create!(display_name: 'Group 3')
|
13
23
|
end
|
14
24
|
|
15
25
|
# ===========================================================================
|
@@ -32,21 +42,41 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
32
42
|
end # "context 'with no items' do"
|
33
43
|
|
34
44
|
context 'with items' do
|
35
|
-
|
36
|
-
|
45
|
+
context 'with a UUID, renamed primary key column' do
|
46
|
+
it 'returns all items' do
|
47
|
+
get '/Users', params: { format: :scim }
|
37
48
|
|
38
|
-
|
39
|
-
|
49
|
+
expect(response.status).to eql(200)
|
50
|
+
result = JSON.parse(response.body)
|
40
51
|
|
41
|
-
|
42
|
-
|
52
|
+
expect(result['totalResults']).to eql(3)
|
53
|
+
expect(result['Resources'].size).to eql(3)
|
43
54
|
|
44
|
-
|
45
|
-
|
55
|
+
ids = result['Resources'].map { |resource| resource['id'] }
|
56
|
+
expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s, @u3.primary_key.to_s])
|
46
57
|
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
usernames = result['Resources'].map { |resource| resource['userName'] }
|
59
|
+
expect(usernames).to match_array(['1', '2', '3'])
|
60
|
+
end
|
61
|
+
end # "context 'with a UUID, renamed primary key column' do"
|
62
|
+
|
63
|
+
context 'with an integer, conventionally named primary key column' do
|
64
|
+
it 'returns all items' do
|
65
|
+
get '/Groups', params: { format: :scim }
|
66
|
+
|
67
|
+
expect(response.status).to eql(200)
|
68
|
+
result = JSON.parse(response.body)
|
69
|
+
|
70
|
+
expect(result['totalResults']).to eql(3)
|
71
|
+
expect(result['Resources'].size).to eql(3)
|
72
|
+
|
73
|
+
ids = result['Resources'].map { |resource| resource['id'] }
|
74
|
+
expect(ids).to match_array([@g1.id.to_s, @g2.id.to_s, @g3.id.to_s])
|
75
|
+
|
76
|
+
usernames = result['Resources'].map { |resource| resource['displayName'] }
|
77
|
+
expect(usernames).to match_array(['Group 1', 'Group 2', 'Group 3'])
|
78
|
+
end
|
79
|
+
end # "context 'with an integer, conventionally named primary key column' do"
|
50
80
|
|
51
81
|
it 'applies a filter, with case-insensitive value comparison' do
|
52
82
|
get '/Users', params: {
|
@@ -61,7 +91,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
61
91
|
expect(result['Resources'].size).to eql(1)
|
62
92
|
|
63
93
|
ids = result['Resources'].map { |resource| resource['id'] }
|
64
|
-
expect(ids).to match_array([@u2.
|
94
|
+
expect(ids).to match_array([@u2.primary_key.to_s])
|
65
95
|
|
66
96
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
67
97
|
expect(usernames).to match_array(['2'])
|
@@ -80,7 +110,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
80
110
|
expect(result['Resources'].size).to eql(1)
|
81
111
|
|
82
112
|
ids = result['Resources'].map { |resource| resource['id'] }
|
83
|
-
expect(ids).to match_array([@u2.
|
113
|
+
expect(ids).to match_array([@u2.primary_key.to_s])
|
84
114
|
|
85
115
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
86
116
|
expect(usernames).to match_array(['2'])
|
@@ -93,7 +123,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
93
123
|
it 'applies a filter on primary keys, using direct comparison (rather than e.g. case-insensitive operators)' do
|
94
124
|
get '/Users', params: {
|
95
125
|
format: :scim,
|
96
|
-
filter: "id eq \"#{@u3.
|
126
|
+
filter: "id eq \"#{@u3.primary_key}\""
|
97
127
|
}
|
98
128
|
|
99
129
|
expect(response.status).to eql(200)
|
@@ -103,7 +133,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
103
133
|
expect(result['Resources'].size).to eql(1)
|
104
134
|
|
105
135
|
ids = result['Resources'].map { |resource| resource['id'] }
|
106
|
-
expect(ids).to match_array([@u3.
|
136
|
+
expect(ids).to match_array([@u3.primary_key.to_s])
|
107
137
|
|
108
138
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
109
139
|
expect(usernames).to match_array(['3'])
|
@@ -122,7 +152,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
122
152
|
expect(result['Resources'].size).to eql(1)
|
123
153
|
|
124
154
|
ids = result['Resources'].map { |resource| resource['id'] }
|
125
|
-
expect(ids).to match_array([@u2.
|
155
|
+
expect(ids).to match_array([@u2.primary_key.to_s])
|
126
156
|
|
127
157
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
128
158
|
expect(usernames).to match_array(['2'])
|
@@ -141,7 +171,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
141
171
|
expect(result['Resources'].size).to eql(1)
|
142
172
|
|
143
173
|
ids = result['Resources'].map { |resource| resource['id'] }
|
144
|
-
expect(ids).to match_array([@u3.
|
174
|
+
expect(ids).to match_array([@u3.primary_key.to_s])
|
145
175
|
|
146
176
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
147
177
|
expect(usernames).to match_array(['3'])
|
@@ -161,7 +191,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
161
191
|
expect(result['Resources'].size).to eql(2)
|
162
192
|
|
163
193
|
ids = result['Resources'].map { |resource| resource['id'] }
|
164
|
-
expect(ids).to match_array([@u1.
|
194
|
+
expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s])
|
165
195
|
|
166
196
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
167
197
|
expect(usernames).to match_array(['1', '2'])
|
@@ -180,7 +210,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
180
210
|
expect(result['Resources'].size).to eql(2)
|
181
211
|
|
182
212
|
ids = result['Resources'].map { |resource| resource['id'] }
|
183
|
-
expect(ids).to match_array([@u2.
|
213
|
+
expect(ids).to match_array([@u2.primary_key.to_s, @u3.primary_key.to_s])
|
184
214
|
|
185
215
|
usernames = result['Resources'].map { |resource| resource['userName'] }
|
186
216
|
expect(usernames).to match_array(['2', '3'])
|
@@ -204,18 +234,34 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
204
234
|
# ===========================================================================
|
205
235
|
|
206
236
|
context '#show' do
|
207
|
-
|
208
|
-
|
209
|
-
|
237
|
+
context 'with a UUID, renamed primary key column' do
|
238
|
+
it 'shows an item' do
|
239
|
+
expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
|
240
|
+
get "/Users/#{@u2.primary_key}", params: { format: :scim }
|
210
241
|
|
211
|
-
|
212
|
-
|
242
|
+
expect(response.status).to eql(200)
|
243
|
+
result = JSON.parse(response.body)
|
213
244
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
245
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
246
|
+
expect(result['userName']).to eql('2')
|
247
|
+
expect(result['name']['familyName']).to eql('Bar')
|
248
|
+
expect(result['meta']['resourceType']).to eql('User')
|
249
|
+
end
|
250
|
+
end # "context 'with a UUID, renamed primary key column' do"
|
251
|
+
|
252
|
+
context 'with an integer, conventionally named primary key column' do
|
253
|
+
it 'shows an item' do
|
254
|
+
expect_any_instance_of(MockGroupsController).to receive(:show).once.and_call_original
|
255
|
+
get "/Groups/#{@g2.id}", params: { format: :scim }
|
256
|
+
|
257
|
+
expect(response.status).to eql(200)
|
258
|
+
result = JSON.parse(response.body)
|
259
|
+
|
260
|
+
expect(result['id']).to eql(@g2.id.to_s) # Note - ID was converted String; not Integer
|
261
|
+
expect(result['displayName']).to eql('Group 2')
|
262
|
+
expect(result['meta']['resourceType']).to eql('Group')
|
263
|
+
end
|
264
|
+
end # "context 'with an integer, conventionally named primary key column' do"
|
219
265
|
|
220
266
|
it 'renders 404' do
|
221
267
|
get '/Users/xyz', params: { format: :scim }
|
@@ -234,7 +280,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
234
280
|
it 'with minimal parameters' do
|
235
281
|
mock_before = MockUser.all.to_a
|
236
282
|
|
237
|
-
attributes = { userName: '4' }
|
283
|
+
attributes = { userName: '4' } # Minimum required by schema
|
238
284
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
239
285
|
|
240
286
|
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
@@ -248,7 +294,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
248
294
|
expect(response.status).to eql(201)
|
249
295
|
result = JSON.parse(response.body)
|
250
296
|
|
251
|
-
expect(result['id']).to eql(new_mock.
|
297
|
+
expect(result['id']).to eql(new_mock.primary_key.to_s)
|
252
298
|
expect(result['meta']['resourceType']).to eql('User')
|
253
299
|
expect(new_mock.username).to eql('4')
|
254
300
|
end
|
@@ -363,13 +409,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
363
409
|
|
364
410
|
expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
|
365
411
|
expect {
|
366
|
-
put "/Users/#{@u2.
|
412
|
+
put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
|
367
413
|
}.to_not change { MockUser.count }
|
368
414
|
|
369
415
|
expect(response.status).to eql(200)
|
370
416
|
result = JSON.parse(response.body)
|
371
417
|
|
372
|
-
expect(result['id']).to eql(@u2.
|
418
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
373
419
|
expect(result['meta']['resourceType']).to eql('User')
|
374
420
|
|
375
421
|
@u2.reload
|
@@ -391,7 +437,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
391
437
|
|
392
438
|
it 'notes schema validation failures' do
|
393
439
|
expect {
|
394
|
-
put "/Users/#{@u2.
|
440
|
+
put "/Users/#{@u2.primary_key}", params: {
|
395
441
|
format: :scim
|
396
442
|
# userName parameter is required by schema, but missing
|
397
443
|
}
|
@@ -470,13 +516,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
470
516
|
|
471
517
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
472
518
|
expect {
|
473
|
-
patch "/Users/#{@u2.
|
519
|
+
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
474
520
|
}.to_not change { MockUser.count }
|
475
521
|
|
476
522
|
expect(response.status).to eql(200)
|
477
523
|
result = JSON.parse(response.body)
|
478
524
|
|
479
|
-
expect(result['id']).to eql(@u2.
|
525
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
480
526
|
expect(result['meta']['resourceType']).to eql('User')
|
481
527
|
|
482
528
|
@u2.reload
|
@@ -507,13 +553,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
507
553
|
|
508
554
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
509
555
|
expect {
|
510
|
-
patch "/Users/#{@u2.
|
556
|
+
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
511
557
|
}.to_not change { MockUser.count }
|
512
558
|
|
513
559
|
expect(response.status).to eql(200)
|
514
560
|
result = JSON.parse(response.body)
|
515
561
|
|
516
|
-
expect(result['id']).to eql(@u2.
|
562
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
517
563
|
expect(result['meta']['resourceType']).to eql('User')
|
518
564
|
|
519
565
|
@u2.reload
|
@@ -539,13 +585,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
539
585
|
|
540
586
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
541
587
|
expect {
|
542
|
-
patch "/Users/#{@u2.
|
588
|
+
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
543
589
|
}.to_not change { MockUser.count }
|
544
590
|
|
545
591
|
expect(response.status).to eql(200)
|
546
592
|
result = JSON.parse(response.body)
|
547
593
|
|
548
|
-
expect(result['id']).to eql(@u2.
|
594
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
549
595
|
expect(result['meta']['resourceType']).to eql('User')
|
550
596
|
|
551
597
|
@u2.reload
|
@@ -571,13 +617,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
571
617
|
|
572
618
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
573
619
|
expect {
|
574
|
-
patch "/Users/#{@u2.
|
620
|
+
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
575
621
|
}.to_not change { MockUser.count }
|
576
622
|
|
577
623
|
expect(response.status).to eql(200)
|
578
624
|
result = JSON.parse(response.body)
|
579
625
|
|
580
|
-
expect(result['id']).to eql(@u2.
|
626
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
581
627
|
expect(result['meta']['resourceType']).to eql('User')
|
582
628
|
|
583
629
|
@u2.reload
|
@@ -601,7 +647,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
601
647
|
|
602
648
|
it 'notes Rails validation failures' do
|
603
649
|
expect {
|
604
|
-
patch "/Users/#{@u2.
|
650
|
+
patch "/Users/#{@u2.primary_key}", params: {
|
605
651
|
format: :scim,
|
606
652
|
Operations: [
|
607
653
|
{
|
@@ -654,7 +700,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
654
700
|
expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
|
655
701
|
expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
|
656
702
|
expect {
|
657
|
-
delete "/Users/#{@u2.
|
703
|
+
delete "/Users/#{@u2.primary_key}", params: { format: :scim }
|
658
704
|
}.to change { MockUser.count }.by(-1)
|
659
705
|
|
660
706
|
expect(response.status).to eql(204)
|
@@ -666,7 +712,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
666
712
|
expect_any_instance_of(MockUser).to_not receive(:destroy!)
|
667
713
|
|
668
714
|
expect {
|
669
|
-
delete "/CustomDestroyUsers/#{@u2.
|
715
|
+
delete "/CustomDestroyUsers/#{@u2.primary_key}", params: { format: :scim }
|
670
716
|
}.to_not change { MockUser.count }
|
671
717
|
|
672
718
|
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: 2.
|
4
|
+
version: 2.3.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: 2023-01-
|
12
|
+
date: 2023-01-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -211,7 +211,6 @@ files:
|
|
211
211
|
- spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb
|
212
212
|
- spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
|
213
213
|
- spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
|
214
|
-
- spec/apps/dummy/db/migrate/20230109012729_add_timestamps_to_mock_user.rb
|
215
214
|
- spec/apps/dummy/db/schema.rb
|
216
215
|
- spec/controllers/scimitar/application_controller_spec.rb
|
217
216
|
- spec/controllers/scimitar/resource_types_controller_spec.rb
|
@@ -261,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
261
260
|
- !ruby/object:Gem::Version
|
262
261
|
version: '0'
|
263
262
|
requirements: []
|
264
|
-
rubygems_version: 3.4.
|
263
|
+
rubygems_version: 3.4.4
|
265
264
|
signing_key:
|
266
265
|
specification_version: 4
|
267
266
|
summary: SCIM v2 for Rails
|
@@ -283,7 +282,6 @@ test_files:
|
|
283
282
|
- spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb
|
284
283
|
- spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb
|
285
284
|
- spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb
|
286
|
-
- spec/apps/dummy/db/migrate/20230109012729_add_timestamps_to_mock_user.rb
|
287
285
|
- spec/apps/dummy/db/schema.rb
|
288
286
|
- spec/controllers/scimitar/application_controller_spec.rb
|
289
287
|
- spec/controllers/scimitar/resource_types_controller_spec.rb
|