ExScimEcto

Ecto-based storage adapter for ExScim. Provides persistent storage for SCIM Users and Groups backed by any Ecto-compatible database.

Installation

Add ex_scim_ecto to your dependencies:

def deps do
  [
    {:ex_scim_ecto, "~> 0.1"}
  ]
end

Configuration

Basic setup

config :ex_scim,
  storage_strategy: ExScimEcto.StorageAdapter,
  storage_repo: MyApp.Repo,
  user_model: MyApp.Accounts.User,
  group_model: MyApp.Groups.Group

Preloading associations

config :ex_scim,
  storage_repo: MyApp.Repo,
  user_model: {MyApp.Accounts.User, preload: [:roles, :organizations]},
  group_model: {MyApp.Groups.Group, preload: [:members]}

Custom lookup key

By default, resources are looked up by :id. To use a different column:

config :ex_scim,
  user_model: {MyApp.Accounts.User, lookup_key: :resource_id},
  group_model: {MyApp.Groups.Group, lookup_key: :uuid}

Filter mapping

Map SCIM complex attribute paths to database columns:

config :ex_scim,
  user_model: {MyApp.Accounts.User,
    filter_mapping: %{
      "emails.value" => :email,
      "name.givenName" => :given_name
    }}

For association-based filtering (e.g. filtering on a has_many relation):

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}
    }}

Multi-tenant scoping

Scope all queries by a tenant discriminator column:

config :ex_scim,
  user_model: {MyApp.Accounts.User, tenant_key: :organization_id},
  group_model: {MyApp.Groups.Group, tenant_key: :organization_id}

Field mapping

Map domain fields to database 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 entry is domain_field => {db_field, to_storage_fn, from_storage_fn}.

Ecto schema requirements

Your Ecto schema must define a changeset/2 function that the adapter will call for inserts and updates:

defmodule MyApp.Accounts.User do
  use Ecto.Schema

  schema "users" do
    field :user_name, :string
    field :display_name, :string
    # ...
    timestamps()
  end

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:user_name, :display_name])
    |> validate_required([:user_name])
  end
end