scimitar 2.9.0 → 2.11.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 +18 -14
- 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/controllers/scimitar/service_provider_configurations_controller.rb +3 -1
- data/app/models/scimitar/complex_types/base.rb +2 -2
- data/app/models/scimitar/lists/query_parser.rb +13 -13
- 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 +9 -9
- data/app/models/scimitar/schema/attribute.rb +4 -3
- data/app/models/scimitar/schema/base.rb +1 -1
- data/lib/scimitar/engine.rb +2 -2
- data/lib/scimitar/support/utilities.rb +2 -2
- data/lib/scimitar/version.rb +2 -2
- data/spec/apps/dummy/config/application.rb +8 -0
- data/spec/apps/dummy/config/initializers/scimitar.rb +2 -2
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +8 -4
- data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +1 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +2 -2
- data/spec/models/scimitar/resources/mixin_spec.rb +1 -1
- data/spec/requests/active_record_backed_resources_controller_spec.rb +2 -2
- data/spec/requests/engine_spec.rb +6 -6
- metadata +19 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cb5109af12b712e491949d6627588dff6a4d70877253be26c4ee0af3b56c61e
|
4
|
+
data.tar.gz: 4271bb70e89fd17b97103073bc063de748e261b62b243fa0681b3e2934085f77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aed187b7c9cc56fb1b0c3bae86b3274c6753078a71fb4fa6162776c056033edf19c085b830cfc3867acf7ab4e12732a6fe8100d758bd107840edd2e26c068aa0
|
7
|
+
data.tar.gz: f753d11931026a98c27d27525e88e512ee5a40d1920533243b352f6345ddb285de8efb38d6101afd360156b618365805c4a9bc440941701d7e9cd9cee5488810
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -60,7 +60,7 @@ Scimitar is best used with Rails and ActiveRecord, but it can be used with other
|
|
60
60
|
|
61
61
|
### Authentication
|
62
62
|
|
63
|
-
Noting the _Security_ section later - to set up an authentication method, create a `config/initializers/scimitar.rb` in your Rails application and define a token-based authenticator and/or a username-password authenticator in the [engine configuration section documented in the sample file](https://github.com/
|
63
|
+
Noting the _Security_ section later - to set up an authentication method, create a `config/initializers/scimitar.rb` in your Rails application and define a token-based authenticator and/or a username-password authenticator in the [engine configuration section documented in the sample file](https://github.com/pond/scimitar/blob/main/config/initializers/scimitar.rb). For example:
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
@@ -112,15 +112,19 @@ All routes then will be available at `https://.../scim_v2/...` via controllers y
|
|
112
112
|
|
113
113
|
#### URL helpers
|
114
114
|
|
115
|
-
Internally Scimitar always invokes URL helpers in the controller layer. I.e. any variable path parameters will be resolved by Rails automatically. If you need more control over the way URLs are generated you can override any URL helper by redefining it in the application controller mixin. See the [`application_controller_mixin` engine configuration option](https://github.com/
|
115
|
+
Internally Scimitar always invokes URL helpers in the controller layer. I.e. any variable path parameters will be resolved by Rails automatically. If you need more control over the way URLs are generated you can override any URL helper by redefining it in the application controller mixin. See the [`application_controller_mixin` engine configuration option](https://github.com/pond/scimitar/blob/main/config/initializers/scimitar.rb).
|
116
|
+
|
117
|
+
#### Okta note
|
118
|
+
|
119
|
+
Note that Okta has some [curious documentation on its use of `POST` vs `PATCH` for Groups](https://developer.okta.com/docs/api/openapi/okta-scim/guides/scim-20/#update-a-specific-group-name), which per [this Scimitar issue](https://github.com/pond/scimitar/issues/153#issuecomment-2468897194) has caused at least one person some trouble. Defining the routes for both verbs as shown above (though in that issue's case, it was for the Group resource) _does_ still seem to work, but take care if integrating with Okta to try and at least manually test, if not auto-test, `PATCH`/`PUT` operations initiated from Okta's side, just to make sure.
|
116
120
|
|
117
121
|
### Data models
|
118
122
|
|
119
123
|
Scimitar assumes that each SCIM resource maps to a single corresponding class in your system. This might be an abstraction over more complex underpinings, but either way, a 1:1 relationship is expected. For example, a SCIM User might map to a User ActiveRecord model in your Rails application, while a SCIM Group might map to some custom class called Team which operates on a more complex set of data "under the hood".
|
120
124
|
|
121
|
-
Before writing any controllers, it's a good idea to examine the SCIM specification and figure out how you intend to map SCIM attributes in any resources of interest, to your local data. A [mixin is provided](https://github.com/
|
125
|
+
Before writing any controllers, it's a good idea to examine the SCIM specification and figure out how you intend to map SCIM attributes in any resources of interest, to your local data. A [mixin is provided](https://github.com/pond/scimitar/blob/main/app/models/scimitar/resources/mixin.rb) which you can include in any plain old Ruby class (including, but not limited to ActiveRecord model classes) - a more readable form of the comments in this file [is in the RDoc output](https://www.rubydoc.info/gems/scimitar/Scimitar/Resources/Mixin).
|
122
126
|
|
123
|
-
The functionality exposed by the mixin is relatively complicated because the range of operations that the SCIM API supports is quite extensive. Rather than duplicate all the information here, please see the extensive comments in the mixin linked above for more information. There are examples in the [test suite's Rails models](https://github.com/
|
127
|
+
The functionality exposed by the mixin is relatively complicated because the range of operations that the SCIM API supports is quite extensive. Rather than duplicate all the information here, please see the extensive comments in the mixin linked above for more information. There are examples in the [test suite's Rails models](https://github.com/pond/scimitar/tree/main/spec/apps/dummy/app/models), or for another example:
|
124
128
|
|
125
129
|
```ruby
|
126
130
|
class User < ActiveRecord::Base
|
@@ -260,7 +264,7 @@ module Scim
|
|
260
264
|
end
|
261
265
|
```
|
262
266
|
|
263
|
-
All data-layer actions are taken via `#find` or `#save!`, with exceptions such as `ActiveRecord::RecordNotFound`, `ActiveRecord::RecordInvalid` or generalised SCIM exceptions handled by various superclasses. For a real Rails example of this, see the [test suite's controllers](https://github.com/
|
267
|
+
All data-layer actions are taken via `#find` or `#save!`, with exceptions such as `ActiveRecord::RecordNotFound`, `ActiveRecord::RecordInvalid` or generalised SCIM exceptions handled by various superclasses. For a real Rails example of this, see the [test suite's controllers](https://github.com/pond/scimitar/tree/main/spec/apps/dummy/app/controllers) which are invoked via its [routing declarations](https://github.com/pond/scimitar/blob/main/spec/apps/dummy/config/routes.rb).
|
264
268
|
|
265
269
|
##### Overriding controller methods
|
266
270
|
|
@@ -321,7 +325,7 @@ end
|
|
321
325
|
|
322
326
|
#### Other source types
|
323
327
|
|
324
|
-
If you do _not_ use ActiveRecord to store data, or if you have very esoteric read-write requirements, you can subclass [`
|
328
|
+
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
329
|
|
326
330
|
```ruby
|
327
331
|
class UsersController < Scimitar::ResourcesController
|
@@ -447,7 +451,7 @@ end
|
|
447
451
|
|
448
452
|
```
|
449
453
|
|
450
|
-
Note that the [`Scimitar::ApplicationController` parent class](https://www.rubydoc.info/gems/scimitar/Scimitar/ApplicationController) of `Scimitar::ResourcesController` has a few methods to help with handling exceptions and rendering them as SCIM responses; for example, if a resource were not found by ID, you might wish to use [`Scimitar::ApplicationController#handle_resource_not_found`](https://github.com/
|
454
|
+
Note that the [`Scimitar::ApplicationController` parent class](https://www.rubydoc.info/gems/scimitar/Scimitar/ApplicationController) of `Scimitar::ResourcesController` has a few methods to help with handling exceptions and rendering them as SCIM responses; for example, if a resource were not found by ID, you might wish to use [`Scimitar::ApplicationController#handle_resource_not_found`](https://github.com/pond/scimitar/blob/v1.0.3/app/controllers/scimitar/application_controller.rb#L22).
|
451
455
|
|
452
456
|
### Extension schema
|
453
457
|
|
@@ -544,7 +548,7 @@ Whatever you provide in the `::id` method in your extension class will be used a
|
|
544
548
|
}
|
545
549
|
```
|
546
550
|
|
547
|
-
**IMPORTANT: Attribute names must be unique** across your entire combined schema, regardless of URNs used. This is because of a limitation in Scimitar's implementation. [This GitHub issue](https://github.com/
|
551
|
+
**IMPORTANT: Attribute names must be unique** across your entire combined schema, regardless of URNs used. This is because of a limitation in Scimitar's implementation. [This GitHub issue](https://github.com/pond/scimitar/issues/130) explains more. If this is a problem for you, please comment on the GitHub issue to help the maintainers understand the level of demand for remediation.
|
548
552
|
|
549
553
|
Resource extensions can provide any fields you choose, under any ID/URN you choose, to either RFC-described resources or entirely custom SCIM resources. There are no hard-coded assumptions or other "magic" that might require you to only extend RFC-described resources with RFC-described extensions. Of course, if you use custom resources or custom extensions that are not described by the SCIM RFCs, then the SCIM API you provide may only work with custom-written API callers that are aware of your bespoke resources and/or extensions.
|
550
554
|
|
@@ -594,7 +598,7 @@ By default, Scimitar advertises (via things like [the `/Schemas` endpoint](https
|
|
594
598
|
|
595
599
|
```ruby
|
596
600
|
Rails.application.config.to_prepare do
|
597
|
-
Scimitar::Engine
|
601
|
+
Scimitar::Engine.set_default_resources([Scimitar::Resources::User])
|
598
602
|
# ...other Scimitar configuration / initialisation code...
|
599
603
|
end
|
600
604
|
```
|
@@ -621,7 +625,7 @@ Often, you'll find that bearer tokens are in use by SCIM API consumers, but the
|
|
621
625
|
|
622
626
|
* Several complex types for User contain the same set of `value`, `display`, `type` and `primary` fields, all used in synonymous ways.
|
623
627
|
|
624
|
-
- The `value` field - which is e.g. an e-mail address or phone number - is described as optional by [the RFC 7643 core schema](https://tools.ietf.org/html/rfc7643#section-8.7.1), also using "SHOULD" rather than "MUST" in field descriptions elsewhere. Scimitar marks this as required by default, since there's not much point being sent (say) an e-mail section which has entries that don't provide the e-mail address. Some services might send `null` values here regardless so, if you need to be able to accept such data, you can set [engine configuration option `optional_value_fields_required`](https://github.com/
|
628
|
+
- The `value` field - which is e.g. an e-mail address or phone number - is described as optional by [the RFC 7643 core schema](https://tools.ietf.org/html/rfc7643#section-8.7.1), also using "SHOULD" rather than "MUST" in field descriptions elsewhere. Scimitar marks this as required by default, since there's not much point being sent (say) an e-mail section which has entries that don't provide the e-mail address. Some services might send `null` values here regardless so, if you need to be able to accept such data, you can set [engine configuration option `optional_value_fields_required`](https://github.com/pond/scimitar/blob/main/config/initializers/scimitar.rb) to `false`.
|
625
629
|
|
626
630
|
- The schema _descriptions_ for `display` declare that the field is something optionally sent by the service provider and state clearly that it is read-only - yet the formal schema declares it `readWrite`. Scimitar marks it as read-only.
|
627
631
|
|
@@ -629,7 +633,7 @@ Often, you'll find that bearer tokens are in use by SCIM API consumers, but the
|
|
629
633
|
|
630
634
|
* In the `members` section of a [`Group` in the RFC 7643 core schema](https://tools.ietf.org/html/rfc7643#page-69), any member's `value` is noted as _not_ required but [the RFC also says](https://tools.ietf.org/html/rfc7643#section-4.2) "Service providers MAY require clients to provide a non-empty value by setting the "required" attribute characteristic of a sub-attribute of the "members" attribute in the "Group" resource schema". Scimitar does this. The `value` field would contain the `id` of a SCIM resource, which is the primary key on "our side" as a service provider. Just as we must store `externalId` values to maintain a mapping on "our side", we in turn _do_ require clients to provide our ID in group member lists via the `value` field.
|
631
635
|
|
632
|
-
* While the gem attempts to support difficult/complex filter strings via incorporating code and ideas in [SCIM Query Filter Parser](https://github.com/ingydotnet/scim-query-filter-parser-rb), it is possible that ActiveRecord / Rails precedence on some query operations in complex cases might not exactly match the SCIM specification. Please do submit a bug report if you encounter this. You may also wish to view [`query_parser_spec.rb`](https://github.com/
|
636
|
+
* While the gem attempts to support difficult/complex filter strings via incorporating code and ideas in [SCIM Query Filter Parser](https://github.com/ingydotnet/scim-query-filter-parser-rb), it is possible that ActiveRecord / Rails precedence on some query operations in complex cases might not exactly match the SCIM specification. Please do submit a bug report if you encounter this. You may also wish to view [`query_parser_spec.rb`](https://github.com/pond/scimitar/blob/main/spec/models/scimitar/lists/query_parser_spec.rb) to get an idea of the tested examples - more interesting test cases are in the "`context 'with complex cases' do`" section.
|
633
637
|
|
634
638
|
* Group resource examples show the `members` array including field `display`, but this is not in the [formal schema](https://tools.ietf.org/html/rfc7643#page-69); Scimitar includes it in the Group definition.
|
635
639
|
|
@@ -639,7 +643,7 @@ Often, you'll find that bearer tokens are in use by SCIM API consumers, but the
|
|
639
643
|
|
640
644
|
* As noted earlier, extension schema attribute names must be unique across your entire combined schema, regardless of schema IDs (URNs) used.
|
641
645
|
|
642
|
-
If you believe choices made in this section may be incorrect, please [create a GitHub issue](https://github.com/
|
646
|
+
If you believe choices made in this section may be incorrect, please [create a GitHub issue](https://github.com/pond/scimitar/issues/new) describing the problem.
|
643
647
|
|
644
648
|
### Omissions
|
645
649
|
|
@@ -653,11 +657,11 @@ If you believe choices made in this section may be incorrect, please [create a G
|
|
653
657
|
|
654
658
|
* Currently filtering for lists is always matched case-insensitive regardless of schema declarations that might indicate otherwise, for `eq`, `ne`, `co`, `sw` and `ew` operators; for greater/less-thank style filters, case is maintained with simple `>`, `<` etc. database operations in use. The standard Group and User schema have `caseExact` set to `false` for just about anything readily queryable, so this hopefully would only ever potentially be an issue for custom schema.
|
655
659
|
|
656
|
-
* As an exception to the above, attributes `id`, `externalId` and `meta.*` are matched case-sensitive. Filters that use `eq` on such attributes will end up a comparison using `=` rather than e.g. `ILIKE` (arising from https://github.com/
|
660
|
+
* As an exception to the above, attributes `id`, `externalId` and `meta.*` are matched case-sensitive. Filters that use `eq` on such attributes will end up a comparison using `=` rather than e.g. `ILIKE` (arising from https://github.com/pond/scimitar/issues/36).
|
657
661
|
|
658
662
|
* The `PATCH` mechanism is supported, but where filters are included, only a single "attribute eq value" is permitted - no other operators or combinations. For example, a work e-mail address's value could be replaced by a PATCH patch of `emails[type eq "work"].value`. For in-path filters such as this, other operators such as `ne` are not supported; combinations with "and"/"or" are not supported; negation with "not" is not supported.
|
659
663
|
|
660
|
-
If you would like to see something listed in the session implemented, please [create a GitHub issue](https://github.com/
|
664
|
+
If you would like to see something listed in the session implemented, please [create a GitHub issue](https://github.com/pond/scimitar/issues/new) asking for it to be implemented, or if possible, implement the feature and send a Pull Request.
|
661
665
|
|
662
666
|
|
663
667
|
|
@@ -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
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module Scimitar
|
2
2
|
class ServiceProviderConfigurationsController < ApplicationController
|
3
3
|
def show
|
4
|
-
|
4
|
+
service_provider_configuration = Scimitar.service_provider_configuration(location: request.url).as_json
|
5
|
+
service_provider_configuration.delete("uses_defaults")
|
6
|
+
render json: service_provider_configuration
|
5
7
|
end
|
6
8
|
end
|
7
9
|
end
|
@@ -75,8 +75,8 @@ module Scimitar
|
|
75
75
|
# attributes of this complex type object.
|
76
76
|
#
|
77
77
|
def as_json(options={})
|
78
|
-
options[:except]
|
79
|
-
super.except
|
78
|
+
exclusions = options[:except] || ['errors']
|
79
|
+
super(options.merge(except: exclusions))
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -296,7 +296,7 @@ module Scimitar
|
|
296
296
|
# the part before the "[" as a prefix - "emails[type" to "emails.type",
|
297
297
|
# with similar substitutions therein.
|
298
298
|
#
|
299
|
-
# Further, via https://github.com/
|
299
|
+
# Further, via https://github.com/pond/scimitar/issues/115 we see
|
300
300
|
# a requirement to support a broken form emitted by Microsoft; that is
|
301
301
|
# supported herein.
|
302
302
|
#
|
@@ -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,13 +374,13 @@ 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]
|
381
381
|
characters_after_closing_bracket = matches[2]
|
382
382
|
|
383
|
-
# https://github.com/
|
383
|
+
# https://github.com/pond/scimitar/issues/115 - detect
|
384
384
|
# bad Microsoft filters. After the closing bracket, we expect a
|
385
385
|
# dot then valid attribute characters and at least one white
|
386
386
|
# space character and filter operator, but we split on spaces,
|
@@ -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
|
@@ -470,7 +470,7 @@ module Scimitar
|
|
470
470
|
#
|
471
471
|
# Scimitar currently has a limitation where it strips schema IDs in
|
472
472
|
# things like PATCH operation path traversal; see
|
473
|
-
# https://github.com/
|
473
|
+
# https://github.com/pond/scimitar/issues/130. At least that
|
474
474
|
# makes things easy here; use the same approach and strip them out!
|
475
475
|
#
|
476
476
|
# We don't know which resource is being queried at this layer of the
|
@@ -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!(
|
@@ -769,8 +769,8 @@ module Scimitar
|
|
769
769
|
# Handle extension schema. Contributed by @bettysteger and
|
770
770
|
# @MorrisFreeman via:
|
771
771
|
#
|
772
|
-
# https://github.com/
|
773
|
-
# https://github.com/
|
772
|
+
# https://github.com/pond/scimitar/issues/48
|
773
|
+
# https://github.com/pond/scimitar/pull/49
|
774
774
|
#
|
775
775
|
# Note the shortcoming that attribute names within extensions
|
776
776
|
# must be unique, as this mechanism basically just pulls out
|
@@ -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
|
@@ -1139,7 +1139,7 @@ module Scimitar
|
|
1139
1139
|
# Handle the Azure (Entra) case where keys might use
|
1140
1140
|
# dotted paths - see:
|
1141
1141
|
#
|
1142
|
-
# https://github.com/
|
1142
|
+
# https://github.com/pond/scimitar/issues/123
|
1143
1143
|
#
|
1144
1144
|
# ...along with keys containing schema IDs - see:
|
1145
1145
|
#
|
@@ -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
|
@@ -122,9 +122,10 @@ module Scimitar
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def as_json(options = {})
|
125
|
-
options[:except]
|
126
|
-
|
127
|
-
|
125
|
+
exclusions = options[:except] || ['complexType']
|
126
|
+
exclusions << 'canonicalValues' if canonicalValues.empty?
|
127
|
+
|
128
|
+
super(options.merge(except: exclusions))
|
128
129
|
end
|
129
130
|
|
130
131
|
end
|
@@ -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
|
@@ -50,8 +50,8 @@ module Scimitar
|
|
50
50
|
# Schema ID-aware splitter handling ":" or "." separators. Adapted from
|
51
51
|
# contribution by @bettysteger and @MorrisFreeman in:
|
52
52
|
#
|
53
|
-
# https://github.com/
|
54
|
-
# https://github.com/
|
53
|
+
# https://github.com/pond/scimitar/issues/48
|
54
|
+
# https://github.com/pond/scimitar/pull/49
|
55
55
|
#
|
56
56
|
# +schemas:: Array of extension schemas, e.g. a SCIM resource class'
|
57
57
|
# <tt>scim_resource_type.extended_schemas</tt> value. The
|
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.11.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 = '2025-03-05'
|
12
12
|
|
13
13
|
end
|
@@ -13,6 +13,14 @@ require 'scimitar'
|
|
13
13
|
module Dummy
|
14
14
|
class Application < Rails::Application
|
15
15
|
config.load_defaults 7.0
|
16
|
+
|
17
|
+
# Silence the following under Rails 8.0:
|
18
|
+
#
|
19
|
+
# "DEPRECATION WARNING: `to_time` will always preserve the full timezone
|
20
|
+
# rather than offset of the receiver in Rails 8.1. To opt in to the new
|
21
|
+
# behavior, set `config.active_support.to_time_preserves_timezone = :zone`"
|
22
|
+
#
|
23
|
+
config.active_support.to_time_preserves_timezone = :zone
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Test app configuration.
|
2
2
|
#
|
3
|
-
# Note that as a result of https://github.com/
|
3
|
+
# Note that as a result of https://github.com/pond/scimitar/issues/48,
|
4
4
|
# tests include a custom extension of the core User schema. A shortcoming of
|
5
5
|
# some of the code from which Scimitar was originally built is that those
|
6
6
|
# extensions are done with class-level ivars, so it is largely impossible (or
|
@@ -59,7 +59,7 @@ Rails.application.config.to_prepare do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
# In https://github.com/
|
62
|
+
# In https://github.com/pond/scimitar/issues/122 we learn that with
|
63
63
|
# more than one extension, things can go wrong - so now we test with two.
|
64
64
|
#
|
65
65
|
class Manager < Scimitar::Schema::Base
|
@@ -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
|
|
@@ -15,6 +15,7 @@ RSpec.describe Scimitar::ServiceProviderConfigurationsController do
|
|
15
15
|
|
16
16
|
expect(response).to be_ok
|
17
17
|
expect(JSON.parse(response.body)).to include('patch' => {'supported' => true})
|
18
|
+
expect(JSON.parse(response.body)).to_not include('uses_defaults' => true)
|
18
19
|
expect(JSON.parse(response.body)).to include('filter' => {'maxResults' => Scimitar::Filter::MAX_RESULTS_DEFAULT, 'supported' => true})
|
19
20
|
end
|
20
21
|
end
|
@@ -352,7 +352,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
352
352
|
expect(result).to eql('filter=userType eq "Employee" and emails.type eq "work" and emails.value co "@example.com"')
|
353
353
|
end
|
354
354
|
|
355
|
-
# https://github.com/
|
355
|
+
# https://github.com/pond/scimitar/issues/116
|
356
356
|
#
|
357
357
|
context 'with schema IDs (GitHub issue #116)' do
|
358
358
|
it 'handles simple attributes' do
|
@@ -381,7 +381,7 @@ RSpec.describe Scimitar::Lists::QueryParser do
|
|
381
381
|
end
|
382
382
|
end
|
383
383
|
|
384
|
-
# https://github.com/
|
384
|
+
# https://github.com/pond/scimitar/issues/115
|
385
385
|
#
|
386
386
|
context 'broken filters from Microsoft (GitHub issue #115)' do
|
387
387
|
it 'work with "eq"' do
|
@@ -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
|
@@ -136,7 +136,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
136
136
|
expect(result.dig('Resources', 0, 'name', 'familyName')).to eql 'Ark'
|
137
137
|
end
|
138
138
|
|
139
|
-
# https://github.com/
|
139
|
+
# https://github.com/pond/scimitar/issues/37
|
140
140
|
#
|
141
141
|
it 'applies a filter, with case-insensitive attribute matching (GitHub issue #37)' do
|
142
142
|
get '/Users', params: {
|
@@ -159,7 +159,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
159
159
|
expect(usernames).to match_array(['2'])
|
160
160
|
end
|
161
161
|
|
162
|
-
# https://github.com/
|
162
|
+
# https://github.com/pond/scimitar/issues/115
|
163
163
|
#
|
164
164
|
it 'handles broken Microsoft filters (GitHub issue #115)' do
|
165
165
|
get '/Users', params: {
|
@@ -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,28 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scimitar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RIPA Global
|
8
8
|
- Andrew David Hodgkinson
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2025-03-05 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
16
|
requirements:
|
18
|
-
- - "
|
17
|
+
- - ">="
|
19
18
|
- !ruby/object:Gem::Version
|
20
19
|
version: '7.0'
|
21
20
|
type: :runtime
|
22
21
|
prerelease: false
|
23
22
|
version_requirements: !ruby/object:Gem::Requirement
|
24
23
|
requirements:
|
25
|
-
- - "
|
24
|
+
- - ">="
|
26
25
|
- !ruby/object:Gem::Version
|
27
26
|
version: '7.0'
|
28
27
|
- !ruby/object:Gem::Dependency
|
@@ -31,14 +30,14 @@ dependencies:
|
|
31
30
|
requirements:
|
32
31
|
- - "~>"
|
33
32
|
- !ruby/object:Gem::Version
|
34
|
-
version: '1.
|
33
|
+
version: '1.10'
|
35
34
|
type: :development
|
36
35
|
prerelease: false
|
37
36
|
version_requirements: !ruby/object:Gem::Requirement
|
38
37
|
requirements:
|
39
38
|
- - "~>"
|
40
39
|
- !ruby/object:Gem::Version
|
41
|
-
version: '1.
|
40
|
+
version: '1.10'
|
42
41
|
- !ruby/object:Gem::Dependency
|
43
42
|
name: rake
|
44
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,45 +86,45 @@ dependencies:
|
|
87
86
|
requirements:
|
88
87
|
- - "~>"
|
89
88
|
- !ruby/object:Gem::Version
|
90
|
-
version: '6.
|
89
|
+
version: '6.12'
|
91
90
|
type: :development
|
92
91
|
prerelease: false
|
93
92
|
version_requirements: !ruby/object:Gem::Requirement
|
94
93
|
requirements:
|
95
94
|
- - "~>"
|
96
95
|
- !ruby/object:Gem::Version
|
97
|
-
version: '6.
|
96
|
+
version: '6.12'
|
98
97
|
- !ruby/object:Gem::Dependency
|
99
98
|
name: rspec-rails
|
100
99
|
requirement: !ruby/object:Gem::Requirement
|
101
100
|
requirements:
|
102
101
|
- - "~>"
|
103
102
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
103
|
+
version: '7.1'
|
105
104
|
type: :development
|
106
105
|
prerelease: false
|
107
106
|
version_requirements: !ruby/object:Gem::Requirement
|
108
107
|
requirements:
|
109
108
|
- - "~>"
|
110
109
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
110
|
+
version: '7.1'
|
112
111
|
- !ruby/object:Gem::Dependency
|
113
112
|
name: doggo
|
114
113
|
requirement: !ruby/object:Gem::Requirement
|
115
114
|
requirements:
|
116
115
|
- - "~>"
|
117
116
|
- !ruby/object:Gem::Version
|
118
|
-
version: '1.
|
117
|
+
version: '1.4'
|
119
118
|
type: :development
|
120
119
|
prerelease: false
|
121
120
|
version_requirements: !ruby/object:Gem::Requirement
|
122
121
|
requirements:
|
123
122
|
- - "~>"
|
124
123
|
- !ruby/object:Gem::Version
|
125
|
-
version: '1.
|
124
|
+
version: '1.4'
|
126
125
|
description: SCIM v2 support for Users and Groups in Ruby On Rails
|
127
126
|
email:
|
128
|
-
-
|
127
|
+
- ahodgkin@rowing.org.uk
|
129
128
|
executables: []
|
130
129
|
extensions: []
|
131
130
|
extra_rdoc_files: []
|
@@ -244,15 +243,14 @@ files:
|
|
244
243
|
- spec/spec_helper.rb
|
245
244
|
- spec/spec_helper_spec.rb
|
246
245
|
- spec/support/hash_with_indifferent_case_insensitive_access_spec.rb
|
247
|
-
homepage: https://
|
246
|
+
homepage: https://github.com/pond/scimitar/
|
248
247
|
licenses:
|
249
248
|
- MIT
|
250
249
|
metadata:
|
251
|
-
homepage_uri: https://
|
252
|
-
source_code_uri: https://github.com/
|
253
|
-
bug_tracker_uri: https://github.com/
|
254
|
-
changelog_uri: https://github.com/
|
255
|
-
post_install_message:
|
250
|
+
homepage_uri: https://github.com/pond/scimitar/
|
251
|
+
source_code_uri: https://github.com/pond/scimitar/
|
252
|
+
bug_tracker_uri: https://github.com/pond/scimitar/issues/
|
253
|
+
changelog_uri: https://github.com/pond/scimitar/blob/main/CHANGELOG.md
|
256
254
|
rdoc_options: []
|
257
255
|
require_paths:
|
258
256
|
- lib
|
@@ -267,8 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
265
|
- !ruby/object:Gem::Version
|
268
266
|
version: '0'
|
269
267
|
requirements: []
|
270
|
-
rubygems_version: 3.
|
271
|
-
signing_key:
|
268
|
+
rubygems_version: 3.6.2
|
272
269
|
specification_version: 4
|
273
270
|
summary: SCIM v2 for Rails
|
274
271
|
test_files:
|