diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..26cac54 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,11 @@ +[ + inputs: [ + "mix.exs", + "{config,lib,test}/**/*.{ex,exs}" + ], + + locals_without_parens: [ + plug: 1, + plug: 2 + ] +] diff --git a/lib/samly.ex b/lib/samly.ex index ff1e0ea..739b01e 100644 --- a/lib/samly.ex +++ b/lib/samly.ex @@ -17,9 +17,10 @@ defmodule Samly do - conn: Plug connection """ - @spec get_active_assertion(Conn.t) :: Assertion.t + @spec get_active_assertion(Conn.t()) :: Assertion.t() def get_active_assertion(conn) do nameid = conn |> Conn.get_session("samly_nameid") + case State.get_by_nameid(nameid) do {^nameid, saml_assertion} -> saml_assertion _ -> nil @@ -37,8 +38,9 @@ defmodule Samly do - assertion: SAML assertion obtained by calling `get_active_assertion/1` - name: Attribute name """ - @spec get_attribute(nil | Assertion.t, String.t) :: nil | String.t + @spec get_attribute(nil | Assertion.t(), String.t()) :: nil | String.t() def get_attribute(nil, _name), do: nil + def get_attribute(%Assertion{} = assertion, name) do computed = assertion.computed attributes = assertion.attributes diff --git a/lib/samly/assertion.ex b/lib/samly/assertion.ex index d911df2..e9965fb 100644 --- a/lib/samly/assertion.ex +++ b/lib/samly/assertion.ex @@ -13,34 +13,31 @@ defmodule Samly.Assertion do """ require Samly.Esaml - alias Samly.Esaml - alias Samly.Subject + alias Samly.{Esaml, Subject} - defstruct [ - version: "2.0", - issue_instant: "", - recipient: "", - issuer: "", - subject: %Subject{}, - conditions: %{}, - attributes: %{}, - authn: %{}, - computed: %{}, - idp_id: "" - ] + defstruct version: "2.0", + issue_instant: "", + recipient: "", + issuer: "", + subject: %Subject{}, + conditions: %{}, + attributes: %{}, + authn: %{}, + computed: %{}, + idp_id: "" @type t :: %__MODULE__{ - version: String.t, - issue_instant: String.t, - recipient: String.t, - issuer: String.t, - subject: Subject.t, - conditions: map, - attributes: map, - authn: map, - computed: map, - idp_id: String.t - } + version: String.t(), + issue_instant: String.t(), + recipient: String.t(), + issuer: String.t(), + subject: Subject.t(), + conditions: map, + attributes: map, + authn: map, + computed: map, + idp_id: String.t() + } @doc false def from_rec(assertion_rec) do diff --git a/lib/samly/auth_handler.ex b/lib/samly/auth_handler.ex index 4a8db35..ec367eb 100644 --- a/lib/samly/auth_handler.ex +++ b/lib/samly/auth_handler.ex @@ -26,10 +26,11 @@ defmodule Samly.AuthHandler do """ def valid_referer?(conn) do - referer = case conn |> get_req_header("referer") do - [uri] -> URI.parse(uri) - _ -> %URI{} - end + referer = + case conn |> get_req_header("referer") do + [uri] -> URI.parse(uri) + _ -> %URI{} + end [request_authority] = conn |> get_req_header("host") request_authority == referer.authority && referer.scheme == Atom.to_string(conn.scheme) @@ -38,26 +39,26 @@ defmodule Samly.AuthHandler do def initiate_sso_req(conn) do import Plug.CSRFProtection, only: [get_csrf_token: 0] - with true <- valid_referer?(conn), - target_url = conn.params["target_url"], - target_url = (if target_url, do: URI.decode_www_form(target_url), else: nil) - do + with true <- valid_referer?(conn), target_url = conn.params["target_url"] do + target_url = if target_url, do: URI.decode_www_form(target_url), else: nil + opts = [ action: conn.request_path, - target_url: (if target_url, do: URI.encode_www_form(target_url), else: nil), + target_url: target_url, csrf_token: get_csrf_token() ] conn - |> put_resp_header("Content-Type", "text/html") - |> send_resp(200, EEx.eval_string(@sso_init_resp_template, opts)) + |> put_resp_header("Content-Type", "text/html") + |> send_resp(200, EEx.eval_string(@sso_init_resp_template, opts)) else _ -> conn |> send_resp(403, "invalid_request") end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end def send_signin_req(conn) do @@ -65,30 +66,34 @@ defmodule Samly.AuthHandler do %IdpData{esaml_idp_rec: idp_rec, esaml_sp_rec: sp_rec} = idp sp = ensure_sp_uris_set(sp_rec, conn) - target_url = conn.params["target_url"] || "/" - |> URI.decode_www_form() - + target_url = (conn.params["target_url"] || "/") |> URI.decode_www_form() nameid = get_session(conn, "samly_nameid") + case State.get_by_nameid(nameid) do {^nameid, %Assertion{idp_id: ^idp_id}} -> - conn - |> redirect(302, target_url) + conn |> redirect(302, target_url) + _ -> relay_state = State.gen_id() {idp_signin_url, req_xml_frag} = Helper.gen_idp_signin_req(sp, idp_rec) conn - |> configure_session(renew: true) - |> put_session("relay_state", relay_state) - |> put_session("idp_id", idp_id) - |> put_session("target_url", target_url) - |> send_saml_request(idp_signin_url, idp.use_redirect_for_req, - req_xml_frag, relay_state |> URI.encode_www_form()) + |> configure_session(renew: true) + |> put_session("relay_state", relay_state) + |> put_session("idp_id", idp_id) + |> put_session("target_url", target_url) + |> send_saml_request( + idp_signin_url, + idp.use_redirect_for_req, + req_xml_frag, + relay_state |> URI.encode_www_form() + ) end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end def send_signout_req(conn) do @@ -96,10 +101,9 @@ defmodule Samly.AuthHandler do %IdpData{esaml_idp_rec: idp_rec, esaml_sp_rec: sp_rec} = idp sp = ensure_sp_uris_set(sp_rec, conn) - target_url = conn.params["target_url"] || "/" - |> URI.decode_www_form() - + target_url = (conn.params["target_url"] || "/") |> URI.decode_www_form() nameid = get_session(conn, "samly_nameid") + case State.get_by_nameid(nameid) do {^nameid, %Assertion{idp_id: ^idp_id}} -> {idp_signout_url, req_xml_frag} = Helper.gen_idp_signout_req(sp, idp_rec, nameid) @@ -108,19 +112,24 @@ defmodule Samly.AuthHandler do relay_state = State.gen_id() conn - |> put_session("target_url", target_url) - |> put_session("relay_state", relay_state) - |> put_session("idp_id", idp_id) - |> delete_session("samly_nameid") - |> send_saml_request(idp_signout_url, idp.use_redirect_for_req, - req_xml_frag, relay_state |> URI.encode_www_form()) + |> put_session("target_url", target_url) + |> put_session("relay_state", relay_state) + |> put_session("idp_id", idp_id) + |> delete_session("samly_nameid") + |> send_saml_request( + idp_signout_url, + idp.use_redirect_for_req, + req_xml_frag, + relay_state |> URI.encode_www_form() + ) + _ -> - conn - |> send_resp(403, "access_denied") + conn |> send_resp(403, "access_denied") end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end end diff --git a/lib/samly/auth_router.ex b/lib/samly/auth_router.ex index a456b93..143aa09 100644 --- a/lib/samly/auth_router.ex +++ b/lib/samly/auth_router.ex @@ -12,23 +12,19 @@ defmodule Samly.AuthRouter do plug :dispatch get "/signin/*idp_id_seg" do - conn - |> Samly.AuthHandler.initiate_sso_req() + conn |> Samly.AuthHandler.initiate_sso_req() end post "/signin/*idp_id_seg" do - conn - |> Samly.AuthHandler.send_signin_req() + conn |> Samly.AuthHandler.send_signin_req() end get "/signout/*idp_id_seg" do - conn - |> Samly.AuthHandler.initiate_sso_req() + conn |> Samly.AuthHandler.initiate_sso_req() end post "/signout/*idp_id_seg" do - conn - |> Samly.AuthHandler.send_signout_req() + conn |> Samly.AuthHandler.send_signout_req() end match _ do diff --git a/lib/samly/config_error.ex b/lib/samly/config_error.ex index b50ca96..b0a71de 100644 --- a/lib/samly/config_error.ex +++ b/lib/samly/config_error.ex @@ -3,8 +3,8 @@ defmodule Samly.ConfigError do defexception [:message] - @spec exception(map) :: Exception.t + @spec exception(map) :: Exception.t() def exception(data) when is_map(data) do - %__MODULE__{message: "invalid_config: #{inspect data}"} + %__MODULE__{message: "invalid_config: #{inspect(data)}"} end end diff --git a/lib/samly/esaml.ex b/lib/samly/esaml.ex index eceeee0..f01140e 100644 --- a/lib/samly/esaml.ex +++ b/lib/samly/esaml.ex @@ -5,26 +5,15 @@ defmodule Samly.Esaml do import Record, only: [defrecord: 2, extract: 2] @esaml_hrl "esaml/include/esaml.hrl" - defrecord :esaml_org, - extract(:esaml_org, from_lib: @esaml_hrl) - defrecord :esaml_contact, - extract(:esaml_contact, from_lib: @esaml_hrl) - defrecord :esaml_sp_metadata, - extract(:esaml_sp_metadata, from_lib: @esaml_hrl) - defrecord :esaml_idp_metadata, - extract(:esaml_idp_metadata, from_lib: @esaml_hrl) - defrecord :esaml_authnreq, - extract(:esaml_authnreq, from_lib: @esaml_hrl) - defrecord :esaml_subject, - extract(:esaml_subject, from_lib: @esaml_hrl) - defrecord :esaml_assertion, - extract(:esaml_assertion, from_lib: @esaml_hrl) - defrecord :esaml_logoutreq, - extract(:esaml_logoutreq, from_lib: @esaml_hrl) - defrecord :esaml_logoutresp, - extract(:esaml_logoutresp, from_lib: @esaml_hrl) - defrecord :esaml_response, - extract(:esaml_response, from_lib: @esaml_hrl) - defrecord :esaml_sp, - extract(:esaml_sp, from_lib: @esaml_hrl) + defrecord :esaml_org, extract(:esaml_org, from_lib: @esaml_hrl) + defrecord :esaml_contact, extract(:esaml_contact, from_lib: @esaml_hrl) + defrecord :esaml_sp_metadata, extract(:esaml_sp_metadata, from_lib: @esaml_hrl) + defrecord :esaml_idp_metadata, extract(:esaml_idp_metadata, from_lib: @esaml_hrl) + defrecord :esaml_authnreq, extract(:esaml_authnreq, from_lib: @esaml_hrl) + defrecord :esaml_subject, extract(:esaml_subject, from_lib: @esaml_hrl) + defrecord :esaml_assertion, extract(:esaml_assertion, from_lib: @esaml_hrl) + defrecord :esaml_logoutreq, extract(:esaml_logoutreq, from_lib: @esaml_hrl) + defrecord :esaml_logoutresp, extract(:esaml_logoutresp, from_lib: @esaml_hrl) + defrecord :esaml_response, extract(:esaml_response, from_lib: @esaml_hrl) + defrecord :esaml_sp, extract(:esaml_sp, from_lib: @esaml_hrl) end diff --git a/lib/samly/helper.ex b/lib/samly/helper.ex index 9712eed..29293e0 100644 --- a/lib/samly/helper.ex +++ b/lib/samly/helper.ex @@ -4,7 +4,7 @@ defmodule Samly.Helper do require Samly.Esaml alias Samly.{Assertion, Esaml, IdpData} - @spec get_idp(binary) :: nil | IdpData.t + @spec get_idp(binary) :: nil | IdpData.t() def get_idp(idp_id) do idps = Application.get_env(:samly, :identity_providers, %{}) Map.get(idps, idp_id) @@ -12,27 +12,33 @@ defmodule Samly.Helper do @spec get_metadata_uri(nil | binary, binary) :: nil | charlist def get_metadata_uri(nil, _idp_id), do: nil + def get_metadata_uri(sp_base_url, nil) when is_binary(sp_base_url) do "#{sp_base_url}/sp/metadata" |> String.to_charlist() end + def get_metadata_uri(sp_base_url, idp_id) when is_binary(sp_base_url) do "#{sp_base_url}/sp/metadata/#{idp_id}" |> String.to_charlist() end @spec get_consume_uri(nil | binary, binary) :: nil | charlist def get_consume_uri(nil, _idp_id), do: nil + def get_consume_uri(sp_base_url, nil) when is_binary(sp_base_url) do "#{sp_base_url}/sp/consume" |> String.to_charlist() end + def get_consume_uri(sp_base_url, idp_id) when is_binary(sp_base_url) do "#{sp_base_url}/sp/consume/#{idp_id}" |> String.to_charlist() end @spec get_logout_uri(nil | binary, binary) :: nil | charlist def get_logout_uri(nil, _idp_id), do: nil + def get_logout_uri(sp_base_url, nil) when is_binary(sp_base_url) do "#{sp_base_url}/sp/logout" |> String.to_charlist() end + def get_logout_uri(sp_base_url, idp_id) when is_binary(sp_base_url) do "#{sp_base_url}/sp/logout/#{idp_id}" |> String.to_charlist() end @@ -60,26 +66,24 @@ defmodule Samly.Helper do end def decode_idp_auth_resp(sp, saml_encoding, saml_response) do - with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_response), - {:ok, assertion_rec} <- :esaml_sp.validate_assertion(xml_frag, sp) - do + with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_response), + {:ok, assertion_rec} <- :esaml_sp.validate_assertion(xml_frag, sp) do {:ok, Assertion.from_rec(assertion_rec)} else - error -> {:error, {:invalid_request, "#{inspect error}"}} + error -> {:error, {:invalid_request, "#{inspect(error)}"}} end end def decode_idp_signout_resp(sp, saml_encoding, saml_response) do resp_ns = [ {'samlp', 'urn:oasis:names:tc:SAML:2.0:protocol'}, - {'saml', 'urn:oasis:names:tc:SAML:2.0:assertion'}, + {'saml', 'urn:oasis:names:tc:SAML:2.0:assertion'}, {'ds', 'http://www.w3.org/2000/09/xmldsig#'} ] - with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_response), - nodes when is_list(nodes) and length(nodes) == 1 <- - :xmerl_xpath.string('/samlp:LogoutResponse', xml_frag, [{:namespace, resp_ns}]) - do + with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_response), + nodes when is_list(nodes) and length(nodes) == 1 <- + :xmerl_xpath.string('/samlp:LogoutResponse', xml_frag, [{:namespace, resp_ns}]) do :esaml_sp.validate_logout_response(xml_frag, sp) else _ -> {:error, :invalid_request} @@ -89,13 +93,12 @@ defmodule Samly.Helper do def decode_idp_signout_req(sp, saml_encoding, saml_request) do req_ns = [ {'samlp', 'urn:oasis:names:tc:SAML:2.0:protocol'}, - {'saml', 'urn:oasis:names:tc:SAML:2.0:assertion'} + {'saml', 'urn:oasis:names:tc:SAML:2.0:assertion'} ] - with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_request), - nodes when is_list(nodes) and length(nodes) == 1 <- - :xmerl_xpath.string('/samlp:LogoutRequest', xml_frag, [{:namespace, req_ns}]) - do + with {:ok, xml_frag} <- decode_saml_payload(saml_encoding, saml_request), + nodes when is_list(nodes) and length(nodes) == 1 <- + :xmerl_xpath.string('/samlp:LogoutRequest', xml_frag, [{:namespace, req_ns}]) do :esaml_sp.validate_logout_request(xml_frag, sp) else _ -> {:error, :invalid_request} @@ -107,7 +110,7 @@ defmodule Samly.Helper do xml = :esaml_binding.decode_response(saml_encoding, saml_payload) {:ok, xml} rescue - error -> {:error, {:invalid_response, "#{inspect error}"}} + error -> {:error, {:invalid_response, "#{inspect(error)}"}} end end end diff --git a/lib/samly/idp_data.ex b/lib/samly/idp_data.ex index 92d8421..eff887a 100644 --- a/lib/samly/idp_data.ex +++ b/lib/samly/idp_data.ex @@ -2,12 +2,8 @@ defmodule Samly.IdpData do @moduledoc false require Logger - alias Samly.IdpData - alias Samly.SpData - alias Samly.ConfigError - require Samly.Esaml - alias Samly.{Esaml, Helper} + alias Samly.{ConfigError, Esaml, Helper, IdpData, SpData} @boolean_attrs [ :use_redirect_for_req, @@ -17,42 +13,41 @@ defmodule Samly.IdpData do :signed_envelopes_in_resp ] - defstruct [ - id: nil, - sp_id: nil, - base_url: nil, - metadata_file: nil, - pre_session_create_pipeline: nil, - use_redirect_for_req: false, - sign_requests: true, - sign_metadata: true, - signed_assertion_in_resp: true, - signed_envelopes_in_resp: true, - fingerprints: [], - esaml_idp_rec: nil, - esaml_sp_rec: nil - ] + defstruct id: nil, + sp_id: nil, + base_url: nil, + metadata_file: nil, + pre_session_create_pipeline: nil, + use_redirect_for_req: false, + sign_requests: true, + sign_metadata: true, + signed_assertion_in_resp: true, + signed_envelopes_in_resp: true, + fingerprints: [], + esaml_idp_rec: nil, + esaml_sp_rec: nil @type t :: %__MODULE__{ - id: nil | String.t, - sp_id: nil | String.t, - base_url: nil | String.t, - metadata_file: nil | String.t, - pre_session_create_pipeline: nil | module, - use_redirect_for_req: boolean, - sign_requests: boolean, - sign_metadata: boolean, - signed_assertion_in_resp: boolean, - signed_envelopes_in_resp: boolean, - fingerprints: keyword(binary), - esaml_idp_rec: nil | tuple, - esaml_sp_rec: nil | tuple - } - - @type id :: String.t - - @spec load_identity_providers(list(map), %{required(id) => SpData.t}, binary) - :: %{required(id) => t} + id: nil | String.t(), + sp_id: nil | String.t(), + base_url: nil | String.t(), + metadata_file: nil | String.t(), + pre_session_create_pipeline: nil | module, + use_redirect_for_req: boolean, + sign_requests: boolean, + sign_metadata: boolean, + signed_assertion_in_resp: boolean, + signed_envelopes_in_resp: boolean, + fingerprints: keyword(binary), + esaml_idp_rec: nil | tuple, + esaml_sp_rec: nil | tuple + } + + @type id :: String.t() + + @spec load_identity_providers(list(map), %{required(id) => SpData.t()}, binary) :: %{ + required(id) => __MODULE__.t() + } def load_identity_providers(prov_config, service_providers, base_url) do prov_config |> Enum.map(fn idp -> load_idp_data(idp, service_providers, base_url) end) @@ -61,20 +56,18 @@ defmodule Samly.IdpData do @default_idp_metadata_file "idp_metadata.xml" - @spec load_idp_data(map, %{required(id) => SpData.t}, binary) - :: {id, IdpData.t} | no_return + @spec load_idp_data(map, %{required(id) => SpData.t()}, binary) :: {id, IdpData.t()} | no_return defp load_idp_data(%{} = idp_entry, service_providers, default_base_url) do - with idp_id when idp_id != nil <- Map.get(idp_entry, :id), - base_url when (base_url == nil or is_binary(base_url)) <- - Map.get(idp_entry, :base_url, default_base_url), - metadata_file when metadata_file != nil <- - Map.get(idp_entry, :metadata_file, @default_idp_metadata_file), - pl when (pl == nil or is_atom(pl)) <- Map.get(idp_entry, :pre_session_create_pipeline), - {:reading, {:ok, xml}} <- {:reading, File.read(metadata_file)}, - {:parsing, {:ok, mdt}} <- {:parsing, idp_metadata_from_xml(xml)}, - sp_id when sp_id != nil <- Map.get(idp_entry, :sp_id, nil), - sp when sp != nil <- Map.get(service_providers, sp_id, nil) - do + with idp_id when idp_id != nil <- Map.get(idp_entry, :id), + base_url when base_url == nil or is_binary(base_url) <- + Map.get(idp_entry, :base_url, default_base_url), + metadata_file when metadata_file != nil <- + Map.get(idp_entry, :metadata_file, @default_idp_metadata_file), + pl when pl == nil or is_atom(pl) <- Map.get(idp_entry, :pre_session_create_pipeline), + {:reading, {:ok, xml}} <- {:reading, File.read(metadata_file)}, + {:parsing, {:ok, mdt}} <- {:parsing, idp_metadata_from_xml(xml)}, + sp_id when sp_id != nil <- Map.get(idp_entry, :sp_id, nil), + sp when sp != nil <- Map.get(service_providers, sp_id, nil) do idp = @boolean_attrs |> Enum.reduce(%__MODULE__{}, fn attr, idp -> @@ -82,24 +75,27 @@ defmodule Samly.IdpData do if is_boolean(v), do: Map.put(idp, attr, v), else: idp end) - idp = %__MODULE__{idp + idp = %__MODULE__{ + idp | id: idp_id, sp_id: sp_id, base_url: base_url, metadata_file: metadata_file, pre_session_create_pipeline: pl, fingerprints: idp_cert_fingerprints(mdt), - esaml_idp_rec: mdt, + esaml_idp_rec: mdt } {idp.id, %__MODULE__{idp | esaml_sp_rec: get_esaml_sp_rec(sp, idp, base_url)}} else {:reading, {:error, reason}} -> - Logger.error("[Samly] Failed to read metadata_file: #{inspect reason}") + Logger.error("[Samly] Failed to read metadata_file: #{inspect(reason)}") raise ConfigError, idp_entry + {:parsing, {:error, reason}} -> - Logger.error("[Samly] Invalid metadata_file content: #{inspect reason}") + Logger.error("[Samly] Invalid metadata_file content: #{inspect(reason)}") raise ConfigError, idp_entry + _ -> raise ConfigError, idp_entry end @@ -107,9 +103,11 @@ defmodule Samly.IdpData do defp idp_metadata_from_xml(metadata_xml) when is_binary(metadata_xml) do try do - {xml, _} = metadata_xml - |> String.to_charlist() - |> :xmerl_scan.string(namespace_conformant: true) + {xml, _} = + metadata_xml + |> String.to_charlist() + |> :xmerl_scan.string(namespace_conformant: true) + :esaml.decode_idp_metadata(xml) rescue _ -> {:error, :invalid_metadata_xml} @@ -117,10 +115,11 @@ defmodule Samly.IdpData do end defp idp_cert_fingerprints(idp_metadata) do - fingerprint = idp_metadata - |> Esaml.esaml_idp_metadata(:certificate) - |> cert_fingerprint() - |> String.to_charlist() + fingerprint = + idp_metadata + |> Esaml.esaml_idp_metadata(:certificate) + |> cert_fingerprint() + |> String.to_charlist() [fingerprint] |> :esaml_util.convert_fingerprints() end @@ -130,35 +129,41 @@ defmodule Samly.IdpData do end def get_esaml_sp_rec(%SpData{} = sp, %IdpData{} = idp, base_url) do - entity_id = case sp.entity_id do - nil -> :undefined - :undefined -> :undefined - id -> String.to_charlist(id) - end + entity_id = + case sp.entity_id do + nil -> :undefined + :undefined -> :undefined + id -> String.to_charlist(id) + end idp_id_from = Application.get_env(:samly, :idp_id_from) path_segment_idp_id = if idp_id_from == :subdomain, do: nil, else: idp.id - sp_rec = Esaml.esaml_sp( - key: sp.key, - certificate: sp.cert, - sp_sign_requests: idp.sign_requests, - sp_sign_metadata: idp.sign_metadata, - idp_signs_envelopes: idp.signed_envelopes_in_resp, - idp_signs_assertions: idp.signed_assertion_in_resp, - trusted_fingerprints: idp.fingerprints, - metadata_uri: Helper.get_metadata_uri(base_url, path_segment_idp_id), - consume_uri: Helper.get_consume_uri(base_url, path_segment_idp_id), - logout_uri: Helper.get_logout_uri(base_url, path_segment_idp_id), - entity_id: entity_id, - org: Esaml.esaml_org( - name: String.to_charlist(sp.org_name), - displayname: String.to_charlist(sp.org_displayname), - url: String.to_charlist(sp.org_url)), - tech: Esaml.esaml_contact( - name: String.to_charlist(sp.contact_name), - email: String.to_charlist(sp.contact_email)) - ) + sp_rec = + Esaml.esaml_sp( + key: sp.key, + certificate: sp.cert, + sp_sign_requests: idp.sign_requests, + sp_sign_metadata: idp.sign_metadata, + idp_signs_envelopes: idp.signed_envelopes_in_resp, + idp_signs_assertions: idp.signed_assertion_in_resp, + trusted_fingerprints: idp.fingerprints, + metadata_uri: Helper.get_metadata_uri(base_url, path_segment_idp_id), + consume_uri: Helper.get_consume_uri(base_url, path_segment_idp_id), + logout_uri: Helper.get_logout_uri(base_url, path_segment_idp_id), + entity_id: entity_id, + org: + Esaml.esaml_org( + name: String.to_charlist(sp.org_name), + displayname: String.to_charlist(sp.org_displayname), + url: String.to_charlist(sp.org_url) + ), + tech: + Esaml.esaml_contact( + name: String.to_charlist(sp.contact_name), + email: String.to_charlist(sp.contact_email) + ) + ) sp_rec end diff --git a/lib/samly/provider.ex b/lib/samly/provider.ex index 90a3a14..a588dcd 100644 --- a/lib/samly/provider.ex +++ b/lib/samly/provider.ex @@ -35,23 +35,32 @@ defmodule Samly.Provider do opts = Application.get_env(:samly, Samly.Provider, []) # must be done prior to loading the providers - idp_id_from = case opts[:idp_id_from] do - nil -> :path_segment - value when value in [:subdomain, :path_segment] -> value - unknown -> - Logger.warn("[Samly] invalid_data idp_id_from: #{inspect unknown}. Using :path_segment") - :path_segment - end + idp_id_from = + case opts[:idp_id_from] do + nil -> + :path_segment + + value when value in [:subdomain, :path_segment] -> + value + + unknown -> + Logger.warn( + "[Samly] invalid_data idp_id_from: #{inspect(unknown)}. Using :path_segment" + ) + + :path_segment + end + Application.put_env(:samly, :idp_id_from, idp_id_from) - service_providers = Samly.SpData.load_service_providers( - opts[:service_providers] || [] - ) - identity_providers = Samly.IdpData.load_identity_providers( - opts[:identity_providers] || [], - service_providers, - opts[:base_url] - ) + service_providers = Samly.SpData.load_service_providers(opts[:service_providers] || []) + + identity_providers = + Samly.IdpData.load_identity_providers( + opts[:identity_providers] || [], + service_providers, + opts[:base_url] + ) Application.put_env(:samly, :service_providers, service_providers) Application.put_env(:samly, :identity_providers, identity_providers) diff --git a/lib/samly/router.ex b/lib/samly/router.ex index b878d87..500ef46 100644 --- a/lib/samly/router.ex +++ b/lib/samly/router.ex @@ -7,8 +7,8 @@ defmodule Samly.Router do plug :match plug :dispatch - forward "/auth", to: Samly.AuthRouter - forward "/sp", to: Samly.SPRouter + forward("/auth", to: Samly.AuthRouter) + forward("/sp", to: Samly.SPRouter) match _ do conn |> send_resp(404, "not_found") @@ -16,13 +16,13 @@ defmodule Samly.Router do defp secure_samly(conn, _opts) do conn - |> register_before_send(fn connection -> - connection - |> put_resp_header("Cache-Control", "no-cache") - |> put_resp_header("Pragma", "no-cache") - |> put_resp_header("X-Frame-Options", "SAMEORIGIN") - |> put_resp_header("X-XSS-Protection", "1; mode=block") - |> put_resp_header("X-Content-Type-Options", "nosniff") - end) + |> register_before_send(fn connection -> + connection + |> put_resp_header("Cache-Control", "no-cache") + |> put_resp_header("Pragma", "no-cache") + |> put_resp_header("X-Frame-Options", "SAMEORIGIN") + |> put_resp_header("X-XSS-Protection", "1; mode=block") + |> put_resp_header("X-Content-Type-Options", "nosniff") + end) end end diff --git a/lib/samly/router_util.ex b/lib/samly/router_util.ex index 80d3aa0..6d0fe85 100644 --- a/lib/samly/router_util.ex +++ b/lib/samly/router_util.ex @@ -9,19 +9,22 @@ defmodule Samly.RouterUtil do def check_idp_id(conn, _opts) do idp_id_from = Application.get_env(:samly, :idp_id_from) - idp_id = if idp_id_from == :subdomain do - case Regex.named_captures(@subdomain_re, conn.host) do - %{"subdomain" => idp_id} -> idp_id - _ -> nil - end - else - case conn.params["idp_id_seg"] do - [idp_id] -> idp_id - _ -> nil + + idp_id = + if idp_id_from == :subdomain do + case Regex.named_captures(@subdomain_re, conn.host) do + %{"subdomain" => idp_id} -> idp_id + _ -> nil + end + else + case conn.params["idp_id_seg"] do + [idp_id] -> idp_id + _ -> nil + end end - end idp = idp_id && Helper.get_idp(idp_id) + if idp do conn |> Conn.put_private(:samly_idp, idp) else @@ -30,7 +33,7 @@ defmodule Samly.RouterUtil do end # generate URIs using the idp_id - @spec ensure_sp_uris_set(tuple, Conn.t) :: tuple + @spec ensure_sp_uris_set(tuple, Conn.t()) :: tuple def ensure_sp_uris_set(sp, conn) do case Esaml.esaml_sp(sp, :metadata_uri) do [?/ | _] -> @@ -42,40 +45,47 @@ defmodule Samly.RouterUtil do } base_url = URI.to_string(uri) - idp_id_from = Application.get_env(:samly, :idp_id_from) - path_segment_idp_id = if idp_id_from == :subdomain do - nil - else - %IdpData{id: idp_id} = conn.private[:samly_idp] - idp_id - end - Esaml.esaml_sp(sp, + path_segment_idp_id = + if idp_id_from == :subdomain do + nil + else + %IdpData{id: idp_id} = conn.private[:samly_idp] + idp_id + end + + Esaml.esaml_sp( + sp, metadata_uri: Helper.get_metadata_uri(base_url, path_segment_idp_id), consume_uri: Helper.get_consume_uri(base_url, path_segment_idp_id), logout_uri: Helper.get_logout_uri(base_url, path_segment_idp_id) ) - _ -> sp + + _ -> + sp end end def send_saml_request(conn, idp_url, use_redirect?, signed_xml_payload, relay_state) do if use_redirect? do - url = :esaml_binding.encode_http_redirect(idp_url, signed_xml_payload, :undefined, relay_state) + url = + :esaml_binding.encode_http_redirect(idp_url, signed_xml_payload, :undefined, relay_state) + conn |> redirect(302, url) else resp_body = :esaml_binding.encode_http_post(idp_url, signed_xml_payload, relay_state) + conn - |> Conn.put_resp_header("Content-Type", "text/html") - |> Conn.send_resp(200, resp_body) + |> Conn.put_resp_header("Content-Type", "text/html") + |> Conn.send_resp(200, resp_body) end end def redirect(conn, status_code, dest) do conn - |> Conn.put_resp_header("Location", dest) - |> Conn.send_resp(status_code, "") - |> Conn.halt() + |> Conn.put_resp_header("Location", dest) + |> Conn.send_resp(status_code, "") + |> Conn.halt() end end diff --git a/lib/samly/sp_data.ex b/lib/samly/sp_data.ex index 275ed93..2947951 100644 --- a/lib/samly/sp_data.ex +++ b/lib/samly/sp_data.ex @@ -4,35 +4,33 @@ defmodule Samly.SpData do require Logger alias Samly.ConfigError - defstruct [ - id: nil, - entity_id: :undefined, - certfile: nil, - keyfile: nil, - contact_name: nil, - contact_email: nil, - org_name: nil, - org_displayname: nil, - org_url: nil, - cert: nil, - key: nil - ] + defstruct id: nil, + entity_id: :undefined, + certfile: nil, + keyfile: nil, + contact_name: nil, + contact_email: nil, + org_name: nil, + org_displayname: nil, + org_url: nil, + cert: nil, + key: nil @type t :: %__MODULE__{ - id: nil | String.t, - entity_id: nil | :undefined | String.t, - certfile: nil | String.t, - keyfile: nil | String.t, - contact_name: nil | String.t, - contact_email: nil | String.t, - org_name: nil | String.t, - org_displayname: nil | String.t, - org_url: nil | String.t, - cert: nil | binary, - key: nil | tuple - } + id: nil | String.t(), + entity_id: nil | :undefined | String.t(), + certfile: nil | String.t(), + keyfile: nil | String.t(), + contact_name: nil | String.t(), + contact_email: nil | String.t(), + org_name: nil | String.t(), + org_displayname: nil | String.t(), + org_url: nil | String.t(), + cert: nil | binary, + key: nil | tuple + } - @type id :: String.t + @type id :: String.t() @default_contact_name "Samly SP Admin" @default_contact_email "admin@samly" @@ -43,11 +41,11 @@ defmodule Samly.SpData do @spec load_service_providers(list(map)) :: %{required(id) => t} def load_service_providers(providers) do providers - |> Enum.map(&load_sp/1) - |> Enum.into(%{}) + |> Enum.map(&load_sp/1) + |> Enum.into(%{}) end - @spec load_sp(map) :: {String.t, t} | no_return + @spec load_sp(map) :: {String.t(), t} | no_return defp load_sp(%{} = provider) do sp = %__MODULE__{ id: Map.get(provider, :id, nil), @@ -58,13 +56,10 @@ defmodule Samly.SpData do contact_email: Map.get(provider, :contact_email, @default_contact_email), org_name: Map.get(provider, :org_name, @default_org_name), org_displayname: Map.get(provider, :org_displayname, @default_org_displayname), - org_url: Map.get(provider, :org_url, @default_org_url), + org_url: Map.get(provider, :org_url, @default_org_url) } - sp = %__MODULE__{sp - | cert: load_cert(sp.certfile, sp.id), - key: load_key(sp.keyfile, sp.id) - } + sp = %__MODULE__{sp | cert: load_cert(sp.certfile, sp.id), key: load_key(sp.keyfile, sp.id)} if sp.id == nil || sp.certfile == nil || sp.keyfile == nil do raise ConfigError, provider diff --git a/lib/samly/sp_handler.ex b/lib/samly/sp_handler.ex index f88f2fb..4a3b060 100644 --- a/lib/samly/sp_handler.ex +++ b/lib/samly/sp_handler.ex @@ -5,7 +5,7 @@ defmodule Samly.SPHandler do import Plug.Conn alias Plug.Conn require Samly.Esaml - alias Samly.{Assertion, Esaml, Helper, State, IdpData} + alias Samly.{Assertion, Esaml, Helper, IdpData, State} import Samly.RouterUtil, only: [ensure_sp_uris_set: 2, send_saml_request: 5, redirect: 3] @@ -13,16 +13,16 @@ defmodule Samly.SPHandler do %IdpData{} = idp = conn.private[:samly_idp] %IdpData{esaml_idp_rec: _idp_rec, esaml_sp_rec: sp_rec} = idp sp = ensure_sp_uris_set(sp_rec, conn) - metadata = Helper.sp_metadata(sp) conn - |> put_resp_header("Content-Type", "text/xml") - |> send_resp(200, metadata) - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + |> put_resp_header("Content-Type", "text/xml") + |> send_resp(200, metadata) + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end def consume_signin_response(conn) do @@ -32,47 +32,40 @@ defmodule Samly.SPHandler do saml_encoding = conn.body_params["SAMLEncoding"] saml_response = conn.body_params["SAMLResponse"] - relay_state = conn.body_params["RelayState"] |> URI.decode_www_form() - - with ^relay_state when relay_state != nil <- get_session(conn, "relay_state"), - ^idp_id <- get_session(conn, "idp_id"), - target_url when target_url != nil <- get_session(conn, "target_url"), - {:ok, assertion} <- Helper.decode_idp_auth_resp(sp, saml_encoding, saml_response), - conn = conn |> put_private(:samly_assertion, assertion), - {:halted, %Conn{halted: false} = conn} <- - {:halted, pipethrough(conn, pipeline)} - do + relay_state = conn.body_params["RelayState"] |> URI.decode_www_form() + + with ^relay_state when relay_state != nil <- get_session(conn, "relay_state"), + ^idp_id <- get_session(conn, "idp_id"), + target_url when target_url != nil <- get_session(conn, "target_url"), + {:ok, assertion} <- Helper.decode_idp_auth_resp(sp, saml_encoding, saml_response), + conn = conn |> put_private(:samly_assertion, assertion), + {:halted, %Conn{halted: false} = conn} <- {:halted, pipethrough(conn, pipeline)} do updated_assertion = conn.private[:samly_assertion] computed = updated_assertion.computed - assertion = %Assertion{assertion - | computed: computed, - idp_id: idp_id - } + assertion = %Assertion{assertion | computed: computed, idp_id: idp_id} # TODO: use idp_id + nameid nameid = assertion.subject.name State.put(nameid, assertion) conn - |> configure_session(renew: true) - |> put_session("samly_nameid", nameid) - |> redirect(302, target_url |> URI.decode_www_form()) + |> configure_session(renew: true) + |> put_session("samly_nameid", nameid) + |> redirect(302, target_url |> URI.decode_www_form()) else {:halted, conn} -> conn - {:error, reason} -> - conn - |> send_resp(403, "access_denied #{inspect reason}") - _ -> - conn - |> send_resp(403, "access_denied") + {:error, reason} -> conn |> send_resp(403, "access_denied #{inspect(reason)}") + _ -> conn |> send_resp(403, "access_denied") end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end defp pipethrough(conn, nil), do: conn + defp pipethrough(conn, pipeline) do pipeline.call(conn, []) end @@ -84,25 +77,23 @@ defmodule Samly.SPHandler do saml_encoding = conn.body_params["SAMLEncoding"] saml_response = conn.body_params["SAMLResponse"] - relay_state = conn.body_params["RelayState"] |> URI.decode_www_form() + relay_state = conn.body_params["RelayState"] |> URI.decode_www_form() - with {:ok, _payload} <- Helper.decode_idp_signout_resp(sp, saml_encoding, saml_response), - ^relay_state when relay_state != nil <- get_session(conn, "relay_state"), - ^idp_id <- get_session(conn, "idp_id"), - target_url when target_url != nil <- get_session(conn, "target_url") - do + with {:ok, _payload} <- Helper.decode_idp_signout_resp(sp, saml_encoding, saml_response), + ^relay_state when relay_state != nil <- get_session(conn, "relay_state"), + ^idp_id <- get_session(conn, "idp_id"), + target_url when target_url != nil <- get_session(conn, "target_url") do conn - |> configure_session(drop: true) - |> redirect(302, target_url |> URI.decode_www_form()) + |> configure_session(drop: true) + |> redirect(302, target_url |> URI.decode_www_form()) else - error -> - conn - |> send_resp(403, "invalid_request #{inspect error}") + error -> conn |> send_resp(403, "invalid_request #{inspect(error)}") end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end # non-ui logout request from IDP @@ -112,22 +103,24 @@ defmodule Samly.SPHandler do sp = ensure_sp_uris_set(sp_rec, conn) saml_encoding = conn.body_params["SAMLEncoding"] - saml_request = conn.body_params["SAMLRequest"] - relay_state = conn.body_params["RelayState"] + saml_request = conn.body_params["SAMLRequest"] + relay_state = conn.body_params["RelayState"] - with {:ok, payload} <- Helper.decode_idp_signout_req(sp, saml_encoding, saml_request) - do + with {:ok, payload} <- Helper.decode_idp_signout_req(sp, saml_encoding, saml_request) do # nameid = Esaml.esaml_logoutreq(payload, :name) # issuer = Esaml.esaml_logoutreq(payload, :issuer) Esaml.esaml_logoutreq(name: nameid, issuer: _issuer) = payload + return_status = case State.get_by_nameid(nameid) do {^nameid, %Assertion{idp_id: ^idp_id}} -> State.delete(nameid) :success + {^nameid, _saml_assertion} -> State.delete(nameid) :denied + _ -> :denied end @@ -135,20 +128,25 @@ defmodule Samly.SPHandler do {idp_signout_url, resp_xml_frag} = Helper.gen_idp_signout_resp(sp, idp_rec, return_status) conn - |> configure_session(drop: true) - |> send_saml_request(idp_signout_url, idp.use_redirect_for_req, - resp_xml_frag, relay_state) + |> configure_session(drop: true) + |> send_saml_request(idp_signout_url, idp.use_redirect_for_req, resp_xml_frag, relay_state) else error -> - Logger.error("#{inspect error}") + Logger.error("#{inspect(error)}") {idp_signout_url, resp_xml_frag} = Helper.gen_idp_signout_resp(sp, idp_rec, :denied) + conn - |> send_saml_request(idp_signout_url, idp.use_redirect_for_req, - resp_xml_frag, relay_state) + |> send_saml_request( + idp_signout_url, + idp.use_redirect_for_req, + resp_xml_frag, + relay_state + ) end - # rescue - # error -> - # Logger.error("#{inspect error}") - # conn |> send_resp(500, "request_failed") + + # rescue + # error -> + # Logger.error("#{inspect error}") + # conn |> send_resp(500, "request_failed") end end diff --git a/lib/samly/sp_router.ex b/lib/samly/sp_router.ex index 911ac60..661e9c7 100644 --- a/lib/samly/sp_router.ex +++ b/lib/samly/sp_router.ex @@ -12,23 +12,18 @@ defmodule Samly.SPRouter do get "/metadata/*idp_id_seg" do # TODO: Make a release task to generate SP metadata - conn - |> Samly.SPHandler.send_metadata() + conn |> Samly.SPHandler.send_metadata() end post "/consume/*idp_id_seg" do - conn - |> Samly.SPHandler.consume_signin_response() + conn |> Samly.SPHandler.consume_signin_response() end post "/logout/*idp_id_seg" do cond do - conn.params["SAMLResponse"] != nil -> - Samly.SPHandler.handle_logout_response(conn) - conn.params["SAMLRequest"] != nil -> - Samly.SPHandler.handle_logout_request(conn) - true -> - conn |> send_resp(403, "invalid_request") + conn.params["SAMLResponse"] != nil -> Samly.SPHandler.handle_logout_response(conn) + conn.params["SAMLRequest"] != nil -> Samly.SPHandler.handle_logout_request(conn) + true -> conn |> send_resp(403, "invalid_request") end end diff --git a/lib/samly/subject.ex b/lib/samly/subject.ex index 4bef914..29ba29d 100644 --- a/lib/samly/subject.ex +++ b/lib/samly/subject.ex @@ -10,17 +10,15 @@ defmodule Samly.Subject do require Samly.Esaml alias Samly.Esaml - defstruct [ - name: "", - confirmation_method: :bearer, - notonorafter: "" - ] + defstruct name: "", + confirmation_method: :bearer, + notonorafter: "" @type t :: %__MODULE__{ - name: String.t, - confirmation_method: atom, - notonorafter: String.t - } + name: String.t(), + confirmation_method: atom, + notonorafter: String.t() + } @doc false def from_rec(subject_rec) do diff --git a/mix.exs b/mix.exs index c862c86..71a00f1 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Samly.Mixfile do use Mix.Project - @version "0.8.0" + @version "0.8.1" @description "SAML SP SSO made easy" @source_url "https://github.com/handnot2/samly" @@ -13,7 +13,7 @@ defmodule Samly.Mixfile do docs: docs(), package: package(), elixir: "~> 1.5", - start_permanent: Mix.env == :prod, + start_permanent: Mix.env() == :prod, deps: deps() ] end @@ -31,7 +31,7 @@ defmodule Samly.Mixfile do {:plug, "~> 1.4"}, {:esaml, "~> 3.1"}, {:ex_doc, "~> 0.18", only: :dev}, - {:inch_ex, "~> 0.5", only: :docs}, + {:inch_ex, "~> 0.5", only: :docs} ] end @@ -50,7 +50,7 @@ defmodule Samly.Mixfile do files: ["config", "lib", "LICENSE", "mix.exs", "README.md"], licenses: ["MIT"], links: %{ - "GitHub" => @source_url, + "GitHub" => @source_url } ] end diff --git a/mix.lock b/mix.lock index 09818cc..41dec00 100644 --- a/mix.lock +++ b/mix.lock @@ -1,10 +1,10 @@ -%{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, - "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"}, - "esaml": {:hex, :esaml, "3.1.0", "76337f00b5953a6c249fa8c322905c7a069b7c20339ece3756072279e6dcb41c", [], [{:cowboy, "1.1.2", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, - "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, - "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, - "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"}, - "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}} +%{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"}, + "esaml": {:hex, :esaml, "3.1.0", "76337f00b5953a6c249fa8c322905c7a069b7c20339ece3756072279e6dcb41c", [:rebar3], [{:cowboy, "1.1.2", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], [], "hexpm"}, + "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}} diff --git a/test/samly_test.exs b/test/samly_test.exs index e13fd7a..9836ef8 100644 --- a/test/samly_test.exs +++ b/test/samly_test.exs @@ -4,7 +4,7 @@ defmodule SamlyTest do @test_opts [ certfile: "test/data/test.crt", - keyfile: "test/data/test.pem", + keyfile: "test/data/test.pem", idp_metadata_file: "test/data/idp_metadata.xml", base_url: "http://my.app:4000/sso" ]