From de20c9187111eb1f21abce31891681339ec9a165 Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 22 Sep 2017 20:07:02 +0200 Subject: [PATCH] Limited support for app-logout on SSO-logout --- README.md | 4 +++ access.lua | 9 ++++++ conf.json.example | 3 ++ config.lua | 3 +- headers.lua | 8 +++++ helpers.lua | 77 ++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 headers.lua diff --git a/README.md b/README.md index bff7ce4..e2f73cd 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,10 @@ Array of regular expressions to be matched against URLS **and** URIs and their r 2-level array containing usernames and their allowed URLs along with an App name (**example**: `{ "kload": { "kload.fr/myapp/": "My App" } }`) +#### logout + +Associative array; when logging out of SSOwat, any existing cookie that is found as a key of this array triggers the associated logout URL. This only works on `http[s]://[*.]portal_domaini/`, though. (**example**: `{ "dcxd": "https://example.org/dotclear/admin/index.php?logout=1" }`) + #### default_language Language code used by default in views (**default**: `en`) diff --git a/access.lua b/access.lua index 810ae16..f51cc4a 100644 --- a/access.lua +++ b/access.lua @@ -42,6 +42,15 @@ end ngx.header["X-SSO-WAT"] = "You've just been SSOed" +-- +-- 0. LOGOUT if requested, but only if logged in +-- +local logout_ck = ngx.var.cookie_SSOwFullLogout +if logout_ck and logout_ck ~= "" and hlp.is_logged_in() then + return hlp.logout() +end + + -- -- 1. LOGIN -- diff --git a/conf.json.example b/conf.json.example index 84e5118..d6f9e8b 100644 --- a/conf.json.example +++ b/conf.json.example @@ -26,5 +26,8 @@ "example.org/myapp": "My other domain App", "example.com/myapp2": "My second App" } + }, + "logout": { + "dcxd": "https://example.org/dotclear/admin/index.php?logout=1" } } diff --git a/config.lua b/config.lua index 7aa459e..597d0dc 100644 --- a/config.lua +++ b/config.lua @@ -43,7 +43,7 @@ function get_config() portal_scheme = "https", portal_path = "/ssowat/", local_portal_domain = "yunohost.local", - domains = { conf["portal_domain"], "yunohost.local" }, + domains = { "yunohost.local", conf["portal_domain"] }, session_timeout = 60 * 60 * 24, -- one day session_max_timeout = 60 * 60 * 24 * 7, -- one week login_arg = "sso_login", @@ -53,6 +53,7 @@ function get_config() ldap_enforce_crypt = true, skipped_urls = {}, users = {}, + logout = {}, ldap_attributes = {"uid", "givenname", "sn", "cn", "homedirectory", "mail", "maildrop"}, additional_headers = {["Remote-User"] = "uid"}, allow_mail_authentication = true, diff --git a/headers.lua b/headers.lua new file mode 100644 index 0000000..f173324 --- /dev/null +++ b/headers.lua @@ -0,0 +1,8 @@ +-- Redirect to the SSO if logout is in progress +if ngx.ctx.SSOwFullLogout then + local next_cookie, back_url = ngx.ctx.SSOwFullLogout:match('^(.-)|(http.+)$') + ngx.log(ngx.DEBUG, "LOGOUT STEP DONE; next: "..next_cookie..", back to: "..back_url) + ngx.status = ngx.HTTP_TEMPORARY_REDIRECT + ngx.header['Set-Cookie'] = {next_cookie} + ngx.header.Location = back_url +end diff --git a/helpers.lua b/helpers.lua index 459beea..7eb6b58 100644 --- a/helpers.lua +++ b/helpers.lua @@ -55,6 +55,10 @@ function string.ends(String, End) return End=='' or string.sub(String, -string.len(End)) == End end +-- Escape special characters in a string +function string.pcre_escape(String) + return ngx.re.gsub(String, '([]({+?\\*.})[])', '\\$1') +end -- Find a string by its translate key in the right language function t(key) @@ -177,7 +181,8 @@ function delete_cookie() ngx.header["Set-Cookie"] = { "SSOwAuthUser="..cookie_str, "SSOwAuthHash="..cookie_str, - "SSOwAuthExpire="..cookie_str + "SSOwAuthExpire="..cookie_str, + "SSOwFullLogout="..cookie_str } end end @@ -883,18 +888,76 @@ end -- It deletes session cached information to invalidate client side cookie -- information. function logout() + conf = config.get_config() -- We need this call since we are in a POST request local args = ngx.req.get_uri_args() - -- Delete user cookie if logged in (that should always be the case) - if is_logged_in() then - delete_cookie() - cache:delete("session_"..authUser) - cache:delete(authUser.."-"..conf["ldap_identifier"]) -- Ugly trick to reload cache - flash("info", t("logged_out")) + -- Login if not logged in (that should always be the case) + if not is_logged_in() then + return redirect(conf.portal_url) end + -- Loop over session cookies. + -- For now, this will only work for domains under that of SSOwat. + -- The SSOwFullLogout cookie always contains the next cookie to check. + local cur_logout_step = ngx.var.cookie_SSOwFullLogout or '*' + local url_re = "^(?:https?://(?:[^/]+\\.)?"..string.pcre_escape(conf['portal_domain'])..")?/" + local sess_ck + local sess_ck_val + local logout_url + local read_next_and_proceed = false + local cookie_str = "; Domain=."..conf['portal_domain'].. + "; Path=/".. + "; Expires="..os.date("%a, %d %b %Y %X UTC;", ngx.req.start_time() + 60) + for sess_ck, logout_url in pairs(conf['logout']) do + ngx.log(ngx.DEBUG, "LOGOUT step="..cur_logout_step..", evaluate="..sess_ck) + + -- Run a logout URL, but point to the next, to avoid a loop + if read_next_and_proceed then + ngx.ctx.SSOwFullLogout = "SSOwFullLogout="..sess_ck..cookie_str..'|'..conf.portal_url + ngx.log(ngx.DEBUG, "LOGOUT pass: "..req_data['request_uri']) + return pass() + + -- A cookie must be checked; do so + elseif cur_logout_step == '*' or cur_logout_step == sess_ck then + if ngx.re.match(logout_url, url_re) then + + -- Not the right URI for this cookie; redirect + if string.gsub(logout_url, '^https?://[^/]+/', '/') ~= req_data['request_uri'] then + ngx.header["Set-Cookie"] = {"SSOwFullLogout="..sess_ck..cookie_str} + ngx.log(ngx.DEBUG, "LOGOUT visit: "..logout_url) + return redirect(logout_url) + end + sess_ck_val = ngx.var["cookie_"..sess_ck] + + -- The cookie must be deleted + if sess_ck_val and sess_ck_val ~= "" then + read_next_and_proceed = true + + -- No cookie; check next + else + ngx.log(ngx.DEBUG, "LOGOUT skip") + cur_logout_step = '*' + end + else + -- This URL is not handled :-( + ngx.log(ngx.DEBUG, "LOGOUT unhandled") + cur_logout_step = '*' + end + end + end + if read_next_and_proceed then + ngx.ctx.SSOwFullLogout = "SSOwFullLogout=0"..cookie_str..'|'..conf.portal_url + ngx.log(ngx.DEBUG, "LOGOUT pass: "..req_data['request_uri']) + return pass() + end + + delete_cookie() + cache:delete("session_"..authUser) + cache:delete(authUser.."-"..conf["ldap_identifier"]) -- Ugly trick to reload cache + flash("info", t("logged_out")) + -- Redirect to portal anyway return redirect(conf.portal_url) end