scimitar 2.9.0 → 2.10.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/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/app/controllers/scimitar/application_controller.rb +4 -5
- data/app/controllers/scimitar/resource_types_controller.rb +7 -1
- data/app/controllers/scimitar/schemas_controller.rb +1 -1
- data/app/models/scimitar/lists/query_parser.rb +10 -10
- data/app/models/scimitar/resource_type.rb +4 -6
- data/app/models/scimitar/resources/base.rb +1 -1
- data/app/models/scimitar/resources/mixin.rb +6 -6
- data/app/models/scimitar/schema/base.rb +1 -1
- data/lib/scimitar/engine.rb +2 -2
- data/lib/scimitar/version.rb +2 -2
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +8 -4
- data/spec/models/scimitar/resources/mixin_spec.rb +1 -1
- data/spec/requests/engine_spec.rb +6 -6
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2e3306d8e05674efa4a53f63e833304623187a3191ecd6ef5685ac03a3ae8a7
|
4
|
+
data.tar.gz: 346c86fe3728208d6e4afb6a2d684a2b46de6326549d1b5d3b1e96622f220dd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 723b88d08085f32e36caca03eceeee22c6653fb9b8fcbe94aa7c35aa6ae37f1de59fef29201ea42a00b4ec01540c3d77375c5d55ea07be4cee1447cdb3cc5e9f
|
7
|
+
data.tar.gz: 00e7ced581373571d0f086a0df9f18334b7b73ae6078ecd095edbfd2b5d7f5c6181515074bbbd49be3c8819531dbad9affa782a7f5087dec7d51a371c79646ea
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -321,7 +321,7 @@ end
|
|
321
321
|
|
322
322
|
#### Other source types
|
323
323
|
|
324
|
-
If you do _not_ use ActiveRecord to store data, or if you have very esoteric read-write requirements, you can subclass [`
|
324
|
+
If you do _not_ use ActiveRecord to store data, or if you have very esoteric read-write requirements, you can subclass [`Scimitar::ResourcesController`](https://www.rubydoc.info/gems/scimitar/Scimitar/ResourcesController) in a manner similar to this:
|
325
325
|
|
326
326
|
```ruby
|
327
327
|
class UsersController < Scimitar::ResourcesController
|
@@ -594,7 +594,7 @@ By default, Scimitar advertises (via things like [the `/Schemas` endpoint](https
|
|
594
594
|
|
595
595
|
```ruby
|
596
596
|
Rails.application.config.to_prepare do
|
597
|
-
Scimitar::Engine
|
597
|
+
Scimitar::Engine.set_default_resources([Scimitar::Resources::User])
|
598
598
|
# ...other Scimitar configuration / initialisation code...
|
599
599
|
end
|
600
600
|
```
|
@@ -9,10 +9,6 @@ module Scimitar
|
|
9
9
|
before_action :add_mandatory_response_headers
|
10
10
|
before_action :authenticate
|
11
11
|
|
12
|
-
if Scimitar.engine_configuration.application_controller_mixin
|
13
|
-
include Scimitar.engine_configuration.application_controller_mixin
|
14
|
-
end
|
15
|
-
|
16
12
|
# =========================================================================
|
17
13
|
# PROTECTED INSTANCE METHODS
|
18
14
|
# =========================================================================
|
@@ -47,7 +43,7 @@ module Scimitar
|
|
47
43
|
#
|
48
44
|
# *exception+:: If a Ruby exception was the reason this method is being
|
49
45
|
# called, pass it here. Any configured exception reporting
|
50
|
-
# mechanism will be
|
46
|
+
# mechanism will be invoked with the given parameter.
|
51
47
|
# Otherwise, the +error_response+ value is reported.
|
52
48
|
#
|
53
49
|
def handle_scim_error(error_response, exception = error_response)
|
@@ -153,5 +149,8 @@ module Scimitar
|
|
153
149
|
return result
|
154
150
|
end
|
155
151
|
|
152
|
+
if Scimitar.engine_configuration.application_controller_mixin
|
153
|
+
include Scimitar.engine_configuration.application_controller_mixin
|
154
|
+
end
|
156
155
|
end
|
157
156
|
end
|
@@ -5,7 +5,13 @@ module Scimitar
|
|
5
5
|
resource.resource_type(scim_resource_type_url(name: resource.resource_type_id))
|
6
6
|
end
|
7
7
|
|
8
|
-
render json:
|
8
|
+
render json: {
|
9
|
+
schemas: [
|
10
|
+
'urn:ietf:params:scim:api:messages:2.0:ListResponse'
|
11
|
+
],
|
12
|
+
totalResults: resource_types.size,
|
13
|
+
Resources: resource_types
|
14
|
+
}
|
9
15
|
end
|
10
16
|
|
11
17
|
def show
|
@@ -201,7 +201,7 @@ module Scimitar
|
|
201
201
|
if mapped_multivalue_attribute.is_a?(Array)
|
202
202
|
|
203
203
|
# A single-entry array with "list using" semantics, for a
|
204
|
-
# collection of an
|
204
|
+
# collection of an arbitrary number of same-class items - e.g.
|
205
205
|
# Groups to which a User belongs.
|
206
206
|
#
|
207
207
|
# If this is an up-to-date mapping, there's a "class" entry that
|
@@ -342,14 +342,14 @@ module Scimitar
|
|
342
342
|
skip_next_component = false
|
343
343
|
|
344
344
|
components.each.with_index do | component, index |
|
345
|
-
if skip_next_component
|
345
|
+
if skip_next_component
|
346
346
|
skip_next_component = false
|
347
347
|
next
|
348
348
|
end
|
349
349
|
|
350
350
|
downcased = component.downcase.strip
|
351
351
|
|
352
|
-
if
|
352
|
+
if expecting_attribute
|
353
353
|
if downcased.match?(/[^\\]\[/) # Not backslash then literal '['
|
354
354
|
attribute_prefix = component.match(/(.*?[^\\])\[/ )[1] # Everything before no-backslash-then-literal (unescaped) '['
|
355
355
|
first_attribute_inside = component.match( /[^\\]\[(.*)/)[1] # Everything after no-backslash-then-literal (unescaped) '['
|
@@ -362,7 +362,7 @@ module Scimitar
|
|
362
362
|
expecting_attribute = false
|
363
363
|
expecting_operator = true
|
364
364
|
|
365
|
-
elsif
|
365
|
+
elsif expecting_operator
|
366
366
|
rewritten << component
|
367
367
|
if BINARY_OPERATORS.include?(downcased)
|
368
368
|
expecting_operator = false
|
@@ -374,7 +374,7 @@ module Scimitar
|
|
374
374
|
raise 'Expected operator'
|
375
375
|
end
|
376
376
|
|
377
|
-
elsif
|
377
|
+
elsif expecting_value
|
378
378
|
matches = downcased.match(/([^\\])\](.*)/) # Contains no-backslash-then-literal (unescaped) ']'; also capture anything after
|
379
379
|
unless matches.nil? # Contains no-backslash-then-literal (unescaped) ']'
|
380
380
|
character_before_closing_bracket = matches[1]
|
@@ -395,7 +395,7 @@ module Scimitar
|
|
395
395
|
# So - NOTE RECURSION AND EARLY EXIT POSSIBILITY HEREIN.
|
396
396
|
#
|
397
397
|
if (
|
398
|
-
!
|
398
|
+
!attribute_prefix.nil? &&
|
399
399
|
OPERATORS.key?(components[index + 1]&.downcase) &&
|
400
400
|
characters_after_closing_bracket.match?(/^\.#{ATTRNAME}$/)
|
401
401
|
)
|
@@ -437,7 +437,7 @@ module Scimitar
|
|
437
437
|
if downcased.start_with?('"')
|
438
438
|
expecting_closing_quote = true
|
439
439
|
downcased = downcased[1..-1] # Strip off opening '"' to avoid false-positive on 'contains closing quote' check below
|
440
|
-
elsif expecting_closing_quote
|
440
|
+
elsif !expecting_closing_quote # If not expecting a closing quote, then the component must be the entire no-spaces value
|
441
441
|
expecting_value = false
|
442
442
|
expecting_logic_word = true
|
443
443
|
end
|
@@ -450,7 +450,7 @@ module Scimitar
|
|
450
450
|
end
|
451
451
|
end
|
452
452
|
|
453
|
-
elsif
|
453
|
+
elsif expecting_logic_word
|
454
454
|
if downcased == 'and' || downcased == 'or'
|
455
455
|
rewritten << component
|
456
456
|
next_downcased_component = components[index + 1].downcase.strip
|
@@ -586,11 +586,11 @@ module Scimitar
|
|
586
586
|
|
587
587
|
# Recursively process an expression tree. Calls itself with nested tree
|
588
588
|
# fragments. Each inner expression fragment calculates on the given
|
589
|
-
# base scope, with
|
589
|
+
# base scope, with aggregation at each level into a wider query using
|
590
590
|
# AND or OR depending on the expression tree contents.
|
591
591
|
#
|
592
592
|
# +base_scope+:: Base scope (ActiveRecord::Relation, e.g. User.all
|
593
|
-
# -
|
593
|
+
# - never changes during recursion).
|
594
594
|
#
|
595
595
|
# +expression_tree+:: Top-level expression tree or fragments inside if
|
596
596
|
# self-calling recursively.
|
@@ -748,7 +748,7 @@ module Scimitar
|
|
748
748
|
|
749
749
|
# Returns the mapped-to-your-domain column name(s) that a filter string
|
750
750
|
# is operating upon, in an Array. If empty, the attribute is to be
|
751
|
-
# ignored. Raises an exception if
|
751
|
+
# ignored. Raises an exception if entirely unmapped (thus unsupported).
|
752
752
|
#
|
753
753
|
# Note plural - the return value is always an array any of which should
|
754
754
|
# be used (implicit 'OR').
|
@@ -17,12 +17,10 @@ module Scimitar
|
|
17
17
|
|
18
18
|
def as_json(options = {})
|
19
19
|
without_extensions = super(except: 'schemaExtensions')
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
without_extensions
|
25
|
-
end
|
20
|
+
return without_extensions unless schemaExtensions.present? # NOTE EARLY EXIT
|
21
|
+
|
22
|
+
extensions = schemaExtensions.map{|extension| {"schema" => extension, "required" => false}}
|
23
|
+
without_extensions.merge('schemaExtensions' => extensions)
|
26
24
|
end
|
27
25
|
|
28
26
|
end
|
@@ -158,7 +158,7 @@ module Scimitar
|
|
158
158
|
hash.with_indifferent_access.each_pair do |attr_name, attr_value|
|
159
159
|
scim_attribute = self.class.complex_scim_attributes[attr_name].try(:first)
|
160
160
|
|
161
|
-
if scim_attribute
|
161
|
+
if scim_attribute&.complexType
|
162
162
|
if scim_attribute.multiValued
|
163
163
|
self.send("#{attr_name}=", attr_value&.map {|attr_for_each_item| complex_type_from_hash(scim_attribute, attr_for_each_item)})
|
164
164
|
else
|
@@ -473,7 +473,7 @@ module Scimitar
|
|
473
473
|
path_str = operation['path' ]
|
474
474
|
value = operation['value']
|
475
475
|
|
476
|
-
unless [
|
476
|
+
unless %w[add remove replace].include?(nature)
|
477
477
|
raise Scimitar::InvalidSyntaxError.new("Unrecognised PATCH \"op\" value of \"#{nature}\"")
|
478
478
|
end
|
479
479
|
|
@@ -613,7 +613,7 @@ module Scimitar
|
|
613
613
|
built_dynamic_list = false
|
614
614
|
mapped_array = attrs_map_or_leaf_value.map do |value|
|
615
615
|
if ! value.is_a?(Hash)
|
616
|
-
raise 'Bad attribute map: Array contains
|
616
|
+
raise 'Bad attribute map: Array contains something other than mapping Hash(es)'
|
617
617
|
|
618
618
|
elsif value.key?(:match) # Static map
|
619
619
|
static_hash = { value[:match] => value[:with] }
|
@@ -741,7 +741,7 @@ module Scimitar
|
|
741
741
|
# +path+:: Array of SCIM attribute names giving a
|
742
742
|
# path into the SCIM schema where
|
743
743
|
# iteration has reached. Used to find the
|
744
|
-
# schema attribute
|
744
|
+
# schema attribute definition and check
|
745
745
|
# mutability before writing.
|
746
746
|
#
|
747
747
|
def from_scim_backend!(
|
@@ -1088,7 +1088,7 @@ module Scimitar
|
|
1088
1088
|
# associated collection or clearing a local model attribute
|
1089
1089
|
# directly to "nil").
|
1090
1090
|
#
|
1091
|
-
|
1091
|
+
unless handled
|
1092
1092
|
current_data_at_path[matched_index] = nil
|
1093
1093
|
compact_after = true
|
1094
1094
|
end
|
@@ -1290,7 +1290,7 @@ module Scimitar
|
|
1290
1290
|
end
|
1291
1291
|
end
|
1292
1292
|
|
1293
|
-
|
1293
|
+
unless handled
|
1294
1294
|
altering_hash[path_component] = []
|
1295
1295
|
end
|
1296
1296
|
|
@@ -1445,7 +1445,7 @@ module Scimitar
|
|
1445
1445
|
# { value: :work_email }
|
1446
1446
|
#
|
1447
1447
|
# If there was a SCIM entry with a type of something unrecognised,
|
1448
|
-
# such as '
|
1448
|
+
# such as 'holiday', then +nil+ would be returned since there is no
|
1449
1449
|
# matching attribute map entry.
|
1450
1450
|
#
|
1451
1451
|
# Note that the <tt>:with_attr_map</tt> array can contain dynamic
|
@@ -36,7 +36,7 @@ module Scimitar
|
|
36
36
|
scim_attributes.map { |scim_attribute| scim_attribute.clone }
|
37
37
|
end
|
38
38
|
|
39
|
-
# Find a given attribute this schema, travelling down a path to any
|
39
|
+
# Find a given attribute of this schema, travelling down a path to any
|
40
40
|
# sub-attributes within. Given that callers might be dealing with paths
|
41
41
|
# into actual SCIM data, array indices for multi-value attributes are
|
42
42
|
# allowed (as integers) and simply skipped - only the names are of
|
data/lib/scimitar/engine.rb
CHANGED
@@ -88,9 +88,9 @@ module Scimitar
|
|
88
88
|
unrecognised_resources = resource_array - @standard_default_resources
|
89
89
|
|
90
90
|
if unrecognised_resources.any?
|
91
|
-
raise "Scimitar::Engine
|
91
|
+
raise "Scimitar::Engine.set_default_resources: Only #{@standard_default_resources.map(&:name).join(', ')} are supported"
|
92
92
|
elsif resource_array.empty?
|
93
|
-
raise 'Scimitar::Engine
|
93
|
+
raise 'Scimitar::Engine.set_default_resources: At least one resource must be given'
|
94
94
|
end
|
95
95
|
|
96
96
|
@default_resources = resource_array
|
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.10.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 = '2024-
|
11
|
+
DATE = '2024-10-22'
|
12
12
|
|
13
13
|
end
|
@@ -9,11 +9,15 @@ RSpec.describe Scimitar::ResourceTypesController do
|
|
9
9
|
it 'renders the resource type for user' do
|
10
10
|
get :index, format: :scim
|
11
11
|
response_hash = JSON.parse(response.body)
|
12
|
-
expected_response =
|
13
|
-
|
14
|
-
|
12
|
+
expected_response = {
|
13
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
|
14
|
+
totalResults: 2,
|
15
|
+
Resources: [
|
16
|
+
Scimitar::Resources::User.resource_type(scim_resource_type_url(name: 'User', test: 1)),
|
17
|
+
Scimitar::Resources::Group.resource_type(scim_resource_type_url(name: 'Group', test: 1))
|
18
|
+
]
|
19
|
+
}.to_json
|
15
20
|
|
16
|
-
response_hash = JSON.parse(response.body)
|
17
21
|
expect(response_hash).to eql(JSON.parse(expected_response))
|
18
22
|
end
|
19
23
|
|
@@ -555,7 +555,7 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
555
555
|
|
556
556
|
expect do
|
557
557
|
scim = instance.to_scim(location: 'https://test.com/static_map_test')
|
558
|
-
end.to raise_error(RuntimeError) { |e| expect(e.message).to include('Array contains
|
558
|
+
end.to raise_error(RuntimeError) { |e| expect(e.message).to include('Array contains something other than mapping Hash(es)') }
|
559
559
|
end
|
560
560
|
|
561
561
|
it 'complains about bad Hash entries in mapping Arrays' do
|
@@ -78,26 +78,26 @@ RSpec.describe Scimitar::Engine do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'notes changes to defaults' do
|
81
|
-
Scimitar::Engine
|
81
|
+
Scimitar::Engine.set_default_resources([Scimitar::Resources::User])
|
82
82
|
expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User])
|
83
83
|
end
|
84
84
|
|
85
85
|
it 'notes changes to defaults with custom resources added' do
|
86
|
-
Scimitar::Engine
|
86
|
+
Scimitar::Engine.set_default_resources([Scimitar::Resources::User])
|
87
87
|
Scimitar::Engine.add_custom_resource(@license_resource)
|
88
88
|
expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User, @license_resource])
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'rejects bad defaults' do
|
92
92
|
expect {
|
93
|
-
Scimitar::Engine
|
94
|
-
}.to raise_error('Scimitar::Engine
|
93
|
+
Scimitar::Engine.set_default_resources([@license_resource])
|
94
|
+
}.to raise_error('Scimitar::Engine.set_default_resources: Only Scimitar::Resources::User, Scimitar::Resources::Group are supported')
|
95
95
|
end
|
96
96
|
|
97
97
|
it 'rejects empty defaults' do
|
98
98
|
expect {
|
99
|
-
Scimitar::Engine
|
100
|
-
}.to raise_error('Scimitar::Engine
|
99
|
+
Scimitar::Engine.set_default_resources([])
|
100
|
+
}.to raise_error('Scimitar::Engine.set_default_resources: At least one resource must be given')
|
101
101
|
end
|
102
102
|
end # "context '::resources, :add_custom_resource, ::set_default_resources' do"
|
103
103
|
|
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.10.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: 2024-
|
12
|
+
date: 2024-10-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -101,14 +101,14 @@ dependencies:
|
|
101
101
|
requirements:
|
102
102
|
- - "~>"
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
104
|
+
version: '7.0'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
109
|
- - "~>"
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
111
|
+
version: '7.0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: doggo
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -267,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
267
|
- !ruby/object:Gem::Version
|
268
268
|
version: '0'
|
269
269
|
requirements: []
|
270
|
-
rubygems_version: 3.5.
|
270
|
+
rubygems_version: 3.5.16
|
271
271
|
signing_key:
|
272
272
|
specification_version: 4
|
273
273
|
summary: SCIM v2 for Rails
|