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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '083949bb1e8b8d0afb8e62a87245a0f05189b05669e49eb8b5e3f8fa3af277e6'
4
- data.tar.gz: 46a3f0af432f0980280ed8cc8f5c0309bc36f30c15673f9da650f8225f0f406d
3
+ metadata.gz: 8cb5109af12b712e491949d6627588dff6a4d70877253be26c4ee0af3b56c61e
4
+ data.tar.gz: 4271bb70e89fd17b97103073bc063de748e261b62b243fa0681b3e2934085f77
5
5
  SHA512:
6
- metadata.gz: 6d10c2377f023f1b3201148bd57813296b1746d64e738838410045a128a10c980ac675c3a7474b56848d2cfe97ab2421d07bfa1ada5512890bfa08bfa2556f82
7
- data.tar.gz: 5ff25a1f64a5bccf0ee2289223d31f8bc6b8636586bf09f1903e0b5824aed483d0dba60d9f7736bdc20c5c71ef6858ab38529eee6ff2e00aae20d0b18cb5d647
6
+ metadata.gz: aed187b7c9cc56fb1b0c3bae86b3274c6753078a71fb4fa6162776c056033edf19c085b830cfc3867acf7ab4e12732a6fe8100d758bd107840edd2e26c068aa0
7
+ data.tar.gz: f753d11931026a98c27d27525e88e512ee5a40d1920533243b352f6345ddb285de8efb38d6101afd360156b618365805c4a9bc440941701d7e9cd9cee5488810
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 RIPA Global
3
+ Copyright (c) 2024 RIPA Global
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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/RIPAGlobal/scimitar/blob/main/config/initializers/scimitar.rb). For example:
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/RIPAGlobal/scimitar/blob/main/config/initializers/scimitar.rb).
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/RIPAGlobal/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).
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/RIPAGlobal/scimitar/tree/main/spec/apps/dummy/app/models), or for another example:
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/RIPAGlobal/scimitar/tree/main/spec/apps/dummy/app/controllers) which are invoked via its [routing declarations](https://github.com/RIPAGlobal/scimitar/blob/main/spec/apps/dummy/config/routes.rb).
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 [`Scimigar::ResourcesController`](https://www.rubydoc.info/gems/scimitar/Scimitar/ResourcesController) in a manner similar to this:
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/RIPAGlobal/scimitar/blob/v1.0.3/app/controllers/scimitar/application_controller.rb#L22).
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/RIPAGlobal/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.
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::set_default_resources([Scimitar::Resources::User])
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/RIPAGlobal/scimitar/blob/main/config/initializers/scimitar.rb) to `false`.
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/RIPAGlobal/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.
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/RIPAGlobal/scimitar/issues/new) describing the problem.
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/RIPAGlobal/scimitar/issues/36).
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/RIPAGlobal/scimitar/issues/new) asking for it to be implemented, or if possible, implement the feature and send a Pull Request.
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 invokved with the given parameter.
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: resource_types
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 artbirary number of same-class items - e.g.
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
- render json: Scimitar.service_provider_configuration(location: request.url)
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] ||= ['errors']
79
- super.except(options)
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/RIPAGlobal/scimitar/issues/115 we see
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 == true
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 (expecting_attribute)
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 (expecting_operator)
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 (expecting_value)
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/RIPAGlobal/scimitar/issues/115 - detect
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
- ! attribute_prefix.nil? &&
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 == false # If not expecting a closing quote, then the component must be the entire no-spaces value
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 (expecting_logic_word)
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/RIPAGlobal/scimitar/issues/130. At least that
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 aggregration at each level into a wider query using
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
- # - neverchanges during recursion).
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 entirey unmapped (thus unsupported).
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
- if schemaExtensions.present?
21
- extensions = schemaExtensions.map{|extension| {"schema" => extension, "required" => false}}
22
- without_extensions.merge('schemaExtensions' => extensions)
23
- else
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 && scim_attribute.complexType
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 ['add', 'remove', 'replace'].include?(nature)
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 someting other than mapping Hash(es)'
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 definiton and check
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/RIPAGlobal/scimitar/issues/48
773
- # https://github.com/RIPAGlobal/scimitar/pull/49
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
- if handled == false
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/RIPAGlobal/scimitar/issues/123
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
- if handled == false
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 'holday', then +nil+ would be returned since there is no
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] ||= ['complexType']
126
- options[:except] << 'canonicalValues' if canonicalValues.empty?
127
- super.except(options)
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
@@ -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::set_default_resources: Only #{@standard_default_resources.map(&:name).join(', ')} are supported"
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::set_default_resources: At least one resource must be given'
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/RIPAGlobal/scimitar/issues/48
54
- # https://github.com/RIPAGlobal/scimitar/pull/49
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
@@ -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.9.0'
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 = '2024-06-27'
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/RIPAGlobal/scimitar/issues/48,
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/RIPAGlobal/scimitar/issues/122 we learn that with
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 = [ Scimitar::Resources::User.resource_type(scim_resource_type_url(name: 'User', test: 1)),
13
- Scimitar::Resources::Group.resource_type(scim_resource_type_url(name: 'Group', test: 1))
14
- ].to_json
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/RIPAGlobal/scimitar/issues/116
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/RIPAGlobal/scimitar/issues/115
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 someting other than mapping Hash(es)') }
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/RIPAGlobal/scimitar/issues/37
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/RIPAGlobal/scimitar/issues/115
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::set_default_resources([Scimitar::Resources::User])
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::set_default_resources([Scimitar::Resources::User])
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::set_default_resources([@license_resource])
94
- }.to raise_error('Scimitar::Engine::set_default_resources: Only Scimitar::Resources::User, Scimitar::Resources::Group are supported')
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::set_default_resources([])
100
- }.to raise_error('Scimitar::Engine::set_default_resources: At least one resource must be given')
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.9.0
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: 2024-06-27 00:00:00.000000000 Z
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.9'
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.9'
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.7'
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.7'
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: '6.1'
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: '6.1'
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.3'
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.3'
124
+ version: '1.4'
126
125
  description: SCIM v2 support for Users and Groups in Ruby On Rails
127
126
  email:
128
- - dev@ripaglobal.com
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://www.ripaglobal.com/
246
+ homepage: https://github.com/pond/scimitar/
248
247
  licenses:
249
248
  - MIT
250
249
  metadata:
251
- homepage_uri: https://www.ripaglobal.com/
252
- source_code_uri: https://github.com/RIPAGlobal/scimitar/
253
- bug_tracker_uri: https://github.com/RIPAGlobal/scimitar/issues/
254
- changelog_uri: https://github.com/RIPAGlobal/scimitar/blob/main/CHANGELOG.md
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.5.4
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: