Merge pull request #220 from selfhoster1312/lua-optimizations

portal-api: Optimization by caching & no check on public routes
This commit is contained in:
Alexandre Aubin 2023-09-27 18:43:26 +02:00 committed by GitHub
commit ea9e084688
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -21,6 +21,33 @@ local rex = require("rex_pcre")
local config = require("config") local config = require("config")
local conf = config.get_config() local conf = config.get_config()
-- Cache expensive calculations
local cache = ngx.shared.cache
-- Hash a string using hmac_sha512, return a hexa string
function cached_jwt_verify(data, secret)
res = cache:get(data)
if res == nil then
logger:debug("Result not found in cache, checking login")
-- Perform expensive calculation
decoded, err = jwt.verify(data, "HS256", cookie_secret)
if not decoded then
logger:error(err)
return nil, nil, err
end
-- As explained in set_basic_auth_header(), user and hashed password do not contain ':'
-- And cache cannot contain tables, so we use "user:password" format
cached = decoded["user"]..":"..decoded["pwd"]
cache:set(data, cached, 120)
logger:debug("Result saved in cache")
return decoded["user"], decoded["pwd"], err
else
logger:debug("Result found in cache")
user, pwd = res:match("([^:]+):(.*)")
return user, pwd, nil
end
end
-- The 'match' function uses PCRE regex as default -- The 'match' function uses PCRE regex as default
-- If '%.' is found in the regex, we assume it's a LUA regex (legacy code) -- If '%.' is found in the regex, we assume it's a LUA regex (legacy code)
-- 'match' returns the matched text. -- 'match' returns the matched text.
@ -55,6 +82,9 @@ end
-- ########################################################################### -- ###########################################################################
-- 1. AUTHENTICATION -- 1. AUTHENTICATION
-- Check wether or not this is a logged-in user -- Check wether or not this is a logged-in user
-- This is not run immediately but only if:
-- - the app is not public
-- - and/or auth_headers is enabled for this app
-- ########################################################################### -- ###########################################################################
function check_authentication() function check_authentication()
@ -62,23 +92,24 @@ function check_authentication()
-- cf. src/authenticators/ldap_ynhuser.py in YunoHost to see how the cookie is actually created -- cf. src/authenticators/ldap_ynhuser.py in YunoHost to see how the cookie is actually created
local cookie = ngx.var["cookie_" .. conf["cookie_name"]] local cookie = ngx.var["cookie_" .. conf["cookie_name"]]
if cookie == nil then
return false, nil, nil
end
decoded, err = jwt.verify(cookie, "HS256", cookie_secret) user, pwd, err = cached_jwt_verify(cookie, cookie_secret)
-- FIXME : maybe also check that the cookie was delivered for the requested domain (or a parent?) -- FIXME : maybe also check that the cookie was delivered for the requested domain (or a parent?)
-- FIXME : we might want also a way to identify expired/invalidated cookies, -- FIXME : we might want also a way to identify expired/invalidated cookies,
-- e.g. a user that got deleted after being logged in ... -- e.g. a user that got deleted after being logged in, or a user that logged out ...
if err ~= nil then if err ~= nil then
return false, nil, nil return false, nil, nil
else else
return true, decoded["user"], decoded["pwd"] return true, user, pwd
end end
end end
local is_logged_in, authUser, authPasswordEnc = check_authentication()
-- ########################################################################### -- ###########################################################################
-- 2. REDIRECTED URLS -- 2. REDIRECTED URLS
-- 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,
@ -184,15 +215,23 @@ function element_is_in_table(element, table)
return false return false
end end
-- Check whether a user is allowed to access a URL using the `permissions` directive -- Check whether the app is public access
-- of the configuration file function check_public_access(permission)
function check_has_access(permission)
if permission == nil then if permission == nil then
logger:debug("No permission matching request for "..ngx.var.uri.." ... Assuming access is denied") logger:debug("No permission matching request for "..ngx.var.uri.." ... Assuming access is denied")
return false return false
end end
if permission["public"] then
logger:debug("Someone tries to access "..ngx.var.uri.." (corresponding perm: "..permission["id"]..")")
return true
end
end
-- Check whether a user is allowed to access a URL using the `permissions` directive
-- of the configuration file
function check_has_access(permission)
-- Public access -- Public access
if authUser == nil or permission["public"] then if authUser == nil or permission["public"] then
user = authUser or "A visitor" user = authUser or "A visitor"
@ -212,7 +251,12 @@ function check_has_access(permission)
end end
end end
if check_public_access(permission) then
has_access = true
else
is_logged_in, authUser, authPasswordEnc = check_authentication()
has_access = check_has_access(permission) has_access = check_has_access(permission)
end
-- ########################################################################### -- ###########################################################################
-- 5. CLEAR USER-PROVIDED AUTH HEADER -- 5. CLEAR USER-PROVIDED AUTH HEADER
@ -270,11 +314,16 @@ end
-- 1st case : client has access -- 1st case : client has access
if has_access then if has_access then
if is_logged_in then
-- If Basic Authorization header are enable for this permission, -- If Basic Authorization header are enable for this permission,
-- add it to the response -- check if the user is actually logged in...
if permission["auth_header"] then if permission["auth_header"] then
if is_logged_in == nil then
-- Login check was not performed yet because the app is public
logger:debug("Checking authentication because the app requires auth_header")
is_logged_in, authUser, authPasswordEnc = check_authentication()
end
if is_logged_in then
-- add it to the response
set_basic_auth_header() set_basic_auth_header()
end end
end end