scimitar 2.13.0 → 2.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 172c9b71a2da57bcc0b0ebc02c551a5ed8e70c7efda9fcbb01a6cea7b99f6c6b
4
- data.tar.gz: 0d6fc7e2dcbe4fabcef481c197b96c6f757af8cc5f1ad8b5990a13c346d39517
3
+ metadata.gz: c5f63ca5b1572be0cb53a95a47280099713fd32be8f5650396a0db6a0c92550e
4
+ data.tar.gz: 2f1f2a7ec7a6877607875f6b86e7b9859ae9124f71d295541847940dd31c8b1f
5
5
  SHA512:
6
- metadata.gz: 060e4aff1aa65516fef31e8c0403dfda82d2b015e990bcfed7b45e78830b8f545db30c8abe915126e4a760e62c4f5f34660b19b0d30f1815cc6575406961f9c4
7
- data.tar.gz: 85659a28c38997bbb937fe153b48f77c4fe75f98d6c6dbad82006d8cd30d029db5f5e7e33e7fd614ee2630aba2ec558338d9de4e888be884fa11eb1159c261a4
6
+ metadata.gz: '08626642f4b3e9dd29a6141c72bea784ab094ec197d9f3b9278957a59bfe230f4ef7ec84d52b1584127af647809351607bc01ef408d7269cc39dcf1f17180d49'
7
+ data.tar.gz: 53845ba684f54f8e2e90e696b769aadfe803809fa3004fa35f779794d6e04bfa034ee231847382591cdc5600cc7544bbad4c959e2031c0271e67e5632d431f6c
data/README.md CHANGED
@@ -64,7 +64,7 @@ Some aspects of configuration are handled via a `config/initializers/scimitar.rb
64
64
  Rails.application.config.to_prepare do
65
65
  Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
66
66
  # ...see subsections below for configuration options...
67
- end
67
+ })
68
68
  end
69
69
  ```
70
70
 
@@ -184,7 +184,36 @@ Note that Okta has some [curious documentation on its use of `POST` vs `PATCH` f
184
184
 
185
185
  ### Google Workspace note
186
186
 
187
- Using SCIM with Google Workspace might only work for a subset of applications. Since web UIs for major service providers change very often, it doesn't make sense to provide extensive documentation here as it would get out of date quickly; you may have to figure out the setup as best you can using whatever current Google documentation exists for their system. There are [some notes which were relevant around mid-2025](https://github.com/pond/scimitar/issues/142#issuecomment-2699050541) (from when a workarond/fix was incorporated into Scimitar to allow it to work with Google Workspace) which may help you get started.
187
+ Using SCIM with Google Workspace might only work for a subset of applications. Since web UIs for major service providers change very often, it doesn't make sense to provide extensive documentation here as it would get out of date quickly; you may have to figure out the setup as best you can using whatever current Google documentation exists for their system. There are [some notes which were relevant around mid-2025](https://github.com/pond/scimitar/issues/142#issuecomment-2699050541) (from when a workaround/fix was incorporated into Scimitar to allow it to work with Google Workspace) which may help you get started.
188
+
189
+ ### Request content type handling
190
+
191
+ The correct content type for SCIM is `application/scim+json`. Scimitar tolerates some variants of this, rewriting things internally so that the request continues to be processed by the rest of the gem (including ending up in subclass code you write) under this media type, with a Rails request format of `:scim`. Sometimes, callers into SCIM endpoints might use a content type that Scimitar rejects. If so, you can configure a custom request sanitizer Proc (with a "z", in keeping with Rails spelling of "sanitize"):
192
+
193
+ ```ruby
194
+ Rails.application.config.to_prepare do
195
+ Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
196
+
197
+ custom_request_sanitizer: Proc.new do | request |
198
+ #
199
+ # Examine e.g. 'request.media_type' and evaluate to a Symbol:
200
+ #
201
+ # :success - set the standard content type and ensure the Rails request
202
+ # format is :scim; continue processing normally
203
+ #
204
+ # :preserve - retain the existing content type and Rails request format;
205
+ # continue processing normally
206
+ #
207
+ # :fail - the request format appears to be invalid; generate a 406
208
+ # ("Not Acceptable") response
209
+ #
210
+ end
211
+
212
+ })
213
+ end
214
+ ```
215
+
216
+ The Proc is passed the [`ActionDispatch::Request`](https://api.rubyonrails.org/classes/ActionDispatch/Request.html) instance for the current request. It **must** evaluable to a Symbol as shown above. Typically, `:preserve` is only for very special use cases where you understand that the request headers and/or format might not match what other parts of Scimitar expect, but have written appropriate custom code elsewhere to deal with that.
188
217
 
189
218
  ### Data models
190
219
 
@@ -93,19 +93,37 @@ module Scimitar
93
93
  #
94
94
  def require_scim
95
95
  scim_mime_type = Mime::Type.lookup_by_extension(:scim).to_s
96
+ failure_detail = "Only #{scim_mime_type} type is accepted."
97
+
98
+ if Scimitar.engine_configuration.custom_request_sanitizer.is_a?(Proc)
99
+
100
+ result = Scimitar.engine_configuration.custom_request_sanitizer.call(request)
101
+ case result
102
+ when :fail
103
+ handle_scim_error(ErrorResponse.new(status: 406, detail: failure_detail))
104
+ when :preserve
105
+ # Do nothing
106
+ else
107
+ request.format = :scim
108
+ request.headers['CONTENT_TYPE'] = scim_mime_type
109
+ end
110
+
111
+ else # "if Scimitar.engine_configuration.custom_request_sanitizer.present?"
112
+
113
+ if request.media_type.nil? || request.media_type.empty?
114
+ request.format = :scim
115
+ request.headers['CONTENT_TYPE'] = scim_mime_type
116
+ elsif request.media_type.downcase == scim_mime_type
117
+ request.format = :scim
118
+ elsif request.format == :scim
119
+ request.headers['CONTENT_TYPE'] = scim_mime_type
120
+ elsif request.media_type.downcase == 'application/json' && request.user_agent&.start_with?('Google') # https://github.com/pond/scimitar/issues/142
121
+ request.format = :scim
122
+ request.headers["CONTENT_TYPE"] = scim_mime_type
123
+ else
124
+ handle_scim_error(ErrorResponse.new(status: 406, detail: failure_detail))
125
+ end
96
126
 
97
- if request.media_type.nil? || request.media_type.empty?
98
- request.format = :scim
99
- request.headers['CONTENT_TYPE'] = scim_mime_type
100
- elsif request.media_type.downcase == scim_mime_type
101
- request.format = :scim
102
- elsif request.format == :scim
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
107
- else
108
- handle_scim_error(ErrorResponse.new(status: 406, detail: "Only #{scim_mime_type} type is accepted."))
109
127
  end
110
128
  end
111
129
 
@@ -12,6 +12,7 @@ module Scimitar
12
12
  :basic_authenticator,
13
13
  :token_authenticator,
14
14
  :custom_authenticator,
15
+ :custom_request_sanitizer,
15
16
  :application_controller_mixin,
16
17
  :exception_reporter,
17
18
  :optional_value_fields_required,
@@ -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.13.0'
6
+ VERSION = '2.14.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-09-12'
11
+ DATE = '2025-11-14'
12
12
 
13
13
  end
@@ -435,6 +435,61 @@ RSpec.describe Scimitar::ApplicationController do
435
435
  expect(@exception.message).to eql('Only application/scim+json type is accepted.')
436
436
  end
437
437
  end # "context 'and with Google SCIM calls' do"
438
+
439
+ context 'and with a custom request sanitizer' do
440
+ around :each do | example |
441
+ original_configuration = Scimitar.engine_configuration.custom_request_sanitizer
442
+ Scimitar.engine_configuration.custom_request_sanitizer = Proc.new do | request |
443
+ case request.media_type
444
+ when 'application/json+success'
445
+ :success
446
+ when 'application/json+preserve'
447
+ :preserve
448
+ else
449
+ :fail
450
+ end
451
+ end
452
+ example.run()
453
+ ensure
454
+ Scimitar.engine_configuration.custom_request_sanitizer = original_configuration
455
+ end
456
+
457
+ context 'returning "success"' do
458
+ it 'reaches the controller action with cleaned up request data' do
459
+ request.headers['Content-Type'] = 'application/json+success'
460
+ get :index
461
+
462
+ expect(@exception).to be_a(RuntimeError)
463
+ expect(@exception.message).to eql('Bang')
464
+
465
+ expect(request.format == :scim).to eql(true)
466
+ expect(request.headers['CONTENT_TYPE']).to eql('application/scim+json')
467
+ end
468
+ end # "context 'returning "success"' do"
469
+
470
+ context 'returning "preserve"' do
471
+ it 'reaches the controller action with unmodified request data' do
472
+ request.headers['Content-Type'] = 'application/json+preserve'
473
+ get :index
474
+
475
+ expect(@exception).to be_a(RuntimeError)
476
+ expect(@exception.message).to eql('Bang')
477
+
478
+ expect(request.format == :html).to eql(true)
479
+ expect(request.headers['CONTENT_TYPE']).to eql('application/json+preserve')
480
+ end
481
+ end # "context 'returning "keep"' do"
482
+
483
+ context 'returning "fail"' do
484
+ it 'is invoked' do
485
+ request.headers['Content-Type'] = 'application/json+fail'
486
+ get :index
487
+
488
+ expect(@exception).to be_a(Scimitar::ErrorResponse)
489
+ expect(@exception.message).to eql('Only application/scim+json type is accepted.')
490
+ end
491
+ end # "context 'returning "fail"' do"
492
+ end # "context 'and with a custom request sanitizer' do"
438
493
  end # "context 'exception reporter' do"
439
494
  end # "context 'error handling' do"
440
495
  end
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.13.0
4
+ version: 2.14.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-09-12 00:00:00.000000000 Z
11
+ date: 2025-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '13.2'
47
+ version: '13.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '13.2'
54
+ version: '13.3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pg
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '6.14'
89
+ version: '6.15'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '6.14'
96
+ version: '6.15'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: warden
99
99
  requirement: !ruby/object:Gem::Requirement