From 662ffcbc8882bdac782d8b1592c3c4a986b527e8 Mon Sep 17 00:00:00 2001 From: handnot2 Date: Thu, 30 Nov 2017 23:46:51 -0800 Subject: [PATCH] Shibboleth SLO Session match fix. Fixes #11 --- CHANGELOG.md | 4 ++++ lib/samly/auth_handler.ex | 8 +++++--- lib/samly/helper.ex | 8 +++++--- lib/samly/subject.ex | 30 ++++++++++++++++++++++++++++++ mix.exs | 4 ++-- mix.lock | 4 ++-- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d6ca6..aeed291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +### v0.8.4 + ++ Shibboleth Single Logout session match related fix. Uptake `esaml v3.3.0`. + ### v0.8.3 + Generates SP metadata XML that passes XSD validation diff --git a/lib/samly/auth_handler.ex b/lib/samly/auth_handler.ex index ec367eb..e3aeeb9 100644 --- a/lib/samly/auth_handler.ex +++ b/lib/samly/auth_handler.ex @@ -3,7 +3,7 @@ defmodule Samly.AuthHandler do require Logger import Plug.Conn - alias Samly.{Assertion, IdpData, Helper, State} + alias Samly.{Assertion, IdpData, Helper, State, Subject} import Samly.RouterUtil, only: [ensure_sp_uris_set: 2, send_saml_request: 5, redirect: 3] @@ -105,8 +105,10 @@ defmodule Samly.AuthHandler do 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) + {^nameid, %Assertion{idp_id: ^idp_id, authn: authn, subject: subject}} -> + session_index = Map.get(authn, "session_index", "") + subject_rec = Subject.to_rec(subject) + {idp_signout_url, req_xml_frag} = Helper.gen_idp_signout_req(sp, idp_rec, subject_rec, session_index) State.delete(nameid) relay_state = State.gen_id() diff --git a/lib/samly/helper.ex b/lib/samly/helper.ex index 29293e0..d4487ad 100644 --- a/lib/samly/helper.ex +++ b/lib/samly/helper.ex @@ -49,13 +49,15 @@ defmodule Samly.Helper do def gen_idp_signin_req(sp, idp_metadata) do idp_signin_url = Esaml.esaml_idp_metadata(idp_metadata, :login_location) - xml_frag = :esaml_sp.generate_authn_request(idp_signin_url, sp) + # TODO: Expose an config + name_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + xml_frag = :esaml_sp.generate_authn_request(idp_signin_url, sp, name_format) {idp_signin_url, xml_frag} end - def gen_idp_signout_req(sp, idp_metadata, nameid) do + def gen_idp_signout_req(sp, idp_metadata, subject_rec, session_index) do idp_signout_url = Esaml.esaml_idp_metadata(idp_metadata, :logout_location) - xml_frag = :esaml_sp.generate_logout_request(idp_signout_url, nameid, sp) + xml_frag = :esaml_sp.generate_logout_request(idp_signout_url, session_index, subject_rec, sp) {idp_signout_url, xml_frag} end diff --git a/lib/samly/subject.ex b/lib/samly/subject.ex index 29ba29d..fcce2b0 100644 --- a/lib/samly/subject.ex +++ b/lib/samly/subject.ex @@ -11,11 +11,17 @@ defmodule Samly.Subject do alias Samly.Esaml defstruct name: "", + name_qualifier: :undefined, + sp_name_qualifier: :undefined, + name_format: :undefined, confirmation_method: :bearer, notonorafter: "" @type t :: %__MODULE__{ name: String.t(), + name_qualifier: :undefined | String.t(), + sp_name_qualifier: :undefined | String.t(), + name_format: :undefined | String.t(), confirmation_method: atom, notonorafter: String.t() } @@ -24,14 +30,38 @@ defmodule Samly.Subject do def from_rec(subject_rec) do Esaml.esaml_subject( name: name, + name_qualifier: name_qualifier, + sp_name_qualifier: sp_name_qualifier, + name_format: name_format, confirmation_method: confirmation_method, notonorafter: notonorafter ) = subject_rec %__MODULE__{ name: name |> List.to_string(), + name_qualifier: to_string_or_undefined(name_qualifier), + sp_name_qualifier: to_string_or_undefined(sp_name_qualifier), + name_format: to_string_or_undefined(name_format), confirmation_method: confirmation_method, notonorafter: notonorafter |> List.to_string() } end + + @doc false + def to_rec(subject) do + Esaml.esaml_subject( + name: String.to_charlist(subject.name), + name_qualifier: from_string_or_undefined(subject.name_qualifier), + sp_name_qualifier: from_string_or_undefined(subject.sp_name_qualifier), + name_format: from_string_or_undefined(subject.name_format), + confirmation_method: subject.confirmation_method, + notonorafter: String.to_charlist(subject.notonorafter) + ) + end + + defp to_string_or_undefined(:undefined), do: :undefined + defp to_string_or_undefined(s) when is_list(s), do: List.to_string(s) + + defp from_string_or_undefined(:undefined), do: :undefined + defp from_string_or_undefined(s) when is_binary(s), do: String.to_charlist(s) end diff --git a/mix.exs b/mix.exs index 781b73e..be74f52 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Samly.Mixfile do use Mix.Project - @version "0.8.3" + @version "0.8.4" @description "SAML SP SSO made easy" @source_url "https://github.com/handnot2/samly" @@ -29,7 +29,7 @@ defmodule Samly.Mixfile do defp deps() do [ {:plug, "~> 1.4"}, - {:esaml, "~> 3.2"}, + {:esaml, "~> 3.3"}, {:sweet_xml, "~> 0.6"}, {:ex_doc, "~> 0.18", only: :dev}, {:inch_ex, "~> 0.5", only: :docs} diff --git a/mix.lock b/mix.lock index 78b3163..e5b86b4 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{"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.2.0", "fa728c705a6f3212c59a8f78861b3083e0db93b44cd377851eb5656c5a35542c", [:rebar3], [{:cowboy, "1.1.2", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, + "esaml": {:hex, :esaml, "3.3.0", "9b675c1201ef2d60e53cf5603a20560e1a688acc128bf0de476812919e4d2c52", [: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"},