From 9c84e9a4415d7f4eca4fb81d97521ec83d72f179 Mon Sep 17 00:00:00 2001 From: Adrien Anselme Date: Fri, 17 Jul 2020 11:32:36 +0200 Subject: [PATCH 1/7] Provide a behaviour for identity providers storage and a default implementation that does exactly the same as before (read from configuration) --- lib/samly/helper.ex | 3 +-- lib/samly/idp_data.ex | 5 +++++ lib/samly/idp_data_store/config.ex | 35 ++++++++++++++++++++++++++++++ lib/samly/idp_data_store/store.ex | 29 +++++++++++++++++++++++++ lib/samly/provider.ex | 9 +++----- 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 lib/samly/idp_data_store/config.ex create mode 100644 lib/samly/idp_data_store/store.ex diff --git a/lib/samly/helper.ex b/lib/samly/helper.ex index aa1bf7e..d82ede1 100644 --- a/lib/samly/helper.ex +++ b/lib/samly/helper.ex @@ -6,8 +6,7 @@ defmodule Samly.Helper do @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) + IdpData.store().get(idp_id) end @spec get_metadata_uri(nil | binary, binary) :: nil | charlist diff --git a/lib/samly/idp_data.ex b/lib/samly/idp_data.ex index 0aa662c..c281318 100644 --- a/lib/samly/idp_data.ex +++ b/lib/samly/idp_data.ex @@ -85,6 +85,11 @@ defmodule Samly.IdpData do @type id :: binary() + def store() do + Application.get_env(:samly, Samly.Provider, []) + |> Keyword.get(:idp_data_store, Samly.IdpDataStore.Config) + end + @spec load_providers([map], %{required(id()) => %SpData{}}) :: %{required(id()) => %IdpData{}} | no_return() def load_providers(prov_config, service_providers) do diff --git a/lib/samly/idp_data_store/config.ex b/lib/samly/idp_data_store/config.ex new file mode 100644 index 0000000..dafb297 --- /dev/null +++ b/lib/samly/idp_data_store/config.ex @@ -0,0 +1,35 @@ +defmodule Samly.IdpDataStore.Config do + @moduledoc """ + Reads identity providers data from Application environment (config files). + + This is the default behaviour. To change it, set the following config: + + config :samly, Samly.Provider, + idp_data_store: MyApp.IdpStore + + This implementation only provides `init/2` and `get/1`.any() + `delete/1` and `put/2` will return `:unsupported`. + """ + + @behaviour Samly.IdpDataStore.Store + + @impl true + def init(opts, service_providers) do + identity_providers = + Samly.IdpData.load_providers(opts || [], service_providers) + + Application.put_env(:samly, :identity_providers, identity_providers) + end + + @impl true + def get(idp_id) do + idps = Application.get_env(:samly, :identity_providers, %{}) + Map.get(idps, idp_id) + end + + @impl true + def put(_idp_id, _idp_data), do: :unsupported + + @impl true + def delete(_idp_id), do: :unsupported +end diff --git a/lib/samly/idp_data_store/store.ex b/lib/samly/idp_data_store/store.ex new file mode 100644 index 0000000..090d874 --- /dev/null +++ b/lib/samly/idp_data_store/store.ex @@ -0,0 +1,29 @@ +defmodule Samly.IdpDataStore.Store do + alias Samly.IdpData + alias Samly.SpData + + @doc """ + Called during GenServer init to initializes the store. + + Takes an optional list of `identity_providers` to populate the store from, + and the already-configured map of `service_providers` data. + """ + @callback init([map], %{SpData.id() => SpData.t()}) :: :ok | {:error, any()} + + @doc """ + Fetches the IdpData for the given Id from the store. + """ + @callback get(binary) :: nil | IdpData.t() + + @doc """ + Saves the IdpData for the given Id into the store. + Could be omitted by implementation. In that case, it should return `:unsupported` + """ + @callback put(binary, IdpData.t()) :: :ok | :unsupported | {:error, any()} + + @doc """ + Removes the IdpData for the given Id from the store. + Could be omitted by implementation. In that case, it should return `:unsupported` + """ + @callback delete(binary) :: :ok | :unsupported | {:error, any()} +end diff --git a/lib/samly/provider.ex b/lib/samly/provider.ex index 5c9e032..953bcd8 100644 --- a/lib/samly/provider.ex +++ b/lib/samly/provider.ex @@ -22,7 +22,7 @@ defmodule Samly.Provider do require Logger require Samly.Esaml - alias Samly.{State} + alias Samly.{State, IdpData} @doc false def start_link(gs_opts \\ []) do @@ -58,12 +58,9 @@ defmodule Samly.Provider do Application.put_env(:samly, :idp_id_from, idp_id_from) service_providers = Samly.SpData.load_providers(opts[:service_providers] || []) - - identity_providers = - Samly.IdpData.load_providers(opts[:identity_providers] || [], service_providers) - Application.put_env(:samly, :service_providers, service_providers) - Application.put_env(:samly, :identity_providers, identity_providers) + + :ok = IdpData.store().init(opts[:identity_providers], service_providers) {:ok, %{}} end From dc4bfc9a36178ec0be63cc4932a7805577310ee7 Mon Sep 17 00:00:00 2001 From: Adrien Anselme Date: Fri, 4 Sep 2020 14:53:43 +0200 Subject: [PATCH 2/7] Move XML fields parsing into a dedicated module called from IdpData --- lib/samly/idp_data.ex | 121 ++++----------------------------------- lib/samly/xml_adapter.ex | 117 +++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 110 deletions(-) create mode 100644 lib/samly/xml_adapter.ex diff --git a/lib/samly/idp_data.ex b/lib/samly/idp_data.ex index c281318..91184d9 100644 --- a/lib/samly/idp_data.ex +++ b/lib/samly/idp_data.ex @@ -1,10 +1,9 @@ defmodule Samly.IdpData do @moduledoc false - import SweetXml require Logger require Samly.Esaml - alias Samly.{Esaml, Helper, IdpData, SpData} + alias Samly.{Esaml, Helper, IdpData, SpData, XmlAdapter} @type nameid_format :: :unknown | charlist() @type certs :: [binary()] @@ -62,27 +61,6 @@ defmodule Samly.IdpData do valid?: boolean() } - @entdesc "md:EntityDescriptor" - @idpdesc "md:IDPSSODescriptor" - @signedreq "WantAuthnRequestsSigned" - @nameid "md:NameIDFormat" - @keydesc "md:KeyDescriptor" - @ssos "md:SingleSignOnService" - @slos "md:SingleLogoutService" - @redirect "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - @post "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - - @entity_id_selector ~x"//#{@entdesc}/@entityID"sl - @nameid_format_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@nameid}/text()"s - @req_signed_selector ~x"//#{@entdesc}/#{@idpdesc}/@#{@signedreq}"s - @sso_redirect_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@ssos}[@Binding = '#{@redirect}']/@Location"s - @sso_post_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@ssos}[@Binding = '#{@post}']/@Location"s - @slo_redirect_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@slos}[@Binding = '#{@redirect}']/@Location"s - @slo_post_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@slos}[@Binding = '#{@post}']/@Location"s - @signing_keys_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@keydesc}[@use != 'encryption']"l - @enc_keys_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@keydesc}[@use = 'encryption']"l - @cert_selector ~x"./ds:KeyInfo/ds:X509Data/ds:X509Certificate/text()"s - @type id :: binary() def store() do @@ -258,28 +236,20 @@ defmodule Samly.IdpData do @spec from_xml(binary, %IdpData{}) :: {:ok, %IdpData{}} def from_xml(metadata_xml, idp_data) when is_binary(metadata_xml) do - xml_opts = [ - space: :normalize, - namespace_conformant: true, - comments: false, - default_attrs: true - ] - - md_xml = SweetXml.parse(metadata_xml, xml_opts) - signing_certs = get_signing_certs(md_xml) + {:ok, data} = XmlAdapter.import(metadata_xml) {:ok, %IdpData{ idp_data - | entity_id: get_entity_id(md_xml), - signed_requests: get_req_signed(md_xml), - certs: signing_certs, - fingerprints: idp_cert_fingerprints(signing_certs), - sso_redirect_url: get_sso_redirect_url(md_xml), - sso_post_url: get_sso_post_url(md_xml), - slo_redirect_url: get_slo_redirect_url(md_xml), - slo_post_url: get_slo_post_url(md_xml), - nameid_format: get_nameid_format(md_xml) + | entity_id: data.entity_id, + signed_requests: data.signed_requests, + certs: data.certs, + fingerprints: idp_cert_fingerprints(data.certs), + sso_redirect_url: data.sso_redirect_url, + sso_post_url: data.sso_post_url, + slo_redirect_url: data.slo_redirect_url, + slo_post_url: data.slo_post_url, + nameid_format: data.nameid_format }} end @@ -361,73 +331,4 @@ defmodule Samly.IdpData do entity_id: sp_entity_id ) end - - @spec get_entity_id(:xmlElement) :: binary() - def get_entity_id(md_elem) do - md_elem |> xpath(@entity_id_selector |> add_ns()) |> hd() |> String.trim() - end - - @spec get_nameid_format(:xmlElement) :: nameid_format() - def get_nameid_format(md_elem) do - case get_data(md_elem, @nameid_format_selector) do - "" -> :unknown - nameid_format -> to_charlist(nameid_format) - end - end - - @spec get_req_signed(:xmlElement) :: binary() - def get_req_signed(md_elem), do: get_data(md_elem, @req_signed_selector) - - @spec get_signing_certs(:xmlElement) :: certs() - def get_signing_certs(md_elem), do: get_certs(md_elem, @signing_keys_selector) - - @spec get_enc_certs(:xmlElement) :: certs() - def get_enc_certs(md_elem), do: get_certs(md_elem, @enc_keys_selector) - - @spec get_certs(:xmlElement, %SweetXpath{}) :: certs() - defp get_certs(md_elem, key_selector) do - md_elem - |> xpath(key_selector |> add_ns()) - |> Enum.map(fn e -> - # Extract base64 encoded cert from XML (strip away any whitespace) - cert = xpath(e, @cert_selector |> add_ns()) - - cert - |> String.split() - |> Enum.map(&String.trim/1) - |> Enum.join() - end) - end - - @spec get_sso_redirect_url(:xmlElement) :: url() - def get_sso_redirect_url(md_elem), do: get_url(md_elem, @sso_redirect_url_selector) - - @spec get_sso_post_url(:xmlElement) :: url() - def get_sso_post_url(md_elem), do: get_url(md_elem, @sso_post_url_selector) - - @spec get_slo_redirect_url(:xmlElement) :: url() - def get_slo_redirect_url(md_elem), do: get_url(md_elem, @slo_redirect_url_selector) - - @spec get_slo_post_url(:xmlElement) :: url() - def get_slo_post_url(md_elem), do: get_url(md_elem, @slo_post_url_selector) - - @spec get_url(:xmlElement, %SweetXpath{}) :: url() - defp get_url(md_elem, selector) do - case get_data(md_elem, selector) do - "" -> nil - url -> url - end - end - - @spec get_data(:xmlElement, %SweetXpath{}) :: binary() - def get_data(md_elem, selector) do - md_elem |> xpath(selector |> add_ns()) |> String.trim() - end - - @spec add_ns(%SweetXpath{}) :: %SweetXpath{} - defp add_ns(xpath) do - xpath - |> SweetXml.add_namespace("md", "urn:oasis:names:tc:SAML:2.0:metadata") - |> SweetXml.add_namespace("ds", "http://www.w3.org/2000/09/xmldsig#") - end end diff --git a/lib/samly/xml_adapter.ex b/lib/samly/xml_adapter.ex new file mode 100644 index 0000000..fd8a7f5 --- /dev/null +++ b/lib/samly/xml_adapter.ex @@ -0,0 +1,117 @@ +defmodule Samly.XmlAdapter do + import SweetXml + + @type nameid_format :: :unknown | charlist() + @type certs :: [binary()] + @type url :: nil | binary() + + @entdesc "md:EntityDescriptor" + @idpdesc "md:IDPSSODescriptor" + @signedreq "WantAuthnRequestsSigned" + @nameid "md:NameIDFormat" + @keydesc "md:KeyDescriptor" + @ssos "md:SingleSignOnService" + @slos "md:SingleLogoutService" + @redirect "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + @post "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + + @entity_id_selector ~x"//#{@entdesc}/@entityID"sl + @nameid_format_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@nameid}/text()"s + @req_signed_selector ~x"//#{@entdesc}/#{@idpdesc}/@#{@signedreq}"s + @sso_redirect_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@ssos}[@Binding = '#{@redirect}']/@Location"s + @sso_post_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@ssos}[@Binding = '#{@post}']/@Location"s + @slo_redirect_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@slos}[@Binding = '#{@redirect}']/@Location"s + @slo_post_url_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@slos}[@Binding = '#{@post}']/@Location"s + @signing_keys_selector ~x"//#{@entdesc}/#{@idpdesc}/#{@keydesc}[@use != 'encryption']"l + @cert_selector ~x"./ds:KeyInfo/ds:X509Data/ds:X509Certificate/text()"s + + def import(metadata_xml) when is_binary(metadata_xml) do + xml_opts = [ + space: :normalize, + namespace_conformant: true, + comments: false, + default_attrs: true + ] + + md_xml = SweetXml.parse(metadata_xml, xml_opts) + signing_certs = get_signing_certs(md_xml) + + {:ok, + %{ + entity_id: get_entity_id(md_xml), + signed_requests: get_req_signed(md_xml), + certs: signing_certs, + sso_redirect_url: get_sso_redirect_url(md_xml), + sso_post_url: get_sso_post_url(md_xml), + slo_redirect_url: get_slo_redirect_url(md_xml), + slo_post_url: get_slo_post_url(md_xml), + nameid_format: get_nameid_format(md_xml) + }} + end + + @spec get_nameid_format(:xmlElement) :: nameid_format() + defp get_nameid_format(md_elem) do + case get_data(md_elem, @nameid_format_selector) do + "" -> :unknown + nameid_format -> to_charlist(nameid_format) + end + end + + @spec get_signing_certs(:xmlElement) :: certs() + defp get_signing_certs(md_elem), do: get_certs(md_elem, @signing_keys_selector) + + @spec get_certs(:xmlElement, %SweetXpath{}) :: certs() + defp get_certs(md_elem, key_selector) do + md_elem + |> xpath(key_selector |> add_ns()) + |> Enum.map(fn e -> + # Extract base64 encoded cert from XML (strip away any whitespace) + cert = xpath(e, @cert_selector |> add_ns()) + + cert + |> String.split() + |> Enum.map(&String.trim/1) + |> Enum.join() + end) + end + + @spec get_entity_id(:xmlElement) :: binary() + defp get_entity_id(md_elem) do + md_elem |> xpath(@entity_id_selector |> add_ns()) |> hd() |> String.trim() + end + + @spec get_req_signed(:xmlElement) :: binary() + defp get_req_signed(md_elem), do: get_data(md_elem, @req_signed_selector) + + @spec get_sso_redirect_url(:xmlElement) :: url() + defp get_sso_redirect_url(md_elem), do: get_url(md_elem, @sso_redirect_url_selector) + + @spec get_sso_post_url(:xmlElement) :: url() + defp get_sso_post_url(md_elem), do: get_url(md_elem, @sso_post_url_selector) + + @spec get_slo_redirect_url(:xmlElement) :: url() + defp get_slo_redirect_url(md_elem), do: get_url(md_elem, @slo_redirect_url_selector) + + @spec get_slo_post_url(:xmlElement) :: url() + defp get_slo_post_url(md_elem), do: get_url(md_elem, @slo_post_url_selector) + + @spec get_url(:xmlElement, %SweetXpath{}) :: url() + defp get_url(md_elem, selector) do + case get_data(md_elem, selector) do + "" -> nil + url -> url + end + end + + @spec get_data(:xmlElement, %SweetXpath{}) :: binary() + defp get_data(md_elem, selector) do + md_elem |> xpath(selector |> add_ns()) |> String.trim() + end + + @spec add_ns(%SweetXpath{}) :: %SweetXpath{} + defp add_ns(xpath) do + xpath + |> SweetXml.add_namespace("md", "urn:oasis:names:tc:SAML:2.0:metadata") + |> SweetXml.add_namespace("ds", "http://www.w3.org/2000/09/xmldsig#") + end +end From 1b5dffd48368165816dca28110f53c0b60622381 Mon Sep 17 00:00:00 2001 From: Adrien Anselme Date: Fri, 4 Sep 2020 15:01:44 +0200 Subject: [PATCH 3/7] Added basic xml validation test. --- test/xml_adapter_test.exs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/xml_adapter_test.exs diff --git a/test/xml_adapter_test.exs b/test/xml_adapter_test.exs new file mode 100644 index 0000000..1756df7 --- /dev/null +++ b/test/xml_adapter_test.exs @@ -0,0 +1,28 @@ +defmodule XmlAdapterTest do + use ExUnit.Case + alias Samly.XmlAdapter + + test "valid parsed fields" do + file = "test/data/shibboleth_idp_metadata.xml" + expected = expected() + + assert {:reading, {:ok, raw_xml}} = {:reading, File.read(file)} + assert {:ok, ^expected} = XmlAdapter.import(raw_xml) + end + + defp expected() do + %{ + certs: [ + "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" + ], + entity_id: "https://samly.idp:4443/idp/shibboleth", + nameid_format: :unknown, + signed_requests: "", + slo_post_url: "https://samly.idp:4443/idp/profile/SAML2/POST/SLO", + slo_redirect_url: "https://samly.idp:4443/idp/profile/SAML2/Redirect/SLO", + sso_post_url: "https://samly.idp:4443/idp/profile/SAML2/POST/SSO", + sso_redirect_url: "https://samly.idp:4443/idp/profile/SAML2/Redirect/SSO" + } + end +end From b673e4bb925b11835d9fd579032ebe45c3db8793 Mon Sep 17 00:00:00 2001 From: Adrien Anselme Date: Fri, 4 Sep 2020 16:49:56 +0200 Subject: [PATCH 4/7] Hack around to allow using a map in place of the metadata xml file. --- lib/samly/idp_data.ex | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/samly/idp_data.ex b/lib/samly/idp_data.ex index 91184d9..c1a7451 100644 --- a/lib/samly/idp_data.ex +++ b/lib/samly/idp_data.ex @@ -38,7 +38,7 @@ defmodule Samly.IdpData do id: binary(), sp_id: binary(), base_url: nil | binary(), - metadata_file: nil | binary(), + metadata_file: nil | binary() | map(), pre_session_create_pipeline: nil | module(), use_redirect_for_req: boolean(), sign_requests: boolean(), @@ -104,31 +104,55 @@ defmodule Samly.IdpData do end @spec load_metadata(%IdpData{}, map()) :: %IdpData{} - defp load_metadata(idp_data, _opts_map) do - with {:reading, {:ok, raw_xml}} <- {:reading, File.read(idp_data.metadata_file)}, + defp load_metadata(%IdpData{metadata_file: metadata_file} = idp_data, _opts_map) + when is_binary(metadata_file) do + with {:reading, {:ok, raw_xml}} <- {:reading, File.read(metadata_file)}, {:parsing, {:ok, idp_data}} <- {:parsing, from_xml(raw_xml, idp_data)} do idp_data else {:reading, {:error, reason}} -> Logger.error( - "[Samly] Failed to read metadata_file [#{inspect(idp_data.metadata_file)}]: #{ - inspect(reason) - }" + "[Samly] Failed to read metadata_file [#{inspect(metadata_file)}]: #{inspect(reason)}" ) idp_data {:parsing, {:error, reason}} -> Logger.error( - "[Samly] Invalid metadata_file content [#{inspect(idp_data.metadata_file)}]: #{ - inspect(reason) - }" + "[Samly] Invalid metadata_file content [#{inspect(metadata_file)}]: #{inspect(reason)}" ) idp_data end end + defp load_metadata(%IdpData{metadata_file: data} = idp_data, _opts_map) when is_map(data) do + # TODO defstruct on the map: + # %{ + # certs: ["xyz"], + # entity_id: "http://www.abc.com/def", + # nameid_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressurn:oasis:names:tc:SAML:2.0:nameid-format:transient', + # signed_requests: "false", + # slo_post_url: nil, + # slo_redirect_url: nil, + # sso_post_url: "https://url.com/sso/saml", + # sso_redirect_url: "https://url.com/sso/saml" + # } + + %IdpData{ + idp_data + | entity_id: data.entity_id, + signed_requests: data.signed_requests, + certs: data.certs, + fingerprints: idp_cert_fingerprints(data.certs), + sso_redirect_url: data.sso_redirect_url, + sso_post_url: data.sso_post_url, + slo_redirect_url: data.slo_redirect_url, + slo_post_url: data.slo_post_url, + nameid_format: data.nameid_format + } + end + @spec update_esaml_recs(%IdpData{}, %{required(id()) => %SpData{}}, map()) :: %IdpData{} defp update_esaml_recs(idp_data, service_providers, opts_map) do case Map.get(service_providers, idp_data.sp_id) do From 1126a143b48e613476deda20b603ba2f44d3022a Mon Sep 17 00:00:00 2001 From: Adrien Anselme Date: Mon, 28 Sep 2020 15:37:22 +0200 Subject: [PATCH 5/7] Use esaml branch allowing newer cowboy version --- mix.exs | 2 +- mix.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/mix.exs b/mix.exs index 2897126..0a5fa6c 100644 --- a/mix.exs +++ b/mix.exs @@ -30,7 +30,7 @@ defmodule Samly.Mixfile do defp deps() do [ {:plug, "~> 1.6"}, - {:esaml, "~> 4.2"}, + {:esaml, git: "https://github.com/asurin/esaml.git", branch: "main"}, {:sweet_xml, "~> 0.6.6"}, {:ex_doc, "~> 0.19.0", only: :dev, runtime: false}, {:inch_ex, "~> 1.0", only: [:dev, :test]} diff --git a/mix.lock b/mix.lock index a7fcc4d..d9a0cdc 100644 --- a/mix.lock +++ b/mix.lock @@ -1,17 +1,17 @@ %{ - "cowboy": {:hex, :cowboy, "2.6.0", "dc1ff5354c89e36a3e3ef8d10433396dcff0dcbb1d4223b58c64c2d51a6d88d9", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, - "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"}, - "esaml": {:hex, :esaml, "4.2.0", "e3236ec9c1974c50f50766b9923f7f127a95a17b75ea229ef5dad36a7e6dd5fc", [:rebar3], [{:cowboy, "2.6.0", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, - "inch_ex": {:hex, :inch_ex, "1.0.1", "1f0af1a83cec8e56f6fc91738a09c838e858db3d78ef5f2ec040fe4d5a62dabf", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, - "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, - "plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"}, - "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, - "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"}, + "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, + "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, + "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, + "esaml": {:git, "https://github.com/asurin/esaml.git", "45a58c527e24fc753c02fab2f655603e75308057", [branch: "main"]}, + "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0e11d67e662142fc3945b0ee410c73c8c956717fbeae4ad954b418747c734973"}, + "inch_ex": {:hex, :inch_ex, "1.0.1", "1f0af1a83cec8e56f6fc91738a09c838e858db3d78ef5f2ec040fe4d5a62dabf", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "08fd8a9205d3e1aefad9d7cb2a7f6b346e4a3e6ff09e139f6ec978f3a479ba14"}, + "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5fbc8e549aa9afeea2847c0769e3970537ed302f93a23ac612602e805d9d1e7f"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "adf0218695e22caeda2820eaba703fa46c91820d53813a2223413da3ef4ba515"}, + "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"}, + "plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm", "de9825f21c6fd6adfdeae8f9c80dcd88c1e58301f06bf13d659b7e606b88abe0"}, + "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm", "73c1682f0e414cfb5d9b95c8e8cd6ffcfdae699e3b05e1db744e58b7be857759"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, + "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, } From 262e9e4028ca5f8c5e925cd5782d75e447e644d1 Mon Sep 17 00:00:00 2001 From: Yuri Lukyanov Date: Wed, 4 Aug 2021 15:02:33 +0300 Subject: [PATCH 6/7] Upgrade esaml --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index d9a0cdc..5ca61e4 100644 --- a/mix.lock +++ b/mix.lock @@ -2,7 +2,7 @@ "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, - "esaml": {:git, "https://github.com/asurin/esaml.git", "45a58c527e24fc753c02fab2f655603e75308057", [branch: "main"]}, + "esaml": {:git, "https://github.com/asurin/esaml.git", "262181c4bca1ec069a4408be70cdf616eed62e17", [branch: "main"]}, "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0e11d67e662142fc3945b0ee410c73c8c956717fbeae4ad954b418747c734973"}, "inch_ex": {:hex, :inch_ex, "1.0.1", "1f0af1a83cec8e56f6fc91738a09c838e858db3d78ef5f2ec040fe4d5a62dabf", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "08fd8a9205d3e1aefad9d7cb2a7f6b346e4a3e6ff09e139f6ec978f3a479ba14"}, "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5fbc8e549aa9afeea2847c0769e3970537ed302f93a23ac612602e805d9d1e7f"}, From 823a713cd96da8cb131a65fdb18a415740f9e5f5 Mon Sep 17 00:00:00 2001 From: Yuri Lukyanov Date: Wed, 4 Aug 2021 15:30:31 +0300 Subject: [PATCH 7/7] Update mix.lock with newer cowboy --- mix.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index 5ca61e4..f95a2c5 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ - "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, - "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, "esaml": {:git, "https://github.com/asurin/esaml.git", "262181c4bca1ec069a4408be70cdf616eed62e17", [branch: "main"]}, "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0e11d67e662142fc3945b0ee410c73c8c956717fbeae4ad954b418747c734973"}, @@ -12,6 +12,6 @@ "plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm", "de9825f21c6fd6adfdeae8f9c80dcd88c1e58301f06bf13d659b7e606b88abe0"}, "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm", "73c1682f0e414cfb5d9b95c8e8cd6ffcfdae699e3b05e1db744e58b7be857759"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, - "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, + "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, }