Ecto-based implementation of ExScim.Storage.Adapter.
Expects the following in your application config:
config :ex_scim,
storage_repo: MyApp.Repo,
user_model: MyApp.Accounts.User,
group_model: MyApp.Groups.GroupTo preload associations:
config :ex_scim,
storage_repo: MyApp.Repo,
user_model: {MyApp.Accounts.User, preload: [:roles, :organizations]},
group_model: {MyApp.Groups.Group, preload: [:members]}To configure a custom lookup key (defaults to :id):
config :ex_scim,
user_model: {MyApp.Accounts.User, lookup_key: :resource_id},
group_model: {MyApp.Groups.Group, preload: [:members], lookup_key: :uuid}To map SCIM complex attribute paths to DB columns:
config :ex_scim,
user_model: {MyApp.Accounts.User,
filter_mapping: %{
"emails.value" => :email,
"name.givenName" => :given_name
}}To map SCIM paths to association fields (for has_many/has_one relations):
config :ex_scim,
user_model: {MyApp.Accounts.User,
preload: [:user_emails],
filter_mapping: %{
"emails.value" => {:assoc, :user_emails, :value},
"emails.type" => {:assoc, :user_emails, :type}
}}The association tuple format is {:assoc, assoc_name, field_name} where:
assoc_nameis the association name as defined in your schemafield_nameis the column in the associated table to filter on
When filtering on associations, a LEFT JOIN is automatically added and results are made DISTINCT to avoid duplicate root records.
To enable multi-tenant scoping via a discriminator column:
config :ex_scim,
user_model: {MyApp.Accounts.User, lookup_key: :id, tenant_key: :organization_id},
group_model: {MyApp.Groups.Group, tenant_key: :organization_id}When tenant_key is configured and scope.tenant_id is not nil, all queries
include a WHERE clause on the tenant column, and creates inject the tenant_id.
To map domain fields to DB columns with value transformation:
config :ex_scim,
user_model: {MyApp.Accounts.User,
field_mapping: %{
active: {:status,
fn true -> "active"; false -> "inactive" end,
fn "active" -> true; _ -> false end}
}}Each field_mapping entry is domain_field => {db_field, to_storage_fn, from_storage_fn}:
domain_field- the field name used in the domain/SCIM layer (atom)db_field- the column name in the Ecto schema (atom)to_storage_fn-(domain_value -> db_value), applied on writes and filter queriesfrom_storage_fn-(db_value -> domain_value), applied on reads
When field_mapping is configured, read operations return a plain map instead
of an Ecto struct (since the domain key may not exist on the struct). Your
mapper's to_scim/2 should accept a map rather than pattern-matching on the
struct (e.g., use def to_scim(user, ...) instead of def to_scim(%User{} = user, ...)).
field_mapping works together with filter_mapping: filter_mapping resolves
SCIM attribute paths to domain field atoms, and field_mapping then resolves
domain field atoms to DB columns with value transformation.
See also ExScim.Resources.Resource.