Skip to content

Commit

Permalink
Merge pull request #135 from RIPAGlobal/feature/attempt-schema-attrib…
Browse files Browse the repository at this point in the history
…ute-map-match

Schema-attribute-to-mapped-attribute matching for 'Schemas' endpoint
  • Loading branch information
bagp1 authored Jun 24, 2024
2 parents a6c5b95 + 69aff9b commit 835a94a
Show file tree
Hide file tree
Showing 9 changed files with 753 additions and 92 deletions.
352 changes: 349 additions & 3 deletions app/controllers/scimitar/schemas_controller.rb

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion app/models/scimitar/engine_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class EngineConfiguration
:application_controller_mixin,
:exception_reporter,
:optional_value_fields_required,
:schema_list_from_attribute_mappings,
)

def initialize(attributes = {})
Expand All @@ -22,7 +23,8 @@ def initialize(attributes = {})
# Set defaults that may be overridden by the initializer.
#
defaults = {
optional_value_fields_required: true
optional_value_fields_required: true,
schema_list_from_attribute_mappings: []
}

super(defaults.merge(attributes))
Expand Down
13 changes: 9 additions & 4 deletions app/models/scimitar/resources/mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,12 @@ module Resources
# # ...
# groups: [
# {
# list: :users, # <-- i.e. Team.users,
# list: :users, # <-- i.e. Team.users,
# using: {
# value: :id, # <-- i.e. Team.users[n].id
# display: :full_name # <-- i.e. Team.users[n].full_name
# },
# class: Team, # Optional; see below
# find_with: -> (scim_list_entry) {...} # See below
# }
# ],
Expand All @@ -159,7 +160,10 @@ module Resources
# example above, "find_with"'s Proc might look at a SCIM entry value which
# is expected to be a user ID and find that User. The mapped set of User
# data thus found would be written back with "#users=", due to the ":list"
# key declaring the method name ":users".
# key declaring the method name ":users". The optional "class" key is
# recommended but not really *needed* unless the configuration option
# Scimitar::EngineConfiguration::schema_list_from_attribute_mappings is
# defined; see documentation of that option for more information.
#
# Note that you can only use either:
#
Expand All @@ -176,7 +180,8 @@ module Resources
# == scim_mutable_attributes
#
# Define this method to return a Set (preferred) or Array of names of
# attributes which may be written in the mixing-in class.
# attributes which may be written in the mixing-in class. The names MUST be
# expressed as Symbols, *not* Strings.
#
# If you return +nil+, it is assumed that +any+ attribute mapped by
# ::scim_attributes_map which has a write accessor will be eligible for
Expand Down Expand Up @@ -291,7 +296,7 @@ module Mixin
# the result in an instance variable.
#
def scim_mutable_attributes
@scim_mutable_attributes ||= self.class.scim_mutable_attributes()
@scim_mutable_attributes ||= self.class.scim_mutable_attributes()&.map(&:to_sym)

if @scim_mutable_attributes.nil?
@scim_mutable_attributes = Set.new
Expand Down
41 changes: 41 additions & 0 deletions config/initializers/scimitar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,47 @@
# whatever that means for you receiving system in your model code.
#
# optional_value_fields_required: false

# The SCIM standard `/Schemas` endpoint lists, by default, all known schema
# definitions with the mutabilty (read-write, read-only, write-only) state
# described by those definitions, and includes all defined attributes. For
# user-defined schema, this will typically exactly match your underlying
# mapped attribute and model capability - it wouldn't make sense to define
# your own schema that misrepresented the implementation! For core SCIM RFC
# schema, though, you might want to only list actually mapped attributes.
# Further, if you happen to have a non-compliant implementation especially
# in relation to mutability of some attributes, you may want to report that
# accurately in the '/Schemas' list, for auto-discovery purposes. To switch
# to a significantly slower but more accurate render method for the list,
# driven by your resource subclasses and their attribute maps, set:
#
# schema_list_from_attribute_mappings: [...array...]
#
# ...where you provide an Array of *models*, your classes that include the
# Scimitar::Resources::Mixin module and, therefore, define an attribute map
# translating SCIM schema attributes into actual implemented data. These
# must *uniquely* describe, via the Scimitar resources they each declare in
# their Scimitar::Resources::Mixin::scim_resource_type implementation, the
# set of schemas and extended schemas you want to render. Should resources
# share schema, the '/Schemas' endpoint will fail since it cannot determine
# which model attribute map it should use and it needs the map in order to
# resolve the differences (if any) between what the schema might say, and
# what the actual underlying model supports.
#
# It is further _very_ _strongly_ _recommended_ that, for any
# +scim_attributes_map+ containing a collection which has "list:" key (for
# an associative array of zero or more entities; the Groups to which a User
# might belong is a good example) then you should also specify the "class:"
# key, giving the class used for objects in that associated collection. The
# class *must* include Scimitar::Resources::Mixin, since its own attribute
# map is consulted in order to render the part of the schema describing
# those associated properties in the owning resource. If you don't do this,
# and if you're using ActiveRecord, then Scimitar attempts association
# reflection to determine the collection class - but that's more fragile
# than just being told the exact class in the attribute map. No matter how
# this class is determined, though, it must be possible to create a simple
# instance with +new+ and no parameters, since that's needed in order to
# call Scimitar::Resources::Mixin#scim_mutable_attributes.
})

end
10 changes: 7 additions & 3 deletions spec/apps/dummy/app/models/mock_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def self.scim_attributes_map
externalId: :scim_uid,
userName: :username,
password: :password,
active: :is_active,
name: {
givenName: :first_name,
familyName: :last_name
Expand Down Expand Up @@ -81,23 +82,26 @@ def self.scim_attributes_map
}
},
],
groups: [ # NB read-only, so no :find_with key
groups: [
{
# Read-only, so no :find_with key. There's no 'class' specified here
# either, to help test the "/Schemas" endpoint's reflection code.
#
list: :mock_groups,
using: {
value: :id,
display: :display_name
}
}
],
active: :is_active,
primaryEmail: :scim_primary_email,

# Custom extension schema - see configuration in
# "spec/apps/dummy/config/initializers/scimitar.rb".
#
organization: :organization,
department: :department,
primaryEmail: :scim_primary_email,

manager: :manager,

userGroups: [
Expand Down
Loading

0 comments on commit 835a94a

Please sign in to comment.