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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
+ 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
- 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
+ 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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