| defmodule Plausible.Auth.SSO.Integration do |
| @moduledoc """ |
| Instance of particular SSO integration for a given team. |
| |
| Configuration is embedded and its type is dynamic, paving the |
| way for potentially supporting other SSO mechanisms in the future, |
| like OIDC. |
| |
| The UUID identifier can be used to uniquely identify the integration |
| when configuring external services like IdPs. |
| """ |
|
|
| use Ecto.Schema |
|
|
| import Ecto.Changeset |
| import PolymorphicEmbed |
|
|
| alias Plausible.Auth.SSO |
| alias Plausible.Teams |
|
|
| @type t() :: %__MODULE__{} |
|
|
| @derive {Plausible.Audit.Encoder, only: [:id, :identifier]} |
|
|
| schema "sso_integrations" do |
| field :identifier, Ecto.UUID |
|
|
| polymorphic_embeds_one :config, |
| types: [ |
| saml: SSO.SAMLConfig |
| ], |
| on_type_not_found: :raise, |
| on_replace: :update |
|
|
| belongs_to :team, Plausible.Teams.Team |
| has_many :users, Plausible.Auth.User, foreign_key: :sso_integration_id |
| has_many :sso_domains, SSO.Domain, foreign_key: :sso_integration_id |
|
|
| timestamps() |
| end |
|
|
| @spec configured?(t()) :: boolean() |
| def configured?(%__MODULE__{config: %config_mod{} = config}) do |
| config_mod.configured?(config) |
| end |
|
|
| @spec init_changeset(Teams.Team.t()) :: Ecto.Changeset.t() |
| def init_changeset(team) do |
| params = %{config: %{__type__: :saml}} |
|
|
| %__MODULE__{} |
| |> cast(params, []) |
| |> put_change(:identifier, Ecto.UUID.generate()) |
| |> cast_polymorphic_embed(:config) |
| |> put_assoc(:team, team) |
| end |
|
|
| @spec update_changeset(t(), map()) :: Ecto.Changeset.t() |
| def update_changeset(integration, config_params) do |
| params = tag_params(:saml, config_params) |
|
|
| integration |
| |> cast(params, []) |
| |> cast_polymorphic_embed(:config, |
| with: [ |
| saml: &SSO.SAMLConfig.update_changeset/2 |
| ] |
| ) |
| end |
|
|
| defp tag_params(type, params) when is_atom(type) and is_map(params) do |
| case Enum.take(params, 1) do |
| [{key, _}] when is_binary(key) -> |
| %{"config" => Map.merge(%{"__type__" => Atom.to_string(type)}, params)} |
|
|
| _ -> |
| %{config: Map.merge(%{__type__: type}, params)} |
| end |
| end |
| end |
|
|