scimitar 2.6.1 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +39 -0
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +51 -16
- data/app/controllers/scimitar/application_controller.rb +6 -2
- data/app/models/scimitar/complex_types/address.rb +0 -6
- data/app/models/scimitar/resource_invalid_error.rb +1 -1
- data/app/models/scimitar/resources/mixin.rb +4 -1
- data/lib/scimitar/support/utilities.rb +51 -0
- data/lib/scimitar/version.rb +2 -2
- data/lib/scimitar.rb +1 -0
- data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +25 -0
- data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +25 -0
- data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +25 -0
- data/spec/apps/dummy/config/routes.rb +13 -1
- data/spec/controllers/scimitar/application_controller_spec.rb +54 -0
- data/spec/models/scimitar/complex_types/address_spec.rb +3 -4
- data/spec/models/scimitar/resources/mixin_spec.rb +22 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +302 -18
- data/spec/spec_helper.rb +9 -1
- metadata +19 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be3285b59b4b124288a68cbfcc308caff2c44c788d66ee537e6a1db3780e8633
|
4
|
+
data.tar.gz: e04d556655ee3dc440dd5eda89b217c3a5a6d9b96697686c0bb71241268d09d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53a364cee0f0ea429b51a521131c186a0b51f16b78dacf89dd62a0eb3565ecd3dc21c22acb2cb453c9ca61d6764bc18ff3a0a4c2f4ef805988e1a5adb3a18cd0
|
7
|
+
data.tar.gz: 8e649ec7793576b31acca746025c02f325162c3d7f715a9554d8c6bdfc3b57b6734b571473c84e9d74eda7e2c3d909d576960464d8a00be8f418e9a56510a6dc
|
data/README.md
CHANGED
@@ -262,6 +262,45 @@ end
|
|
262
262
|
|
263
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).
|
264
264
|
|
265
|
+
##### Overriding controller methods
|
266
|
+
|
267
|
+
You can overwrite write-based controller methods `#create`, `#update`, `#replace` and `#destroy` in your controller subclass, should you wish, wherein a call to `super` is passed a block. The block is invoked with the instance of a new unsaved record for `#create`, the updated record that needs to have its changes saved for `#update` and `#replace` and the record that should be destroyed for `#destroy`. This allows you to do things like applying business logic, default values, extra request-derived data and so-forth before then calling `record.save!`, or using some different method other than `record.destroy!` to discard a record (e.g. you might be using soft-delete, or want to skip all callbacks for some reason via `record.delete`).
|
268
|
+
|
269
|
+
* The `#destroy` method just calls `record.destroy!` unless a block is given, with nothing much else to say about it.
|
270
|
+
|
271
|
+
* The other methods all establish a database transaction and call through to the _controller's_ protected `#save!` method, passing it the record; it is _this_ method which then either calls `record.save!` or invokes a block. Using the exception-throwing versions of persistence methods is recommended, as there is exception handling within the controller's implementation which rescues things like `ActiveRecord::RecordInvalid` and builds an appropriate SCIM error response when they occur. You can change the list of exceptions handled in this way by overriding protected method `#scimitar_rescuable_exceptions'.
|
272
|
+
|
273
|
+
* If you want to override saving behaviour for both new and modified records, overriding `#save!` in your controller subclass, rather than overriding all of `#create`, `#update` and `#replace`, is likely to be the better choice.
|
274
|
+
|
275
|
+
* For more information, see the [RDoc output for `Scimitar::ActiveRecordBackedResourcesController`](https://www.rubydoc.info/github/RIPAGlobal/scimitar/main/Scimitar/ActiveRecordBackedResourcesController).
|
276
|
+
|
277
|
+
Example:
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
module Scim
|
281
|
+
class UsersController < Scimitar::ActiveRecordBackedResourcesController
|
282
|
+
|
283
|
+
# Create all new records with some special internal field set to a value
|
284
|
+
# determined by a bespoke-to-your-application mechanism.
|
285
|
+
#
|
286
|
+
def create
|
287
|
+
super do | user |
|
288
|
+
user.some_special_on_creation_field = method_that_calculates_value()
|
289
|
+
user.save!
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Use #discard! rather than #destroy! as an example of soft-delete via the
|
294
|
+
# 'discard' gem - https://rubygems.org/gems/discard.
|
295
|
+
#
|
296
|
+
def destroy
|
297
|
+
super do | user |
|
298
|
+
user.discard!
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
265
304
|
#### Queries & Optimisations
|
266
305
|
|
267
306
|
The scope can be optimised to eager load the data exposed by the SCIM interface, i.e.:
|
@@ -60,12 +60,20 @@ module Scimitar
|
|
60
60
|
|
61
61
|
# POST (create)
|
62
62
|
#
|
63
|
-
|
63
|
+
# Calls #save! on the new record if no block is given, else invokes the
|
64
|
+
# block, passing it the new ActiveRecord model instance to be saved. It
|
65
|
+
# is up to the block to make any further changes and persist the record.
|
66
|
+
#
|
67
|
+
# Blocks are invoked from within a wrapping database transaction.
|
68
|
+
# ActiveRecord::RecordInvalid exceptions are handled for you, rendering
|
69
|
+
# an appropriate SCIM error.
|
70
|
+
#
|
71
|
+
def create(&block)
|
64
72
|
super do |scim_resource|
|
65
73
|
self.storage_class().transaction do
|
66
74
|
record = self.storage_class().new
|
67
75
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
68
|
-
self.save!(record)
|
76
|
+
self.save!(record, &block)
|
69
77
|
record_to_scim(record)
|
70
78
|
end
|
71
79
|
end
|
@@ -73,12 +81,16 @@ module Scimitar
|
|
73
81
|
|
74
82
|
# PUT (replace)
|
75
83
|
#
|
76
|
-
|
84
|
+
# Calls #save! on the updated record if no block is given, else invokes the
|
85
|
+
# block, passing the updated record which the block must persist, with the
|
86
|
+
# same rules as for #create.
|
87
|
+
#
|
88
|
+
def replace(&block)
|
77
89
|
super do |record_id, scim_resource|
|
78
90
|
self.storage_class().transaction do
|
79
91
|
record = self.find_record(record_id)
|
80
92
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
81
|
-
self.save!(record)
|
93
|
+
self.save!(record, &block)
|
82
94
|
record_to_scim(record)
|
83
95
|
end
|
84
96
|
end
|
@@ -86,12 +98,16 @@ module Scimitar
|
|
86
98
|
|
87
99
|
# PATCH (update)
|
88
100
|
#
|
89
|
-
|
101
|
+
# Calls #save! on the updated record if no block is given, else invokes the
|
102
|
+
# block, passing the updated record which the block must persist, with the
|
103
|
+
# same rules as for #create.
|
104
|
+
#
|
105
|
+
def update(&block)
|
90
106
|
super do |record_id, patch_hash|
|
91
107
|
self.storage_class().transaction do
|
92
108
|
record = self.find_record(record_id)
|
93
109
|
record.from_scim_patch!(patch_hash: patch_hash)
|
94
|
-
self.save!(record)
|
110
|
+
self.save!(record, &block)
|
95
111
|
record_to_scim(record)
|
96
112
|
end
|
97
113
|
end
|
@@ -134,6 +150,17 @@ module Scimitar
|
|
134
150
|
raise NotImplementedError
|
135
151
|
end
|
136
152
|
|
153
|
+
# Return an Array of exceptions that #save! can rescue and handle with a
|
154
|
+
# SCIM error automatically.
|
155
|
+
#
|
156
|
+
def scimitar_rescuable_exceptions
|
157
|
+
[
|
158
|
+
ActiveRecord::RecordInvalid,
|
159
|
+
ActiveRecord::RecordNotSaved,
|
160
|
+
ActiveRecord::RecordNotUnique,
|
161
|
+
]
|
162
|
+
end
|
163
|
+
|
137
164
|
# Find a record by ID. Subclasses can override this if they need special
|
138
165
|
# lookup behaviour.
|
139
166
|
#
|
@@ -173,17 +200,25 @@ module Scimitar
|
|
173
200
|
else
|
174
201
|
record.save!
|
175
202
|
end
|
176
|
-
rescue
|
177
|
-
|
203
|
+
rescue *self.scimitar_rescuable_exceptions() => exception
|
204
|
+
handle_on_save_exception(record, exception)
|
178
205
|
end
|
179
206
|
|
180
|
-
# Deal with
|
181
|
-
# error.
|
207
|
+
# Deal with exceptions related to errors upon saving, by responding with
|
208
|
+
# an appropriate SCIM error. This is most effective if the record has
|
209
|
+
# validation errors defined, but falls back to the provided exception's
|
210
|
+
# message otherwise.
|
182
211
|
#
|
183
|
-
# +record+::
|
212
|
+
# +record+:: The record that provoked the exception. Mandatory.
|
213
|
+
# +exception+:: The exception that was raised. If omitted, a default of
|
214
|
+
# 'Unknown', in English with no I18n, is used.
|
184
215
|
#
|
185
|
-
def
|
186
|
-
|
216
|
+
def handle_on_save_exception(record, exception = RuntimeError.new('Unknown'))
|
217
|
+
details = if record.errors.present?
|
218
|
+
record.errors.full_messages.join('; ')
|
219
|
+
else
|
220
|
+
exception.message
|
221
|
+
end
|
187
222
|
|
188
223
|
# https://tools.ietf.org/html/rfc7644#page-12
|
189
224
|
#
|
@@ -193,14 +228,14 @@ module Scimitar
|
|
193
228
|
# status code 409 (Conflict) with a "scimType" error code of
|
194
229
|
# "uniqueness"
|
195
230
|
#
|
196
|
-
if record.errors.any? { | e | e.type == :taken }
|
231
|
+
if exception.is_a?(ActiveRecord::RecordNotUnique) || record.errors.any? { | e | e.type == :taken }
|
197
232
|
raise Scimitar::ErrorResponse.new(
|
198
233
|
status: 409,
|
199
234
|
scimType: 'uniqueness',
|
200
|
-
detail:
|
235
|
+
detail: "Operation failed due to a uniqueness constraint: #{details}"
|
201
236
|
)
|
202
237
|
else
|
203
|
-
raise Scimitar::ResourceInvalidError.new(
|
238
|
+
raise Scimitar::ResourceInvalidError.new(details)
|
204
239
|
end
|
205
240
|
end
|
206
241
|
|
@@ -139,11 +139,15 @@ module Scimitar
|
|
139
139
|
|
140
140
|
def authenticated?
|
141
141
|
result = if Scimitar.engine_configuration.basic_authenticator.present?
|
142
|
-
authenticate_with_http_basic
|
142
|
+
authenticate_with_http_basic do |username, password|
|
143
|
+
instance_exec(username, password, &Scimitar.engine_configuration.basic_authenticator)
|
144
|
+
end
|
143
145
|
end
|
144
146
|
|
145
147
|
result ||= if Scimitar.engine_configuration.token_authenticator.present?
|
146
|
-
authenticate_with_http_token
|
148
|
+
authenticate_with_http_token do |token, options|
|
149
|
+
instance_exec(token, options, &Scimitar.engine_configuration.token_authenticator)
|
150
|
+
end
|
147
151
|
end
|
148
152
|
|
149
153
|
return result
|
@@ -2,7 +2,7 @@ module Scimitar
|
|
2
2
|
class ResourceInvalidError < ErrorResponse
|
3
3
|
|
4
4
|
def initialize(error_message)
|
5
|
-
super(status: 400, scimType: 'invalidValue', detail:"Operation failed since record has become invalid: #{error_message}")
|
5
|
+
super(status: 400, scimType: 'invalidValue', detail: "Operation failed since record has become invalid: #{error_message}")
|
6
6
|
end
|
7
7
|
|
8
8
|
end
|
@@ -952,7 +952,10 @@ module Scimitar
|
|
952
952
|
|
953
953
|
when 'replace'
|
954
954
|
if path_component == 'root'
|
955
|
-
|
955
|
+
dot_pathed_value = value.inject({}) do |hash, (k, v)|
|
956
|
+
hash.deep_merge!(::Scimitar::Support::Utilities.dot_path(k.split('.'), v))
|
957
|
+
end
|
958
|
+
altering_hash[path_component].deep_merge!(dot_pathed_value)
|
956
959
|
else
|
957
960
|
altering_hash[path_component] = value
|
958
961
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Scimitar
|
2
|
+
|
3
|
+
# Namespace containing various chunks of Scimitar support code that don't
|
4
|
+
# logically fit into other areas.
|
5
|
+
#
|
6
|
+
module Support
|
7
|
+
|
8
|
+
# A namespace that contains various stand-alone utility methods which act
|
9
|
+
# as helpers for other parts of the code base, without risking namespace
|
10
|
+
# pollution by e.g. being part of a module loaded into a client class.
|
11
|
+
#
|
12
|
+
module Utilities
|
13
|
+
|
14
|
+
# Takes an array of components that usually come from a dotted path such
|
15
|
+
# as <tt>foo.bar.baz</tt>, along with a value that is found at the end of
|
16
|
+
# that path, then converts it into a nested Hash with each level of the
|
17
|
+
# Hash corresponding to a step along the path.
|
18
|
+
#
|
19
|
+
# This was written to help with edge case SCIM uses where (most often, at
|
20
|
+
# least) inbound calls use a dotted notation where nested values are more
|
21
|
+
# commonly accepted; converting to nesting makes it easier for subsequent
|
22
|
+
# processing code, which needs only handle nested Hash data.
|
23
|
+
#
|
24
|
+
# As an example, passing:
|
25
|
+
#
|
26
|
+
# ['foo', 'bar', 'baz'], 'value'
|
27
|
+
#
|
28
|
+
# ...yields:
|
29
|
+
#
|
30
|
+
# {'foo' => {'bar' => {'baz' => 'value'}}}
|
31
|
+
#
|
32
|
+
# Parameters:
|
33
|
+
#
|
34
|
+
# +array+:: Array containing path components, usually acquired from a
|
35
|
+
# string with dot separators and a call to String#split.
|
36
|
+
#
|
37
|
+
# +value+:: The value found at the path indicated by +array+.
|
38
|
+
#
|
39
|
+
# If +array+ is empty, +value+ is returned directly, with no nesting
|
40
|
+
# Hash wrapping it.
|
41
|
+
#
|
42
|
+
def self.dot_path(array, value)
|
43
|
+
return value if array.empty?
|
44
|
+
|
45
|
+
{}.tap do | hash |
|
46
|
+
hash[array.shift()] = self.dot_path(array, value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/scimitar/version.rb
CHANGED
@@ -3,11 +3,11 @@ module Scimitar
|
|
3
3
|
# Gem version. If this changes, be sure to re-run "bundle install" or
|
4
4
|
# "bundle update".
|
5
5
|
#
|
6
|
-
VERSION = '2.
|
6
|
+
VERSION = '2.7.1'
|
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 = '2024-01-16'
|
12
12
|
|
13
13
|
end
|
data/lib/scimitar.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# For tests only - uses custom 'create' implementation which passes a block to
|
2
|
+
# Scimitar::ActiveRecordBackedResourcesController#create.
|
3
|
+
#
|
4
|
+
class CustomCreateMockUsersController < Scimitar::ActiveRecordBackedResourcesController
|
5
|
+
|
6
|
+
OVERRIDDEN_NAME = SecureRandom.uuid
|
7
|
+
|
8
|
+
def create
|
9
|
+
super do | resource |
|
10
|
+
resource.first_name = OVERRIDDEN_NAME
|
11
|
+
resource.save!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def storage_class
|
18
|
+
MockUser
|
19
|
+
end
|
20
|
+
|
21
|
+
def storage_scope
|
22
|
+
MockUser.all
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# For tests only - uses custom 'replace' implementation which passes a block to
|
2
|
+
# Scimitar::ActiveRecordBackedResourcesController#create.
|
3
|
+
#
|
4
|
+
class CustomReplaceMockUsersController < Scimitar::ActiveRecordBackedResourcesController
|
5
|
+
|
6
|
+
OVERRIDDEN_NAME = SecureRandom.uuid
|
7
|
+
|
8
|
+
def replace
|
9
|
+
super do | resource |
|
10
|
+
resource.first_name = OVERRIDDEN_NAME
|
11
|
+
resource.save!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def storage_class
|
18
|
+
MockUser
|
19
|
+
end
|
20
|
+
|
21
|
+
def storage_scope
|
22
|
+
MockUser.all
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# For tests only - uses custom 'update' implementation which passes a block to
|
2
|
+
# Scimitar::ActiveRecordBackedResourcesController#create.
|
3
|
+
#
|
4
|
+
class CustomUpdateMockUsersController < Scimitar::ActiveRecordBackedResourcesController
|
5
|
+
|
6
|
+
OVERRIDDEN_NAME = SecureRandom.uuid
|
7
|
+
|
8
|
+
def update
|
9
|
+
super do | resource |
|
10
|
+
resource.first_name = OVERRIDDEN_NAME
|
11
|
+
resource.save!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def storage_class
|
18
|
+
MockUser
|
19
|
+
end
|
20
|
+
|
21
|
+
def storage_scope
|
22
|
+
MockUser.all
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -17,10 +17,22 @@ Rails.application.routes.draw do
|
|
17
17
|
get 'Groups/:id', to: 'mock_groups#show'
|
18
18
|
patch 'Groups/:id', to: 'mock_groups#update'
|
19
19
|
|
20
|
-
# For testing blocks passed to ActiveRecordBackedResourcesController#
|
20
|
+
# For testing blocks passed to ActiveRecordBackedResourcesController#create,
|
21
|
+
# #update, #replace and #destroy.
|
21
22
|
#
|
23
|
+
post 'CustomCreateUsers', to: 'custom_create_mock_users#create'
|
24
|
+
patch 'CustomUpdateUsers/:id', to: 'custom_update_mock_users#update'
|
25
|
+
put 'CustomReplaceUsers/:id', to: 'custom_replace_mock_users#replace'
|
22
26
|
delete 'CustomDestroyUsers/:id', to: 'custom_destroy_mock_users#destroy'
|
23
27
|
|
28
|
+
# Needed because the auto-render of most of the above includes a 'url_for'
|
29
|
+
# call for a 'show' action, so we must include routes (implemented in the
|
30
|
+
# base class) for the "show" endpoint.
|
31
|
+
#
|
32
|
+
get 'CustomCreateUsers/:id', to: 'custom_create_mock_users#show'
|
33
|
+
get 'CustomUpdateUsers/:id', to: 'custom_update_mock_users#show'
|
34
|
+
get 'CustomReplaceUsers/:id', to: 'custom_replace_mock_users#show'
|
35
|
+
|
24
36
|
# For testing blocks passed to ActiveRecordBackedResourcesController#save!
|
25
37
|
#
|
26
38
|
post 'CustomSaveUsers', to: 'custom_save_mock_users#create'
|
@@ -107,6 +107,60 @@ RSpec.describe Scimitar::ApplicationController do
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
+
context 'authenticator evaluated within controller context' do
|
111
|
+
|
112
|
+
# Define a controller with a custom instance method 'valid_token'.
|
113
|
+
#
|
114
|
+
controller do
|
115
|
+
def index
|
116
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
117
|
+
end
|
118
|
+
|
119
|
+
def valid_token
|
120
|
+
'B'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Call the above controller method from the token authenticator Proc,
|
125
|
+
# proving that it was executed in the controller's context.
|
126
|
+
#
|
127
|
+
before do
|
128
|
+
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new(
|
129
|
+
token_authenticator: Proc.new do | token, options |
|
130
|
+
token == self.valid_token()
|
131
|
+
end
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'renders success when valid creds are given' do
|
136
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer B'
|
137
|
+
|
138
|
+
get :index, params: { format: :scim }
|
139
|
+
expect(response).to be_ok
|
140
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
141
|
+
expect(response.headers['WWW-Authenticate']).to eql('Bearer')
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'renders failure with bad token' do
|
145
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer Invalid'
|
146
|
+
|
147
|
+
get :index, params: { format: :scim }
|
148
|
+
expect(response).not_to be_ok
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'renders failure with blank token' do
|
152
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer'
|
153
|
+
|
154
|
+
get :index, params: { format: :scim }
|
155
|
+
expect(response).not_to be_ok
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'renders failure with missing header' do
|
159
|
+
get :index, params: { format: :scim }
|
160
|
+
expect(response).not_to be_ok
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
110
164
|
context 'authenticated' do
|
111
165
|
controller do
|
112
166
|
rescue_from StandardError, with: :handle_resource_not_found
|
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Scimitar::ComplexTypes::Address do
|
4
4
|
context '#as_json' do
|
5
|
-
it 'assumes
|
6
|
-
expect(described_class.new.as_json).to eq(
|
5
|
+
it 'assumes no defaults' do
|
6
|
+
expect(described_class.new.as_json).to eq({})
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'allows a custom address type' do
|
@@ -11,9 +11,8 @@ RSpec.describe Scimitar::ComplexTypes::Address do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'shows the set address' do
|
14
|
-
expect(described_class.new(country: 'NZ').as_json).to eq('
|
14
|
+
expect(described_class.new(country: 'NZ').as_json).to eq('country' => 'NZ')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
19
|
-
|
@@ -2717,6 +2717,28 @@ RSpec.describe Scimitar::Resources::Mixin do
|
|
2717
2717
|
expect(@instance.username).to eql('1234')
|
2718
2718
|
end
|
2719
2719
|
|
2720
|
+
it 'which updates nested values using root syntax' do
|
2721
|
+
@instance.update!(first_name: 'Foo', last_name: 'Bar')
|
2722
|
+
|
2723
|
+
path = 'name.givenName'
|
2724
|
+
path = path.upcase if force_upper_case
|
2725
|
+
|
2726
|
+
patch = {
|
2727
|
+
'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
|
2728
|
+
'Operations' => [
|
2729
|
+
{
|
2730
|
+
'op' => 'replace',
|
2731
|
+
'value' => {
|
2732
|
+
path => 'Baz'
|
2733
|
+
}
|
2734
|
+
}
|
2735
|
+
]
|
2736
|
+
}
|
2737
|
+
|
2738
|
+
@instance.from_scim_patch!(patch_hash: patch)
|
2739
|
+
expect(@instance.first_name).to eql('Baz')
|
2740
|
+
end
|
2741
|
+
|
2720
2742
|
it 'which updates nested values' do
|
2721
2743
|
@instance.update!(first_name: 'Foo', last_name: 'Bar')
|
2722
2744
|
|
@@ -13,9 +13,9 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
13
13
|
lmt = Time.parse("2023-01-09 14:25:00 +1300")
|
14
14
|
ids = 3.times.map { SecureRandom.uuid }.sort()
|
15
15
|
|
16
|
-
@u1 = MockUser.create(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
|
17
|
-
@u2 = MockUser.create(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2)
|
18
|
-
@u3 = MockUser.create(primary_key: ids.shift(), username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
|
16
|
+
@u1 = MockUser.create!(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
|
17
|
+
@u2 = MockUser.create!(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2)
|
18
|
+
@u3 = MockUser.create!(primary_key: ids.shift(), username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
|
19
19
|
|
20
20
|
@g1 = MockGroup.create!(display_name: 'Group 1')
|
21
21
|
@g2 = MockGroup.create!(display_name: 'Group 2')
|
@@ -315,7 +315,12 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
315
315
|
attributes = { userName: '4' } # Minimum required by schema
|
316
316
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
317
317
|
|
318
|
+
# Prove that certain known pathways are called; can then unit test
|
319
|
+
# those if need be and be sure that this covers #create actions.
|
320
|
+
#
|
318
321
|
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
322
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
323
|
+
|
319
324
|
expect {
|
320
325
|
post "/Users", params: attributes.merge(format: :scim)
|
321
326
|
}.to change { MockUser.count }.by(1)
|
@@ -442,23 +447,75 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
442
447
|
expect(result['detail']).to include('is reserved')
|
443
448
|
end
|
444
449
|
|
445
|
-
|
446
|
-
|
447
|
-
|
450
|
+
context 'with a block' do
|
451
|
+
it 'invokes the block' do
|
452
|
+
mock_before = MockUser.all.to_a
|
448
453
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
454
|
+
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
455
|
+
expect {
|
456
|
+
post "/CustomCreateUsers", params: {
|
457
|
+
format: :scim,
|
458
|
+
userName: '4' # Minimum required by schema
|
459
|
+
}
|
460
|
+
}.to change { MockUser.count }.by(1)
|
453
461
|
|
454
|
-
|
455
|
-
|
462
|
+
mock_after = MockUser.all.to_a
|
463
|
+
new_mock = (mock_after - mock_before).first
|
456
464
|
|
457
|
-
|
458
|
-
|
465
|
+
expect(response.status ).to eql(201)
|
466
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
459
467
|
|
460
|
-
|
461
|
-
|
468
|
+
result = JSON.parse(response.body)
|
469
|
+
|
470
|
+
expect(result['id']).to eql(new_mock.id.to_s)
|
471
|
+
expect(result['meta']['resourceType']).to eql('User')
|
472
|
+
expect(new_mock.first_name).to eql(CustomCreateMockUsersController::OVERRIDDEN_NAME)
|
473
|
+
end
|
474
|
+
|
475
|
+
it 'returns 409 for duplicates (by Rails validation)' do
|
476
|
+
existing_user = MockUser.create!(
|
477
|
+
username: '4',
|
478
|
+
first_name: 'Will Be Overridden',
|
479
|
+
last_name: 'Baz',
|
480
|
+
home_email_address: 'random@test.com',
|
481
|
+
scim_uid: '999'
|
482
|
+
)
|
483
|
+
|
484
|
+
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
485
|
+
expect {
|
486
|
+
post "/CustomCreateUsers", params: {
|
487
|
+
format: :scim,
|
488
|
+
userName: '4' # Already exists
|
489
|
+
}
|
490
|
+
}.to_not change { MockUser.count }
|
491
|
+
|
492
|
+
expect(response.status ).to eql(409)
|
493
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
494
|
+
|
495
|
+
result = JSON.parse(response.body)
|
496
|
+
|
497
|
+
expect(result['scimType']).to eql('uniqueness')
|
498
|
+
expect(result['detail']).to include('already been taken')
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'notes Rails validation failures' do
|
502
|
+
expect_any_instance_of(CustomCreateMockUsersController).to receive(:create).once.and_call_original
|
503
|
+
expect {
|
504
|
+
post "/CustomCreateUsers", params: {
|
505
|
+
format: :scim,
|
506
|
+
userName: MockUser::INVALID_USERNAME
|
507
|
+
}
|
508
|
+
}.to_not change { MockUser.count }
|
509
|
+
|
510
|
+
expect(response.status ).to eql(400)
|
511
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
512
|
+
|
513
|
+
result = JSON.parse(response.body)
|
514
|
+
|
515
|
+
expect(result['scimType']).to eql('invalidValue')
|
516
|
+
expect(result['detail']).to include('is reserved')
|
517
|
+
end
|
518
|
+
end # "context 'with a block' do"
|
462
519
|
end # "context '#create' do"
|
463
520
|
|
464
521
|
# ===========================================================================
|
@@ -469,7 +526,11 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
469
526
|
attributes = { userName: '4' } # Minimum required by schema
|
470
527
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
471
528
|
|
529
|
+
# Prove that certain known pathways are called; can then unit test
|
530
|
+
# those if need be and be sure that this covers #replace actions.
|
531
|
+
#
|
472
532
|
expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
|
533
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
473
534
|
expect {
|
474
535
|
put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
|
475
536
|
}.to_not change { MockUser.count }
|
@@ -525,7 +586,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
525
586
|
|
526
587
|
it 'notes Rails validation failures' do
|
527
588
|
expect {
|
528
|
-
|
589
|
+
put "/Users/#{@u2.primary_key}", params: {
|
529
590
|
format: :scim,
|
530
591
|
userName: MockUser::INVALID_USERNAME
|
531
592
|
}
|
@@ -562,6 +623,58 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
562
623
|
|
563
624
|
expect(result['status']).to eql('404')
|
564
625
|
end
|
626
|
+
|
627
|
+
context 'with a block' do
|
628
|
+
it 'invokes the block' do
|
629
|
+
attributes = { userName: '4' } # Minimum required by schema
|
630
|
+
|
631
|
+
expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
|
632
|
+
expect {
|
633
|
+
put "/CustomReplaceUsers/#{@u2.primary_key}", params: {
|
634
|
+
format: :scim,
|
635
|
+
userName: '4'
|
636
|
+
}
|
637
|
+
}.to_not change { MockUser.count }
|
638
|
+
|
639
|
+
expect(response.status ).to eql(200)
|
640
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
641
|
+
|
642
|
+
result = JSON.parse(response.body)
|
643
|
+
|
644
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
645
|
+
expect(result['meta']['resourceType']).to eql('User')
|
646
|
+
|
647
|
+
@u2.reload
|
648
|
+
|
649
|
+
expect(@u2.username ).to eql('4')
|
650
|
+
expect(@u2.first_name).to eql(CustomReplaceMockUsersController::OVERRIDDEN_NAME)
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'notes Rails validation failures' do
|
654
|
+
expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
|
655
|
+
expect {
|
656
|
+
put "/CustomReplaceUsers/#{@u2.primary_key}", params: {
|
657
|
+
format: :scim,
|
658
|
+
userName: MockUser::INVALID_USERNAME
|
659
|
+
}
|
660
|
+
}.to_not change { MockUser.count }
|
661
|
+
|
662
|
+
expect(response.status ).to eql(400)
|
663
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
664
|
+
|
665
|
+
result = JSON.parse(response.body)
|
666
|
+
|
667
|
+
expect(result['scimType']).to eql('invalidValue')
|
668
|
+
expect(result['detail']).to include('is reserved')
|
669
|
+
|
670
|
+
@u2.reload
|
671
|
+
|
672
|
+
expect(@u2.username).to eql('2')
|
673
|
+
expect(@u2.first_name).to eql('Foo')
|
674
|
+
expect(@u2.last_name).to eql('Bar')
|
675
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
676
|
+
end
|
677
|
+
end # "context 'with a block' do"
|
565
678
|
end # "context '#replace' do"
|
566
679
|
|
567
680
|
# ===========================================================================
|
@@ -586,7 +699,12 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
586
699
|
|
587
700
|
payload = spec_helper_hupcase(payload) if force_upper_case
|
588
701
|
|
702
|
+
# Prove that certain known pathways are called; can then unit test
|
703
|
+
# those if need be and be sure that this covers #update actions.
|
704
|
+
#
|
589
705
|
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
706
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
707
|
+
|
590
708
|
expect {
|
591
709
|
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
592
710
|
}.to_not change { MockUser.count }
|
@@ -916,12 +1034,178 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
916
1034
|
it_behaves_like 'a user remover'
|
917
1035
|
end # context 'and using a Salesforce variant payload' do
|
918
1036
|
end # "context 'when removing users from groups' do"
|
1037
|
+
|
1038
|
+
context 'with a block' do
|
1039
|
+
it 'invokes the block' do
|
1040
|
+
payload = {
|
1041
|
+
format: :scim,
|
1042
|
+
Operations: [
|
1043
|
+
{
|
1044
|
+
op: 'add',
|
1045
|
+
path: 'userName',
|
1046
|
+
value: '4'
|
1047
|
+
},
|
1048
|
+
{
|
1049
|
+
op: 'replace',
|
1050
|
+
path: 'emails[type eq "work"]',
|
1051
|
+
value: { type: 'work', value: 'work_4@test.com' }
|
1052
|
+
}
|
1053
|
+
]
|
1054
|
+
}
|
1055
|
+
|
1056
|
+
expect_any_instance_of(CustomUpdateMockUsersController).to receive(:update).once.and_call_original
|
1057
|
+
expect {
|
1058
|
+
patch "/CustomUpdateUsers/#{@u2.primary_key}", params: payload
|
1059
|
+
}.to_not change { MockUser.count }
|
1060
|
+
|
1061
|
+
expect(response.status ).to eql(200)
|
1062
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1063
|
+
|
1064
|
+
result = JSON.parse(response.body)
|
1065
|
+
|
1066
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
1067
|
+
expect(result['meta']['resourceType']).to eql('User')
|
1068
|
+
|
1069
|
+
@u2.reload
|
1070
|
+
|
1071
|
+
expect(@u2.username ).to eql('4')
|
1072
|
+
expect(@u2.first_name ).to eql(CustomUpdateMockUsersController::OVERRIDDEN_NAME)
|
1073
|
+
expect(@u2.work_email_address).to eql('work_4@test.com')
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
it 'notes Rails validation failures' do
|
1077
|
+
expect_any_instance_of(CustomUpdateMockUsersController).to receive(:update).once.and_call_original
|
1078
|
+
expect {
|
1079
|
+
patch "/CustomUpdateUsers/#{@u2.primary_key}", params: {
|
1080
|
+
format: :scim,
|
1081
|
+
Operations: [
|
1082
|
+
{
|
1083
|
+
op: 'add',
|
1084
|
+
path: 'userName',
|
1085
|
+
value: MockUser::INVALID_USERNAME
|
1086
|
+
}
|
1087
|
+
]
|
1088
|
+
}
|
1089
|
+
}.to_not change { MockUser.count }
|
1090
|
+
|
1091
|
+
expect(response.status ).to eql(400)
|
1092
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1093
|
+
|
1094
|
+
result = JSON.parse(response.body)
|
1095
|
+
|
1096
|
+
expect(result['scimType']).to eql('invalidValue')
|
1097
|
+
expect(result['detail']).to include('is reserved')
|
1098
|
+
|
1099
|
+
@u2.reload
|
1100
|
+
|
1101
|
+
expect(@u2.username).to eql('2')
|
1102
|
+
expect(@u2.first_name).to eql('Foo')
|
1103
|
+
expect(@u2.last_name).to eql('Bar')
|
1104
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
1105
|
+
end
|
1106
|
+
end # "context 'with a block' do"
|
919
1107
|
end # "context '#update' do"
|
920
1108
|
|
1109
|
+
# ===========================================================================
|
1110
|
+
# In-passing parts of tests above show that #create, #replace and #update all
|
1111
|
+
# route through #save!, so now add some unit tests for that and for exception
|
1112
|
+
# handling overrides invoked via #save!.
|
1113
|
+
# ===========================================================================
|
1114
|
+
|
1115
|
+
context 'overriding #save!' do
|
1116
|
+
it 'invokes a block if given one' do
|
1117
|
+
mock_before = MockUser.all.to_a
|
1118
|
+
attributes = { userName: '5' } # Minimum required by schema
|
1119
|
+
|
1120
|
+
expect_any_instance_of(CustomSaveMockUsersController).to receive(:create).once.and_call_original
|
1121
|
+
expect {
|
1122
|
+
post "/CustomSaveUsers", params: attributes.merge(format: :scim)
|
1123
|
+
}.to change { MockUser.count }.by(1)
|
1124
|
+
|
1125
|
+
mock_after = MockUser.all.to_a
|
1126
|
+
new_mock = (mock_after - mock_before).first
|
1127
|
+
|
1128
|
+
expect(response.status ).to eql(201)
|
1129
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1130
|
+
|
1131
|
+
expect(new_mock.username).to eql(CustomSaveMockUsersController::CUSTOM_SAVE_BLOCK_USERNAME_INDICATOR)
|
1132
|
+
end
|
1133
|
+
end # "context 'overriding #save!' do
|
1134
|
+
|
1135
|
+
context 'custom on-save exceptions' do
|
1136
|
+
MockUsersController.new.send(:scimitar_rescuable_exceptions).each do | exception_class |
|
1137
|
+
it "handles out-of-box exception #{exception_class}" do
|
1138
|
+
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1139
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1140
|
+
|
1141
|
+
expect_any_instance_of(MockUser).to receive(:save!).once { raise exception_class }
|
1142
|
+
|
1143
|
+
expect {
|
1144
|
+
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1145
|
+
}.to_not change { MockUser.count }
|
1146
|
+
|
1147
|
+
expected_status, expected_prefix = if exception_class == ActiveRecord::RecordNotUnique
|
1148
|
+
[409, 'Operation failed due to a uniqueness constraint: ']
|
1149
|
+
else
|
1150
|
+
[400, 'Operation failed since record has become invalid: ']
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
expect(response.status ).to eql(expected_status)
|
1154
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1155
|
+
|
1156
|
+
result = JSON.parse(response.body)
|
1157
|
+
|
1158
|
+
# Check basic SCIM error rendering - good enough given other tests
|
1159
|
+
# elsewhere. Exact message varies by exception.
|
1160
|
+
#
|
1161
|
+
expect(result['detail']).to start_with(expected_prefix)
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it 'handles custom exceptions' do
|
1166
|
+
exception_class = RuntimeError # (for testing only; usually, this would provoke a 500 response)
|
1167
|
+
|
1168
|
+
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1169
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1170
|
+
|
1171
|
+
expect_any_instance_of(MockUsersController).to receive(:scimitar_rescuable_exceptions).once { [ exception_class ] }
|
1172
|
+
expect_any_instance_of(MockUser ).to receive(:save! ).once { raise exception_class }
|
1173
|
+
|
1174
|
+
expect {
|
1175
|
+
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1176
|
+
}.to_not change { MockUser.count }
|
1177
|
+
|
1178
|
+
expect(response.status ).to eql(400)
|
1179
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1180
|
+
|
1181
|
+
result = JSON.parse(response.body)
|
1182
|
+
|
1183
|
+
expect(result['detail']).to start_with('Operation failed since record has become invalid: ')
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
it 'reports other exceptions as 500s' do
|
1187
|
+
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
1188
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
1189
|
+
|
1190
|
+
expect_any_instance_of(MockUser).to receive(:save!).once { raise RuntimeError }
|
1191
|
+
|
1192
|
+
expect {
|
1193
|
+
post "/Users", params: { format: :scim, userName: SecureRandom.uuid }
|
1194
|
+
}.to_not change { MockUser.count }
|
1195
|
+
|
1196
|
+
expect(response.status ).to eql(500)
|
1197
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
1198
|
+
|
1199
|
+
result = JSON.parse(response.body)
|
1200
|
+
|
1201
|
+
expect(result['detail']).to eql('RuntimeError')
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
921
1205
|
# ===========================================================================
|
922
1206
|
|
923
1207
|
context '#destroy' do
|
924
|
-
it 'deletes an item if given no
|
1208
|
+
it 'deletes an item if given no block' do
|
925
1209
|
expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
|
926
1210
|
expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
|
927
1211
|
expect {
|
data/spec/spec_helper.rb
CHANGED
@@ -30,14 +30,22 @@ RSpec.configure do | config |
|
|
30
30
|
config.disable_monkey_patching!
|
31
31
|
config.infer_spec_type_from_file_location!
|
32
32
|
config.filter_rails_from_backtrace!
|
33
|
+
config.raise_errors_for_deprecations!
|
33
34
|
|
34
35
|
config.color = true
|
35
36
|
config.tty = true
|
36
37
|
config.order = :random
|
37
|
-
config.
|
38
|
+
config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
|
38
39
|
config.use_transactional_fixtures = true
|
39
40
|
|
40
41
|
Kernel.srand config.seed
|
42
|
+
|
43
|
+
config.around :each do | example |
|
44
|
+
original_engine_configuration = Scimitar.instance_variable_get('@engine_configuration')
|
45
|
+
example.run()
|
46
|
+
ensure
|
47
|
+
Scimitar.instance_variable_set('@engine_configuration', original_engine_configuration)
|
48
|
+
end
|
41
49
|
end
|
42
50
|
|
43
51
|
# ============================================================================
|
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.7.1
|
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:
|
12
|
+
date: 2024-01-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -31,28 +31,28 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '13.
|
34
|
+
version: '13.1'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '13.
|
41
|
+
version: '13.1'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: pg
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '1.
|
48
|
+
version: '1.5'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '1.
|
55
|
+
version: '1.5'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: simplecov-rcov
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,28 +73,28 @@ dependencies:
|
|
73
73
|
requirements:
|
74
74
|
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '6.
|
76
|
+
version: '6.6'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '6.
|
83
|
+
version: '6.6'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: rspec-rails
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
88
|
- - "~>"
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '6.
|
90
|
+
version: '6.1'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
95
|
- - "~>"
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version: '6.
|
97
|
+
version: '6.1'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: byebug
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -195,10 +195,14 @@ files:
|
|
195
195
|
- lib/scimitar.rb
|
196
196
|
- lib/scimitar/engine.rb
|
197
197
|
- lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb
|
198
|
+
- lib/scimitar/support/utilities.rb
|
198
199
|
- lib/scimitar/version.rb
|
200
|
+
- spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb
|
199
201
|
- spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb
|
202
|
+
- spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb
|
200
203
|
- spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
|
201
204
|
- spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb
|
205
|
+
- spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb
|
202
206
|
- spec/apps/dummy/app/controllers/mock_groups_controller.rb
|
203
207
|
- spec/apps/dummy/app/controllers/mock_users_controller.rb
|
204
208
|
- spec/apps/dummy/app/models/mock_group.rb
|
@@ -247,7 +251,7 @@ metadata:
|
|
247
251
|
homepage_uri: https://www.ripaglobal.com/
|
248
252
|
source_code_uri: https://github.com/RIPAGlobal/scimitar/
|
249
253
|
bug_tracker_uri: https://github.com/RIPAGlobal/scimitar/issues/
|
250
|
-
changelog_uri: https://github.com/RIPAGlobal/scimitar/blob/
|
254
|
+
changelog_uri: https://github.com/RIPAGlobal/scimitar/blob/main/CHANGELOG.md
|
251
255
|
post_install_message:
|
252
256
|
rdoc_options: []
|
253
257
|
require_paths:
|
@@ -263,14 +267,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
267
|
- !ruby/object:Gem::Version
|
264
268
|
version: '0'
|
265
269
|
requirements: []
|
266
|
-
rubygems_version: 3.4
|
270
|
+
rubygems_version: 3.5.4
|
267
271
|
signing_key:
|
268
272
|
specification_version: 4
|
269
273
|
summary: SCIM v2 for Rails
|
270
274
|
test_files:
|
275
|
+
- spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb
|
271
276
|
- spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb
|
277
|
+
- spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb
|
272
278
|
- spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb
|
273
279
|
- spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb
|
280
|
+
- spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb
|
274
281
|
- spec/apps/dummy/app/controllers/mock_groups_controller.rb
|
275
282
|
- spec/apps/dummy/app/controllers/mock_users_controller.rb
|
276
283
|
- spec/apps/dummy/app/models/mock_group.rb
|