2013-10-15 13:58:16 +02:00
|
|
|
--
|
2015-02-02 00:05:09 +01:00
|
|
|
-- access.lua
|
|
|
|
--
|
|
|
|
-- This file is executed at every request on a protected domain or server.
|
|
|
|
-- You just have to read this file normally to understand how and when the
|
|
|
|
-- request is handled: redirected, forbidden, bypassed or served.
|
2013-10-15 13:58:16 +02:00
|
|
|
--
|
2014-04-10 20:42:43 +02:00
|
|
|
|
2014-04-17 14:51:47 +02:00
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
-- Initialize configuration
|
|
|
|
require "config"
|
2014-04-17 14:40:52 +02:00
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
-- Initialize the non-persistent cookie table
|
|
|
|
cookies = {}
|
2014-09-13 23:21:21 +02:00
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
-- Import helpers
|
|
|
|
hlp = require "helpers"
|
2013-10-15 10:11:39 +02:00
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
-- Just a note for the client to know that he passed through the SSO
|
2013-10-16 11:57:53 +02:00
|
|
|
ngx.header["X-SSO-WAT"] = "You've just been SSOed"
|
2013-10-15 10:11:39 +02:00
|
|
|
|
2013-10-20 16:38:49 +02:00
|
|
|
|
2013-10-15 13:58:16 +02:00
|
|
|
--
|
2015-02-02 00:05:09 +01:00
|
|
|
-- 1. LOGIN
|
2013-10-15 13:58:16 +02:00
|
|
|
--
|
2015-02-02 00:05:09 +01:00
|
|
|
-- example: https://mydomain.org/?sso_login=a6e5320f
|
|
|
|
--
|
|
|
|
-- If the `sso_login` URI argument is set, try a cross-domain authentication
|
|
|
|
-- with the token passed as argument
|
2013-10-20 17:24:44 +02:00
|
|
|
--
|
2014-04-17 12:21:11 +02:00
|
|
|
if ngx.var.host ~= conf["portal_domain"] and ngx.var.request_method == "GET" then
|
|
|
|
uri_args = ngx.req.get_uri_args()
|
|
|
|
if uri_args[conf.login_arg] then
|
|
|
|
cda_key = uri_args[conf.login_arg]
|
|
|
|
if login[cda_key] then
|
|
|
|
set_auth_cookie(login[cda_key], ngx.var.host)
|
|
|
|
ngx.log(ngx.NOTICE, "Cross-domain authentication: "..login[cda_key].." connected on "..ngx.var.host)
|
|
|
|
login[cda_key] = nil
|
|
|
|
uri_args[conf.login_arg] = nil
|
|
|
|
return redirect(ngx.var.uri..uri_args_string(uri_args))
|
|
|
|
end
|
2014-02-04 16:28:54 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
--
|
|
|
|
-- 2. PORTAL
|
|
|
|
--
|
|
|
|
-- example: https://mydomain.org/ssowat*
|
|
|
|
--
|
|
|
|
-- If the URL matches the portal URL, serve a portal file or proceed to a
|
|
|
|
-- portal operations
|
|
|
|
--
|
2013-10-20 17:24:44 +02:00
|
|
|
if ngx.var.host == conf["portal_domain"]
|
2013-10-21 20:43:12 +02:00
|
|
|
and string.starts(ngx.var.uri, string.sub(conf["portal_path"], 1, -2))
|
2013-10-20 17:24:44 +02:00
|
|
|
then
|
|
|
|
if ngx.var.request_method == "GET" then
|
|
|
|
|
2014-11-13 20:27:01 +01:00
|
|
|
-- Force portal scheme
|
|
|
|
if ngx.var.scheme ~= conf["portal_scheme"] then
|
|
|
|
return redirect(portal_url)
|
|
|
|
end
|
|
|
|
|
2015-02-02 00:05:09 +01:00
|
|
|
-- Add a trailing `/` if not present
|
2013-10-21 20:43:12 +02:00
|
|
|
if ngx.var.uri.."/" == conf["portal_path"] then
|
|
|
|
return redirect(portal_url)
|
|
|
|
end
|
|
|
|
|
2013-10-20 17:24:44 +02:00
|
|
|
uri_args = ngx.req.get_uri_args()
|
|
|
|
if uri_args.action and uri_args.action == 'logout' then
|
|
|
|
-- Logout
|
|
|
|
return do_logout()
|
|
|
|
|
2014-04-17 12:21:11 +02:00
|
|
|
elseif is_logged_in() and uri_args.r then
|
|
|
|
back_url = ngx.decode_base64(uri_args.r)
|
2014-04-21 13:04:05 +02:00
|
|
|
if not string.match(back_url, "^http[s]?://"..ngx.var.host.."/")
|
2014-04-17 12:21:11 +02:00
|
|
|
and not string.match(back_url, ".*"..conf.login_arg.."=%d+$") then
|
|
|
|
cda_key = tostring(math.random(1111111, 9999999))
|
|
|
|
login[cda_key] = ngx.var.cookie_SSOwAuthUser
|
|
|
|
if string.match(back_url, ".*?.*") then
|
|
|
|
back_url = back_url.."&"
|
|
|
|
else
|
|
|
|
back_url = back_url.."?"
|
|
|
|
end
|
|
|
|
back_url = back_url.."sso_login="..cda_key
|
|
|
|
end
|
|
|
|
return redirect(back_url)
|
2014-01-31 21:25:46 +01:00
|
|
|
|
2013-10-21 13:13:43 +02:00
|
|
|
elseif is_logged_in() -- Authenticated
|
2013-10-20 22:07:26 +02:00
|
|
|
or ngx.var.uri == conf["portal_path"] -- OR Want to serve portal login
|
|
|
|
or (string.starts(ngx.var.uri, conf["portal_path"].."assets")
|
2013-11-27 03:48:25 +01:00
|
|
|
and (not ngx.var.http_referer
|
|
|
|
or string.starts(ngx.var.http_referer, portal_url))) -- OR Want to serve assets for portal login
|
2013-10-20 18:25:24 +02:00
|
|
|
then
|
2013-10-20 17:24:44 +02:00
|
|
|
-- Serve normal portal
|
|
|
|
return serve(ngx.var.uri)
|
|
|
|
|
|
|
|
else
|
|
|
|
-- Redirect to portal
|
2014-05-12 14:32:56 +02:00
|
|
|
flash("info", t("please_login"))
|
2013-10-20 17:24:44 +02:00
|
|
|
return redirect(portal_url)
|
|
|
|
end
|
|
|
|
|
|
|
|
elseif ngx.var.request_method == "POST" then
|
|
|
|
|
2013-10-21 13:13:43 +02:00
|
|
|
-- CSRF protection
|
2013-10-20 17:24:44 +02:00
|
|
|
if string.starts(ngx.var.http_referer, portal_url) then
|
2013-10-21 13:13:43 +02:00
|
|
|
if string.ends(ngx.var.uri, conf["portal_path"].."password.html")
|
|
|
|
or string.ends(ngx.var.uri, conf["portal_path"].."edit.html")
|
|
|
|
then
|
2015-02-02 00:05:09 +01:00
|
|
|
return post_edit()
|
2013-10-21 13:13:43 +02:00
|
|
|
else
|
2015-02-02 00:05:09 +01:00
|
|
|
return post_login()
|
2013-10-21 13:13:43 +02:00
|
|
|
end
|
2013-10-20 17:24:44 +02:00
|
|
|
else
|
|
|
|
-- Redirect to portal
|
2014-05-12 14:32:56 +02:00
|
|
|
flash("fail", t("please_login_from_portal"))
|
2013-10-20 17:24:44 +02:00
|
|
|
return redirect(portal_url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-10 17:35:28 +02:00
|
|
|
-- Redirected urls
|
|
|
|
|
|
|
|
function detect_redirection(redirect_url)
|
|
|
|
if string.starts(redirect_url, "http://")
|
|
|
|
or string.starts(redirect_url, "https://") then
|
|
|
|
return redirect(redirect_url)
|
|
|
|
elseif string.starts(redirect_url, "/") then
|
|
|
|
return redirect(ngx.var.scheme.."://"..ngx.var.host..redirect_url)
|
|
|
|
else
|
|
|
|
return redirect(ngx.var.scheme.."://"..redirect_url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if conf["redirected_urls"] then
|
|
|
|
for url, redirect_url in pairs(conf["redirected_urls"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if url == ngx.var.host..ngx.var.uri..uri_args_string()
|
|
|
|
or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string()
|
|
|
|
or url == ngx.var.uri..uri_args_string() then
|
2014-04-10 17:35:28 +02:00
|
|
|
detect_redirection(redirect_url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if conf["redirected_regex"] then
|
|
|
|
for regex, redirect_url in pairs(conf["redirected_regex"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if string.match(ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
|
|
|
or string.match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
|
|
|
or string.match(ngx.var.uri..uri_args_string(), regex) then
|
2014-04-10 17:35:28 +02:00
|
|
|
detect_redirection(redirect_url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-01-31 21:25:46 +01:00
|
|
|
|
2014-03-03 15:44:17 +01:00
|
|
|
-- URL that must be protected
|
|
|
|
function is_protected()
|
|
|
|
if not conf["protected_urls"] then
|
|
|
|
conf["protected_urls"] = {}
|
|
|
|
end
|
|
|
|
if not conf["protected_regex"] then
|
|
|
|
conf["protected_regex"] = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, url in ipairs(conf["protected_urls"]) do
|
|
|
|
if string.starts(ngx.var.host..ngx.var.uri, url)
|
|
|
|
or string.starts(ngx.var.uri, url) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for _, regex in ipairs(conf["protected_regex"]) do
|
|
|
|
if string.match(ngx.var.host..ngx.var.uri, regex)
|
|
|
|
or string.match(ngx.var.uri, regex) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2013-10-20 17:24:44 +02:00
|
|
|
-- Skipped urls
|
|
|
|
-- i.e. http://mydomain.org/no_protection/
|
|
|
|
|
2014-03-03 15:04:08 +01:00
|
|
|
if conf["skipped_urls"] then
|
|
|
|
for _, url in ipairs(conf["skipped_urls"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if (string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url)
|
|
|
|
or string.starts(ngx.var.uri..uri_args_string(), url))
|
2014-03-03 15:44:17 +01:00
|
|
|
and not is_protected() then
|
2014-03-03 15:04:08 +01:00
|
|
|
return pass()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if conf["skipped_regex"] then
|
|
|
|
for _, regex in ipairs(conf["skipped_regex"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if (string.match(ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
|
|
|
or string.match(ngx.var.uri..uri_args_string(), regex))
|
2014-03-03 15:44:17 +01:00
|
|
|
and not is_protected() then
|
2014-03-03 15:04:08 +01:00
|
|
|
return pass()
|
|
|
|
end
|
2013-10-20 17:24:44 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-03-03 15:04:08 +01:00
|
|
|
|
2013-10-20 17:24:44 +02:00
|
|
|
-- Unprotected urls
|
|
|
|
-- i.e. http://mydomain.org/no_protection+headers/
|
|
|
|
|
2014-03-03 15:04:08 +01:00
|
|
|
if conf["unprotected_urls"] then
|
|
|
|
for _, url in ipairs(conf["unprotected_urls"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if (string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url)
|
|
|
|
or string.starts(ngx.var.uri..uri_args_string(), url))
|
2014-03-03 15:44:17 +01:00
|
|
|
and not is_protected() then
|
2014-03-03 15:04:08 +01:00
|
|
|
if is_logged_in() then
|
|
|
|
set_headers()
|
|
|
|
end
|
|
|
|
return pass()
|
2013-10-20 17:24:44 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-03 15:04:08 +01:00
|
|
|
if conf["unprotected_regex"] then
|
|
|
|
for _, regex in ipairs(conf["unprotected_regex"]) do
|
2014-12-07 19:40:03 +01:00
|
|
|
if (string.match(ngx.var.host..ngx.var.uri..uri_args_string(), regex)
|
|
|
|
or string.match(ngx.var.uri..uri_args_string(), regex))
|
2014-03-03 15:44:17 +01:00
|
|
|
and not is_protected() then
|
2014-03-03 15:04:08 +01:00
|
|
|
if is_logged_in() then
|
|
|
|
set_headers()
|
|
|
|
end
|
|
|
|
return pass()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-10-20 17:24:44 +02:00
|
|
|
|
|
|
|
-- Cookie validation
|
|
|
|
--
|
|
|
|
|
2013-10-21 13:13:43 +02:00
|
|
|
if is_logged_in() then
|
2014-02-04 21:41:53 +01:00
|
|
|
if string.match(ngx.var.uri, "^/ynhpanel.js$") then
|
2014-05-14 17:27:34 +02:00
|
|
|
serve("/yunohost/sso/assets/js/ynhpanel.js")
|
2014-02-04 21:41:53 +01:00
|
|
|
end
|
2014-02-17 13:07:28 +01:00
|
|
|
if string.match(ngx.var.uri, "^/ynhpanel.css$") then
|
2014-05-14 17:27:34 +02:00
|
|
|
serve("/yunohost/sso/assets/css/ynhpanel.css")
|
2014-02-17 13:07:28 +01:00
|
|
|
end
|
2014-02-04 21:41:53 +01:00
|
|
|
if string.match(ngx.var.uri, "^/ynhpanel.json$") then
|
2014-05-14 17:27:34 +02:00
|
|
|
serve("/yunohost/sso/assets/js/ynhpanel.json")
|
2014-02-04 21:41:53 +01:00
|
|
|
end
|
2013-10-29 11:48:56 +01:00
|
|
|
if not has_access() then
|
2013-11-23 13:15:13 +01:00
|
|
|
return redirect(portal_url)
|
2013-10-29 11:48:56 +01:00
|
|
|
end
|
2013-10-21 13:13:43 +02:00
|
|
|
set_headers()
|
2013-10-20 22:07:26 +02:00
|
|
|
return pass()
|
2013-10-20 17:24:44 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Login with HTTP Auth if credentials are brought
|
|
|
|
--
|
|
|
|
|
|
|
|
local auth_header = ngx.req.get_headers()["Authorization"]
|
|
|
|
if auth_header then
|
|
|
|
_, _, b64_cred = string.find(auth_header, "^Basic%s+(.+)$")
|
|
|
|
_, _, user, password = string.find(ngx.decode_base64(b64_cred), "^(.+):(.+)$")
|
2014-02-19 12:57:57 +01:00
|
|
|
user = authenticate(user, password)
|
|
|
|
if user then
|
2013-10-20 17:24:44 +02:00
|
|
|
set_headers(user)
|
|
|
|
return pass()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Else redirect to portal
|
|
|
|
--
|
|
|
|
|
2014-05-12 14:32:56 +02:00
|
|
|
flash("info", t("please_login"))
|
2014-04-17 12:21:11 +02:00
|
|
|
local back_url = ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri .. uri_args_string()
|
2013-10-20 17:24:44 +02:00
|
|
|
return redirect(portal_url.."?r="..ngx.encode_base64(back_url))
|