mirror of
https://github.com/YunoHost/SSOwat.git
synced 2024-09-03 20:06:27 +02:00
Rework access
This commit is contained in:
parent
d8c74604c0
commit
0fc89d0fc9
2 changed files with 111 additions and 184 deletions
89
access.lua
89
access.lua
|
@ -29,8 +29,7 @@ local logger = require("log")
|
|||
-- Just a note for the client to know that he passed through the SSO
|
||||
ngx.header["X-SSO-WAT"] = "You've just been SSOed"
|
||||
|
||||
|
||||
local is_logged_in = hlp.is_logged_in()
|
||||
local is_logged_in = hlp.refresh_logged_in()
|
||||
|
||||
--
|
||||
-- 1. LOGIN
|
||||
|
@ -282,66 +281,27 @@ function serveYnhpanel()
|
|||
scandir("/usr/share/ssowat/portal/assets/themes/"..conf.theme, serveThemeFile)
|
||||
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
|
||||
-- --
|
||||
-- -- If the URL matches one of the `skipped_urls` in the configuration file,
|
||||
-- -- it means that the URL should not be protected by the SSO and no header
|
||||
-- -- has to be sent, even if the user is already authenticated.
|
||||
-- --
|
||||
--
|
||||
-- if longest_skipped_match ~= ""
|
||||
-- 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
|
||||
--
|
||||
-- --
|
||||
-- -- 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
|
||||
-- -- to be sent if the user is already authenticated.
|
||||
-- --
|
||||
-- -- It means that you can let anyone access to an app, but if a user has already
|
||||
-- -- been authenticated on the portal, he can have his authentication headers
|
||||
-- -- passed to the app.
|
||||
-- --
|
||||
--
|
||||
-- if longest_unprotected_match ~= ""
|
||||
-- and string.len(longest_unprotected_match) > string.len(longest_protected_match) then
|
||||
-- if is_logged_in then
|
||||
-- serveYnhpanel()
|
||||
--
|
||||
-- hlp.set_headers()
|
||||
-- end
|
||||
-- logger.debug(ngx.var.uri.." is in unprotected_urls")
|
||||
-- return hlp.pass()
|
||||
-- end
|
||||
--
|
||||
-- if 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
|
||||
local permission = hlp.get_best_permission()
|
||||
|
||||
if permission then
|
||||
if is_logged_in then
|
||||
serveYnhpanel()
|
||||
|
||||
-- If the user is authenticated and has access to the URL, set the headers
|
||||
-- and let it be
|
||||
if permission["auth_header"] then
|
||||
logger.debug("Set Headers")
|
||||
hlp.set_headers()
|
||||
end
|
||||
end
|
||||
|
||||
-- If user has no access to this URL, redirect him to the portal
|
||||
if not hlp.has_access(permission) then
|
||||
return hlp.redirect(conf.portal_url)
|
||||
end
|
||||
|
||||
return hlp.pass()
|
||||
end
|
||||
|
||||
--
|
||||
-- 7. Basic HTTP Authentication
|
||||
|
@ -364,11 +324,14 @@ if auth_header then
|
|||
logger.debug("User got authenticated through basic auth")
|
||||
|
||||
-- If user has no access to this URL, redirect him to the portal
|
||||
if not hlp.has_access(user) then
|
||||
if not permission or not hlp.has_access(permission, user) then
|
||||
return hlp.redirect(conf.portal_url)
|
||||
end
|
||||
|
||||
hlp.set_headers(user)
|
||||
if permission["auth_header"] then
|
||||
logger.debug("Set Headers")
|
||||
hlp.set_headers(user)
|
||||
end
|
||||
return hlp.pass()
|
||||
end
|
||||
end
|
||||
|
|
206
helpers.lua
206
helpers.lua
|
@ -17,6 +17,8 @@ local url_parser = require "socket.url"
|
|||
-- Import Perl regular expressions library
|
||||
local rex = require "rex_pcre"
|
||||
|
||||
local is_logged_in = false
|
||||
|
||||
function refresh_config()
|
||||
conf = config.get_config()
|
||||
end
|
||||
|
@ -224,7 +226,7 @@ end
|
|||
-- Check if the session cookies are set, and rehash server + client information
|
||||
-- to match the session hash.
|
||||
--
|
||||
function is_logged_in()
|
||||
function refresh_logged_in()
|
||||
local expireTime = ngx.var.cookie_SSOwAuthExpire
|
||||
local user = ngx.var.cookie_SSOwAuthUser
|
||||
local authHash = ngx.var.cookie_SSOwAuthHash
|
||||
|
@ -250,7 +252,8 @@ function is_logged_in()
|
|||
if hash ~= authHash then
|
||||
logger.info("Hash "..authHash.." rejected for "..user.."@"..ngx.var.remote_addr)
|
||||
end
|
||||
return hash == authHash
|
||||
is_logged_in = hash == authHash
|
||||
return is_logged_in
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -268,34 +271,60 @@ function log_access(user, uri)
|
|||
end
|
||||
end
|
||||
|
||||
function get_best_permission()
|
||||
if not conf["permissions"] then
|
||||
conf["permissions"] = {}
|
||||
end
|
||||
|
||||
local permission_match = nil
|
||||
local longest_url_match = ""
|
||||
|
||||
for permission_name, permission in pairs(conf["permissions"]) do
|
||||
if next(permission['uris']) ~= nil then
|
||||
for _, url in pairs(permission['uris']) do
|
||||
if string.starts(url, "re:") then
|
||||
url = string.sub(url, 4, string.len(url))
|
||||
end
|
||||
|
||||
local m = match(ngx.var.host..ngx.var.uri..uri_args_string(), url)
|
||||
if m ~= nil and string.len(m) > string.len(longest_url_match) then
|
||||
longest_url_match = m
|
||||
permission_match = permission
|
||||
logger.debug("Match "..m)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return permission_match
|
||||
end
|
||||
|
||||
-- Check whether a user is allowed to access a URL using the `permissions` directive
|
||||
-- of the configuration file
|
||||
function has_access(user)
|
||||
function has_access(permission, 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.
|
||||
if longest_permission_match == "" then
|
||||
logger.debug("No access rules defined for user "..user..", assuming it cannot access.")
|
||||
if permission == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Public access
|
||||
if user == nil or permission["public"] then
|
||||
logger.debug("A visitor try to access "..ngx.var.uri)
|
||||
return permission["public"]
|
||||
end
|
||||
|
||||
logger.debug("User "..user.." try to access "..ngx.var.uri)
|
||||
|
||||
-- All user in this permission
|
||||
allowed_users = conf["permissions"][longest_permission_match]
|
||||
allowed_users = permission["users"]
|
||||
|
||||
-- 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)
|
||||
logger.debug("User "..user.." can access "..ngx.var.host..ngx.var.uri..uri_args_string())
|
||||
log_access(user, ngx.var.host..ngx.var.uri..uri_args_string())
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
@ -305,76 +334,6 @@ function has_access(user)
|
|||
return false
|
||||
end
|
||||
|
||||
function permission_matches()
|
||||
if not conf["permissions"] then
|
||||
conf["permissions"] = {}
|
||||
end
|
||||
|
||||
local url_matches = {}
|
||||
|
||||
for url, permission in pairs(conf["permissions"]) do
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
-- Turn the url into a path, e.g.:
|
||||
-- /foo/bar -> /foo/bar
|
||||
-- domain.tld/foo/bar -> /foo/bar
|
||||
-- https://domain.tld:1234/foo/bar -> /foo/bar
|
||||
current = url_parser.parse(url).path
|
||||
if not longest or string.len(longest) < string.len(current) then
|
||||
longest = current
|
||||
end
|
||||
end
|
||||
if longest and string.ends(longest, "/") then
|
||||
longest = string.sub(longest, 1, -2)
|
||||
end
|
||||
return longest
|
||||
end
|
||||
|
||||
|
||||
-- Authenticate a user against the LDAP database using a username or an email
|
||||
-- address.
|
||||
-- Reminder: conf["ldap_identifier"] is "uid" by default
|
||||
|
@ -557,7 +516,7 @@ function serve(uri, cache)
|
|||
|
||||
-- Load login.html as index
|
||||
if rel_path == "/" then
|
||||
if is_logged_in() then
|
||||
if is_logged_in then
|
||||
rel_path = "/portal.html"
|
||||
else
|
||||
rel_path = "/login.html"
|
||||
|
@ -658,45 +617,48 @@ function get_data_for(view)
|
|||
-- Needed if the LDAP db is changed outside ssowat (from the cli for example).
|
||||
-- Not doing it for ynhpanel.json only for performance reasons,
|
||||
-- so the panel could show wrong first name, last name or main email address
|
||||
if view ~= "ynhpanel.json" then
|
||||
delete_user_info_cache(user)
|
||||
end
|
||||
-- TODO: What if we remove info during logout?
|
||||
--if view ~= "ynhpanel.json" then
|
||||
-- delete_user_info_cache(user)
|
||||
--end
|
||||
|
||||
-- Be sure cache is loaded
|
||||
set_headers(user)
|
||||
if user then
|
||||
set_headers(user)
|
||||
|
||||
local mails = get_mails(user)
|
||||
data = {
|
||||
connected = true,
|
||||
theme = conf.theme,
|
||||
portal_url = conf.portal_url,
|
||||
uid = user,
|
||||
cn = cache:get(user.."-cn"),
|
||||
sn = cache:get(user.."-sn"),
|
||||
givenName = cache:get(user.."-givenName"),
|
||||
mail = mails["mail"],
|
||||
mailalias = mails["mailalias"],
|
||||
maildrop = mails["maildrop"],
|
||||
app = {}
|
||||
}
|
||||
local mails = get_mails(user)
|
||||
data = {
|
||||
connected = true,
|
||||
theme = conf.theme,
|
||||
portal_url = conf.portal_url,
|
||||
uid = user,
|
||||
cn = cache:get(user.."-cn"),
|
||||
sn = cache:get(user.."-sn"),
|
||||
givenName = cache:get(user.."-givenName"),
|
||||
mail = mails["mail"],
|
||||
mailalias = mails["mailalias"],
|
||||
maildrop = mails["maildrop"],
|
||||
app = {}
|
||||
}
|
||||
|
||||
local sorted_apps = {}
|
||||
local sorted_apps = {}
|
||||
|
||||
-- Add user's accessible URLs using the ACLs.
|
||||
-- It is typically used to build the app list.
|
||||
for permission_name, permission in pairs(conf["permissions"]) do
|
||||
-- We want to display a tile, and uris is not empty
|
||||
if permission['show_tile'] and next(permission['uris']) ~= nil then
|
||||
url = permission['uris'][1]
|
||||
name = permission['label']
|
||||
-- Add user's accessible URLs using the ACLs.
|
||||
-- It is typically used to build the app list.
|
||||
for permission_name, permission in pairs(conf["permissions"]) do
|
||||
-- We want to display a tile, and uris is not empty
|
||||
if permission['show_tile'] and next(permission['uris']) ~= nil and has_access(permission, user) then
|
||||
url = permission['uris'][1]
|
||||
name = permission['label']
|
||||
|
||||
if ngx.var.host == conf["local_portal_domain"] then
|
||||
url = string.gsub(url, conf["original_portal_domain"], conf["local_portal_domain"])
|
||||
if ngx.var.host == conf["local_portal_domain"] then
|
||||
url = string.gsub(url, conf["original_portal_domain"], conf["local_portal_domain"])
|
||||
end
|
||||
|
||||
table.insert(sorted_apps, name)
|
||||
table.sort(sorted_apps)
|
||||
table.insert(data["app"], index_of(sorted_apps, name), { url = url, name = name })
|
||||
end
|
||||
|
||||
table.insert(sorted_apps, name)
|
||||
table.sort(sorted_apps)
|
||||
table.insert(data["app"], index_of(sorted_apps, name), { url = url, name = name })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -776,7 +738,7 @@ function edit_user()
|
|||
|
||||
-- Ensure that user is logged in and has passed information
|
||||
-- before continuing.
|
||||
if is_logged_in() and args
|
||||
if is_logged_in and args
|
||||
then
|
||||
|
||||
-- Set HTTP status to 201
|
||||
|
@ -1069,12 +1031,14 @@ function logout()
|
|||
local args = ngx.req.get_uri_args()
|
||||
|
||||
-- Delete user cookie if logged in (that should always be the case)
|
||||
if is_logged_in() then
|
||||
if is_logged_in then
|
||||
delete_cookie()
|
||||
cache:delete("session_"..authUser)
|
||||
cache:delete(authUser.."-"..conf["ldap_identifier"]) -- Ugly trick to reload cache
|
||||
cache:delete(authUser.."-password")
|
||||
delete_user_info_cache(authUser)
|
||||
flash("info", t("logged_out"))
|
||||
is_logged_in = false
|
||||
end
|
||||
|
||||
-- Redirect with the `r` URI argument if it exists or redirect to portal
|
||||
|
|
Loading…
Add table
Reference in a new issue