scimitar 1.8.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -220,8 +220,13 @@ module Scimitar
|
|
220
220
|
# allow for different client searching "styles", given ambiguities in RFC
|
221
221
|
# 7644 filter examples).
|
222
222
|
#
|
223
|
-
# Each value is a
|
224
|
-
#
|
223
|
+
# Each value is a Hash with Symbol keys ':column', naming just one simple
|
224
|
+
# column for a mapping; ':columns', with an Array of column names that you
|
225
|
+
# want to map using 'OR' for a single search on the corresponding SCIM
|
226
|
+
# attribute; or ':ignore' with value 'true', which means that a fitler on
|
227
|
+
# the matching attribute is ignored rather than resulting in an "invalid
|
228
|
+
# filter" exception - beware possibilities for surprised clients getting a
|
229
|
+
# broader result set than expected. Example:
|
225
230
|
#
|
226
231
|
# def self.scim_queryable_attributes
|
227
232
|
# return {
|
@@ -229,27 +234,10 @@ module Scimitar
|
|
229
234
|
# 'name.familyName' => { column: :last_name },
|
230
235
|
# 'emails' => { columns: [ :work_email_address, :home_email_address ] },
|
231
236
|
# 'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
|
232
|
-
# 'emails.type' => { ignore: true }
|
233
|
-
# 'groups.value' => { column: Group.arel_table[:id] }
|
237
|
+
# 'emails.type' => { ignore: true }
|
234
238
|
# }
|
235
239
|
# end
|
236
240
|
#
|
237
|
-
# Column references can be either a Symbol representing a column within
|
238
|
-
# the resource model table, or an <tt>Arel::Attribute</tt> instance via
|
239
|
-
# e.g. <tt>MyModel.arel_table[:my_column]</tt>.
|
240
|
-
#
|
241
|
-
# === Queryable SCIM attribute options
|
242
|
-
#
|
243
|
-
# +:column+:: Just one simple column for a mapping.
|
244
|
-
#
|
245
|
-
# +:columns+:: An Array of columns that you want to map using 'OR' for a
|
246
|
-
# single search of the corresponding entity.
|
247
|
-
#
|
248
|
-
# +:ignore+:: When set to +true+, the matching attribute is ignored rather
|
249
|
-
# than resulting in an "invalid filter" exception. Beware
|
250
|
-
# possibilities for surprised clients getting a broader result
|
251
|
-
# set than expected, since a constraint may have been ignored.
|
252
|
-
#
|
253
241
|
# Filtering is currently limited and searching within e.g. arrays of data
|
254
242
|
# is not supported; only simple top-level keys can be mapped.
|
255
243
|
#
|
@@ -418,11 +406,8 @@ module Scimitar
|
|
418
406
|
def from_scim_patch!(patch_hash:)
|
419
407
|
frozen_ci_patch_hash = patch_hash.with_indifferent_case_insensitive_access().freeze()
|
420
408
|
ci_scim_hash = self.to_scim(location: '(unused)').as_json().with_indifferent_case_insensitive_access()
|
421
|
-
operations = frozen_ci_patch_hash['operations']
|
422
|
-
|
423
|
-
raise Scimitar::InvalidSyntaxError.new("Missing PATCH \"operations\"") unless operations
|
424
409
|
|
425
|
-
operations.each do |operation|
|
410
|
+
frozen_ci_patch_hash['operations'].each do |operation|
|
426
411
|
nature = operation['op' ]&.downcase
|
427
412
|
path_str = operation['path' ]
|
428
413
|
value = operation['value']
|
@@ -458,30 +443,9 @@ module Scimitar
|
|
458
443
|
ci_scim_hash = { 'root' => ci_scim_hash }.with_indifferent_case_insensitive_access()
|
459
444
|
end
|
460
445
|
|
461
|
-
# Handle extension schema. Contributed by @bettysteger and
|
462
|
-
# @MorrisFreeman via:
|
463
|
-
#
|
464
|
-
# https://github.com/RIPAGlobal/scimitar/issues/48
|
465
|
-
# https://github.com/RIPAGlobal/scimitar/pull/49
|
466
|
-
#
|
467
|
-
# Note the ":" separating the schema ID (URN) from the attribute.
|
468
|
-
# The nature of JSON rendering / other payloads might lead you to
|
469
|
-
# expect a "." as with any complex types, but that's not the case;
|
470
|
-
# see https://tools.ietf.org/html/rfc7644#section-3.10, or
|
471
|
-
# https://tools.ietf.org/html/rfc7644#section-3.5.2 of which in
|
472
|
-
# particular, https://tools.ietf.org/html/rfc7644#page-35.
|
473
|
-
#
|
474
|
-
paths = []
|
475
|
-
self.class.scim_resource_type.extended_schemas.each do |schema|
|
476
|
-
path_str.downcase.split(schema.id.downcase + ':').drop(1).each do |path|
|
477
|
-
paths += [schema.id] + path.split('.')
|
478
|
-
end
|
479
|
-
end
|
480
|
-
paths = path_str.split('.') if paths.empty?
|
481
|
-
|
482
446
|
self.from_patch_backend!(
|
483
447
|
nature: nature,
|
484
|
-
path:
|
448
|
+
path: (path_str || '').split('.'),
|
485
449
|
value: value,
|
486
450
|
altering_hash: ci_scim_hash
|
487
451
|
)
|
@@ -652,19 +616,7 @@ module Scimitar
|
|
652
616
|
attrs_map_or_leaf_value.each do | scim_attribute, sub_attrs_map_or_leaf_value |
|
653
617
|
next if scim_attribute&.to_s&.downcase == 'id' && path.empty?
|
654
618
|
|
655
|
-
|
656
|
-
# @MorrisFreeman via:
|
657
|
-
#
|
658
|
-
# https://github.com/RIPAGlobal/scimitar/issues/48
|
659
|
-
# https://github.com/RIPAGlobal/scimitar/pull/49
|
660
|
-
#
|
661
|
-
attribute_tree = []
|
662
|
-
resource_class.extended_schemas.each do |schema|
|
663
|
-
attribute_tree << schema.id and break if schema.scim_attributes.any? { |attribute| attribute.name == scim_attribute.to_s }
|
664
|
-
end
|
665
|
-
attribute_tree << scim_attribute.to_s
|
666
|
-
|
667
|
-
sub_scim_hash_or_leaf_value = scim_hash_or_leaf_value&.dig(*attribute_tree)
|
619
|
+
sub_scim_hash_or_leaf_value = scim_hash_or_leaf_value&.dig(scim_attribute.to_s)
|
668
620
|
|
669
621
|
self.from_scim_backend!(
|
670
622
|
attrs_map_or_leaf_value: sub_attrs_map_or_leaf_value,
|
@@ -949,89 +901,10 @@ module Scimitar
|
|
949
901
|
else
|
950
902
|
altering_hash[path_component] = value
|
951
903
|
end
|
952
|
-
|
953
904
|
when 'replace'
|
954
|
-
|
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)
|
959
|
-
else
|
960
|
-
altering_hash[path_component] = value
|
961
|
-
end
|
962
|
-
|
963
|
-
# The array check handles payloads seen from e.g. Microsoft for
|
964
|
-
# remove-user-from-group, where contrary to examples in the RFC
|
965
|
-
# which would imply "payload removes all users", there is the
|
966
|
-
# clear intent to remove just one.
|
967
|
-
#
|
968
|
-
# https://tools.ietf.org/html/rfc7644#section-3.5.2.2
|
969
|
-
# https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
|
970
|
-
#
|
971
|
-
# Since remove-all in the face of remove-one is destructive, we
|
972
|
-
# do a special check here to see if there's an array value for
|
973
|
-
# the array path that the payload yielded. If so, we can match
|
974
|
-
# each value against array items and remove just those items.
|
975
|
-
#
|
976
|
-
# There is an additional special case to handle a bad example
|
977
|
-
# from Salesforce:
|
978
|
-
#
|
979
|
-
# https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
|
980
|
-
#
|
905
|
+
altering_hash[path_component] = value
|
981
906
|
when 'remove'
|
982
|
-
|
983
|
-
|
984
|
-
# Handle bad Salesforce example. That might be simply a
|
985
|
-
# documentation error, but just in case...
|
986
|
-
#
|
987
|
-
value = value.values.first if (
|
988
|
-
path_component&.downcase == 'members' &&
|
989
|
-
value.is_a?(Hash) &&
|
990
|
-
value.keys.size == 1 &&
|
991
|
-
value.keys.first&.downcase == 'members'
|
992
|
-
)
|
993
|
-
|
994
|
-
# The Microsoft example provides an array of values, but we
|
995
|
-
# may as well cope with a value specified 'flat'. Promote
|
996
|
-
# such a thing to an Array to simplify the following code.
|
997
|
-
#
|
998
|
-
value = [value] unless value.is_a?(Array)
|
999
|
-
|
1000
|
-
# For each value item, delete matching array entries. The
|
1001
|
-
# concept of "matching" is:
|
1002
|
-
#
|
1003
|
-
# * For simple non-Hash values (if possible) just delete on
|
1004
|
-
# an exact match
|
1005
|
-
#
|
1006
|
-
# * For Hash-based values, only delete if all 'patch' keys
|
1007
|
-
# are present in the resource and all values thus match.
|
1008
|
-
#
|
1009
|
-
# Special case to ignore '$ref' from the Microsoft payload.
|
1010
|
-
#
|
1011
|
-
# Note coercion to strings to account for SCIM vs the usual
|
1012
|
-
# tricky case of underlying implementations with (say)
|
1013
|
-
# integer primary keys, which all end up as strings anyway.
|
1014
|
-
#
|
1015
|
-
value.each do | value_item |
|
1016
|
-
altering_hash[path_component].delete_if do | item |
|
1017
|
-
if item.is_a?(Hash) && value_item.is_a?(Hash)
|
1018
|
-
matched_all = true
|
1019
|
-
value_item.each do | value_key, value_value |
|
1020
|
-
next if value_key == '$ref'
|
1021
|
-
if ! item.key?(value_key) || item[value_key]&.to_s != value_value&.to_s
|
1022
|
-
matched_all = false
|
1023
|
-
end
|
1024
|
-
end
|
1025
|
-
matched_all
|
1026
|
-
else
|
1027
|
-
item&.to_s == value_item&.to_s
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
end
|
1031
|
-
else
|
1032
|
-
altering_hash.delete(path_component)
|
1033
|
-
end
|
1034
|
-
|
907
|
+
altering_hash.delete(path_component)
|
1035
908
|
end
|
1036
909
|
end
|
1037
910
|
end
|
@@ -10,7 +10,6 @@ module Scimitar
|
|
10
10
|
def self.scim_attributes
|
11
11
|
@scim_attributes ||= [
|
12
12
|
Attribute.new(name: 'type', type: 'string'),
|
13
|
-
Attribute.new(name: 'primary', type: 'boolean'),
|
14
13
|
Attribute.new(name: 'formatted', type: 'string'),
|
15
14
|
Attribute.new(name: 'streetAddress', type: 'string'),
|
16
15
|
Attribute.new(name: 'locality', type: 'string'),
|
@@ -93,23 +93,14 @@ module Scimitar
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def valid_simple_type?(value)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
errors.add(self.name, "has the wrong type. It has to be a(n) #{self.type}.") unless valid
|
102
|
-
end
|
96
|
+
valid = (type == 'string' && value.is_a?(String)) ||
|
97
|
+
(type == 'boolean' && (value.is_a?(TrueClass) || value.is_a?(FalseClass))) ||
|
98
|
+
(type == 'integer' && (value.is_a?(Integer))) ||
|
99
|
+
(type == 'dateTime' && valid_date_time?(value))
|
100
|
+
errors.add(self.name, "has the wrong type. It has to be a(n) #{self.type}.") unless valid
|
103
101
|
valid
|
104
102
|
end
|
105
103
|
|
106
|
-
def simple_type?(value)
|
107
|
-
(type == 'string' && value.is_a?(String)) ||
|
108
|
-
(type == 'boolean' && (value.is_a?(TrueClass) || value.is_a?(FalseClass))) ||
|
109
|
-
(type == 'integer' && (value.is_a?(Integer))) ||
|
110
|
-
(type == 'dateTime' && valid_date_time?(value))
|
111
|
-
end
|
112
|
-
|
113
104
|
def valid_date_time?(value)
|
114
105
|
!!Time.iso8601(value)
|
115
106
|
rescue ArgumentError
|
@@ -13,7 +13,7 @@ module Scimitar
|
|
13
13
|
|
14
14
|
# Converts the schema to its json representation that will be returned by /SCHEMAS end-point of a SCIM service provider.
|
15
15
|
def as_json(options = {})
|
16
|
-
@meta.location
|
16
|
+
@meta.location = Scimitar::Engine.routes.url_helpers.scim_schemas_path(name: id)
|
17
17
|
original = super
|
18
18
|
original.merge('attributes' => original.delete('scim_attributes'))
|
19
19
|
end
|
@@ -7,7 +7,7 @@ module Scimitar
|
|
7
7
|
class Vdtp < Base
|
8
8
|
def self.scim_attributes
|
9
9
|
@scim_attributes ||= [
|
10
|
-
Attribute.new(name: 'value', type: 'string', required:
|
10
|
+
Attribute.new(name: 'value', type: 'string', required: true),
|
11
11
|
Attribute.new(name: 'display', type: 'string', mutability: 'readOnly'),
|
12
12
|
Attribute.new(name: 'type', type: 'string'),
|
13
13
|
Attribute.new(name: 'primary', type: 'boolean'),
|
@@ -9,22 +9,11 @@ module Scimitar
|
|
9
9
|
class ServiceProviderConfiguration
|
10
10
|
include ActiveModel::Model
|
11
11
|
|
12
|
-
attr_accessor
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:bulk,
|
16
|
-
:filter,
|
17
|
-
:changePassword,
|
18
|
-
:sort,
|
19
|
-
:etag,
|
20
|
-
:authenticationSchemes,
|
21
|
-
:schemas,
|
22
|
-
:meta,
|
23
|
-
)
|
12
|
+
attr_accessor :patch, :bulk, :filter, :changePassword,
|
13
|
+
:sort, :etag, :authenticationSchemes,
|
14
|
+
:schemas, :meta
|
24
15
|
|
25
16
|
def initialize(attributes = {})
|
26
|
-
@uses_defaults = attributes.empty?
|
27
|
-
|
28
17
|
defaults = {
|
29
18
|
bulk: Supportable.unsupported,
|
30
19
|
changePassword: Supportable.unsupported,
|
@@ -38,10 +38,9 @@ Rails.application.config.to_prepare do # (required for >= Rails 7 / Zeitwerk)
|
|
38
38
|
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
39
39
|
|
40
40
|
# If you have filters you want to run for any Scimitar action/route, you
|
41
|
-
# can define them here.
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# customise how Scimitar generates URLs:
|
41
|
+
# can define them here. For example, you might use a before-action to set
|
42
|
+
# up some multi-tenancy related state, or skip Rails CSRF token
|
43
|
+
# verification. For example:
|
45
44
|
#
|
46
45
|
# application_controller_mixin: Module.new do
|
47
46
|
# def self.included(base)
|
@@ -55,10 +54,6 @@ Rails.application.config.to_prepare do # (required for >= Rails 7 / Zeitwerk)
|
|
55
54
|
# prepend_before_action :setup_some_kind_of_multi_tenancy_data
|
56
55
|
# end
|
57
56
|
# end
|
58
|
-
#
|
59
|
-
# def scim_schemas_url(options)
|
60
|
-
# super(custom_param: 'value', **options)
|
61
|
-
# end
|
62
57
|
# end, # ...other configuration entries might follow...
|
63
58
|
|
64
59
|
# If you want to support username/password authentication:
|
@@ -86,26 +81,6 @@ Rails.application.config.to_prepare do # (required for >= Rails 7 / Zeitwerk)
|
|
86
81
|
# Note that both basic and token authentication can be declared, with the
|
87
82
|
# parameters in the inbound HTTP request determining which is invoked.
|
88
83
|
|
89
|
-
# Scimitar rescues certain error cases and exceptions, in order to return a
|
90
|
-
# JSON response to the API caller. If you want exceptions to also be
|
91
|
-
# reported to a third party system such as sentry.io or raygun.com, you can
|
92
|
-
# configure a Proc to do so. It is passed a Ruby exception subclass object.
|
93
|
-
# For example, a minimal sentry.io reporter might do this:
|
94
|
-
#
|
95
|
-
# exception_reporter: Proc.new do | exception |
|
96
|
-
# Sentry.capture_exception(exception)
|
97
|
-
# end
|
98
|
-
#
|
99
|
-
# You will still need to configure your reporting system according to its
|
100
|
-
# documentation (e.g. via a Rails "config/initializers/<foo>.rb" file).
|
101
|
-
|
102
|
-
# Scimilar treats "VDTP" (Value, Display, Type, Primary) attribute values,
|
103
|
-
# used for e.g. e-mail addresses or phone numbers, as required by default.
|
104
|
-
# If you encounter a service which calls these with e.g. "null" value data,
|
105
|
-
# you can configure all values to be optional. You'll need to deal with
|
106
|
-
# whatever that means for you receiving system in your model code.
|
107
|
-
#
|
108
|
-
# optional_value_fields_required: false
|
109
84
|
})
|
110
85
|
|
111
86
|
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 = '
|
6
|
+
VERSION = '2.0.0'
|
7
7
|
|
8
8
|
# Date for VERSION. If this changes, be sure to re-run "bundle install"
|
9
9
|
# or "bundle update".
|
10
10
|
#
|
11
|
-
DATE = '
|
11
|
+
DATE = '2022-03-04'
|
12
12
|
|
13
13
|
end
|
data/lib/scimitar.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
require 'scimitar/version'
|
2
2
|
require 'scimitar/support/hash_with_indifferent_case_insensitive_access'
|
3
|
-
require 'scimitar/support/utilities'
|
4
3
|
require 'scimitar/engine'
|
5
4
|
|
6
5
|
module Scimitar
|
7
6
|
def self.service_provider_configuration=(custom_configuration)
|
8
|
-
|
9
|
-
@service_provider_configuration = custom_configuration
|
10
|
-
end
|
7
|
+
@service_provider_configuration = custom_configuration
|
11
8
|
end
|
12
9
|
|
13
10
|
def self.service_provider_configuration(location:)
|
@@ -17,9 +14,7 @@ module Scimitar
|
|
17
14
|
end
|
18
15
|
|
19
16
|
def self.engine_configuration=(custom_configuration)
|
20
|
-
|
21
|
-
@engine_configuration = custom_configuration
|
22
|
-
end
|
17
|
+
@engine_configuration = custom_configuration
|
23
18
|
end
|
24
19
|
|
25
20
|
def self.engine_configuration
|
@@ -1,24 +1,18 @@
|
|
1
1
|
class MockUser < ActiveRecord::Base
|
2
2
|
|
3
|
-
self.primary_key = :primary_key
|
4
|
-
|
5
3
|
# ===========================================================================
|
6
4
|
# TEST ATTRIBUTES - see db/migrate/20210304014602_create_mock_users.rb etc.
|
7
5
|
# ===========================================================================
|
8
6
|
|
9
7
|
READWRITE_ATTRS = %w{
|
10
|
-
|
8
|
+
id
|
11
9
|
scim_uid
|
12
10
|
username
|
13
|
-
password
|
14
11
|
first_name
|
15
12
|
last_name
|
16
13
|
work_email_address
|
17
14
|
home_email_address
|
18
15
|
work_phone_number
|
19
|
-
organization
|
20
|
-
department
|
21
|
-
mock_groups
|
22
16
|
}
|
23
17
|
|
24
18
|
has_and_belongs_to_many :mock_groups
|
@@ -44,10 +38,9 @@ class MockUser < ActiveRecord::Base
|
|
44
38
|
|
45
39
|
def self.scim_attributes_map
|
46
40
|
return {
|
47
|
-
id: :
|
41
|
+
id: :id,
|
48
42
|
externalId: :scim_uid,
|
49
43
|
userName: :username,
|
50
|
-
password: :password,
|
51
44
|
name: {
|
52
45
|
givenName: :first_name,
|
53
46
|
familyName: :last_name
|
@@ -89,23 +82,7 @@ class MockUser < ActiveRecord::Base
|
|
89
82
|
}
|
90
83
|
}
|
91
84
|
],
|
92
|
-
active: :is_active
|
93
|
-
|
94
|
-
# Custom extension schema - see configuration in
|
95
|
-
# "spec/apps/dummy/config/initializers/scimitar.rb".
|
96
|
-
#
|
97
|
-
organization: :organization,
|
98
|
-
department: :department,
|
99
|
-
userGroups: [
|
100
|
-
{
|
101
|
-
list: :mock_groups,
|
102
|
-
find_with: ->(value) { MockGroup.find(value["value"]) },
|
103
|
-
using: {
|
104
|
-
value: :id,
|
105
|
-
display: :display_name
|
106
|
-
}
|
107
|
-
}
|
108
|
-
]
|
85
|
+
active: :is_active
|
109
86
|
}
|
110
87
|
end
|
111
88
|
|
@@ -115,16 +92,11 @@ class MockUser < ActiveRecord::Base
|
|
115
92
|
|
116
93
|
def self.scim_queryable_attributes
|
117
94
|
return {
|
118
|
-
'
|
119
|
-
'
|
120
|
-
'
|
121
|
-
'
|
122
|
-
'
|
123
|
-
'groups' => { column: MockGroup.arel_table[:id] },
|
124
|
-
'groups.value' => { column: MockGroup.arel_table[:id] },
|
125
|
-
'emails' => { columns: [ :work_email_address, :home_email_address ] },
|
126
|
-
'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
|
127
|
-
'emails.type' => { ignore: true } # We can't filter on that; it'll just search all e-mails
|
95
|
+
'name.givenName' => { column: :first_name },
|
96
|
+
'name.familyName' => { column: :last_name },
|
97
|
+
'emails' => { columns: [ :work_email_address, :home_email_address ] },
|
98
|
+
'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
|
99
|
+
'emails.type' => { ignore: true } # We can't filter on that; it'll just search all e-mails
|
128
100
|
}
|
129
101
|
end
|
130
102
|
|
@@ -1,15 +1,38 @@
|
|
1
|
+
require 'active_support/core_ext/integer/time'
|
2
|
+
|
1
3
|
Rails.application.configure do
|
2
4
|
config.cache_classes = true
|
3
5
|
config.eager_load = false
|
4
|
-
config.serve_static_files = true
|
5
|
-
config.static_cache_control = 'public, max-age=3600'
|
6
|
-
config.consider_all_requests_local = true
|
7
6
|
|
8
|
-
|
7
|
+
# Configure public file server for tests with Cache-Control for performance.
|
8
|
+
config.public_file_server.enabled = true
|
9
|
+
config.public_file_server.headers = {
|
10
|
+
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
|
11
|
+
}
|
9
12
|
|
13
|
+
# Show full error reports and disable caching.
|
14
|
+
config.consider_all_requests_local = true
|
10
15
|
config.action_controller.perform_caching = false
|
16
|
+
config.cache_store = :null_store
|
17
|
+
|
18
|
+
# Raise exceptions instead of rendering exception templates.
|
19
|
+
config.action_dispatch.show_exceptions = false
|
20
|
+
|
21
|
+
# Disable request forgery protection in test environment.
|
11
22
|
config.action_controller.allow_forgery_protection = false
|
12
23
|
|
13
|
-
|
24
|
+
# Print deprecation notices to the stderr.
|
14
25
|
config.active_support.deprecation = :stderr
|
26
|
+
|
27
|
+
# Raise exceptions for disallowed deprecations.
|
28
|
+
config.active_support.disallowed_deprecation = :raise
|
29
|
+
|
30
|
+
# Tell Active Support which deprecation messages to disallow.
|
31
|
+
config.active_support.disallowed_deprecation_warnings = []
|
32
|
+
|
33
|
+
# Raises error for missing translations.
|
34
|
+
config.i18n.raise_on_missing_translations = true
|
35
|
+
|
36
|
+
# Annotate rendered view with file names.
|
37
|
+
# config.action_view.annotate_rendered_view_with_filenames = true
|
15
38
|
end
|
@@ -1,67 +1,16 @@
|
|
1
1
|
# Test app configuration.
|
2
2
|
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# Further, https://github.com/RIPAGlobal/scimitar/pull/54 fixed warning
|
13
|
-
# messages in a way that worked on Rails 6+ but, for V1 Scimitar, it would
|
14
|
-
# break existing working setups that didn't use the +to_prepare+ wrapper. Their
|
15
|
-
# application configuration would be written *first* but then *overwritten* by
|
16
|
-
# the default +to_prepare+ block in Scimitar itself, since that runs later. The
|
17
|
-
# file below does *not* use +to_prepare+ in order to test the workaround that
|
18
|
-
# was produced; it should work on all Ruby versions as-is.
|
19
|
-
#
|
20
|
-
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
21
|
-
|
22
|
-
application_controller_mixin: Module.new do
|
23
|
-
def self.included(base)
|
24
|
-
base.class_eval do
|
25
|
-
def test_hook; end
|
26
|
-
before_action :test_hook
|
3
|
+
Rails.application.config.to_prepare do
|
4
|
+
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
5
|
+
|
6
|
+
application_controller_mixin: Module.new do
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval do
|
9
|
+
def test_hook; end
|
10
|
+
before_action :test_hook
|
11
|
+
end
|
27
12
|
end
|
28
13
|
end
|
29
14
|
|
30
|
-
|
31
|
-
super(test: 1, **options)
|
32
|
-
end
|
33
|
-
|
34
|
-
def scim_resource_type_url(options)
|
35
|
-
super(test: 1, **options)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
})
|
40
|
-
|
41
|
-
module ScimSchemaExtensions
|
42
|
-
module User
|
43
|
-
class Enterprise < Scimitar::Schema::Base
|
44
|
-
def initialize(options = {})
|
45
|
-
super(
|
46
|
-
name: 'ExtendedUser',
|
47
|
-
description: 'Enterprise extension for a User',
|
48
|
-
id: self.class.id,
|
49
|
-
scim_attributes: self.class.scim_attributes
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.id
|
54
|
-
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.scim_attributes
|
58
|
-
[
|
59
|
-
Scimitar::Schema::Attribute.new(name: 'organization', type: 'string'),
|
60
|
-
Scimitar::Schema::Attribute.new(name: 'department', type: 'string')
|
61
|
-
]
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
15
|
+
})
|
65
16
|
end
|
66
|
-
|
67
|
-
Scimitar::Resources::User.extend_schema ScimSchemaExtensions::User::Enterprise
|
@@ -6,38 +6,17 @@
|
|
6
6
|
Rails.application.routes.draw do
|
7
7
|
mount Scimitar::Engine, at: '/'
|
8
8
|
|
9
|
-
get 'Users',
|
10
|
-
get 'Users/:id',
|
11
|
-
post 'Users',
|
12
|
-
put 'Users/:id',
|
13
|
-
patch 'Users/:id',
|
14
|
-
delete 'Users/:id',
|
9
|
+
get 'Users', to: 'mock_users#index'
|
10
|
+
get 'Users/:id', to: 'mock_users#show'
|
11
|
+
post 'Users', to: 'mock_users#create'
|
12
|
+
put 'Users/:id', to: 'mock_users#replace'
|
13
|
+
patch 'Users/:id', to: 'mock_users#update'
|
14
|
+
delete 'Users/:id', to: 'mock_users#destroy'
|
15
15
|
|
16
|
-
|
17
|
-
get 'Groups/:id', to: 'mock_groups#show'
|
18
|
-
patch 'Groups/:id', to: 'mock_groups#update'
|
19
|
-
|
20
|
-
# For testing blocks passed to ActiveRecordBackedResourcesController#create,
|
21
|
-
# #update, #replace and #destroy.
|
16
|
+
# For testing blocks passed to ActiveRecordBackedResourcesController#destroy
|
22
17
|
#
|
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'
|
26
18
|
delete 'CustomDestroyUsers/:id', to: 'custom_destroy_mock_users#destroy'
|
27
19
|
|
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
|
-
|
36
|
-
# For testing blocks passed to ActiveRecordBackedResourcesController#save!
|
37
|
-
#
|
38
|
-
post 'CustomSaveUsers', to: 'custom_save_mock_users#create'
|
39
|
-
get 'CustomSaveUsers/:id', to: 'custom_save_mock_users#show'
|
40
|
-
|
41
20
|
# For testing environment inside Scimitar::ApplicationController subclasses.
|
42
21
|
#
|
43
22
|
get 'CustomRequestVerifiers', to: 'custom_request_verifiers#index'
|