scimitar 1.8.1 → 2.0.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/app/controllers/scimitar/active_record_backed_resources_controller.rb +20 -94
- data/app/controllers/scimitar/application_controller.rb +13 -41
- data/app/controllers/scimitar/schemas_controller.rb +0 -5
- data/app/models/scimitar/complex_types/address.rb +6 -0
- data/app/models/scimitar/engine_configuration.rb +5 -13
- data/app/models/scimitar/error_response.rb +0 -12
- data/app/models/scimitar/lists/query_parser.rb +10 -25
- data/app/models/scimitar/resource_invalid_error.rb +1 -1
- data/app/models/scimitar/resources/base.rb +4 -14
- data/app/models/scimitar/resources/mixin.rb +13 -140
- data/app/models/scimitar/schema/address.rb +0 -1
- data/app/models/scimitar/schema/attribute.rb +5 -14
- data/app/models/scimitar/schema/base.rb +1 -1
- data/app/models/scimitar/schema/vdtp.rb +1 -1
- data/app/models/scimitar/service_provider_configuration.rb +3 -14
- data/config/initializers/scimitar.rb +3 -28
- data/lib/scimitar/version.rb +2 -2
- data/lib/scimitar.rb +2 -7
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
- data/spec/apps/dummy/app/models/mock_group.rb +1 -1
- data/spec/apps/dummy/app/models/mock_user.rb +8 -36
- data/spec/apps/dummy/config/application.rb +1 -0
- data/spec/apps/dummy/config/environments/test.rb +28 -5
- data/spec/apps/dummy/config/initializers/scimitar.rb +10 -61
- data/spec/apps/dummy/config/routes.rb +7 -28
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -10
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
- data/spec/apps/dummy/db/schema.rb +4 -11
- data/spec/controllers/scimitar/application_controller_spec.rb +3 -126
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +2 -2
- data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -10
- data/spec/models/scimitar/complex_types/address_spec.rb +4 -3
- data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +9 -76
- data/spec/models/scimitar/resources/base_spec.rb +70 -208
- data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
- data/spec/models/scimitar/resources/mixin_spec.rb +43 -790
- data/spec/models/scimitar/schema/attribute_spec.rb +3 -22
- data/spec/models/scimitar/schema/base_spec.rb +1 -1
- data/spec/models/scimitar/schema/user_spec.rb +0 -10
- data/spec/requests/active_record_backed_resources_controller_spec.rb +66 -709
- data/spec/requests/application_controller_spec.rb +3 -16
- data/spec/spec_helper.rb +0 -8
- metadata +14 -25
- data/LICENSE.txt +0 -21
- data/README.md +0 -710
- data/lib/scimitar/support/utilities.rb +0 -51
- data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +0 -25
- data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +0 -24
- data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95a2166cc921a400959f9d8d4398f6bf8ecb772f8d7a0a0a73950892e85d808a
|
4
|
+
data.tar.gz: cdf5aab3812f10f69c96304e738a150f4208850267527b66d36eeb99548d7b1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0925517599b107e44fd93db9be142aebe608892c2c5069c50d22b353c51238290710474b062a002fdb010be3d783c4dea3b314f72f47b4aca3c2385a8fc1377
|
7
|
+
data.tar.gz: eef6eebfc64bb2d4adabfca110f26e3a6c9e227f47387da6fb384925899ddf0ae260cf68176f24040a6bb356cf34f6576c920bd44264d4a1fee415aeadc237e6
|
@@ -21,8 +21,6 @@ module Scimitar
|
|
21
21
|
|
22
22
|
rescue_from ActiveRecord::RecordNotFound, with: :handle_resource_not_found # See Scimitar::ApplicationController
|
23
23
|
|
24
|
-
before_action :obtain_id_column_name_from_attribute_map
|
25
|
-
|
26
24
|
# GET (list)
|
27
25
|
#
|
28
26
|
def index
|
@@ -39,13 +37,12 @@ module Scimitar
|
|
39
37
|
pagination_info = scim_pagination_info(query.count())
|
40
38
|
|
41
39
|
page_of_results = query
|
42
|
-
.order(@id_column => :asc)
|
43
40
|
.offset(pagination_info.offset)
|
44
41
|
.limit(pagination_info.limit)
|
45
42
|
.to_a()
|
46
43
|
|
47
44
|
super(pagination_info, page_of_results) do | record |
|
48
|
-
|
45
|
+
record.to_scim(location: url_for(action: :show, id: record.id))
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
@@ -54,61 +51,45 @@ module Scimitar
|
|
54
51
|
def show
|
55
52
|
super do |record_id|
|
56
53
|
record = self.find_record(record_id)
|
57
|
-
|
54
|
+
record.to_scim(location: url_for(action: :show, id: record_id))
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
61
58
|
# POST (create)
|
62
59
|
#
|
63
|
-
|
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)
|
60
|
+
def create
|
72
61
|
super do |scim_resource|
|
73
62
|
self.storage_class().transaction do
|
74
63
|
record = self.storage_class().new
|
75
64
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
76
|
-
self.save!(record
|
77
|
-
|
65
|
+
self.save!(record)
|
66
|
+
record.to_scim(location: url_for(action: :show, id: record.id))
|
78
67
|
end
|
79
68
|
end
|
80
69
|
end
|
81
70
|
|
82
71
|
# PUT (replace)
|
83
72
|
#
|
84
|
-
|
85
|
-
# block, passing the updated record which the block must persist, with the
|
86
|
-
# same rules as for #create.
|
87
|
-
#
|
88
|
-
def replace(&block)
|
73
|
+
def replace
|
89
74
|
super do |record_id, scim_resource|
|
90
75
|
self.storage_class().transaction do
|
91
76
|
record = self.find_record(record_id)
|
92
77
|
record.from_scim!(scim_hash: scim_resource.as_json())
|
93
|
-
self.save!(record
|
94
|
-
|
78
|
+
self.save!(record)
|
79
|
+
record.to_scim(location: url_for(action: :show, id: record.id))
|
95
80
|
end
|
96
81
|
end
|
97
82
|
end
|
98
83
|
|
99
84
|
# PATCH (update)
|
100
85
|
#
|
101
|
-
|
102
|
-
# block, passing the updated record which the block must persist, with the
|
103
|
-
# same rules as for #create.
|
104
|
-
#
|
105
|
-
def update(&block)
|
86
|
+
def update
|
106
87
|
super do |record_id, patch_hash|
|
107
88
|
self.storage_class().transaction do
|
108
89
|
record = self.find_record(record_id)
|
109
90
|
record.from_scim_patch!(patch_hash: patch_hash)
|
110
|
-
self.save!(record
|
111
|
-
|
91
|
+
self.save!(record)
|
92
|
+
record.to_scim(location: url_for(action: :show, id: record.id))
|
112
93
|
end
|
113
94
|
end
|
114
95
|
end
|
@@ -150,43 +131,19 @@ module Scimitar
|
|
150
131
|
raise NotImplementedError
|
151
132
|
end
|
152
133
|
|
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
|
-
|
164
134
|
# Find a record by ID. Subclasses can override this if they need special
|
165
135
|
# lookup behaviour.
|
166
136
|
#
|
167
137
|
# +record_id+:: Record ID (SCIM schema 'id' value - "our" ID).
|
168
138
|
#
|
169
139
|
def find_record(record_id)
|
170
|
-
self.storage_scope().
|
171
|
-
end
|
172
|
-
|
173
|
-
# DRY up controller actions - pass a record; returns the SCIM
|
174
|
-
# representation, with a "show" location specified via #url_for.
|
175
|
-
#
|
176
|
-
def record_to_scim(record)
|
177
|
-
record.to_scim(location: url_for(action: :show, id: record.send(@id_column)))
|
140
|
+
self.storage_scope().find(record_id)
|
178
141
|
end
|
179
142
|
|
180
143
|
# Save a record, dealing with validation exceptions by raising SCIM
|
181
144
|
# errors.
|
182
145
|
#
|
183
|
-
# +record+:: ActiveRecord subclass to save.
|
184
|
-
#
|
185
|
-
# If you just let this superclass handle things, it'll call the standard
|
186
|
-
# +#save!+ method on the record. If you pass a block, then this block is
|
187
|
-
# invoked and passed the ActiveRecord model instance to be saved. You can
|
188
|
-
# then do things like calling a different method, using a service object
|
189
|
-
# of some kind, perform audit-related operations and so-on.
|
146
|
+
# +record+:: ActiveRecord subclass to save (via #save!).
|
190
147
|
#
|
191
148
|
# The return value is not used internally, making life easier for
|
192
149
|
# overriding subclasses to "do the right thing" / avoid mistakes (instead
|
@@ -194,31 +151,11 @@ module Scimitar
|
|
194
151
|
# and relying upon this to generate correct response payloads - an early
|
195
152
|
# version of the gem did this and it caused a confusing subclass bug).
|
196
153
|
#
|
197
|
-
def save!(record
|
198
|
-
|
199
|
-
yield(record)
|
200
|
-
else
|
201
|
-
record.save!
|
202
|
-
end
|
203
|
-
rescue *self.scimitar_rescuable_exceptions() => exception
|
204
|
-
handle_on_save_exception(record, exception)
|
205
|
-
end
|
154
|
+
def save!(record)
|
155
|
+
record.save!
|
206
156
|
|
207
|
-
|
208
|
-
|
209
|
-
# validation errors defined, but falls back to the provided exception's
|
210
|
-
# message otherwise.
|
211
|
-
#
|
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.
|
215
|
-
#
|
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
|
157
|
+
rescue ActiveRecord::RecordInvalid => exception
|
158
|
+
joined_errors = record.errors.full_messages.join('; ')
|
222
159
|
|
223
160
|
# https://tools.ietf.org/html/rfc7644#page-12
|
224
161
|
#
|
@@ -228,27 +165,16 @@ module Scimitar
|
|
228
165
|
# status code 409 (Conflict) with a "scimType" error code of
|
229
166
|
# "uniqueness"
|
230
167
|
#
|
231
|
-
if
|
168
|
+
if record.errors.any? { | e | e.type == :taken }
|
232
169
|
raise Scimitar::ErrorResponse.new(
|
233
170
|
status: 409,
|
234
171
|
scimType: 'uniqueness',
|
235
|
-
detail:
|
172
|
+
detail: joined_errors
|
236
173
|
)
|
237
174
|
else
|
238
|
-
raise Scimitar::ResourceInvalidError.new(
|
175
|
+
raise Scimitar::ResourceInvalidError.new(joined_errors)
|
239
176
|
end
|
240
177
|
end
|
241
178
|
|
242
|
-
# Called via +before_action+ - stores in @id_column the name of whatever
|
243
|
-
# model column is used to store the record ID, via
|
244
|
-
# Scimitar::Resources::Mixin::scim_attributes_map.
|
245
|
-
#
|
246
|
-
# Default is <tt>:id</tt>.
|
247
|
-
#
|
248
|
-
def obtain_id_column_name_from_attribute_map
|
249
|
-
attrs = storage_class().scim_attributes_map() || {}
|
250
|
-
@id_column = attrs[:id] || :id
|
251
|
-
end
|
252
|
-
|
253
179
|
end
|
254
180
|
end
|
@@ -25,11 +25,10 @@ module Scimitar
|
|
25
25
|
#
|
26
26
|
# ...to "globally" invoke this handler if you wish.
|
27
27
|
#
|
28
|
-
# +
|
29
|
-
# via #handle_scim_error (if present).
|
28
|
+
# +_exception+:: Exception instance (currently unused).
|
30
29
|
#
|
31
|
-
def handle_resource_not_found(
|
32
|
-
handle_scim_error(NotFoundError.new(params[:id])
|
30
|
+
def handle_resource_not_found(_exception)
|
31
|
+
handle_scim_error(NotFoundError.new(params[:id]))
|
33
32
|
end
|
34
33
|
|
35
34
|
# This base controller uses:
|
@@ -39,22 +38,9 @@ module Scimitar
|
|
39
38
|
# ...to "globally" invoke this handler for all Scimitar errors (including
|
40
39
|
# subclasses).
|
41
40
|
#
|
42
|
-
# Mandatory parameters are:
|
43
|
-
#
|
44
41
|
# +error_response+:: Scimitar::ErrorResponse (or subclass) instance.
|
45
42
|
#
|
46
|
-
|
47
|
-
#
|
48
|
-
# *exception+:: If a Ruby exception was the reason this method is being
|
49
|
-
# called, pass it here. Any configured exception reporting
|
50
|
-
# mechanism will be invokved with the given parameter.
|
51
|
-
# Otherwise, the +error_response+ value is reported.
|
52
|
-
#
|
53
|
-
def handle_scim_error(error_response, exception = error_response)
|
54
|
-
unless Scimitar.engine_configuration.exception_reporter.nil?
|
55
|
-
Scimitar.engine_configuration.exception_reporter.call(exception)
|
56
|
-
end
|
57
|
-
|
43
|
+
def handle_scim_error(error_response)
|
58
44
|
render json: error_response, status: error_response.status
|
59
45
|
end
|
60
46
|
|
@@ -69,7 +55,7 @@ module Scimitar
|
|
69
55
|
# +exception+:: Exception instance.
|
70
56
|
#
|
71
57
|
def handle_bad_json_error(exception)
|
72
|
-
handle_scim_error(ErrorResponse.new(status: 400, detail: "Invalid JSON - #{exception.message}")
|
58
|
+
handle_scim_error(ErrorResponse.new(status: 400, detail: "Invalid JSON - #{exception.message}"))
|
73
59
|
end
|
74
60
|
|
75
61
|
# This base controller uses:
|
@@ -82,7 +68,7 @@ module Scimitar
|
|
82
68
|
#
|
83
69
|
def handle_unexpected_error(exception)
|
84
70
|
Rails.logger.error("#{exception.message}\n#{exception.backtrace}")
|
85
|
-
handle_scim_error(ErrorResponse.new(status: 500, detail: exception.message)
|
71
|
+
handle_scim_error(ErrorResponse.new(status: 500, detail: exception.message))
|
86
72
|
end
|
87
73
|
|
88
74
|
# =========================================================================
|
@@ -96,17 +82,12 @@ module Scimitar
|
|
96
82
|
# request and subclass processing.
|
97
83
|
#
|
98
84
|
def require_scim
|
99
|
-
|
100
|
-
|
101
|
-
if request.media_type.nil? || request.media_type.empty?
|
102
|
-
request.format = :scim
|
103
|
-
request.headers['CONTENT_TYPE'] = scim_mime_type
|
104
|
-
elsif request.media_type.downcase == scim_mime_type
|
85
|
+
if request.content_type&.downcase == Mime::Type.lookup_by_extension(:scim).to_s
|
105
86
|
request.format = :scim
|
106
87
|
elsif request.format == :scim
|
107
|
-
request.headers['CONTENT_TYPE'] =
|
88
|
+
request.headers['CONTENT_TYPE'] = Mime::Type.lookup_by_extension(:scim).to_s
|
108
89
|
else
|
109
|
-
handle_scim_error(ErrorResponse.new(status: 406, detail: "Only #{
|
90
|
+
handle_scim_error(ErrorResponse.new(status: 406, detail: "Only #{Mime::Type.lookup_by_extension(:scim)} type is accepted."))
|
110
91
|
end
|
111
92
|
end
|
112
93
|
|
@@ -124,13 +105,8 @@ module Scimitar
|
|
124
105
|
#
|
125
106
|
# https://stackoverflow.com/questions/10239970/what-is-the-delimiter-for-www-authenticate-for-multiple-schemes
|
126
107
|
#
|
127
|
-
response.set_header('
|
128
|
-
response.set_header('
|
129
|
-
|
130
|
-
# No matter what a caller might request via headers, the only content
|
131
|
-
# type we can ever respond with is JSON-for-SCIM.
|
132
|
-
#
|
133
|
-
response.set_header('Content-Type', "#{Mime::Type.lookup_by_extension(:scim)}; charset=utf-8")
|
108
|
+
response.set_header('WWW_AUTHENTICATE', 'Basic' ) if Scimitar.engine_configuration.basic_authenticator.present?
|
109
|
+
response.set_header('WWW_AUTHENTICATE', 'Bearer') if Scimitar.engine_configuration.token_authenticator.present?
|
134
110
|
end
|
135
111
|
|
136
112
|
def authenticate
|
@@ -139,15 +115,11 @@ module Scimitar
|
|
139
115
|
|
140
116
|
def authenticated?
|
141
117
|
result = if Scimitar.engine_configuration.basic_authenticator.present?
|
142
|
-
authenticate_with_http_basic
|
143
|
-
instance_exec(username, password, &Scimitar.engine_configuration.basic_authenticator)
|
144
|
-
end
|
118
|
+
authenticate_with_http_basic(&Scimitar.engine_configuration.basic_authenticator)
|
145
119
|
end
|
146
120
|
|
147
121
|
result ||= if Scimitar.engine_configuration.token_authenticator.present?
|
148
|
-
authenticate_with_http_token
|
149
|
-
instance_exec(token, options, &Scimitar.engine_configuration.token_authenticator)
|
150
|
-
end
|
122
|
+
authenticate_with_http_token(&Scimitar.engine_configuration.token_authenticator)
|
151
123
|
end
|
152
124
|
|
153
125
|
return result
|
@@ -4,11 +4,6 @@ module Scimitar
|
|
4
4
|
class SchemasController < ApplicationController
|
5
5
|
def index
|
6
6
|
schemas = Scimitar::Engine.schemas
|
7
|
-
|
8
|
-
schemas.each do |schema|
|
9
|
-
schema.meta.location = scim_schemas_url(name: schema.id)
|
10
|
-
end
|
11
|
-
|
12
7
|
schemas_by_id = schemas.reduce({}) do |hash, schema|
|
13
8
|
hash[schema.id] = schema
|
14
9
|
hash
|
@@ -7,23 +7,15 @@ module Scimitar
|
|
7
7
|
class EngineConfiguration
|
8
8
|
include ActiveModel::Model
|
9
9
|
|
10
|
-
attr_accessor
|
11
|
-
|
12
|
-
|
13
|
-
:token_authenticator,
|
14
|
-
:application_controller_mixin,
|
15
|
-
:exception_reporter,
|
16
|
-
:optional_value_fields_required,
|
17
|
-
)
|
10
|
+
attr_accessor :basic_authenticator,
|
11
|
+
:token_authenticator,
|
12
|
+
:application_controller_mixin
|
18
13
|
|
19
14
|
def initialize(attributes = {})
|
20
|
-
@uses_defaults = attributes.empty?
|
21
15
|
|
22
|
-
#
|
16
|
+
# No defaults yet - reserved for future use.
|
23
17
|
#
|
24
|
-
defaults = {
|
25
|
-
optional_value_fields_required: true
|
26
|
-
}
|
18
|
+
defaults = {}
|
27
19
|
|
28
20
|
super(defaults.merge(attributes))
|
29
21
|
end
|
@@ -16,17 +16,5 @@ module Scimitar
|
|
16
16
|
data['scimType'] = scimType if scimType
|
17
17
|
data
|
18
18
|
end
|
19
|
-
|
20
|
-
# Originally Scimitar used attribute "detail" for exception text; it was
|
21
|
-
# only for JSON responses at the time, but in hindsight was a bad choice.
|
22
|
-
# It should have been "message" given inheritance from StandardError, which
|
23
|
-
# then works properly with e.g. error reporting services.
|
24
|
-
#
|
25
|
-
# The "detail" attribute is still present, for backwards compatibility with
|
26
|
-
# any client code that might be using this class.
|
27
|
-
#
|
28
|
-
def message
|
29
|
-
self.detail
|
30
|
-
end
|
31
19
|
end
|
32
20
|
end
|
@@ -78,7 +78,7 @@ module Scimitar
|
|
78
78
|
# method's return value here.
|
79
79
|
#
|
80
80
|
def initialize(attribute_map)
|
81
|
-
@attribute_map = attribute_map
|
81
|
+
@attribute_map = attribute_map
|
82
82
|
end
|
83
83
|
|
84
84
|
# Parse SCIM filter query into RPN stack
|
@@ -192,7 +192,7 @@ module Scimitar
|
|
192
192
|
|
193
193
|
ast.push(self.start_group? ? self.parse_group() : self.pop())
|
194
194
|
|
195
|
-
|
195
|
+
unless ! ast.last.is_a?(String) || UNARY_OPERATORS.include?(ast.last.downcase)
|
196
196
|
expect_op ^= true
|
197
197
|
end
|
198
198
|
end
|
@@ -601,27 +601,12 @@ module Scimitar
|
|
601
601
|
column_names = self.activerecord_columns(scim_attribute)
|
602
602
|
value = self.activerecord_parameter(scim_parameter)
|
603
603
|
value_for_like = self.sql_modified_value(scim_operator, value)
|
604
|
-
|
605
|
-
if base_scope.model.column_names.include?(column.to_s)
|
606
|
-
arel_table[column]
|
607
|
-
elsif column.is_a?(Arel::Attribute)
|
608
|
-
column
|
609
|
-
end
|
610
|
-
end
|
611
|
-
|
612
|
-
raise Scimitar::FilterError unless arel_columns.all?
|
604
|
+
all_supported = column_names.all? { | column_name | base_scope.model.column_names.include?(column_name.to_s) }
|
613
605
|
|
614
|
-
unless
|
615
|
-
lc_scim_attribute = scim_attribute.downcase()
|
616
|
-
|
617
|
-
case_sensitive = (
|
618
|
-
lc_scim_attribute == 'id' ||
|
619
|
-
lc_scim_attribute == 'externalid' ||
|
620
|
-
lc_scim_attribute.start_with?('meta.')
|
621
|
-
)
|
622
|
-
end
|
606
|
+
raise Scimitar::FilterError unless all_supported
|
623
607
|
|
624
|
-
|
608
|
+
column_names.each.with_index do | column_name, index |
|
609
|
+
arel_column = arel_table[column_name]
|
625
610
|
arel_operation = case scim_operator
|
626
611
|
when 'eq'
|
627
612
|
if case_sensitive
|
@@ -646,9 +631,9 @@ module Scimitar
|
|
646
631
|
when 'co', 'sw', 'ew'
|
647
632
|
arel_column.matches(value_for_like, nil, case_sensitive)
|
648
633
|
when 'pr'
|
649
|
-
|
634
|
+
arel_table.grouping(arel_column.not_eq_all(['', nil]))
|
650
635
|
else
|
651
|
-
raise Scimitar::FilterError
|
636
|
+
raise Scimitar::FilterError
|
652
637
|
end
|
653
638
|
|
654
639
|
if index == 0
|
@@ -671,10 +656,10 @@ module Scimitar
|
|
671
656
|
# +scim_attribute+:: SCIM attribute from a filter string.
|
672
657
|
#
|
673
658
|
def activerecord_columns(scim_attribute)
|
674
|
-
raise Scimitar::FilterError
|
659
|
+
raise Scimitar::FilterError if scim_attribute.blank?
|
675
660
|
|
676
661
|
mapped_attribute = self.attribute_map()[scim_attribute]
|
677
|
-
raise Scimitar::FilterError
|
662
|
+
raise Scimitar::FilterError if mapped_attribute.blank?
|
678
663
|
|
679
664
|
if mapped_attribute[:ignore]
|
680
665
|
return []
|
@@ -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:
|
5
|
+
super(status: 400, scimType: 'invalidValue', detail:"Operation failed since record has become invalid: #{error_message}")
|
6
6
|
end
|
7
7
|
|
8
8
|
end
|
@@ -112,7 +112,7 @@ module Scimitar
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def self.complex_scim_attributes
|
115
|
-
|
115
|
+
schema.scim_attributes.select(&:complexType).group_by(&:name)
|
116
116
|
end
|
117
117
|
|
118
118
|
def complex_type_from_hash(scim_attribute, attr_value)
|
@@ -138,24 +138,14 @@ module Scimitar
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def as_json(options = {})
|
141
|
-
self.meta = Meta.new unless self.meta
|
142
|
-
|
143
|
-
|
144
|
-
non_returnable_attributes = self.class
|
145
|
-
.schemas
|
146
|
-
.flat_map(&:scim_attributes)
|
147
|
-
.filter_map { |attribute| attribute.name if attribute.returned == 'never' }
|
148
|
-
|
149
|
-
non_returnable_attributes << 'errors'
|
150
|
-
|
151
|
-
original_hash = super(options).except(*non_returnable_attributes)
|
141
|
+
self.meta = Meta.new unless self.meta
|
142
|
+
meta.resourceType = self.class.resource_type_id
|
143
|
+
original_hash = super(options).except('errors')
|
152
144
|
original_hash.merge!('schemas' => self.class.schemas.map(&:id))
|
153
|
-
|
154
145
|
self.class.extended_schemas.each do |extension_schema|
|
155
146
|
extension_attributes = extension_schema.scim_attributes.map(&:name)
|
156
147
|
original_hash.merge!(extension_schema.id => original_hash.extract!(*extension_attributes))
|
157
148
|
end
|
158
|
-
|
159
149
|
original_hash
|
160
150
|
end
|
161
151
|
|