From 35e69a1bf26af7c5ff4f906f3ad066cd9c8f26d3 Mon Sep 17 00:00:00 2001 From: kload Date: Thu, 12 Feb 2015 12:08:52 +0100 Subject: [PATCH] [fix] Separate files properly --- access.lua | 157 ++++++++++++++++++++++++++++---------------------- config.lua | 162 +++++++++++++++++++++++++--------------------------- helpers.lua | 48 ++++++++-------- 3 files changed, 189 insertions(+), 178 deletions(-) diff --git a/access.lua b/access.lua index a545d27..332c7d7 100644 --- a/access.lua +++ b/access.lua @@ -6,9 +6,20 @@ -- request is handled: redirected, forbidden, bypassed or served. -- +-- Get the `cache` persistent shared table +cache = ngx.shared.cache --- Initialize configuration -require "config" +-- Generate a unique token if it has not been generated yet +srvkey = cache:get("srvkey") +if not srvkey then + math.randomseed(os.time()) + srvkey = tostring(math.random(1111111, 9999999)) + cache:add("srvkey", srvkey) +end + +-- Initialize and get configuration +config = require "config" +conf = config.get_config() -- Initialize the non-persistent cookie table cookies = {} @@ -32,13 +43,17 @@ if ngx.var.host ~= conf["portal_domain"] and ngx.var.request_method == "GET" the uri_args = ngx.req.get_uri_args() if uri_args[conf.login_arg] then cda_key = uri_args[conf.login_arg] + + -- Use the `login` shared table where a username is associated with + -- a CDA key if login[cda_key] then - set_auth_cookie(login[cda_key], ngx.var.host) + hlp.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 + + uri_args[conf.login_arg] = nil + return hlp.redirect(ngx.var.uri..hlp.uri_args_string(uri_args)) end end @@ -52,26 +67,30 @@ end -- portal operations -- if ngx.var.host == conf["portal_domain"] - and string.starts(ngx.var.uri, string.sub(conf["portal_path"], 1, -2)) + and hlp.string.starts(ngx.var.uri, string.sub(conf["portal_path"], 1, -2)) then + + -- `GET` method will serve a portal file if ngx.var.request_method == "GET" then -- Force portal scheme if ngx.var.scheme ~= conf["portal_scheme"] then - return redirect(portal_url) + return hlp.redirect(conf.portal_url) end -- Add a trailing `/` if not present if ngx.var.uri.."/" == conf["portal_path"] then - return redirect(portal_url) + return hlp.redirect(conf.portal_url) end uri_args = ngx.req.get_uri_args() - if uri_args.action and uri_args.action == 'logout' then - -- Logout - return do_logout() - elseif is_logged_in() and uri_args.r then + -- Logout is also called via a `GET` method + -- TODO: change this ? + if uri_args.action and uri_args.action == 'logout' then + return hlp.logout() + + elseif hlp.is_logged_in() and uri_args.r then back_url = ngx.decode_base64(uri_args.r) if not string.match(back_url, "^http[s]?://"..ngx.var.host.."/") and not string.match(back_url, ".*"..conf.login_arg.."=%d+$") then @@ -84,38 +103,38 @@ then end back_url = back_url.."sso_login="..cda_key end - return redirect(back_url) + return hlp.redirect(back_url) - elseif is_logged_in() -- Authenticated + elseif hlp.is_logged_in() -- Authenticated or ngx.var.uri == conf["portal_path"] -- OR Want to serve portal login or (string.starts(ngx.var.uri, conf["portal_path"].."assets") and (not ngx.var.http_referer - or string.starts(ngx.var.http_referer, portal_url))) -- OR Want to serve assets for portal login + or hlp.string.starts(ngx.var.http_referer, conf.portal_url))) -- OR Want to serve assets for portal login then -- Serve normal portal - return serve(ngx.var.uri) + return hlp.serve(ngx.var.uri) else -- Redirect to portal - flash("info", t("please_login")) - return redirect(portal_url) + hlp.flash("info", t("please_login")) + return hlp.redirect(conf.portal_url) end elseif ngx.var.request_method == "POST" then -- CSRF protection - if string.starts(ngx.var.http_referer, portal_url) then - if string.ends(ngx.var.uri, conf["portal_path"].."password.html") - or string.ends(ngx.var.uri, conf["portal_path"].."edit.html") + if hlp.string.starts(ngx.var.http_referer, conf.portal_url) then + if hlp.string.ends(ngx.var.uri, conf["portal_path"].."password.html") + or hlp.string.ends(ngx.var.uri, conf["portal_path"].."edit.html") then - return post_edit() + return hlp.edit_user() else - return post_login() + return hlp.login() end else -- Redirect to portal - flash("fail", t("please_login_from_portal")) - return redirect(portal_url) + hlp.flash("fail", t("please_login_from_portal")) + return hlp.redirect(conf.portal_url) end end end @@ -123,21 +142,21 @@ end -- 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) + if hlp.string.starts(redirect_url, "http://") + or hlp.string.starts(redirect_url, "https://") then + return hlp.redirect(redirect_url) + elseif hlp.string.starts(redirect_url, "/") then + return hlp.redirect(ngx.var.scheme.."://"..ngx.var.host..redirect_url) else - return redirect(ngx.var.scheme.."://"..redirect_url) + return hlp.redirect(ngx.var.scheme.."://"..redirect_url) end end if conf["redirected_urls"] then for url, redirect_url in pairs(conf["redirected_urls"]) do - 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 + if url == ngx.var.host..ngx.var.uri..hlp.uri_args_string() + or url == ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string() + or url == ngx.var.uri..hlp.uri_args_string() then detect_redirection(redirect_url) end end @@ -145,9 +164,9 @@ end if conf["redirected_regex"] then for regex, redirect_url in pairs(conf["redirected_regex"]) do - 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 + if string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) + or string.match(ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) + or string.match(ngx.var.uri..hlp.uri_args_string(), regex) then detect_redirection(redirect_url) end end @@ -163,8 +182,8 @@ function is_protected() 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 + if hlp.string.starts(ngx.var.host..ngx.var.uri, url) + or hlp.string.starts(ngx.var.uri, url) then return true end end @@ -183,20 +202,20 @@ end if conf["skipped_urls"] then for _, url in ipairs(conf["skipped_urls"]) do - if (string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url) - or string.starts(ngx.var.uri..uri_args_string(), url)) + if (hlp.string.starts(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), url) + or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url)) and not is_protected() then - return pass() + return hlp.pass() end end end if conf["skipped_regex"] then for _, regex in ipairs(conf["skipped_regex"]) do - if (string.match(ngx.var.host..ngx.var.uri..uri_args_string(), regex) - or string.match(ngx.var.uri..uri_args_string(), regex)) + if (string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) + or string.match(ngx.var.uri..hlp.uri_args_string(), regex)) and not is_protected() then - return pass() + return hlp.pass() end end end @@ -208,26 +227,26 @@ end if conf["unprotected_urls"] then for _, url in ipairs(conf["unprotected_urls"]) do - if (string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url) - or string.starts(ngx.var.uri..uri_args_string(), url)) + if (hlp.string.starts(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), url) + or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url)) and not is_protected() then - if is_logged_in() then - set_headers() + if hlp.is_logged_in() then + hlp.set_headers() end - return pass() + return hlp.pass() end end end if conf["unprotected_regex"] then for _, regex in ipairs(conf["unprotected_regex"]) do - if (string.match(ngx.var.host..ngx.var.uri..uri_args_string(), regex) - or string.match(ngx.var.uri..uri_args_string(), regex)) + if (string.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) + or string.match(ngx.var.uri..hlp.uri_args_string(), regex)) and not is_protected() then - if is_logged_in() then - set_headers() + if hlp.is_logged_in() then + hlp.set_headers() end - return pass() + return hlp.pass() end end end @@ -235,21 +254,21 @@ end -- Cookie validation -- -if is_logged_in() then +if hlp.is_logged_in() then if string.match(ngx.var.uri, "^/ynhpanel.js$") then - serve("/yunohost/sso/assets/js/ynhpanel.js") + hlp.serve("/yunohost/sso/assets/js/ynhpanel.js") end if string.match(ngx.var.uri, "^/ynhpanel.css$") then - serve("/yunohost/sso/assets/css/ynhpanel.css") + hlp.serve("/yunohost/sso/assets/css/ynhpanel.css") end if string.match(ngx.var.uri, "^/ynhpanel.json$") then - serve("/yunohost/sso/assets/js/ynhpanel.json") + hlp.serve("/yunohost/sso/assets/js/ynhpanel.json") end - if not has_access() then - return redirect(portal_url) + if not hlp.has_access() then + return hlp.redirect(conf.portal_url) end - set_headers() - return pass() + hlp.set_headers() + return hlp.pass() end @@ -260,16 +279,16 @@ 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), "^(.+):(.+)$") - user = authenticate(user, password) + user = hlp.authenticate(user, password) if user then - set_headers(user) - return pass() + hlp.set_headers(user) + return hlp.pass() end end -- Else redirect to portal -- -flash("info", t("please_login")) -local back_url = ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri .. uri_args_string() -return redirect(portal_url.."?r="..ngx.encode_base64(back_url)) +hlp.flash("info", t("please_login")) +local back_url = ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri .. hlp.uri_args_string() +return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url)) diff --git a/config.lua b/config.lua index dd210ca..03ac69b 100644 --- a/config.lua +++ b/config.lua @@ -5,95 +5,87 @@ -- --- Get the `cache` persistent shared table -cache = ngx.shared.cache +function get_config () + + -- Load the configuration file + local conf_file = assert(io.open(conf_path, "r"), "Configuration file is missing") + local conf = json.decode(conf_file:read("*all")) --- Generate a unique token if it has not been generated yet -srvkey = cache:get("srvkey") -if not srvkey then - math.randomseed(os.time()) - srvkey = tostring(math.random(1111111, 9999999)) - cache:add("srvkey", srvkey) -end + -- Load additional rules from the `.persistent` configuration file. + -- The `.persistent` file contains rules that will overwrite previous rules. + -- It typically enables you to set custom rules. + local persistent_conf_file = io.open(conf_path..".persistent", "r") + if persistent_conf_file ~= nil then + for k, v in pairs(json.decode(persistent_conf_file:read("*all"))) do - --- Set the prefered language from the `Accept-Language` header -lang = ngx.req.get_headers()["Accept-Language"] - -if lang then - lang = string.sub(lang, 1, 2) -end - - --- Load the configuration file -local conf_file = assert(io.open(conf_path, "r"), "Configuration file is missing") -local conf = json.decode(conf_file:read("*all")) - - --- Load additional rules from the `.persistent` configuration file. --- The `.persistent` file contains rules that will overwrite previous rules. --- It typically enables you to set custom rules. -local persistent_conf_file = io.open(conf_path..".persistent", "r") -if persistent_conf_file ~= nil then - for k, v in pairs(json.decode(persistent_conf_file:read("*all"))) do - - -- If the configuration key already exists and is a table, merge it - if conf[k] and type(v) == "table" then - for subk, subv in pairs(v) do - if type(subk) == "number" then - table.insert(conf[k], subv) - else - conf[k][subk] = subv + -- If the configuration key already exists and is a table, merge it + if conf[k] and type(v) == "table" then + for subk, subv in pairs(v) do + if type(subk) == "number" then + table.insert(conf[k], subv) + else + conf[k][subk] = subv + end end - end - -- Else just take the persistent rule's value - else - conf[k] = v - end + -- Else just take the persistent rule's value + else + conf[k] = v + end + end end + + + -- Default configuration values + default_conf = { + portal_scheme = "https", + portal_path = "/ssowat", + local_portal_domain = "yunohost.local", + domains = { conf["portal_domain"], "yunohost.local" }, + session_timeout = 60 * 60 * 24, -- one day + session_max_timeout = 60 * 60 * 24 * 7, -- one week + login_arg = "sso_login", + ldap_host = "localhost", + ldap_group = "ou=users,dc=yunohost,dc=org", + ldap_identifier = "uid", + ldap_attributes = {"uid", "givenname", "sn", "cn", "homedirectory", "mail", "maildrop"}, + allow_mail_authentication = true, + default_language = "en" + } + + + -- Load default values unless they are set in the configuration file. + for param, default_value in pairs(default_conf) do + conf[param] = conf[param] or default_value + end + + + + -- If you access the SSO by a local domain, change the portal domain to + -- avoid unwanted redirections. + if ngx.var.host == conf["local_portal_domain"] then + conf["original_portal_domain"] = conf["portal_domain"] + conf["portal_domain"] = conf["local_portal_domain"] + end + + + -- Build portal full URL out of the configuration values + conf.portal_url = conf["portal_scheme"].."://".. + conf["portal_domain"].. + conf["portal_path"] + + + -- Always skip the portal to avoid redirection looping. + table.insert(conf["skipped_urls"], conf["portal_domain"]..conf["portal_path"]) + + + -- Set the prefered language from the `Accept-Language` header + conf.lang = ngx.req.get_headers()["Accept-Language"] + + if conf.lang then + conf.lang = string.sub(lang, 1, 2) + end + + return conf end - - --- Default configuration values -default_conf = { - portal_scheme = "https", - portal_path = "/ssowat", - local_portal_domain = "yunohost.local", - domains = { conf["portal_domain"], "yunohost.local" }, - session_timeout = 60 * 60 * 24, -- one day - session_max_timeout = 60 * 60 * 24 * 7, -- one week - login_arg = "sso_login", - ldap_host = "localhost", - ldap_group = "ou=users,dc=yunohost,dc=org", - ldap_identifier = "uid", - ldap_attributes = {"uid", "givenname", "sn", "cn", "homedirectory", "mail", "maildrop"}, - allow_mail_authentication = true, - default_language = "en" -} - - --- Load default values unless they are set in the configuration file. -for param, default_value in pairs(default_conf) do - conf[param] = conf[param] or default_value -end - - - --- If you access the SSO by a local domain, change the portal domain to --- avoid unwanted redirections. -if ngx.var.host == conf["local_portal_domain"] then - conf["original_portal_domain"] = conf["portal_domain"] - conf["portal_domain"] = conf["local_portal_domain"] -end - - --- Build portal full URL out of the configuration values -local portal_url = conf["portal_scheme"].."://".. - conf["portal_domain"].. - conf["portal_path"] - - --- Always skip the portal to avoid redirection looping. -table.insert(conf["skipped_urls"], conf["portal_domain"]..conf["portal_path"]) diff --git a/helpers.lua b/helpers.lua index 34f922f..44c15b1 100644 --- a/helpers.lua +++ b/helpers.lua @@ -38,12 +38,12 @@ end -- Find a string by its translate key in the right language function t (key) - if lang and i18n[lang] then - return i18n[lang][key] or "" + if conf.lang and i18n[conf.lang] then + return i18n[conf.lang][key] or "" else return i18n[conf["default_language"]][key] or "" end -end +end -- Set a cookie @@ -261,7 +261,7 @@ function set_headers (user) if not cache:get(user.."-password") then flash("info", t("please_login")) local back_url = ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri .. uri_args_string() - return redirect(portal_url.."?r="..ngx.encode_base64(back_url)) + return redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url)) end -- If the user information is not in cache, open an LDAP connection and @@ -425,8 +425,8 @@ function get_data_for(view) local data = {} -- Pass all the translated strings to the view (to use with t_) - if lang and i18n[lang] then - translate_table = i18n[lang] + if conf.lang and i18n[conf.lang] then + translate_table = i18n[conf.lang] else translate_table = i18n[conf["default_language"]] end @@ -456,7 +456,7 @@ function get_data_for(view) local mails = get_mails(user) data = { connected = true, - portal_url = portal_url, + portal_url = conf.portal_url, uid = user, cn = cache:get(user.."-cn"), sn = cache:get(user.."-sn"), @@ -485,7 +485,7 @@ end -- Compute the user modification POST request -- It has to update cached information and edit the LDAP user entry -- according to the changes detected. -function post_edit () +function edit_user () -- We need these calls since we are in a POST request ngx.req.read_body() @@ -522,7 +522,7 @@ function post_edit () -- Reset the password cache cache:set(user.."-password", args.newpassword, conf["session_timeout"]) - return redirect(portal_url.."info.html") + return redirect(conf.portal_url.."info.html") else flash("fail", t("password_changed_error")) end @@ -532,9 +532,9 @@ function post_edit () else flash("fail", t("wrong_current_password")) end - return redirect(portal_url.."password.html") + return redirect(conf.portal_url.."password.html") + - -- In case of profile modification -- TODO: split this into a new function elseif string.ends(ngx.var.uri, "edit.html") then @@ -607,7 +607,7 @@ function post_edit () -- Check the mail pattern if not mail:match(mail_pattern) then flash("fail", t("invalid_mail")..": "..mail) - return redirect(portal_url.."edit.html") + return redirect(conf.portal_url.."edit.html") -- Check that the domain is known and allowed else @@ -623,7 +623,7 @@ function post_edit () filter = filter.."(mail="..mail..")" else flash("fail", t("invalid_domain").." "..mail) - return redirect(portal_url.."edit.html") + return redirect(conf.portal_url.."edit.html") end end end @@ -632,7 +632,7 @@ function post_edit () -- filter should look like "(|(mail=my@mail.tld)(mail=my@mail2.tld))" filter = filter..")" - + -- For email forwards, we only need to check that they look -- like actual emails local drops = {} @@ -640,7 +640,7 @@ function post_edit () if mail ~= "" then if not mail:match(mail_pattern) then flash("fail", t("invalid_mailforward")..": "..mail) - return redirect(portal_url.."edit.html") + return redirect(conf.portal_url.."edit.html") end table.insert(drops, mail) end @@ -671,7 +671,7 @@ function post_edit () flash("fail", t("mail_already_used").." "..mail) end end - return redirect(portal_url.."edit.html") + return redirect(conf.portal_url.."edit.html") end end @@ -700,7 +700,7 @@ function post_edit () -- Ugly trick to force cache reloading set_headers(user) flash("win", t("information_updated")) - return redirect(portal_url.."info.html") + return redirect(conf.portal_url.."info.html") else flash("fail", t("user_saving_fail")) @@ -708,7 +708,7 @@ function post_edit () else flash("fail", t("missing_required_fields")) end - return redirect(portal_url.."edit.html") + return redirect(conf.portal_url.."edit.html") end end end @@ -716,7 +716,7 @@ end -- Compute the user login POST request -- It authenticates the user against the LDAP base then redirects to the portal -function do_login () +function login () -- We need these calls since we are in a POST request ngx.req.read_body() @@ -735,17 +735,17 @@ function do_login () -- Forward the `r` URI argument if it exists to redirect -- the user properly after a successful login. if uri_args.r then - return redirect(portal_url.."?r="..uri_args.r) + return redirect(conf.portal_url.."?r="..uri_args.r) else - return redirect(portal_url) + return redirect(conf.portal_url) end end --- Compute the user logout POST request +-- Compute the user logout request -- It deletes session cached information to invalidate client side cookie -- information. -function do_logout() +function logout() -- We need this call since we are in a POST request local args = ngx.req.get_uri_args() @@ -754,7 +754,7 @@ function do_logout() cache:delete("session_"..ngx.var.cookie_SSOwAuthUser) cache:delete(ngx.var.cookie_SSOwAuthUser.."-"..conf["ldap_identifier"]) -- Ugly trick to reload cache flash("info", t("logged_out")) - return redirect(portal_url) + return redirect(conf.portal_url) end end