scimitar 2.12.0 → 2.13.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/README.md +3 -3
- data/app/controllers/scimitar/application_controller.rb +3 -0
- data/app/models/scimitar/resources/mixin.rb +18 -4
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/app/controllers/mock_batch_groups_controller.rb +13 -0
- data/spec/apps/dummy/app/models/mock_group_batch.rb +20 -0
- data/spec/apps/dummy/config/routes.rb +6 -0
- data/spec/controllers/scimitar/application_controller_spec.rb +19 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +33 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 172c9b71a2da57bcc0b0ebc02c551a5ed8e70c7efda9fcbb01a6cea7b99f6c6b
|
4
|
+
data.tar.gz: 0d6fc7e2dcbe4fabcef481c197b96c6f757af8cc5f1ad8b5990a13c346d39517
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 060e4aff1aa65516fef31e8c0403dfda82d2b015e990bcfed7b45e78830b8f545db30c8abe915126e4a760e62c4f5f34660b19b0d30f1815cc6575406961f9c4
|
7
|
+
data.tar.gz: 85659a28c38997bbb937fe153b48f77c4fe75f98d6c6dbad82006d8cd30d029db5f5e7e33e7fd614ee2630aba2ec558338d9de4e888be884fa11eb1159c261a4
|
data/README.md
CHANGED
@@ -123,7 +123,7 @@ Here's an example where Warden is being used for authentication, with Warden sto
|
|
123
123
|
|
124
124
|
```ruby
|
125
125
|
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
126
|
-
|
126
|
+
custom_authenticator: Proc.new do
|
127
127
|
|
128
128
|
# In this example we catch the Warden 'throw' for failed authentication, as
|
129
129
|
# well as allowing Warden to successfully find an *authenticated* user, but
|
@@ -255,8 +255,8 @@ class User < ActiveRecord::Base
|
|
255
255
|
],
|
256
256
|
|
257
257
|
# NB The 'groups' collection in a SCIM User resource is read-only, so
|
258
|
-
# we provide no ":find_with" key for looking up
|
259
|
-
# updates to the associated collection.
|
258
|
+
# we provide no ":find_with" or ":find_all_with" key for looking up
|
259
|
+
# records for writing updates to the associated collection.
|
260
260
|
#
|
261
261
|
groups: [
|
262
262
|
{
|
@@ -101,6 +101,9 @@ module Scimitar
|
|
101
101
|
request.format = :scim
|
102
102
|
elsif request.format == :scim
|
103
103
|
request.headers['CONTENT_TYPE'] = scim_mime_type
|
104
|
+
elsif request.media_type.downcase == 'application/json' && request.user_agent.start_with?('Google') # https://github.com/pond/scimitar/issues/142
|
105
|
+
request.format = :scim
|
106
|
+
request.headers["CONTENT_TYPE"] = scim_mime_type
|
104
107
|
else
|
105
108
|
handle_scim_error(ErrorResponse.new(status: 406, detail: "Only #{scim_mime_type} type is accepted."))
|
106
109
|
end
|
@@ -145,7 +145,8 @@ module Scimitar
|
|
145
145
|
# display: :full_name # <-- i.e. Team.users[n].full_name
|
146
146
|
# },
|
147
147
|
# class: Team, # Optional; see below
|
148
|
-
# find_with:
|
148
|
+
# find_with: -> (scim_list_entry) {...}, # See below
|
149
|
+
# find_all_with: -> (scim_list_entries) {...} # Optional, See below
|
149
150
|
# }
|
150
151
|
# ],
|
151
152
|
# #...
|
@@ -165,6 +166,11 @@ module Scimitar
|
|
165
166
|
# Scimitar::EngineConfiguration::schema_list_from_attribute_mappings is
|
166
167
|
# defined; see documentation of that option for more information.
|
167
168
|
#
|
169
|
+
# To avoid N+1 queries when resolving many entries (e.g. Group members
|
170
|
+
# during PATCH), you can instead provide ":find_all_with" which is passed
|
171
|
+
# the entire Array of SCIM entries and should return an Array of resolved
|
172
|
+
# model instances. If both are provided, ":find_all_with" is preferred.
|
173
|
+
#
|
168
174
|
# Note that you can only use either:
|
169
175
|
#
|
170
176
|
# * One or more static maps where each matches some other piece of source
|
@@ -315,7 +321,7 @@ module Scimitar
|
|
315
321
|
enum.each do | static_or_dynamic_mapping |
|
316
322
|
if static_or_dynamic_mapping.key?(:match) # Static
|
317
323
|
extractor.call(static_or_dynamic_mapping[:using])
|
318
|
-
elsif static_or_dynamic_mapping.key?(:find_with) # Dynamic
|
324
|
+
elsif static_or_dynamic_mapping.key?(:find_with) || static_or_dynamic_mapping.key?(:find_all_with) # Dynamic
|
319
325
|
@scim_mutable_attributes << static_or_dynamic_mapping[:list]
|
320
326
|
end
|
321
327
|
end
|
@@ -839,9 +845,17 @@ module Scimitar
|
|
839
845
|
method = "#{mapped_array_entry[:list]}="
|
840
846
|
|
841
847
|
if (attribute&.mutability == 'readWrite' || attribute&.mutability == 'writeOnly') && self.respond_to?(method)
|
842
|
-
|
848
|
+
find_all_with_proc = mapped_array_entry[:find_all_with]
|
849
|
+
find_with_proc = mapped_array_entry[:find_with]
|
850
|
+
|
851
|
+
if find_all_with_proc.respond_to?(:call)
|
852
|
+
scim_entries = (scim_hash_or_leaf_value || [])
|
853
|
+
mapped_list = find_all_with_proc.call(scim_entries) || []
|
843
854
|
|
844
|
-
|
855
|
+
mapped_list.compact!
|
856
|
+
|
857
|
+
self.public_send(method, mapped_list)
|
858
|
+
elsif find_with_proc.respond_to?(:call)
|
845
859
|
mapped_list = (scim_hash_or_leaf_value || []).map do | source_list_entry |
|
846
860
|
find_with_proc.call(source_list_entry)
|
847
861
|
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.13.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 = '2025-
|
11
|
+
DATE = '2025-09-12'
|
12
12
|
|
13
13
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class MockGroupBatch < MockGroup
|
2
|
+
def self.scim_attributes_map
|
3
|
+
{
|
4
|
+
id: :id,
|
5
|
+
externalId: :scim_uid,
|
6
|
+
displayName: :display_name,
|
7
|
+
members: [
|
8
|
+
{
|
9
|
+
list: :scim_users_and_groups,
|
10
|
+
using: { value: :id },
|
11
|
+
# Minimal mock: assume user-only entries (type omitted => User)
|
12
|
+
find_all_with: -> (entries) do
|
13
|
+
ids = entries.map { |e| e['value'] }
|
14
|
+
MockUser.where(primary_key: ids).to_a
|
15
|
+
end
|
16
|
+
}
|
17
|
+
]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
@@ -17,6 +17,12 @@ Rails.application.routes.draw do
|
|
17
17
|
get 'Groups/:id', to: 'mock_groups#show'
|
18
18
|
patch 'Groups/:id', to: 'mock_groups#update'
|
19
19
|
|
20
|
+
# Batch lookup variant for testing the mixin 'find_all_with' option.
|
21
|
+
#
|
22
|
+
get 'BatchGroups', to: 'mock_batch_groups#index'
|
23
|
+
get 'BatchGroups/:id', to: 'mock_batch_groups#show'
|
24
|
+
patch 'BatchGroups/:id', to: 'mock_batch_groups#update'
|
25
|
+
|
20
26
|
# For testing blocks passed to ActiveRecordBackedResourcesController#create,
|
21
27
|
# #update, #replace and #destroy.
|
22
28
|
#
|
@@ -416,6 +416,25 @@ RSpec.describe Scimitar::ApplicationController do
|
|
416
416
|
expect(@exception.message).to eql('Only application/scim+json type is accepted.')
|
417
417
|
end
|
418
418
|
end
|
419
|
+
|
420
|
+
context 'and with Google SCIM calls' do
|
421
|
+
it 'reaches the controller action if the expected agent is making the request' do
|
422
|
+
request.headers['Content-Type'] = 'application/json'
|
423
|
+
request.headers['User-Agent' ] = 'Google-Auto-Provisioning'
|
424
|
+
get :index
|
425
|
+
|
426
|
+
expect(@exception).to be_a(RuntimeError)
|
427
|
+
expect(@exception.message).to eql('Bang')
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'is invoked early for unrecognised agents' do
|
431
|
+
request.headers['Content-Type'] = 'application/json'
|
432
|
+
get :index
|
433
|
+
|
434
|
+
expect(@exception).to be_a(Scimitar::ErrorResponse)
|
435
|
+
expect(@exception.message).to eql('Only application/scim+json type is accepted.')
|
436
|
+
end
|
437
|
+
end # "context 'and with Google SCIM calls' do"
|
419
438
|
end # "context 'exception reporter' do"
|
420
439
|
end # "context 'error handling' do"
|
421
440
|
end
|
@@ -1137,6 +1137,39 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
1137
1137
|
expect(@u2.password).to eql('correcthorsebatterystaple')
|
1138
1138
|
end
|
1139
1139
|
|
1140
|
+
context "when updating group members using :find_all_with" do
|
1141
|
+
it "uses :find_all_with to batch-resolve users and updates associations" do
|
1142
|
+
payload = {
|
1143
|
+
schemas: [ 'urn:ietf:params:scim:api:messages:2.0:PatchOp' ],
|
1144
|
+
Operations: [
|
1145
|
+
{
|
1146
|
+
op: 'add',
|
1147
|
+
path: 'members',
|
1148
|
+
value: [
|
1149
|
+
{ 'value' => @u1.primary_key },
|
1150
|
+
{ 'value' => @u2.primary_key }
|
1151
|
+
]
|
1152
|
+
}
|
1153
|
+
]
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
1157
|
+
|
1158
|
+
patch "/BatchGroups/#{@g1.id}", params: payload.merge({ format: :scim })
|
1159
|
+
|
1160
|
+
expect(response.status).to eql(200)
|
1161
|
+
|
1162
|
+
# Verify membership updated
|
1163
|
+
get "/BatchGroups/#{@g1.id}", params: { format: :scim }
|
1164
|
+
expect(response.status).to eql(200)
|
1165
|
+
result = JSON.parse(response.body)
|
1166
|
+
|
1167
|
+
values = result.fetch('members', []).map { |m| m['value'] }
|
1168
|
+
expect(values).to include(@u1.primary_key.to_s)
|
1169
|
+
expect(values).to include(@u2.primary_key.to_s)
|
1170
|
+
end
|
1171
|
+
end
|
1172
|
+
|
1140
1173
|
context 'which clears attributes' do
|
1141
1174
|
before :each do
|
1142
1175
|
@u2.update!(work_email_address: 'work_2@test.com')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scimitar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RIPA Global
|
8
8
|
- Andrew David Hodgkinson
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -230,9 +230,11 @@ files:
|
|
230
230
|
- spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
|
231
231
|
- spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb
|
232
232
|
- spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb
|
233
|
+
- spec/apps/dummy/app/controllers/mock_batch_groups_controller.rb
|
233
234
|
- spec/apps/dummy/app/controllers/mock_groups_controller.rb
|
234
235
|
- spec/apps/dummy/app/controllers/mock_users_controller.rb
|
235
236
|
- spec/apps/dummy/app/models/mock_group.rb
|
237
|
+
- spec/apps/dummy/app/models/mock_group_batch.rb
|
236
238
|
- spec/apps/dummy/app/models/mock_user.rb
|
237
239
|
- spec/apps/dummy/config/application.rb
|
238
240
|
- spec/apps/dummy/config/boot.rb
|
@@ -304,9 +306,11 @@ test_files:
|
|
304
306
|
- spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
|
305
307
|
- spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb
|
306
308
|
- spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb
|
309
|
+
- spec/apps/dummy/app/controllers/mock_batch_groups_controller.rb
|
307
310
|
- spec/apps/dummy/app/controllers/mock_groups_controller.rb
|
308
311
|
- spec/apps/dummy/app/controllers/mock_users_controller.rb
|
309
312
|
- spec/apps/dummy/app/models/mock_group.rb
|
313
|
+
- spec/apps/dummy/app/models/mock_group_batch.rb
|
310
314
|
- spec/apps/dummy/app/models/mock_user.rb
|
311
315
|
- spec/apps/dummy/config/application.rb
|
312
316
|
- spec/apps/dummy/config/boot.rb
|