diff --git a/lib/samly/application_config.ex b/lib/samly/application_config.ex new file mode 100644 index 0000000..6202f00 --- /dev/null +++ b/lib/samly/application_config.ex @@ -0,0 +1,6 @@ +defmodule Samly.ApplicationConfig do + @behaviour Samly.ConfigBehaviour + + @impl true + def get_idp(_conn, idp_id), do: Samly.Helper.get_idp(idp_id) +end diff --git a/lib/samly/auth_router.ex b/lib/samly/auth_router.ex index ac64b9c..29a6a46 100644 --- a/lib/samly/auth_router.ex +++ b/lib/samly/auth_router.ex @@ -6,7 +6,7 @@ defmodule Samly.AuthRouter do import Samly.RouterUtil, only: [check_idp_id: 2, check_target_url: 2] plug :fetch_session - plug Plug.CSRFProtection + plug Plug.CSRFProtection, with: :clear_session plug :match plug :check_idp_id plug :check_target_url diff --git a/lib/samly/config_behaviour.ex b/lib/samly/config_behaviour.ex new file mode 100644 index 0000000..49657f1 --- /dev/null +++ b/lib/samly/config_behaviour.ex @@ -0,0 +1,3 @@ +defmodule Samly.ConfigBehaviour do + @callback get_idp(conn :: Plug.Conn.t(), idp_id :: binary) :: nil | Samly.IdpData.t() +end diff --git a/lib/samly/helper.ex b/lib/samly/helper.ex index 32b1374..2fd4602 100644 --- a/lib/samly/helper.ex +++ b/lib/samly/helper.ex @@ -1,6 +1,5 @@ defmodule Samly.Helper do @moduledoc false - require Samly.Esaml alias Samly.{Assertion, Esaml, IdpData} diff --git a/lib/samly/idp_data.ex b/lib/samly/idp_data.ex index cc21cb7..496e917 100644 --- a/lib/samly/idp_data.ex +++ b/lib/samly/idp_data.ex @@ -89,6 +89,13 @@ defmodule Samly.IdpData do @type id :: binary() + @spec load_providers(map(), map()) :: %IdpData{} + def from_config(sp_config, idp_config) do + service_providers = Samly.SpData.load_providers([sp_config]) + config_map = load_providers([idp_config], service_providers) + config_map[idp_config.id] + 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/router_util.ex b/lib/samly/router_util.ex index 66407db..5f77a78 100644 --- a/lib/samly/router_util.ex +++ b/lib/samly/router_util.ex @@ -24,7 +24,9 @@ defmodule Samly.RouterUtil do end end - idp = idp_id && Helper.get_idp(idp_id) + config = Application.get_env(:samly, :config_provider, Samly.ApplicationConfig) + + idp = idp_id && config.get_idp(conn, idp_id) if idp do conn |> Conn.put_private(:samly_idp, idp) diff --git a/mix.exs b/mix.exs index 3c4cc6c..8176ac8 100644 --- a/mix.exs +++ b/mix.exs @@ -10,6 +10,7 @@ defmodule Samly.Mixfile do app: :samly, version: @version, description: @description, + elixirc_paths: elixirc_paths(Mix.env()), docs: docs(), package: package(), elixir: "~> 1.10", @@ -25,6 +26,9 @@ defmodule Samly.Mixfile do ] end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + # Run "mix help deps" to learn about dependencies. defp deps() do [ @@ -32,7 +36,9 @@ defmodule Samly.Mixfile do {:esaml, "~> 4.3"}, {:sweet_xml, "~> 0.6"}, {:dialyxir, "~> 1.0", only: [:dev], runtime: false}, - {:ex_doc, "~> 0.19", only: :dev, runtime: false} + {:ex_doc, "~> 0.19", only: :dev, runtime: false}, + {:mix_test_watch, "~> 1.1", only: [:dev, :test], runtime: false}, + {:floki, "~> 0.36.2", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index cffc36f..0c2ebe6 100644 --- a/mix.lock +++ b/mix.lock @@ -6,10 +6,13 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esaml": {:hex, :esaml, "4.6.0", "8fb5a3a0d56ccfce3e081a2f72b29058511047a2abbafb64cb6f595bf7465124", [:rebar3], [{:cowboy, "< 3.0.0", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm", "d34d0b259cd8ac8215fd2c333fac9dbbb91b5f5da5a9304508612ff3ac0afa7a"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, + "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, + "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.2.0", "1f9acd9e1104f62f280e30fc2243ae5e6d8ddc2f7f4dc9bceb454b9a41c82b42", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "278dc955c20b3fb9a3168b5c2493c2e5cffad133548d307e0a50c7f2cfbf34f6"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, diff --git a/test/auth_router_test.exs b/test/auth_router_test.exs new file mode 100644 index 0000000..6b6f677 --- /dev/null +++ b/test/auth_router_test.exs @@ -0,0 +1,36 @@ +defmodule Samly.AuthRouterTest do + use Samly.RouterCase + + setup do + setup_providers([@sp_config], [@idp_config]) + end + + test "GET on signin uri returns saml html form" do + conn(:get, "/signin/idp1") + |> init_test_session(%{}) + |> AuthRouter.call([]) + |> assert_initial_saml_form("%2F") + end + + test "GET on signin uri returns saml html form with the given target url" do + conn(:get, "/signin/idp1", target_url: "/Home") + |> init_test_session(%{}) + |> AuthRouter.call([]) + |> assert_initial_saml_form("%2FHome") + end + + test "POST on signin uri returns form that will be submited to idp" do + assert ~c"urn:test:sp1" = + conn(:post, "/signin/idp1", %{RelayState: "OOhdIq-_PagPusisHCjYBZsYSwr-bVUs"}) + |> put_private(:plug_skip_csrf_protection, true) + |> put_private(:samly_nonce, "1mv+7BUs8o1nkOxa6ufS6kJ") + |> init_test_session(%{}) + |> AuthRouter.call([]) + |> assert_form("POST", "http://samly.idp:8082/simplesaml/saml2/idp/SSOService.php") + |> Floki.attribute("input[name=SAMLRequest]", "value") + |> List.first() + |> Base.decode64!() + |> SweetXml.parse() + |> SweetXml.xpath(~x"//saml:Issuer/text()") + end +end diff --git a/test/config_behaviour_test.exs b/test/config_behaviour_test.exs new file mode 100644 index 0000000..ffc6b64 --- /dev/null +++ b/test/config_behaviour_test.exs @@ -0,0 +1,42 @@ +defmodule Samly.ConfigBehaviourTest do + use Samly.RouterCase + alias Samly.SPRouter + + def get_idp(_conn, idp_id) do + assert idp_id == @idp_config.id + Samly.IdpData.from_config(@sp_config, @idp_config) + end + + setup do + Application.put_env(:samly, :config_provider, Samly.ConfigBehaviourTest) + setup_providers([], []) + end + + test "GET on signin uri returns saml html form" do + conn(:get, "/signin/idp1") + |> init_test_session(%{}) + |> AuthRouter.call([]) + |> assert_initial_saml_form("%2F") + end + + test "POST consume saml assertion" do + assertion = + File.read!("./test/data/simplesaml_idp_assertion.xml") + |> Base.encode64() + + conn = + conn(:post, "/consume/idp1", %{ + SAMLResponse: assertion, + RelayState: "OOhdIq-_PagPusisHCjYBZsYSwr-bVUs" + }) + |> init_test_session(%{ + "relay_state" => "OOhdIq-_PagPusisHCjYBZsYSwr-bVUs", + "idp_id" => "idp1", + "target_url" => "/Home" + }) + |> SPRouter.call([]) + + assert conn.status == 302 + assert "/Home" = get_resp_header(conn, "location") |> List.first() + end +end diff --git a/test/data/simplesaml_idp_assertion.xml b/test/data/simplesaml_idp_assertion.xml new file mode 100644 index 0000000..f20a3cc --- /dev/null +++ b/test/data/simplesaml_idp_assertion.xml @@ -0,0 +1,86 @@ + + http://samly.idp:8082/simplesaml/saml2/idp/metadata.php + + + + + + + + + + + SsE1VSW1l4/Uff6lJCjEQDYYMI6t2AdnNq1CBLK1HXo= + + + + wLAVBA8yZpz1eMlR2oqiwAlAnLnjLuQ+kq1Rpn4cQkaTu34EePmWsroDOvEZBkM9vqTPyafCDeRS3SrF+13lv8FdnliO3AgwLGO9Kt2s0q8Oh/t1KJyZagkjmjh/kzEmL4hpgVnz4urGNp8gQMxI9R6gWh8sqGuqT3O9tH2/YG4ctMPghOEw1AKLv6Eq0VMUpNDGP6dUNdsM81UizD+zYc7S7wxbEP4nb1prev5T98fpouG82r5A/OwXBHrRx2QqTAocipIewo6U9OIbD3ctBC1kMvN4hPMnPEF8PVnxA8Fh9El0+E1qXJA0OQ5R9Tz7EfXFJlaQxmCvFeOYeWcPjQ== + + + + MIICmjCCAYICCQDX5sKPsYV3+jANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTE5MTIyMzA5MDI1MVoXDTIwMDEyMjA5MDI1MVowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdtDJ278DQTp84O5Nq5F8s5YOR34GFOGI2Swb/3pU7X7918lVljiKv7WVM65S59nJSyXV+fa15qoXLfsdRnq3yw0hTSTs2YDX+jl98kK3ksk3rROfYh1LIgByj4/4NeNpExgeB6rQk5Ay7YS+ARmMzEjXa0favHxu5BOdB2y6WvRQyjPS2lirT/PKWBZc04QZepsZ56+W7bd557tdedcYdY/nKI1qmSQClG2qgslzgqFOv1KCOw43a3mcK/TiiD8IXyLMJNC6OFW3xTL/BG6SOZ3dQ9rjQOBga+6GIaQsDjC4Xp7Kx+FkSvgaw0sJV8gt1mlZy+27Sza6d+hHD2pWECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAm2fk1+gd08FQxK7TL04O8EK1f0bzaGGUxWzlh98a3Dm8+OPhVQRi/KLsFHliLC86lsZQKunYdDB+qd0KUk2oqDG6tstG/htmRYD/S/jNmt8gyPAVi11dHUqW3IvQgJLwxZtoAv6PNs188hvT1WK3VWJ4YgFKYi5XQYnR5sv69Vsr91lYAxyrIlMKahjSW1jTD3ByRfAQghsSLk6fV0OyJHyhuF1TxOVBVf8XOdaqfmvD90JGIPGtfMLPUX4m35qaGAU48PwCL7L3cRHYs9wZWc0ifXZcBENLtHYCLi5txR8c5lyHB9d3AQHzKHMFNjLswn5HsckKg83RH7+eVqHqGw== + + + + + + + + http://samly.idp:8082/simplesaml/saml2/idp/metadata.php + + + + + + + + + + + r1+smsBJ0NEKEazRJH8f6abeGOOhGXTOiW510t2mBmA= + + + + kbVcFaZ44aDvSt8GHC/RjI1XCGCFWPotO1MWvCUK+0AV2GhU5BTMqk5JNA3fVolHzSsnUh32Eup0CXxUgJL3zs3tvBN9o9zQmvXePQNprdvOd9Ileijyg/o66dTP9neBN3IzfAtRRzuIj3Ay2Ba7JlIfrLa5RQIr/uZ/QiXNtnl4ultaFWue58VjFcuF9Qei4NUbPxnB+zqpHhKsTsL/XYX/cEZ/1HqKNQ0a4YOjPBUJYfIjhMabaN6wN7v0nMrzQHxJtjhseXx+AmbALLCtWjPAoj9yhqGlRC6q2dmSSgJ2eEpDcSPaElmagu0ypwbSRGl8RgwFUsmgIT6fX8gruw== + + + + MIICmjCCAYICCQDX5sKPsYV3+jANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTE5MTIyMzA5MDI1MVoXDTIwMDEyMjA5MDI1MVowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdtDJ278DQTp84O5Nq5F8s5YOR34GFOGI2Swb/3pU7X7918lVljiKv7WVM65S59nJSyXV+fa15qoXLfsdRnq3yw0hTSTs2YDX+jl98kK3ksk3rROfYh1LIgByj4/4NeNpExgeB6rQk5Ay7YS+ARmMzEjXa0favHxu5BOdB2y6WvRQyjPS2lirT/PKWBZc04QZepsZ56+W7bd557tdedcYdY/nKI1qmSQClG2qgslzgqFOv1KCOw43a3mcK/TiiD8IXyLMJNC6OFW3xTL/BG6SOZ3dQ9rjQOBga+6GIaQsDjC4Xp7Kx+FkSvgaw0sJV8gt1mlZy+27Sza6d+hHD2pWECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAm2fk1+gd08FQxK7TL04O8EK1f0bzaGGUxWzlh98a3Dm8+OPhVQRi/KLsFHliLC86lsZQKunYdDB+qd0KUk2oqDG6tstG/htmRYD/S/jNmt8gyPAVi11dHUqW3IvQgJLwxZtoAv6PNs188hvT1WK3VWJ4YgFKYi5XQYnR5sv69Vsr91lYAxyrIlMKahjSW1jTD3ByRfAQghsSLk6fV0OyJHyhuF1TxOVBVf8XOdaqfmvD90JGIPGtfMLPUX4m35qaGAU48PwCL7L3cRHYs9wZWc0ifXZcBENLtHYCLi5txR8c5lyHB9d3AQHzKHMFNjLswn5HsckKg83RH7+eVqHqGw== + + + + + + _7fa378b1b922a7e6793eb432b8ec9dfc0374b4e8a3 + + + + + + + urn:test:sp1 + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + user1@example.com + + + + \ No newline at end of file diff --git a/test/data/simplesaml_idp_metadata.xml b/test/data/simplesaml_idp_metadata.xml index 7eb081e..cf977d8 100644 --- a/test/data/simplesaml_idp_metadata.xml +++ b/test/data/simplesaml_idp_metadata.xml @@ -1,36 +1,26 @@ - - - + + + - - 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 - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - Jane - Doe - jane.doe@company.com + Administrator + mailto:na@example.com diff --git a/test/samly_config_test.exs b/test/samly_config_test.exs index 2e848e3..39b195e 100644 --- a/test/samly_config_test.exs +++ b/test/samly_config_test.exs @@ -33,5 +33,8 @@ defmodule SamlyConfigTest do assert Application.get_env(:samly, :identity_providers) == %{"idp1" => Samly.IdpData.load_provider(@idp_config, sps)} + + Application.delete_env(:samly, :service_providers) + Application.delete_env(:samly, :identity_providers) end end diff --git a/test/support/router_case.ex b/test/support/router_case.ex new file mode 100644 index 0000000..e14ac4e --- /dev/null +++ b/test/support/router_case.ex @@ -0,0 +1,69 @@ +defmodule Samly.RouterCase do + use ExUnit.CaseTemplate + alias Samly.Provider + + using do + quote do + # Import conveniences for testing with connections + use ExUnit.Case + use Plug.Test + + import SweetXml + + alias Samly.AuthRouter + + import Samly.RouterCase + + @sp_config %{ + id: "sp1", + entity_id: "urn:test:sp1", + certfile: "test/data/test.crt", + keyfile: "test/data/test.pem" + } + + @idp_config %{ + id: "idp1", + sp_id: "sp1", + base_url: "http://samly.howto:4003/sso", + metadata_file: "test/data/simplesaml_idp_metadata.xml", + sign_requests: false, + sign_metadata: false, + signed_assertion_in_resp: false, + signed_envelopes_in_resp: false + } + end + end + + def setup_providers(sps, idps) do + Application.put_env(:samly, Provider, + service_providers: sps, + identity_providers: idps + ) + + on_exit(fn -> + Application.delete_env(:samly, :service_providers) + Application.delete_env(:samly, :identity_providers) + Application.delete_env(:samly, :config_provider) + end) + + Provider.init([]) + :ok + end + + def assert_form(conn, method, action) do + assert conn.status == 200 + assert conn.method == method + + form = Floki.parse_document!(conn.resp_body) |> Floki.find("form") + assert [^action] = Floki.attribute(form, "action") + assert ["post"] = Floki.attribute(form, "method") + + form + end + + def assert_initial_saml_form(conn, target_url) do + assert [^target_url] = + assert_form(conn, "GET", "/signin/idp1") + |> Floki.attribute("input[name=target_url]", "value") + end +end