Use request_uri instead of uri; internal redirects break the latter

unstable
Yves G 2017-09-19 13:40:58 +02:00 committed by Y
parent db8724d578
commit 049ad9f48a
2 changed files with 88 additions and 68 deletions

View File

@ -6,6 +6,28 @@
-- request is handled: redirected, forbidden, bypassed or served. -- request is handled: redirected, forbidden, bypassed or served.
-- --
-- Initialize and get configuration
local conf = config.get_config()
-- Import helpers
local hlp = require "helpers"
-- Store the request data
req_data = {
request_uri = ngx.var.request_uri,
uri = ngx.var.uri,
https = ngx.var.proxy_https or ngx.var.https,
host = ngx.var.host,
request_method = ngx.var.request_method,
http_referer = ngx.var.http_referer
}
if req_data["https"] and req_data["https"] ~= "" then
req_data["scheme"] = "https"
else
req_data["scheme"] = "http"
end
hlp.set_req_data(req_data)
-- Get the `cache` persistent shared table -- Get the `cache` persistent shared table
local cache = ngx.shared.cache local cache = ngx.shared.cache
@ -16,12 +38,6 @@ if not srvkey then
cache:add("srvkey", srvkey) cache:add("srvkey", srvkey)
end end
-- Initialize and get configuration
local conf = config.get_config()
-- Import helpers
local hlp = require "helpers"
-- Just a note for the client to know that he passed through the SSO -- Just a note for the client to know that he passed through the SSO
ngx.header["X-SSO-WAT"] = "You've just been SSOed" ngx.header["X-SSO-WAT"] = "You've just been SSOed"
@ -34,7 +50,7 @@ ngx.header["X-SSO-WAT"] = "You've just been SSOed"
-- If the `sso_login` URI argument is set, try a cross-domain authentication -- If the `sso_login` URI argument is set, try a cross-domain authentication
-- with the token passed as argument -- with the token passed as argument
-- --
if ngx.var.host ~= conf["portal_domain"] and ngx.var.request_method == "GET" then if req_data["host"] ~= conf["portal_domain"] and req_data["request_method"] == "GET" then
uri_args = ngx.req.get_uri_args() uri_args = ngx.req.get_uri_args()
if uri_args[conf.login_arg] then if uri_args[conf.login_arg] then
cda_key = uri_args[conf.login_arg] cda_key = uri_args[conf.login_arg]
@ -43,13 +59,13 @@ if ngx.var.host ~= conf["portal_domain"] and ngx.var.request_method == "GET" the
-- a CDA key -- a CDA key
user = cache:get("CDA|"..cda_key) user = cache:get("CDA|"..cda_key)
if user then if user then
hlp.set_auth_cookie(user, ngx.var.host) hlp.set_auth_cookie(user, req_data["host"])
ngx.log(ngx.NOTICE, "Cross-domain authentication: "..user.." connected on "..ngx.var.host) ngx.log(ngx.NOTICE, "Cross-domain authentication: "..user.." connected on "..req_data["host"])
cache:delete("CDA|"..cda_key) cache:delete("CDA|"..cda_key)
end end
uri_args[conf.login_arg] = nil uri_args[conf.login_arg] = nil
return hlp.redirect(ngx.var.uri..hlp.uri_args_string(uri_args)) return hlp.redirect(req_data['request_uri'])
end end
end end
@ -62,15 +78,15 @@ end
-- If the URL matches the portal URL, serve a portal file or proceed to a -- If the URL matches the portal URL, serve a portal file or proceed to a
-- portal operation -- portal operation
-- --
if ngx.var.host == conf["portal_domain"] if req_data["host"] == conf["portal_domain"]
and hlp.string.starts(ngx.var.uri, string.sub(conf["portal_path"], 1, -2)) and hlp.string.starts(req_data['request_uri'], string.sub(conf["portal_path"], 1, -2))
then then
-- `GET` method will serve a portal file -- `GET` method will serve a portal file
if ngx.var.request_method == "GET" then if req_data["request_method"] == "GET" then
-- Add a trailing `/` if not present -- Add a trailing `/` if not present
if ngx.var.uri.."/" == conf["portal_path"] then if req_data['request_uri'].."/" == conf["portal_path"] then
ngx.log(ngx.DEBUG, "REDIRECT MISSING /") ngx.log(ngx.DEBUG, "REDIRECT MISSING /")
return hlp.redirect(conf.portal_url) return hlp.redirect(conf.portal_url)
end end
@ -121,7 +137,7 @@ then
-- In case the `back_url` is not on the same domain than the -- In case the `back_url` is not on the same domain than the
-- current one, create a redirection with a CDA key -- current one, create a redirection with a CDA key
local ngx_host_escaped = ngx.var.host:gsub("-", "%%-") -- escape dash for pattern matching local ngx_host_escaped = req_data["host"]:gsub("-", "%%-") -- escape dash for pattern matching
if not string.match(back_url, "^http[s]?://"..ngx_host_escaped.."/") if not string.match(back_url, "^http[s]?://"..ngx_host_escaped.."/")
and not string.match(back_url, ".*"..conf.login_arg.."=%d+$") then and not string.match(back_url, ".*"..conf.login_arg.."=%d+$") then
local cda_key = hlp.set_cda_key() local cda_key = hlp.set_cda_key()
@ -140,12 +156,18 @@ then
-- In case we want to serve portal login or assets for portal, just -- In case we want to serve portal login or assets for portal, just
-- serve it -- serve it
elseif hlp.is_logged_in() elseif hlp.is_logged_in()
or ngx.var.uri == conf["portal_path"] or req_data['request_uri'] == conf["portal_path"]
or (hlp.string.starts(ngx.var.uri, conf["portal_path"].."assets") or hlp.string.starts(req_data['request_uri'], conf["portal_path"].."?")
and (not ngx.var.http_referer or (hlp.string.starts(req_data['request_uri'], conf["portal_path"].."assets")
or hlp.string.starts(ngx.var.http_referer, conf.portal_url))) and (not req_data["http_referer"]
or hlp.string.starts(req_data["http_referer"], conf.portal_url)))
then then
return hlp.serve(ngx.var.uri) local uri = req_data['request_uri']
local i, _ = string.find(uri, '?')
if i then
uri = string.sub(uri, 1, i-1)
end
return hlp.serve(uri)
-- If all the previous cases have failed, redirect to portal -- If all the previous cases have failed, redirect to portal
@ -157,13 +179,13 @@ then
-- `POST` method is basically use to achieve editing operations -- `POST` method is basically use to achieve editing operations
elseif ngx.var.request_method == "POST" then elseif req_data["request_method"] == "POST" then
-- CSRF protection, only proceed if we are editing from the same -- CSRF protection, only proceed if we are editing from the same
-- domain -- domain
if hlp.string.starts(ngx.var.http_referer, conf.portal_url) then if hlp.string.starts(req_data["http_referer"], conf.portal_url) then
if hlp.string.ends(ngx.var.uri, conf["portal_path"].."password.html") if hlp.string.ends(req_data["uri"], conf["portal_path"].."password.html")
or hlp.string.ends(ngx.var.uri, conf["portal_path"].."edit.html") or hlp.string.ends(req_data["uri"], conf["portal_path"].."edit.html")
then then
return hlp.edit_user() return hlp.edit_user()
else else
@ -185,28 +207,23 @@ end
-- If the URL matches one of the `redirected_urls` in the configuration file, -- If the URL matches one of the `redirected_urls` in the configuration file,
-- just redirect to the target URL/URI -- just redirect to the target URL/URI
-- --
local https = ngx.var.proxy_https or ngx.var.https
local scheme = "http"
if https and https ~= "" then
scheme = "https"
end
function detect_redirection(redirect_url) function detect_redirection(redirect_url)
if hlp.string.starts(redirect_url, "http://") if hlp.string.starts(redirect_url, "http://")
or hlp.string.starts(redirect_url, "https://") then or hlp.string.starts(redirect_url, "https://") then
return hlp.redirect(redirect_url) return hlp.redirect(redirect_url)
elseif hlp.string.starts(redirect_url, "/") then elseif hlp.string.starts(redirect_url, "/") then
return hlp.redirect(scheme.."://"..ngx.var.host..redirect_url) return hlp.redirect(req_data["scheme"].."://"..req_data["host"]..redirect_url)
else else
return hlp.redirect(scheme.."://"..redirect_url) return hlp.redirect(req_data["scheme"].."://"..redirect_url)
end end
end end
if conf["redirected_urls"] then if conf["redirected_urls"] then
for url, redirect_url in pairs(conf["redirected_urls"]) do for url, redirect_url in pairs(conf["redirected_urls"]) do
if url == ngx.var.host..ngx.var.uri..hlp.uri_args_string() if url == req_data["host"]..req_data['request_uri']
or url == scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string() or url == req_data["scheme"].."://"..req_data["host"]..req_data['request_uri']
or url == ngx.var.uri..hlp.uri_args_string() then or url == req_data['request_uri'] then
detect_redirection(redirect_url) detect_redirection(redirect_url)
end end
end end
@ -214,9 +231,9 @@ end
if conf["redirected_regex"] then if conf["redirected_regex"] then
for regex, redirect_url in pairs(conf["redirected_regex"]) do for regex, redirect_url in pairs(conf["redirected_regex"]) do
if string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) if string.match(req_data["host"]..req_data['request_uri'], regex)
or string.match(scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) or string.match(req_data["scheme"].."://"..req_data["host"]..req_data['request_uri'], regex)
or string.match(ngx.var.uri..hlp.uri_args_string(), regex) then or string.match(req_data['request_uri'], regex) then
detect_redirection(redirect_url) detect_redirection(redirect_url)
end end
end end
@ -241,14 +258,14 @@ function is_protected()
end end
for _, url in ipairs(conf["protected_urls"]) do for _, url in ipairs(conf["protected_urls"]) do
if hlp.string.starts(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), url) if hlp.string.starts(req_data["host"]..req_data['request_uri'], url)
or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url) then or hlp.string.starts(req_data['request_uri'], url) then
return true return true
end end
end end
for _, regex in ipairs(conf["protected_regex"]) do for _, regex in ipairs(conf["protected_regex"]) do
if string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) if string.match(req_data["host"]..req_data['request_uri'], regex)
or string.match(ngx.var.uri..hlp.uri_args_string(), regex) then or string.match(req_data['request_uri'], regex) then
return true return true
end end
end end
@ -267,8 +284,8 @@ end
if conf["skipped_urls"] then if conf["skipped_urls"] then
for _, url in ipairs(conf["skipped_urls"]) do for _, url in ipairs(conf["skipped_urls"]) do
if (hlp.string.starts(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), url) if (hlp.string.starts(req_data["host"]..req_data['request_uri'], url)
or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url)) or hlp.string.starts(req_data['request_uri'], url))
and not is_protected() then and not is_protected() then
return hlp.pass() return hlp.pass()
end end
@ -277,8 +294,8 @@ end
if conf["skipped_regex"] then if conf["skipped_regex"] then
for _, regex in ipairs(conf["skipped_regex"]) do for _, regex in ipairs(conf["skipped_regex"]) do
if (string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) if (string.match(req_data["host"]..req_data['request_uri'], regex)
or string.match(ngx.var.uri..hlp.uri_args_string(), regex)) or string.match(req_data['request_uri'], regex))
and not is_protected() then and not is_protected() then
return hlp.pass() return hlp.pass()
end end
@ -296,13 +313,13 @@ end
-- --
if hlp.is_logged_in() then if hlp.is_logged_in() then
if string.match(ngx.var.uri, "^/ynhpanel.js$") then if string.match(req_data['request_uri'], "^/ynhpanel.js$") then
hlp.serve("/yunohost/sso/assets/js/ynhpanel.js") hlp.serve("/yunohost/sso/assets/js/ynhpanel.js")
end end
if string.match(ngx.var.uri, "^/ynhpanel.css$") then if string.match(req_data['request_uri'], "^/ynhpanel.css$") then
hlp.serve("/yunohost/sso/assets/css/ynhpanel.css") hlp.serve("/yunohost/sso/assets/css/ynhpanel.css")
end end
if string.match(ngx.var.uri, "^/ynhpanel.json$") then if string.match(req_data['request_uri'], "^/ynhpanel.json$") then
hlp.serve("/yunohost/sso/assets/js/ynhpanel.json") hlp.serve("/yunohost/sso/assets/js/ynhpanel.json")
end end
@ -334,8 +351,8 @@ end
if conf["unprotected_urls"] then if conf["unprotected_urls"] then
for _, url in ipairs(conf["unprotected_urls"]) do for _, url in ipairs(conf["unprotected_urls"]) do
if (hlp.string.starts(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), url) if (hlp.string.starts(req_data["host"]..req_data['request_uri'], url)
or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url)) or hlp.string.starts(req_data['request_uri'], url))
and not is_protected() then and not is_protected() then
if hlp.is_logged_in() then if hlp.is_logged_in() then
hlp.set_headers() hlp.set_headers()
@ -347,8 +364,8 @@ end
if conf["unprotected_regex"] then if conf["unprotected_regex"] then
for _, regex in ipairs(conf["unprotected_regex"]) do for _, regex in ipairs(conf["unprotected_regex"]) do
if (string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) if (string.match(req_data["host"]..req_data['request_uri'], regex)
or string.match(ngx.var.uri..hlp.uri_args_string(), regex)) or string.match(req_data['request_uri'], regex))
and not is_protected() then and not is_protected() then
if hlp.is_logged_in() then if hlp.is_logged_in() then
hlp.set_headers() hlp.set_headers()
@ -399,6 +416,6 @@ end
-- --
hlp.flash("info", hlp.t("please_login")) hlp.flash("info", hlp.t("please_login"))
local back_url = scheme .. "://" .. ngx.var.host .. ngx.var.uri .. hlp.uri_args_string() local back_url = req_data["scheme"] .. "://" .. req_data["host"] .. req_data['request_uri']
ngx.log(ngx.DEBUG, "REDIRECT BY DEFAULT") ngx.log(ngx.DEBUG, "REDIRECT BY DEFAULT")
return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url)) return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url))

View File

@ -9,6 +9,14 @@ module('helpers', package.seeall)
local cache = ngx.shared.cache local cache = ngx.shared.cache
local conf = config.get_config() local conf = config.get_config()
local req_data
-- Local store for the request data, because:
-- - some data is cleared along the process but is still needed (eg. request_uri)
-- - repeatedly reading ngx.var or ngx.ctx is bad for memory usage and performance
function set_req_data(data)
req_data = data
end
-- Read a FS stored file -- Read a FS stored file
function read_file(file) function read_file(file)
@ -225,7 +233,7 @@ end
-- of the configuration file -- of the configuration file
function has_access(user, url) function has_access(user, url)
user = user or authUser user = user or authUser
url = url or ngx.var.host..ngx.var.uri url = url or req_data["host"]..req_data['request_uri']
if not conf["users"][user] then if not conf["users"][user] then
conf = config.get_config() conf = config.get_config()
@ -242,7 +250,7 @@ function has_access(user, url)
-- Replace the original domain by a local one if you are connected from -- Replace the original domain by a local one if you are connected from
-- a non-global domain name. -- a non-global domain name.
if ngx.var.host == conf["local_portal_domain"] then if req_data["host"] == conf["local_portal_domain"] then
u = string.gsub(u, conf["original_portal_domain"], conf["local_portal_domain"]) u = string.gsub(u, conf["original_portal_domain"], conf["local_portal_domain"])
end end
@ -330,12 +338,7 @@ function set_headers(user)
-- logging. -- logging.
if not cache:get(user.."-password") then if not cache:get(user.."-password") then
flash("info", t("please_login")) flash("info", t("please_login"))
local https = ngx.var.proxy_https or ngx.var.https local back_url = req_data["scheme"] .. "://" .. req_data["host"] .. req_data['request_uri']
local scheme = "http"
if https and https ~= "" then
scheme = "https"
end
local back_url = scheme .. "://" .. ngx.var.host .. ngx.var.uri .. uri_args_string()
return redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url)) return redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url))
end end
@ -548,7 +551,7 @@ function get_data_for(view)
if conf["users"][user] then if conf["users"][user] then
for url, name in pairs(conf["users"][user]) do for url, name in pairs(conf["users"][user]) do
if ngx.var.host == conf["local_portal_domain"] then if req_data["host"] == conf["local_portal_domain"] then
url = string.gsub(url, conf["original_portal_domain"], conf["local_portal_domain"]) url = string.gsub(url, conf["original_portal_domain"], conf["local_portal_domain"])
end end
table.insert(sorted_apps, name) table.insert(sorted_apps, name)
@ -621,7 +624,7 @@ function edit_user()
-- In case of a password modification -- In case of a password modification
-- TODO: split this into a new function -- TODO: split this into a new function
if string.ends(ngx.var.uri, "password.html") then if string.ends(req_data["uri"], "password.html") then
-- Check current password against the cached one -- Check current password against the cached one
if args.currentpassword if args.currentpassword
@ -657,7 +660,7 @@ function edit_user()
-- In case of profile modification -- In case of profile modification
-- TODO: split this into a new function -- TODO: split this into a new function
elseif string.ends(ngx.var.uri, "edit.html") then elseif string.ends(req_data["uri"], "edit.html") then
-- Check that needed arguments exist -- Check that needed arguments exist
if args.givenName and args.sn and args.mail then if args.givenName and args.sn and args.mail then
@ -860,7 +863,7 @@ function login()
local user = authenticate(args.user, args.password) local user = authenticate(args.user, args.password)
if user then if user then
ngx.status = ngx.HTTP_CREATED ngx.status = ngx.HTTP_CREATED
set_auth_cookie(user, ngx.var.host) set_auth_cookie(user, req_data["host"])
else else
ngx.status = ngx.HTTP_UNAUTHORIZED ngx.status = ngx.HTTP_UNAUTHORIZED
flash("fail", t("wrong_username_password")) flash("fail", t("wrong_username_password"))
@ -909,9 +912,9 @@ function pass()
delete_redirect_cookie() delete_redirect_cookie()
-- When we are in the SSOwat portal, we need a default `content-type` -- When we are in the SSOwat portal, we need a default `content-type`
if string.ends(ngx.var.uri, "/") if string.ends(req_data["uri"], "/")
or string.ends(ngx.var.uri, ".html") or string.ends(req_data["uri"], ".html")
or string.ends(ngx.var.uri, ".htm") or string.ends(req_data["uri"], ".htm")
then then
ngx.header["Content-Type"] = "text/html" ngx.header["Content-Type"] = "text/html"
end end