Skip to content
This repository was archived by the owner on Jan 22, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/samly/helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ defmodule Samly.Helper do
require Samly.Esaml
alias Samly.{Assertion, Esaml, IdpData}

@spec get_idp(binary) :: nil | IdpData.t()
def get_idp(idp_id) do
idps = Application.get_env(:samly, :identity_providers, %{})
@spec get_idp(atom(), binary) :: nil | IdpData.t()
def get_idp(otp_app, idp_id) do
idps = Application.get_env(otp_app, Samly.IdentityProviders, %{})
Map.get(idps, idp_id)
end

Expand Down
24 changes: 12 additions & 12 deletions lib/samly/idp_data.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,23 @@ defmodule Samly.IdpData do

@type id :: binary()

@spec load_providers([map], %{required(id()) => %SpData{}}) ::
@spec load_providers([map], %{required(id()) => %SpData{}}, map()) ::
%{required(id()) => %IdpData{}} | no_return()
def load_providers(prov_config, service_providers) do
def load_providers(prov_config, service_providers, config) do
prov_config
|> Enum.map(fn idp_config -> load_provider(idp_config, service_providers) end)
|> Enum.map(fn idp_config -> load_provider(idp_config, service_providers, config) end)
|> Enum.filter(fn idp_data -> idp_data.valid? end)
|> Enum.map(fn idp_data -> {idp_data.id, idp_data} end)
|> Enum.into(%{})
end

@spec load_provider(map(), %{required(id()) => %SpData{}}) :: %IdpData{} | no_return
def load_provider(idp_config, service_providers) do
@spec load_provider(map(), %{required(id()) => %SpData{}}, map()) :: %IdpData{} | no_return
def load_provider(idp_config, service_providers, config) do
%IdpData{}
|> save_idp_config(idp_config)
|> load_metadata(idp_config)
|> override_nameid_format(idp_config)
|> update_esaml_recs(service_providers, idp_config)
|> update_esaml_recs(service_providers, idp_config, config)
|> verify_slo_url()
end

Expand Down Expand Up @@ -146,12 +146,12 @@ defmodule Samly.IdpData do
end
end

@spec update_esaml_recs(%IdpData{}, %{required(id()) => %SpData{}}, map()) :: %IdpData{}
defp update_esaml_recs(idp_data, service_providers, opts_map) do
@spec update_esaml_recs(%IdpData{}, %{required(id()) => %SpData{}}, map(), map()) :: %IdpData{}
defp update_esaml_recs(idp_data, service_providers, opts_map, config) do
case Map.get(service_providers, idp_data.sp_id) do
%SpData{} = sp ->
idp_data = %IdpData{idp_data | esaml_idp_rec: to_esaml_idp_metadata(idp_data, opts_map)}
idp_data = %IdpData{idp_data | esaml_sp_rec: get_esaml_sp(sp, idp_data)}
idp_data = %IdpData{idp_data | esaml_sp_rec: get_esaml_sp(sp, idp_data, config)}
%IdpData{idp_data | valid?: cert_config_ok?(idp_data, sp)}

_ ->
Expand Down Expand Up @@ -320,9 +320,9 @@ defmodule Samly.IdpData do
"sha256:" <> (:sha256 |> :crypto.hash(dercert) |> Base.encode64())
end

# @spec get_esaml_sp(%SpData{}, %IdpData{}) :: :esaml_sp
defp get_esaml_sp(%SpData{} = sp_data, %IdpData{} = idp_data) do
idp_id_from = Application.get_env(:samly, :idp_id_from)
# @spec get_esaml_sp(%SpData{}, %IdpData{}, map()) :: :esaml_sp
defp get_esaml_sp(%SpData{} = sp_data, %IdpData{} = idp_data, config) do
idp_id_from = config.idp_id_from
path_segment_idp_id = if idp_id_from == :subdomain, do: nil, else: idp_data.id

sp_entity_id =
Expand Down
29 changes: 19 additions & 10 deletions lib/samly/provider.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,24 @@ defmodule Samly.Provider do
require Samly.Esaml
alias Samly.{State}

@genserver_options ~W(name timeout debug spawn_opts hibernate_after)a

@doc false
def start_link(gs_opts \\ []) do
GenServer.start_link(__MODULE__, [], gs_opts)
def start_link(opts \\ []) do
{gs_opts, init_args} = Keyword.split(opts, @genserver_options)
GenServer.start_link(__MODULE__, init_args, gs_opts)
end

@doc false
def init([]) do
store_env = Application.get_env(:samly, Samly.State, [])
def init(opts) do
otp_app = Keyword.get(opts, :otp_app, :samly)

store_env = Application.get_env(otp_app, Samly.State, [])
store_provider = store_env[:store] || Samly.State.ETS
store_opts = store_env[:opts] || []
State.init(store_provider, store_opts)
State.init(otp_app, store_provider, store_opts)

opts = Application.get_env(:samly, Samly.Provider, [])
opts = Application.get_env(otp_app, Samly.Provider, [])

# must be done prior to loading the providers
idp_id_from =
Expand All @@ -55,15 +60,19 @@ defmodule Samly.Provider do
:path_segment
end

Application.put_env(:samly, :idp_id_from, idp_id_from)
new_config =
Application.get_env(otp_app, Samly.Config, %{})
|> Map.put(:idp_id_from, idp_id_from)

Application.put_env(otp_app, Samly.Config, new_config)

service_providers = Samly.SpData.load_providers(opts[:service_providers] || [])

identity_providers =
Samly.IdpData.load_providers(opts[:identity_providers] || [], service_providers)
Samly.IdpData.load_providers(opts[:identity_providers] || [], service_providers, new_config)

Application.put_env(:samly, :service_providers, service_providers)
Application.put_env(:samly, :identity_providers, identity_providers)
Application.put_env(otp_app, Samly.ServiceProviders, service_providers)
Application.put_env(otp_app, Samly.IdentityProviders, identity_providers)

{:ok, %{}}
end
Expand Down
6 changes: 6 additions & 0 deletions lib/samly/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule Samly.Router do

use Plug.Router

plug :config_samly, builder_opts()
plug :secure_samly
plug :match
plug :dispatch
Expand Down Expand Up @@ -38,4 +39,9 @@ defmodule Samly.Router do
|> put_resp_header("x-content-type-options", "nosniff")
end)
end

defp config_samly(conn, opts) do
opts = Keyword.put_new(opts, :otp_app, :samly)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This default will ensure that unmodified apps continue working as before

put_private(conn, :samly_config, opts)
end
end
11 changes: 8 additions & 3 deletions lib/samly/router_util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ defmodule Samly.RouterUtil do
@subdomain_re ~r/^(?<subdomain>([^.]+))?\./

def check_idp_id(conn, _opts) do
idp_id_from = Application.get_env(:samly, :idp_id_from)
otp_app = conn.private[:samly_config][:otp_app]

idp_id_from = Application.get_env(otp_app, Samly.Config).idp_id_from
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: is it better to do these get_envs all over, or set the whole config in conn.private?


idp_id =
if idp_id_from == :subdomain do
Expand All @@ -24,7 +26,7 @@ defmodule Samly.RouterUtil do
end
end

idp = idp_id && Helper.get_idp(idp_id)
idp = idp_id && Helper.get_idp(otp_app, idp_id)

if idp do
conn |> Conn.put_private(:samly_idp, idp)
Expand Down Expand Up @@ -60,7 +62,10 @@ defmodule Samly.RouterUtil do
}

base_url = URI.to_string(uri)
idp_id_from = Application.get_env(:samly, :idp_id_from)

otp_app = conn.private[:samly_config][:otp_app]

idp_id_from = Application.get_env(otp_app, Samly.Config).idp_id_from

path_segment_idp_id =
if idp_id_from == :subdomain do
Expand Down
27 changes: 19 additions & 8 deletions lib/samly/state.ex
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
defmodule Samly.State do
@moduledoc false

@state_store :state_store
def init(otp_app, store_provider), do: init(otp_app, store_provider, [])

def init(store_provider), do: init(store_provider, [])

def init(store_provider, opts) do
def init(otp_app, store_provider, opts) do
opts = store_provider.init(opts)
Application.put_env(:samly, @state_store, %{provider: store_provider, opts: opts})
set_state_store(otp_app, %{provider: store_provider, opts: opts})
end

def get_assertion(conn, assertion_key) do
%{provider: store_provider, opts: opts} = Application.get_env(:samly, @state_store)
%{provider: store_provider, opts: opts} = state_store_from(conn)
store_provider.get_assertion(conn, assertion_key, opts)
end

def put_assertion(conn, assertion_key, assertion) do
%{provider: store_provider, opts: opts} = Application.get_env(:samly, @state_store)
%{provider: store_provider, opts: opts} = state_store_from(conn)
store_provider.put_assertion(conn, assertion_key, assertion, opts)
end

def delete_assertion(conn, assertion_key) do
%{provider: store_provider, opts: opts} = Application.get_env(:samly, @state_store)
%{provider: store_provider, opts: opts} = state_store_from(conn)
store_provider.delete_assertion(conn, assertion_key, opts)
end

def gen_id() do
24 |> :crypto.strong_rand_bytes() |> Base.url_encode64()
end

defp state_store_from(conn) do
otp_app = conn.private[:samly_config][:otp_app]
Application.get_env(otp_app, Samly.Config).state_store
end

def set_state_store(otp_app, value) do
new_config =
Application.get_env(otp_app, Samly.Config, %{})
|> Map.put(:state_store, value)

Application.put_env(otp_app, Samly.Config, new_config)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: if this is a pattern worth keeping, consider encapsulating it in a module

end
end
30 changes: 15 additions & 15 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -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.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", "ff7cf0fb7a2762f423d845468b32ed4edb5c7e0d78e6875b0628153239a7bb34"},
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm", "59f952d504b9921a6f53226fdb8b4a671bb694277c7ccf1ddeaadcdc6d43d88e"},
"earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"},
"esaml": {:hex, :esaml, "4.2.0", "e3236ec9c1974c50f50766b9923f7f127a95a17b75ea229ef5dad36a7e6dd5fc", [:rebar3], [{:cowboy, "2.6.0", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm", "010d98b0eacddfd938192de2b493b92db9d0ab094ed10cb6838e63b5d64633f8"},
"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"},
}
Loading