defmodule Samly do
  @moduledoc """
  Elixir library used to enable SAML SP SSO to a Phoenix/Plug based application.
  """

  alias Plug.Conn
  alias Samly.{Assertion, State}

  @doc """
  Returns authenticated user SAML Assertion.

  The struct includes the attributes sent from IdP as well as any corresponding locally
  computed/derived attributes. Returns `nil` if the current Plug session
  is not authenticated.

  ## Parameters

  +   `conn` - Plug connection

  ## Examples

      # When there is an authenticated SAML assertion
      %Assertion{} = Samly.get_active_assertion()
  """
  @spec get_active_assertion(Conn.t()) :: nil | Assertion.t()
  def get_active_assertion(conn) do
    case Conn.get_session(conn, "samly_assertion_key") do
      {_idp_id, _nameid} = assertion_key ->
        State.get_assertion(conn, assertion_key)

      _ ->
        nil
    end
  end

  @doc """
  Returns value of the specified attribute name in the given SAML Assertion.

  Checks for the attribute in `computed` map first and `attributes` map next.
  Returns a UTF-8 binary or a list of UTF-8 binaries (in case of multi-valued)
  if the given attribute is present. Returns `nil` if attribute is not present.

  ## Parameters

  +   `assertion` - SAML assertion obtained by calling `get_active_assertion/1`
  +   `name`: Attribute name

  ## Examples

      assertion = Samly.get_active_assertion()
      # returns a list if the attribute is multi-valued
      roles = Samly.get_attribute(assertion, "roles")
      computed_fullname = Samly.get_attribute(assertion, "fullname")
  """
  @spec get_attribute(nil | Assertion.t(), Assertion.attr_name_t()) ::
          nil | Assertion.attr_value_t()
  def get_attribute(nil, _name), do: nil

  def get_attribute(%Assertion{} = assertion, name) do
    Map.get(assertion.computed, name) || Map.get(assertion.attributes, name)
  end
end
