From 02b4ecec8cf3107a8aa13f09571b37d8bfff4eef Mon Sep 17 00:00:00 2001 From: Kay0u Date: Mon, 20 Jan 2020 22:59:25 +0700 Subject: [PATCH 01/10] Fix legacy/new permissions --- helpers.lua | 93 +++++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 67 deletions(-) diff --git a/helpers.lua b/helpers.lua index fd45715..ca6459a 100644 --- a/helpers.lua +++ b/helpers.lua @@ -267,96 +267,55 @@ end function has_access(user) user = user or authUser - if not conf["users"][user] then - conf = config.get_config() - end + -- Get the longest url permission + longest_permission_match = longest_url_path(permission_matches()) or "" - -- If there are no `users` directive, or if the user has no ACL set, he can - -- access the URL by default - if not conf["users"] or not conf["users"][user] then - logger.debug("No access rules defined for user "..user..", assuming it can access..") + logger.debug("Longest permission match : "..longest_permission_match) + + -- If no permission matches, it means that there is no + -- permission defined for this url, a logged-in user can access it. + if longest_permission_match == "" then + logger.debug("No access rules defined for user "..user..", assuming it can access.") return true end - -- Loop through user's ACLs and return if the URL is authorized. - allowed_url_matches = {} - for url, app in pairs(conf["users"][user]) do + -- All user in this permission + allowed_users = conf["permissions"][longest_permission_match] - -- Replace the original domain by a local one if you are connected from - -- a non-global domain name. - if ngx.var.host == conf["local_portal_domain"] then - url = string.gsub(url, conf["original_portal_domain"], conf["local_portal_domain"]) - end - - if string.ends(url, "/") then - url = string.sub(url, 1, -1) - end - - if string.starts(ngx.var.host..ngx.var.uri, url) then - logger.debug("User is allowed to access this match : "..url) - table.insert(allowed_url_matches,url) + -- The user has permission to access the content if he is in the list of this one + if allowed_users then + for _, u in pairs(allowed_users) do + if u == user then + logger.debug("User "..user.." can access "..ngx.var.uri) + log_access(user, longest_permission_match) + return true + end end end - -- Keep only the longest match and compare it to the longest protected - -- match e.g. we don't want to allow the user to access /foo/admin if - -- /foo/admin is protected, but this user is only allowed to access /foo - local longest_allowed_match = longest_url_path(allowed_url_matches) or "" - local longest_protected_match = longest_url_path(protected_matches()) or "" - - logger.debug("Longest allowed match : "..longest_allowed_match) - logger.debug("Longest protected match : "..longest_protected_match) - - -- For the user to be able to access the content, at least one rule should - -- exist and it should be the longest match - if longest_allowed_match ~= "" - and string.len(longest_allowed_match) >= string.len(longest_protected_match) then - logger.debug("Logged-in user can access "..ngx.var.uri) - log_access(user, longest_allowed_match) - return true - else - logger.debug("Logged-in user cannot access "..ngx.var.uri) - return false - end + logger.debug("User "..user.." cannot access "..ngx.var.uri) + return false end - -function protected_matches() - if not conf["protected_urls"] then - conf["protected_urls"] = {} - end - if not conf["protected_regex"] then - conf["protected_regex"] = {} +function permission_matches() + if not conf["permissions"] then + conf["permissions"] = {} end local url_matches = {} - for _, url in ipairs(conf["protected_urls"]) do + for url, permission in pairs(conf["permissions"]) do if string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url) or string.starts(ngx.var.uri..uri_args_string(), url) then - logger.debug("protected_url match current uri : "..url) + logger.debug("Url permission match current uri : "..url) + table.insert(url_matches, url) - else - logger.debug("no match from "..url.." to "..ngx.var.uri) - end - end - for _, regex in ipairs(conf["protected_regex"]) do - local m1 = match(ngx.var.host..ngx.var.uri..uri_args_string(), regex) - local m2 = match(ngx.var.uri..uri_args_string(), regex) - if m1 then - logger.debug("protected_regex match current uri : "..regex.." with "..m1) - table.insert(url_matches, m1) - end - if m2 then - logger.debug("protected_regex match current uri : "..regex.." with "..m2) - table.insert(url_matches, m2) end end return url_matches end - function longest_url_path(urls) local longest = nil for _, url in ipairs(urls) do From f74619020d6df7f2c0052bb606d02855f7f5346e Mon Sep 17 00:00:00 2001 From: Kay0u Date: Wed, 29 Jan 2020 18:24:25 +0700 Subject: [PATCH 02/10] Fix if no permission exist --- helpers.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/helpers.lua b/helpers.lua index ca6459a..5b2849e 100644 --- a/helpers.lua +++ b/helpers.lua @@ -267,16 +267,17 @@ end function has_access(user) user = user or authUser + logger.debug("User "..user.." try to access "..ngx.var.uri) + -- Get the longest url permission longest_permission_match = longest_url_path(permission_matches()) or "" logger.debug("Longest permission match : "..longest_permission_match) - -- If no permission matches, it means that there is no - -- permission defined for this url, a logged-in user can access it. + -- If no permission matches, it means that there is no permission defined for this url. if longest_permission_match == "" then - logger.debug("No access rules defined for user "..user..", assuming it can access.") - return true + logger.debug("No access rules defined for user "..user..", assuming it cannot access.") + return false end -- All user in this permission From 9628d51d2d4f970dcd105e31a420789825e260b6 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Wed, 29 Jan 2020 18:24:51 +0700 Subject: [PATCH 03/10] check permission after unprotected --- access.lua | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/access.lua b/access.lua index c24ae09..09ca7e0 100644 --- a/access.lua +++ b/access.lua @@ -334,7 +334,9 @@ function serveThemeFile(filename) serveAsset("/ynhtheme/"..filename, "themes/"..conf.theme.."/"..filename) end -if hlp.is_logged_in() then +function serveYnhpanel() + logger.debug("Serving ynhpanel") + -- serve ynhpanel files serveAsset("/ynh_portal.js", "js/ynh_portal.js") serveAsset("/ynh_overlay.css", "css/ynh_overlay.css") @@ -343,20 +345,8 @@ if hlp.is_logged_in() then -- but I didn't succeed to figure out where is the current location of the script -- if you call it from "portal/assets/themes/" the ls fails scandir("/usr/share/ssowat/portal/assets/themes/"..conf.theme, serveThemeFile) - - -- If user has no access to this URL, redirect him to the portal - if not hlp.has_access() then - return hlp.redirect(conf.portal_url) - end - - -- If the user is authenticated and has access to the URL, set the headers - -- and let it be - hlp.set_headers() - return hlp.pass() end - - -- -- 7. Unprotected URLs -- @@ -375,6 +365,8 @@ if conf["unprotected_urls"] then or hlp.string.starts(ngx.var.uri..hlp.uri_args_string(), url)) and not is_protected() then if hlp.is_logged_in() then + serveYnhpanel() + hlp.set_headers() end logger.debug(ngx.var.uri.." is in unprotected_urls") @@ -389,6 +381,8 @@ if conf["unprotected_regex"] then or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex)) and not is_protected() then if hlp.is_logged_in() then + serveYnhpanel() + hlp.set_headers() end logger.debug(ngx.var.uri.." is in unprotected_regex") @@ -398,6 +392,20 @@ if conf["unprotected_regex"] then end +if hlp.is_logged_in() then + serveYnhpanel() + + -- If user has no access to this URL, redirect him to the portal + if not hlp.has_access() then + return hlp.redirect(conf.portal_url) + end + + -- If the user is authenticated and has access to the URL, set the headers + -- and let it be + hlp.set_headers() + return hlp.pass() +end + -- -- 8. Basic HTTP Authentication @@ -452,6 +460,6 @@ end -- when trying to access http://main.domain.tld/ (SSOwat finds that user aint -- logged in, therefore redirects to SSO, which redirects to the back_url, which -- redirect to SSO, ..) -logger.debug("No rule found for this url. By default, redirecting to portal") +logger.debug("No rule found for "..ngx.var.uri..". By default, redirecting to portal") local back_url = "https://" .. ngx.var.host .. ngx.var.uri .. hlp.uri_args_string() return hlp.redirect(conf.portal_url.."?r="..ngx.encode_base64(back_url)) From af892991afda2f8cd9449606bf8600838a931db8 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Thu, 13 Feb 2020 10:06:32 +0700 Subject: [PATCH 04/10] refactor legacy url protections --- access.lua | 112 +++++++++++----------------------------------------- helpers.lua | 36 +++++++++++++++++ 2 files changed, 59 insertions(+), 89 deletions(-) diff --git a/access.lua b/access.lua index 09ca7e0..5a598c3 100644 --- a/access.lua +++ b/access.lua @@ -239,6 +239,14 @@ if conf["redirected_regex"] then end +local longest_protected_match = hlp.longest_url_path(hlp.get_matches("protected")) or "" +local longest_skipped_match = hlp.longest_url_path(hlp.get_matches("skipped")) or "" +local longest_unprotected_match = hlp.longest_url_path(hlp.get_matches("unprotected")) or "" + +logger.debug("longest skipped "..longest_skipped_match) +logger.debug("longest unprotected "..longest_unprotected_match) +logger.debug("longest protected "..longest_protected_match) + -- -- 4. Skipped URLs -- @@ -247,66 +255,14 @@ end -- has to be sent, even if the user is already authenticated. -- -if conf["skipped_urls"] then - for _, url in ipairs(conf["skipped_urls"]) do - 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)) - then - logger.debug("Skipping "..ngx.var.uri) - return hlp.pass() - end - end -end - -if conf["skipped_regex"] then - for _, regex in ipairs(conf["skipped_regex"]) do - if (hlp.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) - or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex)) - then - logger.debug("Skipping "..ngx.var.uri) - return hlp.pass() - end - end +if longest_skipped_match ~= "" +and string.len(longest_skipped_match) >= string.len(longest_protected_match) then + logger.debug("Skipping "..ngx.var.uri) + return hlp.pass() end -- --- 5. Protected URLs --- --- If the URL matches one of the `protected_urls` in the configuration file, --- we have to protect it even if the URL is also set in the `unprotected_urls`. --- It could be useful if you want to unprotect every URL except a few --- particular ones. --- - -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 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) then - logger.debug(ngx.var.uri.." is in protected_urls") - return true - end - end - for _, regex in ipairs(conf["protected_regex"]) do - if hlp.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) - or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex) then - logger.debug(ngx.var.uri.." is in protected_regex") - return true - end - end - - logger.debug(ngx.var.uri.." is not in protected_urls/regex") - return false -end - --- --- 6. Specific files (used in YunoHost) +-- 5. Specific files (used in YunoHost) -- -- We want to serve specific portal assets right at the root of the domain. -- @@ -348,7 +304,7 @@ function serveYnhpanel() end -- --- 7. Unprotected URLs +-- 6. Unprotected URLs -- -- If the URL matches one of the `unprotected_urls` in the configuration file, -- it means that the URL should not be protected by the SSO *but* headers have @@ -359,39 +315,17 @@ end -- passed to the app. -- -if conf["unprotected_urls"] then - for _, url in ipairs(conf["unprotected_urls"]) do - 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 hlp.is_logged_in() then - serveYnhpanel() +if longest_unprotected_match ~= "" +and string.len(longest_unprotected_match) > string.len(longest_protected_match) then + if hlp.is_logged_in() then + serveYnhpanel() - hlp.set_headers() - end - logger.debug(ngx.var.uri.." is in unprotected_urls") - return hlp.pass() - end + hlp.set_headers() end + logger.debug(ngx.var.uri.." is in unprotected_urls") + return hlp.pass() end -if conf["unprotected_regex"] then - for _, regex in ipairs(conf["unprotected_regex"]) do - if (hlp.match(ngx.var.host..ngx.var.uri..hlp.uri_args_string(), regex) - or hlp.match(ngx.var.uri..hlp.uri_args_string(), regex)) - and not is_protected() then - if hlp.is_logged_in() then - serveYnhpanel() - - hlp.set_headers() - end - logger.debug(ngx.var.uri.." is in unprotected_regex") - return hlp.pass() - end - end -end - - if hlp.is_logged_in() then serveYnhpanel() @@ -408,7 +342,7 @@ end -- --- 8. Basic HTTP Authentication +-- 7. Basic HTTP Authentication -- -- If the `Authorization` header is set before reaching the SSO, we want to -- match user and password against the user database. @@ -444,7 +378,7 @@ end -- --- 9. Redirect to login +-- 8. Redirect to login -- -- If no previous rule has matched, just redirect to the portal login. -- The default is to protect every URL by default. diff --git a/helpers.lua b/helpers.lua index 5b2849e..221422a 100644 --- a/helpers.lua +++ b/helpers.lua @@ -317,6 +317,39 @@ function permission_matches() return url_matches end +function get_matches(section) + if not conf[section.."_urls"] then + conf[section.."_urls"] = {} + end + if not conf[section.."_regex"] then + conf[section.."_regex"] = {} + end + + local url_matches = {} + + for _, url in ipairs(conf[section.."_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) then + logger.debug(section.."_url match current uri : "..url) + table.insert(url_matches, url) + end + end + for _, regex in ipairs(conf[section.."_regex"]) do + local m1 = match(ngx.var.host..ngx.var.uri..uri_args_string(), regex) + local m2 = match(ngx.var.uri..uri_args_string(), regex) + if m1 then + logger.debug(section.."_regex match current uri : "..regex.." with "..m1) + table.insert(url_matches, m1) + end + if m2 then + logger.debug(section.."_regex match current uri : "..regex.." with "..m2) + table.insert(url_matches, m2) + end + end + + return url_matches +end + function longest_url_path(urls) local longest = nil for _, url in ipairs(urls) do @@ -329,6 +362,9 @@ function longest_url_path(urls) longest = current end end + if longest and string.ends(longest, "/") then + longest = string.sub(longest, 1, -2) + end return longest end From 97620aaac71f1768ace66cfe177175a0d23fc527 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Wed, 4 Mar 2020 11:32:53 +0100 Subject: [PATCH 05/10] Unused condition --- helpers.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helpers.lua b/helpers.lua index 221422a..3250424 100644 --- a/helpers.lua +++ b/helpers.lua @@ -306,8 +306,7 @@ function permission_matches() local url_matches = {} for url, permission in pairs(conf["permissions"]) do - if string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url) - or string.starts(ngx.var.uri..uri_args_string(), url) then + if string.starts(ngx.var.host..ngx.var.uri..uri_args_string(), url) then logger.debug("Url permission match current uri : "..url) table.insert(url_matches, url) From bf0dc733810a16e549f62897ea80b4fb610292a5 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Wed, 4 Mar 2020 11:34:24 +0100 Subject: [PATCH 06/10] using permissions, not users directive --- helpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.lua b/helpers.lua index 3250424..0df0551 100644 --- a/helpers.lua +++ b/helpers.lua @@ -262,7 +262,7 @@ function log_access(user, uri) end --- Check whether a user is allowed to access a URL using the `users` directive +-- Check whether a user is allowed to access a URL using the `permissions` directive -- of the configuration file function has_access(user) user = user or authUser From eb2872d5ed5eab8b797f1da3dc5ec5def801dac2 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Thu, 5 Mar 2020 00:24:06 +0100 Subject: [PATCH 07/10] Test if we should skip or unprotect the url --- access.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/access.lua b/access.lua index 5a598c3..1ec8672 100644 --- a/access.lua +++ b/access.lua @@ -256,7 +256,8 @@ logger.debug("longest protected "..longest_protected_match) -- if longest_skipped_match ~= "" -and string.len(longest_skipped_match) >= string.len(longest_protected_match) then +and string.len(longest_skipped_match) >= string.len(longest_protected_match) +and string.len(longest_skipped_match) > string.len(longest_unprotected_match) then logger.debug("Skipping "..ngx.var.uri) return hlp.pass() end From 8a9b24a4247aabc81d15bbeaa260d0b19d5ceff2 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 11 Mar 2020 09:28:59 +0000 Subject: [PATCH 08/10] Added translation using Weblate (Nepali) --- portal/locales/ne.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 portal/locales/ne.json diff --git a/portal/locales/ne.json b/portal/locales/ne.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/portal/locales/ne.json @@ -0,0 +1 @@ +{} From ac4974d2d2846da6e94dc682542d4ba0eae1f5df Mon Sep 17 00:00:00 2001 From: kay0u Date: Sun, 15 Mar 2020 16:02:06 +0000 Subject: [PATCH 09/10] Update changelog for 3.7.0.2 release --- debian/changelog | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index ec9c0bd..8658944 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,19 @@ +ssowat (3.7.0.2) stretch-testing; urgency=low + + - [fix] Match undefined function (SSOwat#151) + - [fix] Cohabitation between the old and the new permission system (SSOwat#156) + + - [i18n] Improved translations for French, Basque, Hindi, Turkish, Bengali (Bangladesh), Arabic, Hungarian, Polish, Catalan, Dutch, Portuguese, Italian, German, Swedish, Russian, Esperanto, Occitan, Nepali + + Thanks to all contributors (amirale qt, Quenti, Filip Bengtsson, elie gavoty, xaloc33, ButterflyOfFire, Kay0u) <3 ! + + -- Kay0u Sun, 15 Mar 2020 15:48:58 +0000 + ssowat (3.7.0.1) testing; urgency=low - [mod] Check skipped_urls before protected_urls (#149) - -- Alexandre Aubin Tue, 03 Dev 2019 12:07:00 +0000 + -- Alexandre Aubin Tue, 03 Dec 2019 12:07:00 +0000 ssowat (3.7.0) testing; urgency=low From 4f692c99d81164848975b32a501eaf688147c153 Mon Sep 17 00:00:00 2001 From: Kayou Date: Sun, 15 Mar 2020 17:27:30 +0100 Subject: [PATCH 10/10] Typo in changelog --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 8658944..3cb7dbd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -ssowat (3.7.0.2) stretch-testing; urgency=low +ssowat (3.7.0.2) testing; urgency=low - [fix] Match undefined function (SSOwat#151) - [fix] Cohabitation between the old and the new permission system (SSOwat#156)