refactor: use OOP where appropriate

Yves G 2021-10-02 23:45:31 +02:00
parent 20fc2dbf8b
commit 489350fe21
23 changed files with 382 additions and 397 deletions

View File

@ -29,7 +29,7 @@ test: test-env
${run_test_file} ${ROOT_DIR}/test/config.utest.lua ${run_test_file} ${ROOT_DIR}/test/config.utest.lua
${run_test_file} ${ROOT_DIR}/test/auth.utest.lua ${run_test_file} ${ROOT_DIR}/test/auth.utest.lua
${run_test_file} ${ROOT_DIR}/test/nginx.utest.lua ${run_test_file} ${ROOT_DIR}/test/nginx.utest.lua
${run_test_file} ${ROOT_DIR}/test/profile.utest.lua ${run_test_file} ${ROOT_DIR}/test/identity.utest.lua
${run_test_file} ${ROOT_DIR}/test/sites.utest.lua ${run_test_file} ${ROOT_DIR}/test/sites.utest.lua
${run_test_file} ${ROOT_DIR}/test/crypto.utest.lua ${run_test_file} ${ROOT_DIR}/test/crypto.utest.lua
${run_test_file} ${ROOT_DIR}/test/login.utest.lua ${run_test_file} ${ROOT_DIR}/test/login.utest.lua

View File

@ -9,11 +9,11 @@
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local req_data = nginx.get_request() local req_data = nginx.class__request:current()
if nginx.is(req_data, "/.well-known/webfinger") if req_data:is("/.well-known/webfinger")
and nginx.has_param(req_data, "rel", "http://openid.net/specs/connect/1.0/issuer") and req_data:has_param("rel", "http://openid.net/specs/connect/1.0/issuer")
and nginx.has_param(req_data, "resource") and req_data:has_param("resource")
then then
-- https://openid.net/specs/openid-connect-discovery-1_0.html -- https://openid.net/specs/openid-connect-discovery-1_0.html
local oauth2 = require("ssso_oauth2") local oauth2 = require("ssso_oauth2")
@ -27,14 +27,14 @@ local sites = require("ssso_sites")
local sso_prefix = conf.get_sso_prefix() local sso_prefix = conf.get_sso_prefix()
local auth, status = sess.get_session() local auth, status = sess.get_session()
if nginx.starts_with(req_data, sso_prefix) then if req_data:starts_with(sso_prefix) then
-- SSO-specific URL -- SSO-specific URL
if nginx.starts_with(req_data, sso_prefix .. "/login") then if req_data:starts_with(sso_prefix .. "/login") then
local login = require("ssso_login") local login = require("ssso_login")
return login.answer_request(req_data) return login.answer_request(req_data)
elseif nginx.starts_with(req_data, sso_prefix .. "/oauth2") then elseif req_data:starts_with(sso_prefix .. "/oauth2") then
local oauth2 = require("ssso_oauth2") local oauth2 = require("ssso_oauth2")
return oauth2.answer_request(req_data, auth) return oauth2.answer_request(req_data, auth)
elseif auth then elseif auth then

View File

@ -7,7 +7,7 @@ local function load_conf(prefix)
local file = assert(io.open(prefix .. name_suffix, "r"), "File " .. prefix .. name_suffix .. " not found") local file = assert(io.open(prefix .. name_suffix, "r"), "File " .. prefix .. name_suffix .. " not found")
conf = json.decode(file:read("*all")) conf = json.decode(file:read("*all"))
file:close() file:close()
assert(conf["auth"], "Simple-SSO configuration is missing a `auth` entry") assert(conf["auth"], "Simple-SSO configuration is missing an `auth` entry")
assert(conf["sso_host"], "Simple-SSO configuration is missing a `sso_host` entry") assert(conf["sso_host"], "Simple-SSO configuration is missing a `sso_host` entry")
assert(conf["sso_prefix"], "Simple-SSO configuration is missing a `sso_prefix` entry") assert(conf["sso_prefix"], "Simple-SSO configuration is missing a `sso_prefix` entry")
assert(conf["session_seconds"], "Simple-SSO configuration is missing a `session_seconds` entry") assert(conf["session_seconds"], "Simple-SSO configuration is missing a `session_seconds` entry")

View File

@ -7,7 +7,6 @@ local b64 = require("ssso_base64")
local config = require("ssso_config") local config = require("ssso_config")
local log = require("ssso_log") local log = require("ssso_log")
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local prof = require("ssso_profile")
local sites = require("ssso_sites") local sites = require("ssso_sites")
local KEY_SIZE = 32 -- 256 bits for AES-256-GCMs key and SHA-256 local KEY_SIZE = 32 -- 256 bits for AES-256-GCMs key and SHA-256
@ -88,12 +87,12 @@ end
-- https://www.rfc-editor.org/rfc/rfc7519 -- https://www.rfc-editor.org/rfc/rfc7519
-- https://openid.net/specs/openid-connect-core-1_0.html -- https://openid.net/specs/openid-connect-core-1_0.html
local function get_jws_and_tslimit(data) local function get_jws_and_tslimit(profile)
local user = prof.user(data) local user = profile:user()
local ser_data = prof.serialize(data) .. sites.serialize(data) local ser_profile = profile:serialize()
log.debug("Creating JWS with data: " .. ser_data:gsub("([\031\030\029\028])", function(s) return "[" .. s:byte() .. "]" end)) log.debug("Creating JWS with profile: " .. ser_profile:gsub("([\031\030\029\028\027\026])", function(s) return "[" .. s:byte() .. "]" end))
local crypted_data = encrypt(ser_data) local crypted_profile = encrypt(ser_profile)
if not user or not crypted_data then if not user or not crypted_profile then
return nil, nil return nil, nil
end end
local iat = nginx.get_seconds_since_epoch() local iat = nginx.get_seconds_since_epoch()
@ -104,30 +103,29 @@ local function get_jws_and_tslimit(data)
aud = user, aud = user,
exp = exp, exp = exp,
iat = iat, iat = iat,
x_ssso = b64.encode_base64url(crypted_data), x_ssso = b64.encode_base64url(crypted_profile),
} }
return to_jws(jwt), exp return to_jws(jwt), exp
end end
local function get_data_and_new_jws(jws) local function get_profile_and_new_jws(jws)
local jwt = to_jwt(jws) local jwt = to_jwt(jws)
local iat = nginx.get_seconds_since_epoch() local iat = nginx.get_seconds_since_epoch()
if jwt == nil or not jwt["x_ssso"] or not jwt["exp"] or jwt.exp < iat then if jwt == nil or not jwt["x_ssso"] or not jwt["exp"] or jwt.exp < iat then
return nil, nil, nil return nil, nil, nil
end end
local ser_data = decrypt(b64.decode_base64url(jwt.x_ssso)) local ser_profile = decrypt(b64.decode_base64url(jwt.x_ssso))
if not ser_data then if not ser_profile then
return nil, nil, nil return nil, nil, nil
end end
log.debug("Read data from JWS: " .. ser_data:gsub("([\031\030\029\028])", function(s) return "[" .. s:byte() .. "]" end)) log.debug("Read profile from JWS: " .. ser_profile:gsub("([\031\030\029\028])", function(s) return "[" .. s:byte() .. "]" end))
local data, remainder, _ = prof.deserialize(ser_data) local profile = sites.class__profile:deserialize(ser_profile)
data, _ = sites.deserialize_update(remainder, data)
jwt.iat = iat jwt.iat = iat
jwt.exp = iat + config.get_session_seconds() jwt.exp = iat + config.get_session_seconds()
return data, to_jws(jwt), jwt.exp return profile, to_jws(jwt), jwt.exp
end end
return { return {
get_jws_and_tslimit = get_jws_and_tslimit, get_jws_and_tslimit = get_jws_and_tslimit,
get_data_and_new_jws = get_data_and_new_jws, get_profile_and_new_jws = get_profile_and_new_jws,
} }

60
src/ssso_identity.lua Normal file
View File

@ -0,0 +1,60 @@
local b64 = require("ssso_base64")
local class__identity = {}
function class__identity:build(user, password, name, email)
local identity = {
u = user,
p = password,
n = name,
e = email,
}
setmetatable(identity, {__index = self})
return identity
end
function class__identity:serialize() -- TODO: test
return (self.u or "\025")
.. "\031" .. (self.p or "\025")
.. "\031" .. (self.n or "\025")
.. "\031" .. (self.e or "\025")
end
function class__identity:deserialize(ser) -- TODO: test
local identity
ser:gsub("^(.-)\031(.-)\031(.-)\031(.*)", function (u, p, n, e)
if u == "\025" then u = nil end
if p == "\025" then p = nil end
if n == "\025" then n = nil end
if e == "\025" then e = nil end
identity = self:build(u, p, n, e)
end)
return identity
end
function class__identity:format(template)
local s = template
s = s:gsub("\ru%.", self.u or "")
s = s:gsub("\rp%.", self.p or "")
s = s:gsub("\rn%.", self.n or "")
s = s:gsub("\re%.", self.e or "")
s = s:gsub("\rb64%(([^\r]-)%)%.", function(x) return b64.encode_base64(x) end)
s = s:gsub("\ru64%(([^\r]-)%)%.", function(x) return b64.encode_base64url(x) end)
return s
end
function class__identity:email()
return self.e
end
function class__identity:name()
return self.n
end
function class__identity:user()
return self.u
end
return {
class__identity = class__identity,
}

View File

@ -3,7 +3,6 @@ local conf = require("ssso_config")
local crypto = require("ssso_crypto") local crypto = require("ssso_crypto")
local log = require("ssso_log") local log = require("ssso_log")
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local prof = require("ssso_profile")
local sites = require("ssso_sites") local sites = require("ssso_sites")
local util = require("ssso_util") local util = require("ssso_util")
@ -51,15 +50,14 @@ local function check_credentials_and_get_profile(user, password)
local user_data = auth.read_user(user, password) local user_data = auth.read_user(user, password)
if user_data then if user_data then
log.debug("Credentials accepted for user " .. user_data.name) log.debug("Credentials accepted for user " .. user_data.name)
local profile = prof.build_profile(user, password, user_data.name, user_data.email) return sites.class__profile:build_from_conf(user, password, user_data.name, user_data.email)
return sites.with_sites(profile, user)
else else
return nil return nil
end end
end end
local function check_login(req_data) local function check_login(req_data)
req_data = nginx.with_post_parameters(req_data) req_data = req_data:with_post_parameters()
local user = req_data.query_params["login"] or "" local user = req_data.query_params["login"] or ""
local password = req_data.query_params["password"] or "" local password = req_data.query_params["password"] or ""
local profile = check_credentials_and_get_profile(user, password) local profile = check_credentials_and_get_profile(user, password)
@ -88,17 +86,17 @@ end
local function answer_request(req_data) local function answer_request(req_data)
local login = conf.get_sso_prefix() .. "/login" local login = conf.get_sso_prefix() .. "/login"
if nginx.is(req_data, login) then if req_data:is(login) then
if nginx.has_method(req_data, "POST") then if req_data:has_method("POST") then
log.info("Checking login") log.info("Checking login")
return check_login(req_data) return check_login(req_data)
else else
local html = inject_data(contents("login.html"), req_data, nil) local html = inject_data(contents("login.html"), req_data, nil)
return nginx.return_contents(html, "text/html; charset=UTF-8") return nginx.return_contents(html, "text/html; charset=UTF-8")
end end
elseif nginx.is(req_data, login .. ".css") then elseif req_data:is(login .. ".css") then
return nginx.return_contents(contents("login.css"), "text/css; charset=UTF-8") return nginx.return_contents(contents("login.css"), "text/css; charset=UTF-8")
elseif nginx.is(req_data, login .. ".js") then elseif req_data:is(login .. ".js") then
return nginx.return_contents(contents("login.js"), "application/javascript; charset=UTF-8") return nginx.return_contents(contents("login.js"), "application/javascript; charset=UTF-8")
else else
log.info("Unknown login file") log.info("Unknown login file")

View File

@ -3,7 +3,14 @@ local b64 = require("ssso_base64")
local util = require("ssso_util") local util = require("ssso_util")
local conf = require("ssso_config") local conf = require("ssso_config")
local function get_request() local class__request = {}
local function str_starts_with(str, begin)
local i, _ = str:find(util.str_to_pattern(begin))
return 1 == i
end
function class__request:current()
local vars = ngx.var local vars = ngx.var
local request = { local request = {
referer = vars.http_referer, referer = vars.http_referer,
@ -11,6 +18,8 @@ local function get_request()
method = vars.request_method, method = vars.request_method,
uri = vars.request_uri, uri = vars.request_uri,
} }
setmetatable(request, {__index = self})
if request.referer == "" then if request.referer == "" then
request.referer = nil request.referer = nil
end end
@ -33,20 +42,35 @@ local function get_request()
return request return request
end end
local function with_post_parameters(req_data) function class__request:with_post_parameters()
ngx.req.read_body() ngx.req.read_body()
local args, _ = ngx.req.get_post_args() local args, _ = ngx.req.get_post_args()
if args then if args then
for key, val in pairs(args) do for key, val in pairs(args) do
req_data.query_params[key] = val self.query_params[key] = val
end end
end end
return req_data return self
end end
local function str_starts_with(str, begin) function class__request:has_method(method)
local i, _ = str:find(util.str_to_pattern(begin)) return string.upper(method) == string.upper(self.method)
return 1 == i end
function class__request:is(url)
return self.target == url
end
function class__request:matches(lua_pattern)
return self.target:match(lua_pattern)
end
function class__request:starts_with(prefix)
return str_starts_with(self.target, prefix)
end
function class__request:has_param(param, value)
return self.query_params[param] ~= nil and (value == nil or self.query_params[param] == value)
end end
local function get_basic_auth() local function get_basic_auth()
@ -103,26 +127,6 @@ local function add_header(name, value)
ngx.req.set_header(name, value) ngx.req.set_header(name, value)
end end
local function has_method(req_data, method)
return string.upper(method) == string.upper(req_data.method)
end
local function is(req_data, url)
return req_data.target == url
end
local function matches(req_data, lua_pattern)
return req_data.target:match(lua_pattern)
end
local function starts_with(req_data, prefix)
return str_starts_with(req_data.target, prefix)
end
local function has_param(req_data, param, value)
return req_data.query_params[param] ~= nil and (value == nil or req_data.query_params[param] == value)
end
local function answer_not_found(req_data) local function answer_not_found(req_data)
return ngx.exit(404) return ngx.exit(404)
end end
@ -167,21 +171,15 @@ return {
add_cookie = add_cookie, add_cookie = add_cookie,
add_header = add_header, add_header = add_header,
answer_not_found = answer_not_found, answer_not_found = answer_not_found,
class__request = class__request,
forward_request = forward_request, forward_request = forward_request,
get_basic_auth = get_basic_auth, get_basic_auth = get_basic_auth,
get_jws_cookie = get_jws_cookie, get_jws_cookie = get_jws_cookie,
get_request = get_request,
get_seconds_since_epoch = get_seconds_since_epoch, get_seconds_since_epoch = get_seconds_since_epoch,
has_method = has_method,
has_param = has_param,
is = is,
matches = matches,
redirect_to_login = redirect_to_login, redirect_to_login = redirect_to_login,
redirect_to_page = redirect_to_page, redirect_to_page = redirect_to_page,
redirect_to_portal = redirect_to_portal, redirect_to_portal = redirect_to_portal,
return_contents = return_contents, return_contents = return_contents,
set_jws_cookie = set_jws_cookie, set_jws_cookie = set_jws_cookie,
starts_with = starts_with,
answer_unexpected_error = answer_unexpected_error, answer_unexpected_error = answer_unexpected_error,
with_post_parameters = with_post_parameters,
} }

View File

@ -1,7 +1,6 @@
local util = require("ssso_util") local util = require("ssso_util")
local conf = require("ssso_config") local conf = require("ssso_config")
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local prof = require("ssso_profile")
local sites = require("ssso_sites") local sites = require("ssso_sites")
local root = "" local root = ""
@ -18,14 +17,14 @@ local function contents(relative)
end end
local function inject_data(html, profile) local function inject_data(html, profile)
html = html:gsub("SSSO_USER", util.str_to_html(prof.user(profile))) html = html:gsub("SSSO_USER", util.str_to_html(profile:user()))
html = html:gsub("SSSO_NAME", util.str_to_html(prof.name(profile))) html = html:gsub("SSSO_NAME", util.str_to_html(profile:name()))
html = html:gsub("SSSO_EMAIL", util.str_to_html(prof.email(profile))) html = html:gsub("SSSO_EMAIL", util.str_to_html(profile:email()))
local links = "" local links = ""
local allowed = sites.authorized_links(prof.user(profile)) local allowed = profile:authorized_links()
table.sort(allowed, function(a1, a2) return a1.label < a2.label end) table.sort(allowed, function(a1, a2) return a1.label < a2.label end)
for _, allow in ipairs(allowed) do for _, allow in ipairs(allowed) do
links = links .. '<li><a href="' .. prof.format(allow.link, profile) .. '"><span>' .. util.str_to_html(prof.format(allow.label, profile)) .. "</span></a></li>" links = links .. '<li><a href="' .. profile:format(allow.link) .. '"><span>' .. util.str_to_html(profile:format(allow.label)) .. "</span></a></li>"
end end
if links ~= "" then if links ~= "" then
html = html:gsub('<nav id="sites"></nav>', '<nav id="sites"><ul>' .. links .. "</ul></nav>") html = html:gsub('<nav id="sites"></nav>', '<nav id="sites"><ul>' .. links .. "</ul></nav>")
@ -37,12 +36,12 @@ end
local function answer_request(req_data, profile) local function answer_request(req_data, profile)
local portal = conf.get_sso_prefix() .. "/portal" local portal = conf.get_sso_prefix() .. "/portal"
if nginx.is(req_data, portal) then if req_data:is(portal) then
local html = inject_data(contents("portal.html"), profile) local html = inject_data(contents("portal.html"), profile)
return nginx.return_contents(html, "text/html; charset=UTF-8") return nginx.return_contents(html, "text/html; charset=UTF-8")
elseif nginx.is(req_data, portal .. ".css") then elseif req_data:is(portal .. ".css") then
return nginx.return_contents(contents("portal.css"), "text/css; charset=UTF-8") return nginx.return_contents(contents("portal.css"), "text/css; charset=UTF-8")
elseif nginx.is(req_data, portal .. ".js") then elseif req_data:is(portal .. ".js") then
return nginx.return_contents(contents("portal.js"), "application/javascript; charset=UTF-8") return nginx.return_contents(contents("portal.js"), "application/javascript; charset=UTF-8")
else else
return nginx.answer_not_found(req_data) return nginx.answer_not_found(req_data)

View File

@ -1,63 +0,0 @@
local b64 = require("ssso_base64")
local function build_profile(user, password, name, email)
return {
u = user,
p = password,
n = name,
e = email,
}
end
local function serialize(profile)
return (profile.u or "\025") .. "\031" ..
(profile.p or "\025") .. "\031" ..
(profile.n or "\025") .. "\031" ..
(profile.e or "\025") .. "\031"
end
local function deserialize(ser)
local profile
local remainder = ser:gsub("^(.-)\031(.-)\031(.-)\031(.-)\031", function (u, p, n, e)
if u == "\025" then u = nil end
if p == "\025" then p = nil end
if n == "\025" then n = nil end
if e == "\025" then e = nil end
profile = build_profile(u, p, n, e)
return ""
end)
return profile, remainder
end
local function format(template, profile)
local s = template
s = s:gsub("\ru%.", profile.u or "")
s = s:gsub("\rp%.", profile.p or "")
s = s:gsub("\rn%.", profile.n or "")
s = s:gsub("\re%.", profile.e or "")
s = s:gsub("\rb64%(([^\r]-)%)%.", function(x) return b64.encode_base64(x) end)
s = s:gsub("\ru64%(([^\r]-)%)%.", function(x) return b64.encode_base64url(x) end)
return s
end
local function email(profile)
return profile.e
end
local function name(profile)
return profile.n
end
local function user(profile)
return profile.u
end
return {
build_profile = build_profile,
deserialize = deserialize, -- TODO: test
serialize = serialize, -- TODO: test
email = email,
format = format,
name = name,
user = user,
}

View File

@ -3,27 +3,27 @@ local login = require("ssso_login")
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local function get_session() local function get_session()
local session, jws, tslimit local profile, jws, tslimit
local user, password = nginx.get_basic_auth() local user, password = nginx.get_basic_auth()
if user and password then if user and password then
session = login.check_credentials_and_get_profile(user, password) profile = login.check_credentials_and_get_profile(user, password)
if session then if profile then
jws, tslimit = crypto.get_jws_and_tslimit(session) jws, tslimit = crypto.get_jws_and_tslimit(profile)
end end
end end
if not session then if not profile then
local cookie = nginx.get_jws_cookie() local cookie = nginx.get_jws_cookie()
if not cookie or cookie == "" then if not cookie or cookie == "" then
return nil, 401 return nil, 401
end end
session, jws, tslimit = crypto.get_data_and_new_jws(cookie) profile, jws, tslimit = crypto.get_profile_and_new_jws(cookie)
end end
if session then if profile then
nginx.set_jws_cookie(jws, tslimit) nginx.set_jws_cookie(jws, tslimit)
return session, 200 return profile, 200
else else
return nil, 403 return nil, 403
end end

View File

@ -1,6 +1,6 @@
local json = require("cjson.safe") local json = require("cjson.safe")
local id = require("ssso_identity")
local nginx = require("ssso_nginx") local nginx = require("ssso_nginx")
local prof = require("ssso_profile")
local known_private_re = {} local known_private_re = {}
local known_sites = {} local known_sites = {}
@ -26,7 +26,7 @@ end
local function is_known_private(req_data) local function is_known_private(req_data)
for _, r in ipairs(known_private_re) do for _, r in ipairs(known_private_re) do
if nginx.matches(req_data, r) then if req_data:matches(r) then
return true return true
end end
end end
@ -37,12 +37,12 @@ local function handle_request(req_data, auth)
if auth then if auth then
for _, site in ipairs(auth.ok) do for _, site in ipairs(auth.ok) do
for _, r in ipairs(site.r) do for _, r in ipairs(site.r) do
if nginx.matches(req_data, r) then if req_data:matches(r) then
for _, a in ipairs(site.a) do for _, a in ipairs(site.a) do
if a[1] == "C" then if a[1] == "C" then
nginx.add_cookie(a[2], prof.format(a[3], auth)) nginx.add_cookie(a[2], auth:format(a[3]))
elseif a[1] == "H" then elseif a[1] == "H" then
nginx.add_header(a[2], prof.format(a[3], auth)) nginx.add_header(a[2], auth:format(a[3]))
end end
end end
return nginx.forward_request(req_data) return nginx.forward_request(req_data)
@ -50,7 +50,7 @@ local function handle_request(req_data, auth)
end end
end end
for _, r in ipairs(auth.ko) do for _, r in ipairs(auth.ko) do
if nginx.matches(req_data, r) then if req_data:matches(r) then
return nginx.redirect_to_login(req_data, 403) return nginx.redirect_to_login(req_data, 403)
end end
end end
@ -83,12 +83,31 @@ local function format_pattern(pattern)
return ok return ok
end end
local function with_sites(profile, user) local class__profile = {}
setmetatable(class__profile, {__index = id.class__identity})
function class__profile:build(delegate_identity, ok_list, ko_list)
local profile = {
delegate = delegate_identity,
ok = ok_list,
ko = ko_list,
}
setmetatable(profile, {__index = self})
return profile
end
function class__profile:build_from_lists(user, password, name, email, ok_list, ko_list)
local delegate_identity = id.class__identity:build(user, password, name, email)
return self:build(delegate_identity, ok_list, ko_list)
end
function class__profile:build_from_conf(user, password, name, email)
local f, site, go_on local f, site, go_on
local ok_list = {} local ok_list = {}
local ko_list = {} local ko_list = {}
for _, name in ipairs(known_sites) do local delegate_identity = id.class__identity:build(user, password, name, email)
f = io.open(name, "r") for _, known in ipairs(known_sites) do
f = io.open(known, "r")
if f then if f then
site = json.decode(f:read("*all")) site = json.decode(f:read("*all"))
f:close() f:close()
@ -120,14 +139,28 @@ local function with_sites(profile, user)
end end
end end
end end
profile["ok"] = ok_list return self:build(delegate_identity, ok_list, ko_list)
profile["ko"] = ko_list
return profile
end end
local function serialize(profile) function class__profile:email()
return self.delegate:email()
end
function class__profile:name()
return self.delegate:name()
end
function class__profile:user()
return self.delegate:user()
end
function class__profile:format(template)
return self.delegate:format(template)
end
function class__profile:serialize()
local ser_s = "" local ser_s = ""
for _, site in ipairs(profile.ok or {}) do for _, site in ipairs(self.ok or {}) do
for _, r in ipairs(site.r) do for _, r in ipairs(site.r) do
ser_s = ser_s .. r .. "\029" ser_s = ser_s .. r .. "\029"
end end
@ -136,40 +169,40 @@ local function serialize(profile)
end end
ser_s = ser_s .. "\031" ser_s = ser_s .. "\031"
end end
for _, r in ipairs(profile.ko or {}) do for _, r in ipairs(self.ko or {}) do
ser_s = ser_s .. r .. "\030" ser_s = ser_s .. r .. "\030"
end end
return ser_s return ser_s .. "\026" .. self.delegate:serialize()
end end
local function deserialize_update(ser, profile) function class__profile:deserialize(ser)
if not ser or ser == "" then
return profile
end
local ok_list = {} local ok_list = {}
local ko_list = {} local ko_list = {}
local remainder = ser:gsub("(.-)\031", function (ser_ok) ser = ser:gsub("^(.-)\026", function (ser_sites)
local ok = { ser_sites = ser_sites:gsub("(.-)\031", function (ser_ok)
r = {}, local ok = {
a = {}, r = {},
} a = {},
ser_ok = ser_ok:gsub("(.-)\029", function(r) table.insert(ok.r, r); return "" end) }
ser_ok:gsub("(.)([^=]-)=(.-)\028", function(t, n, v) table.insert(ok.a, {t, n, v}) end) ser_ok = ser_ok:gsub("(.-)\029", function(r) table.insert(ok.r, r); return "" end)
table.insert(ok_list, ok) ser_ok:gsub("(.)([^=]-)=(.-)\028", function(t, n, v) table.insert(ok.a, {t, n, v}) end)
table.insert(ok_list, ok)
return ""
end)
ser_sites = ser_sites:gsub("(.-)\030", function (ko)
table.insert(ko_list, ko)
return ""
end)
return "" return ""
end) end)
remainder = remainder:gsub("(.-)\030", function (ko) local delegate_identity = id.class__identity:deserialize(ser)
table.insert(ko_list, ko) return self:build(delegate_identity, ok_list, ko_list)
return ""
end)
profile.ok = ok_list
profile.ko = ko_list
return profile, remainder
end end
local function authorized_links(user) function class__profile:authorized_links()
local links = {} local links = {}
local f, site, go_on local f, site, go_on
local user = self:user()
for _, name in ipairs(known_sites) do for _, name in ipairs(known_sites) do
f = io.open(name, "r") f = io.open(name, "r")
if f then if f then
@ -206,10 +239,7 @@ local function authorized_links(user)
end end
return { return {
authorized_links = authorized_links, class__profile = class__profile,
deserialize_update = deserialize_update, -- TODO: test
handle_request = handle_request, handle_request = handle_request,
load_sites = load_sites, load_sites = load_sites,
serialize = serialize, -- TODO: test
with_sites = with_sites,
} }

View File

@ -1,14 +1,13 @@
local lu = require("luaunit") local lu = require("luaunit")
local conf = require("ssso_config") local conf = require("ssso_config")
local crypt = require("ssso_crypto") local crypt = require("ssso_crypto")
local sites = require("ssso_sites")
local here = debug.getinfo(1).source:sub(2, -18) local here = debug.getinfo(1).source:sub(2, -18)
conf.load_conf(here) conf.load_conf(here)
local data = { local data = sites.class__profile:build_from_lists("u", nil, nil, "u@h",
u = "u", {
e = "u@h",
ok = {
{ {
r = { r = {
"regex1", "regex1",
@ -19,10 +18,10 @@ local data = {
} }
}, },
}, },
ko = { {
"regex2", "regex2",
}, }
} )
function test_jws_is_well_structured() function test_jws_is_well_structured()
local jws, _ = crypt.get_jws_and_tslimit(data) local jws, _ = crypt.get_jws_and_tslimit(data)
@ -31,17 +30,12 @@ end
function test_jws_can_be_decoded() function test_jws_can_be_decoded()
local jws, _ = crypt.get_jws_and_tslimit(data) local jws, _ = crypt.get_jws_and_tslimit(data)
local stored, _, _ = crypt.get_data_and_new_jws(jws) local stored, _, _ = crypt.get_profile_and_new_jws(jws)
lu.assertEquals(stored, data) lu.assertEquals(stored, data)
end end
function test_data_must_contain_field_u() function test_data_must_be_a_profile_with_a_user()
local wrong = { local wrong = sites.class__profile:build_from_conf(nil, "P", "N", "E")
i = 1,
f = 2.3,
b = true,
n = nil,
}
local jws, ts = crypt.get_jws_and_tslimit(wrong) local jws, ts = crypt.get_jws_and_tslimit(wrong)
lu.assertNil(jws) lu.assertNil(jws)
lu.assertNil(ts) lu.assertNil(ts)

61
test/identity.utest.lua Normal file
View File

@ -0,0 +1,61 @@
local lu = require("luaunit")
local id = require("ssso_identity")
function test_format_replaces_user_placeholders()
local identity = id.class__identity:build("U", nil, nil, nil)
local template = '{user: "\ru.", foo: "bar", name: "\ru."}'
lu.assertEquals(identity:format(template), '{user: "U", foo: "bar", name: "U"}')
end
function test_format_replaces_password_placeholders()
local identity = id.class__identity:build(nil, "P", nil, nil)
local template = '{pass: "\rp.", foo: "bar", secret: "\rp."}'
lu.assertEquals(identity:format(template), '{pass: "P", foo: "bar", secret: "P"}')
end
function test_format_replaces_name_placeholders()
local identity = id.class__identity:build(nil, nil, "N", nil)
local template = '{name: "\rn.", foo: "bar", nickname: "\rn."}'
lu.assertEquals(identity:format(template), '{name: "N", foo: "bar", nickname: "N"}')
end
function test_format_replaces_email_placeholders()
local identity = id.class__identity:build(nil, nil, nil, "user@host")
local template = '{user: "\re.", foo: "bar", mail: "\re."}'
lu.assertEquals(identity:format(template), '{user: "user@host", foo: "bar", mail: "user@host"}')
end
function test_format_replaces_base64_calls()
local identity = id.class__identity:build("👤", "🔒", nil, nil)
local template = 'Authorization: Basic \rb64(\ru.:\rp.).'
lu.assertEquals(identity:format(template), 'Authorization: Basic 8J+RpDrwn5SS')
end
function test_format_replaces_base64url_calls()
local identity = id.class__identity:build("👤", "🔒", nil, nil)
local template = '?authorization=Basic+\ru64(\ru.:\rp.).'
lu.assertEquals(identity:format(template), '?authorization=Basic+8J-RpDrwn5SS')
end
function test_email_returns_the_identity_s_email()
local identity = id.class__identity:build(nil, nil, nil, "E")
lu.assertEquals(identity:email(), "E")
end
function test_name_returns_the_identity_s_name()
local identity = id.class__identity:build(nil, nil, "N", nil)
lu.assertEquals(identity:name(), "N")
end
function test_user_returns_the_identity_s_user()
local identity = id.class__identity:build("U", nil, nil, nil)
lu.assertEquals(identity:user(), "U")
end
function test_build_identity_returns_the_given_information()
lu.assertEquals(id.class__identity:build("U", "P", "N", "E"), {u = "U", p = "P", n = "N", e = "E"})
end
os.exit(lu.LuaUnit.run())

View File

@ -15,7 +15,7 @@ function test_get_login_url_returns_html_with_back_url_substitution()
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "GET" ngx.var.request_method = "GET"
ngx.var.request_uri = "/ssso/login?back=/somewhere" ngx.var.request_uri = "/ssso/login?back=/somewhere"
local r = ng.get_request() local r = ng.class__request:current()
local expected = [[<html><head> local expected = [[<html><head>
<link href="login.css"> <link href="login.css">
<script src="login.js"></script> <script src="login.js"></script>
@ -40,7 +40,7 @@ function test_login_css_url_returns_css()
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "BLABLA" ngx.var.request_method = "BLABLA"
ngx.var.request_uri = "/ssso/login.css" ngx.var.request_uri = "/ssso/login.css"
local r = ng.get_request() local r = ng.class__request:current()
local expected = "/*CSS*/\n" local expected = "/*CSS*/\n"
-- when -- when
local resp = login.answer_request(r) local resp = login.answer_request(r)
@ -58,7 +58,7 @@ function test_login_js_url_returns_js()
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "BLABLA" ngx.var.request_method = "BLABLA"
ngx.var.request_uri = "/ssso/login.js" ngx.var.request_uri = "/ssso/login.js"
local r = ng.get_request() local r = ng.class__request:current()
local expected = "//JS\n" local expected = "//JS\n"
-- when -- when
local resp = login.answer_request(r) local resp = login.answer_request(r)
@ -75,7 +75,7 @@ function test_unknown_login_url_returns_404()
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "BLABLA" ngx.var.request_method = "BLABLA"
ngx.var.request_uri = "/ssso/login/unknown" ngx.var.request_uri = "/ssso/login/unknown"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = login.answer_request(r) local resp = login.answer_request(r)
-- then -- then
@ -90,7 +90,7 @@ function test_get_login_url_with_cause_401_returns_html_with_associated_message(
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "GET" ngx.var.request_method = "GET"
ngx.var.request_uri = "/ssso/login?cause=401" ngx.var.request_uri = "/ssso/login?cause=401"
local r = ng.get_request() local r = ng.class__request:current()
local expected = [[<html><head> local expected = [[<html><head>
<link href="login.css"> <link href="login.css">
<script src="login.js"></script> <script src="login.js"></script>
@ -115,7 +115,7 @@ function test_get_login_url_with_cause_403_returns_html_with_associated_message(
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.var.request_method = "GET" ngx.var.request_method = "GET"
ngx.var.request_uri = "/ssso/login?cause=403" ngx.var.request_uri = "/ssso/login?cause=403"
local r = ng.get_request() local r = ng.class__request:current()
local expected = [[<html><head> local expected = [[<html><head>
<link href="login.css"> <link href="login.css">
<script src="login.js"></script> <script src="login.js"></script>
@ -141,7 +141,7 @@ function test_post_login_url_with_wrong_credentials_returns_html_with_associated
ngx.reset_post_var() ngx.reset_post_var()
ngx.var.request_method = "POST" ngx.var.request_method = "POST"
ngx.var.request_uri = "/ssso/login" ngx.var.request_uri = "/ssso/login"
local r = ng.get_request() local r = ng.class__request:current()
local expected = [[<html><head> local expected = [[<html><head>
<link href="login.css"> <link href="login.css">
<script src="login.js"></script> <script src="login.js"></script>
@ -169,7 +169,7 @@ function test_post_login_url_with_good_credentials_redirects_to_portal_with_sess
ngx.var.request_uri = "/ssso/login" ngx.var.request_uri = "/ssso/login"
ngx.post_var.login = "goodlogin" ngx.post_var.login = "goodlogin"
ngx.post_var.password = "goodpassword" ngx.post_var.password = "goodpassword"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = login.answer_request(r) local resp = login.answer_request(r)
-- then -- then
@ -190,7 +190,7 @@ function test_post_login_url_with_good_credentials_and_back_url_redirects_to_giv
ngx.post_var.login = "goodlogin" ngx.post_var.login = "goodlogin"
ngx.post_var.password = "goodpassword" ngx.post_var.password = "goodpassword"
ngx.post_var.back = "/somewhere" ngx.post_var.back = "/somewhere"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = login.answer_request(r) local resp = login.answer_request(r)
-- then -- then

View File

@ -15,7 +15,7 @@ function test_refe_host_meth_uri_taken_from_ngx()
ngx.var.request_method = "M" ngx.var.request_method = "M"
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.referer, "R") lu.assertEquals(r.referer, "R")
lu.assertEquals(r.host, "H") lu.assertEquals(r.host, "H")
@ -31,7 +31,7 @@ function test_empty_referer_reported_as_nil()
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
ngx.var.http_referer = "" ngx.var.http_referer = ""
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.referer, nil) lu.assertEquals(r.referer, nil)
end end
@ -41,7 +41,7 @@ function test_query_params_split_from_uri_and_decoded()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "U?P=V&Q=W" ngx.var.request_uri = "U?P=V&Q=W"
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.uri, "U?P=V&Q=W") lu.assertEquals(r.uri, "U?P=V&Q=W")
lu.assertEquals(r.target, "U") lu.assertEquals(r.target, "U")
@ -53,7 +53,7 @@ function test_default_scheme_is_http()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.scheme, "http") lu.assertEquals(r.scheme, "http")
end end
@ -64,7 +64,7 @@ function test_scheme_is_https_when_proxy_https_var()
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
ngx.var.proxy_https = 1 ngx.var.proxy_https = 1
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.scheme, "https") lu.assertEquals(r.scheme, "https")
end end
@ -75,7 +75,7 @@ function test_scheme_is_https_when_https_var()
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
ngx.var.https = 1 ngx.var.https = 1
-- when -- when
local r = ng.get_request() local r = ng.class__request:current()
-- then -- then
lu.assertEquals(r.scheme, "https") lu.assertEquals(r.scheme, "https")
end end
@ -142,10 +142,10 @@ function test_method_is_recognized_case_insensitive()
ngx.reset_var() ngx.reset_var()
ngx.var.request_method = "get" ngx.var.request_method = "get"
ngx.var.request_uri = "U" ngx.var.request_uri = "U"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local is_get = ng.has_method(r, "GET") local is_get = r:has_method("GET")
local is_post = ng.has_method(r, "POST") local is_post = r:has_method("POST")
-- then -- then
lu.assertTrue(is_get and true) lu.assertTrue(is_get and true)
lu.assertFalse(is_post or false) lu.assertFalse(is_post or false)
@ -155,10 +155,10 @@ function test_uri_identity_ignores_query_parameters()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "U?P=V&Q=W" ngx.var.request_uri = "U?P=V&Q=W"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local is_without_qp = ng.is(r, "U") local is_without_qp = r:is("U")
local is_with_qp = ng.is(r, "U?P=V&Q=W") local is_with_qp = r:is("U?P=V&Q=W")
-- then -- then
lu.assertTrue(is_without_qp and true) lu.assertTrue(is_without_qp and true)
lu.assertFalse(is_with_qp or false) lu.assertFalse(is_with_qp or false)
@ -168,10 +168,10 @@ function test_uri_match_ignores_query_parameters()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb" ngx.var.request_uri = "/aa?bb"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local matches_without_qp = ng.matches(r, "/a+$") local matches_without_qp = r:matches("/a+$")
local matches_with_qp = ng.matches(r, "/a.*b") local matches_with_qp = r:matches("/a.*b")
-- then -- then
lu.assertTrue(matches_without_qp and true) lu.assertTrue(matches_without_qp and true)
lu.assertFalse(matches_with_qp or false) lu.assertFalse(matches_with_qp or false)
@ -181,10 +181,10 @@ function test_starts_with_ignores_query_parameters()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb" ngx.var.request_uri = "/aa?bb"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local start_without_qp = ng.starts_with(r, "/a") local start_without_qp = r:starts_with("/a")
local start_with_qp = ng.starts_with(r, "/aa?b") local start_with_qp = r:starts_with("/aa?b")
-- then -- then
lu.assertTrue(start_without_qp and true) lu.assertTrue(start_without_qp and true)
lu.assertFalse(start_with_qp or false) lu.assertFalse(start_with_qp or false)
@ -194,10 +194,10 @@ function test_starts_with_must_start_with_given_value()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb" ngx.var.request_uri = "/aa?bb"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local start_in_middle = ng.starts_with(r, "aa") local start_in_middle = r:starts_with("aa")
local does_not_start = ng.starts_with(r, "x") local does_not_start = r:starts_with("x")
-- then -- then
lu.assertFalse(start_in_middle or false) lu.assertFalse(start_in_middle or false)
lu.assertFalse(does_not_start or false) lu.assertFalse(does_not_start or false)
@ -207,11 +207,11 @@ function test_has_param_works_disregarding_the_value()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb&c=1" ngx.var.request_uri = "/aa?bb&c=1"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local has_unknown_param = ng.has_param(r, "b") local has_unknown_param = r:has_param("b")
local has_unvalued_param = ng.has_param(r, "bb") local has_unvalued_param = r:has_param("bb")
local has_valued_param = ng.has_param(r, "c") local has_valued_param = r:has_param("c")
-- then -- then
lu.assertFalse(has_unknown_param or false) lu.assertFalse(has_unknown_param or false)
lu.assertTrue(has_unvalued_param and true) lu.assertTrue(has_unvalued_param and true)
@ -222,10 +222,10 @@ function test_has_param_works_with_a_correct_value()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb&c=1" ngx.var.request_uri = "/aa?bb&c=1"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local has_unvalued_param = ng.has_param(r, "bb", true) local has_unvalued_param = r:has_param("bb", true)
local has_valued_param = ng.has_param(r, "c", "1") local has_valued_param = r:has_param("c", "1")
-- then -- then
lu.assertTrue(has_unvalued_param and true) lu.assertTrue(has_unvalued_param and true)
lu.assertTrue(has_valued_param and true) lu.assertTrue(has_valued_param and true)
@ -235,11 +235,11 @@ function test_has_param_works_with_a_wrong_value()
-- given -- given
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/aa?bb&c=1" ngx.var.request_uri = "/aa?bb&c=1"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local has_unknown_param = ng.has_param(r, "b", "x") local has_unknown_param = r:has_param("b", "x")
local has_unvalued_param = ng.has_param(r, "bb", "x") local has_unvalued_param = r:has_param("bb", "x")
local has_valued_param = ng.has_param(r, "c", "x") local has_valued_param = r:has_param("c", "x")
-- then -- then
lu.assertFalse(has_unknown_param or false) lu.assertFalse(has_unknown_param or false)
lu.assertFalse(has_unvalued_param or false) lu.assertFalse(has_unvalued_param or false)
@ -284,7 +284,7 @@ function test_with_post_parameters_merges_post_parameters_to_request_data()
ngx.post_var.p = "5" ngx.post_var.p = "5"
ngx.post_var.r = "hello" ngx.post_var.r = "hello"
-- when (1) -- when (1)
local r = ng.get_request() local r = ng.class__request:current()
-- then (1) -- then (1)
lu.assertEquals(r, { lu.assertEquals(r, {
scheme = "http", scheme = "http",
@ -296,7 +296,7 @@ function test_with_post_parameters_merges_post_parameters_to_request_data()
}, },
}) })
-- when (2) -- when (2)
r = ng.with_post_parameters(r) r = r:with_post_parameters()
-- then (2) -- then (2)
lu.assertEquals(r, { lu.assertEquals(r, {
scheme = "http", scheme = "http",

View File

@ -1,17 +1,14 @@
local lu = require("luaunit") local lu = require("luaunit")
local ngx = require("ngx") local ngx = require("ngx")
local crypto = require("ssso_crypto") local crypto = require("ssso_crypto")
local sites = require("ssso_sites")
require("do_init") require("do_init")
function test_portal_url_returns_html_with_authorized_links_and_identity() function test_portal_url_returns_html_with_authorized_links_and_identity()
-- given -- given
local jws, _ = crypto.get_jws_and_tslimit({ local profile = sites.class__profile:build_from_lists("guest", "", "Guest", "guest@example.org")
u = "guest", local jws, _ = crypto.get_jws_and_tslimit(profile)
p = "",
n = "Guest",
e = "guest@example.org",
})
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.reset_var() ngx.reset_var()
ngx.var.cookie_SSSO_TOKEN = jws ngx.var.cookie_SSSO_TOKEN = jws

View File

@ -1,12 +1,14 @@
local lu = require("luaunit") local lu = require("luaunit")
local ngx = require("ngx") local ngx = require("ngx")
local crypto = require("ssso_crypto") local crypto = require("ssso_crypto")
local sites = require("ssso_sites")
require("do_init") require("do_init")
function test_portal_css_url_returns_css() function test_portal_css_url_returns_css()
-- given -- given
local jws, _ = crypto.get_jws_and_tslimit({u = "U", p = "P", n = "N", e = "u@h"}) local profile = sites.class__profile:build_from_lists("U", "P", "N", "u@h")
local jws, _ = crypto.get_jws_and_tslimit(profile)
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.reset_var() ngx.reset_var()
ngx.var.cookie_SSSO_TOKEN = jws ngx.var.cookie_SSSO_TOKEN = jws

View File

@ -1,12 +1,14 @@
local lu = require("luaunit") local lu = require("luaunit")
local ngx = require("ngx") local ngx = require("ngx")
local crypto = require("ssso_crypto") local crypto = require("ssso_crypto")
local sites = require("ssso_sites")
require("do_init") require("do_init")
function test_portal_js_url_returns_js() function test_portal_js_url_returns_js()
-- given -- given
local jws, _ = crypto.get_jws_and_tslimit({u = "U", p = "P", n = "N", e = "u@h"}) local profile = sites.class__profile:build_from_lists("U", "P", "N", "u@h")
local jws, _ = crypto.get_jws_and_tslimit(profile)
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.reset_var() ngx.reset_var()
ngx.var.cookie_SSSO_TOKEN = jws ngx.var.cookie_SSSO_TOKEN = jws

View File

@ -1,12 +1,14 @@
local lu = require("luaunit") local lu = require("luaunit")
local ngx = require("ngx") local ngx = require("ngx")
local crypto = require("ssso_crypto") local crypto = require("ssso_crypto")
local sites = require("ssso_sites")
require("do_init") require("do_init")
function test_unknown_portal_url_returns_404() function test_unknown_portal_url_returns_404()
-- given -- given
local jws, _ = crypto.get_jws_and_tslimit({u = "U", p = "P", n = "N", e = "u@h"}) local profile = sites.class__profile:build_from_lists("U", "P", "N", "u@h")
local jws, _ = crypto.get_jws_and_tslimit(profile)
ngx.reset_resp_body() ngx.reset_resp_body()
ngx.reset_var() ngx.reset_var()
ngx.var.cookie_SSSO_TOKEN = jws ngx.var.cookie_SSSO_TOKEN = jws

View File

@ -1,6 +1,5 @@
local lu = require("luaunit") local lu = require("luaunit")
local ngx = require("ngx") local ngx = require("ngx")
local crypto = require("ssso_crypto")
require("do_init") require("do_init")

View File

@ -1,81 +0,0 @@
local lu = require("luaunit")
local prf = require("ssso_profile")
function test_format_replaces_user_placeholders()
local profile = {
u = "U",
}
local template = '{user: "\ru.", foo: "bar", name: "\ru."}'
lu.assertEquals(prf.format(template, profile), '{user: "U", foo: "bar", name: "U"}')
end
function test_format_replaces_password_placeholders()
local profile = {
p = "P",
}
local template = '{pass: "\rp.", foo: "bar", secret: "\rp."}'
lu.assertEquals(prf.format(template, profile), '{pass: "P", foo: "bar", secret: "P"}')
end
function test_format_replaces_name_placeholders()
local profile = {
n = "N",
}
local template = '{name: "\rn.", foo: "bar", nickname: "\rn."}'
lu.assertEquals(prf.format(template, profile), '{name: "N", foo: "bar", nickname: "N"}')
end
function test_format_replaces_email_placeholders()
local profile = {
e = "user@host",
}
local template = '{user: "\re.", foo: "bar", mail: "\re."}'
lu.assertEquals(prf.format(template, profile), '{user: "user@host", foo: "bar", mail: "user@host"}')
end
function test_format_replaces_base64_calls()
local profile = {
u = "👤",
p = "🔒",
}
local template = 'Authorization: Basic \rb64(\ru.:\rp.).'
lu.assertEquals(prf.format(template, profile), 'Authorization: Basic 8J+RpDrwn5SS')
end
function test_format_replaces_base64url_calls()
local profile = {
u = "👤",
p = "🔒",
}
local template = '?authorization=Basic+\ru64(\ru.:\rp.).'
lu.assertEquals(prf.format(template, profile), '?authorization=Basic+8J-RpDrwn5SS')
end
function test_email_returns_the_profile_s_email()
local profile = {
e = "E",
}
lu.assertEquals(prf.email(profile), "E")
end
function test_name_returns_the_profile_s_name()
local profile = {
n = "N",
}
lu.assertEquals(prf.name(profile), "N")
end
function test_user_returns_the_profile_s_user()
local profile = {
u = "U",
}
lu.assertEquals(prf.user(profile), "U")
end
function test_build_profile_returns_the_given_information()
lu.assertEquals(prf.build_profile("U", "P", "N", "E"), {u = "U", p = "P", n = "N", e = "E"})
end
os.exit(lu.LuaUnit.run())

View File

@ -51,13 +51,13 @@ function test_session_and_cookie_renewal_if_good_cookie()
ngx.req.reset() ngx.req.reset()
ngx.reset_header() ngx.reset_header()
ngx.reset_var() ngx.reset_var()
local data = {u = "bob"} local profile = sites.class__profile:build_from_lists("bob", nil, nil, nil, {}, {})
local c, _ = crypt.get_jws_and_tslimit(data) local c, _ = crypt.get_jws_and_tslimit(profile)
ngx.var.cookie_SSSO_TOKEN = c ngx.var.cookie_SSSO_TOKEN = c
-- when -- when
local s, h = sess.get_session() local s, h = sess.get_session()
-- then -- then
lu.assertEquals(s, data) lu.assertEquals(s, profile)
lu.assertEquals(h, 200) lu.assertEquals(h, 200)
lu.assertNil(ngx.header["Set-Cookie"].link) lu.assertNil(ngx.header["Set-Cookie"].link)
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure") lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
@ -84,15 +84,15 @@ function test_basic_auth_takes_precedence_over_cookie()
ngx.req.reset() ngx.req.reset()
ngx.reset_header() ngx.reset_header()
ngx.reset_var() ngx.reset_var()
local data = {u = "forget me"} local profile = sites.class__profile:build_from_lists("forget me", nil, nil, nil, {}, {})
local c, _ = crypt.get_jws_and_tslimit(data) local c, _ = crypt.get_jws_and_tslimit(profile)
ngx.var.cookie_SSSO_TOKEN = c ngx.var.cookie_SSSO_TOKEN = c
ngx.var.Authentication = "Basic " .. b64.encode_base64("bob:goodpassword") ngx.var.Authentication = "Basic " .. b64.encode_base64("bob:goodpassword")
-- when -- when
local s, h = sess.get_session() local s, h = sess.get_session()
-- then -- then
lu.assertEquals(h, 200) lu.assertEquals(h, 200)
lu.assertEquals(s.u, "bob") lu.assertEquals(s:user(), "bob")
lu.assertNil(ngx.header["Set-Cookie"].link) lu.assertNil(ngx.header["Set-Cookie"].link)
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure") lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
end end
@ -102,15 +102,15 @@ function test_basic_auth_ignored_if_invalid()
ngx.req.reset() ngx.req.reset()
ngx.reset_header() ngx.reset_header()
ngx.reset_var() ngx.reset_var()
local data = {u = "do not forget me"} local profile = sites.class__profile:build_from_lists("do not forget me", nil, nil, nil, {}, {})
local c, _ = crypt.get_jws_and_tslimit(data) local c, _ = crypt.get_jws_and_tslimit(profile)
ngx.var.cookie_SSSO_TOKEN = c ngx.var.cookie_SSSO_TOKEN = c
ngx.var.Authentication = "Basic !!!!" ngx.var.Authentication = "Basic !!!!"
-- when -- when
local s, h = sess.get_session() local s, h = sess.get_session()
-- then -- then
lu.assertEquals(h, 200) lu.assertEquals(h, 200)
lu.assertEquals(s.u, "do not forget me") lu.assertEquals(s:user(), "do not forget me")
lu.assertNil(ngx.header["Set-Cookie"].link) lu.assertNil(ngx.header["Set-Cookie"].link)
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure") lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
end end

View File

@ -13,7 +13,7 @@ function test_anonymous_access_to_unknown_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/unknown" ngx.var.request_uri = "/unknown"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = sites.handle_request(r, nil) local resp = sites.handle_request(r, nil)
-- then -- then
@ -26,7 +26,7 @@ function test_anonymous_access_to_public_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/public/page" ngx.var.request_uri = "/public/page"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = sites.handle_request(r, nil) local resp = sites.handle_request(r, nil)
-- then -- then
@ -39,7 +39,7 @@ function test_anonymous_access_to_public_page_of_mixed_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/mixed/bob/wiki/foo.adoc" ngx.var.request_uri = "/mixed/bob/wiki/foo.adoc"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = sites.handle_request(r, nil) local resp = sites.handle_request(r, nil)
-- then -- then
@ -52,7 +52,7 @@ function test_anonymous_access_to_private_page_of_mixed_site_redirected_401()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/mixed/bob/wiki/_new" ngx.var.request_uri = "/mixed/bob/wiki/_new"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = sites.handle_request(r, nil) local resp = sites.handle_request(r, nil)
-- then -- then
@ -65,7 +65,7 @@ function test_anonymous_access_to_private_site_redirected_401()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/private/page" ngx.var.request_uri = "/private/page"
local r = ng.get_request() local r = ng.class__request:current()
-- when -- when
local resp = sites.handle_request(r, nil) local resp = sites.handle_request(r, nil)
-- then -- then
@ -78,12 +78,8 @@ function test_authenticated_access_to_unknown_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/unknown" ngx.var.request_uri = "/unknown"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("U", nil, nil, nil, {}, {})
u = "U",
ok = {},
ko = {},
}
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -96,11 +92,9 @@ function test_authenticated_access_to_public_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/public/page" ngx.var.request_uri = "/public/page"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("U", "P", nil, nil,
u = "U", {
p = "P",
ok = {
{ {
r = { r = {
"^/public", "^/public",
@ -110,8 +104,9 @@ function test_authenticated_access_to_public_site_accepted()
{"C", "X-PROXY-PASS", "\rp."}, {"C", "X-PROXY-PASS", "\rp."},
}, },
}, },
} },
} {}
)
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -125,14 +120,13 @@ function test_authenticated_access_to_public_site_can_be_denied()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/public/page" ngx.var.request_uri = "/public/page"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("banned", nil, nil, nil,
u = "banned", {},
ok = {}, {
ko = {
"^/public", "^/public",
} }
} )
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -145,11 +139,9 @@ function test_authenticated_access_to_public_page_of_mixed_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/mixed/bob/wiki/foo.adoc" ngx.var.request_uri = "/mixed/bob/wiki/foo.adoc"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("U", "P", nil, nil,
u = "U", {
p = "P",
ok = {
{ {
r = { r = {
"^/public", "^/public",
@ -179,8 +171,9 @@ function test_authenticated_access_to_public_page_of_mixed_site_accepted()
{"C", "X-PROXY-PASSWORD", "\rp."}, {"C", "X-PROXY-PASSWORD", "\rp."},
}, },
}, },
} },
} {}
)
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -194,11 +187,9 @@ function test_authenticated_access_to_private_page_of_mixed_site_accepted()
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/mixed/bob/wiki/_new" ngx.var.request_uri = "/mixed/bob/wiki/_new"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("U", "P", nil, nil,
u = "U", {
p = "P",
ok = {
{ {
r = { r = {
"^/public", "^/public",
@ -228,8 +219,9 @@ function test_authenticated_access_to_private_page_of_mixed_site_accepted()
{"C", "X-PROXY-PASSWORD", "\rp."}, {"C", "X-PROXY-PASSWORD", "\rp."},
}, },
}, },
} },
} {}
)
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -243,11 +235,9 @@ function test_authenticated_access_to_private_site_accepted_with_the_right_user(
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/private/page" ngx.var.request_uri = "/private/page"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("jean", "P", nil, nil,
u = "jean", {
p = "P",
ok = {
{ {
r = { r = {
"^/private", "^/private",
@ -256,8 +246,9 @@ function test_authenticated_access_to_private_site_accepted_with_the_right_user(
{"H", "Authorization", "Basic \rb64(\ru.:\rp.)."}, {"H", "Authorization", "Basic \rb64(\ru.:\rp.)."},
}, },
}, },
} },
} {}
)
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then
@ -270,15 +261,13 @@ function test_authenticated_access_to_private_site_redirected_403_with_the_wrong
ngx.req.reset() ngx.req.reset()
ngx.reset_var() ngx.reset_var()
ngx.var.request_uri = "/private/page" ngx.var.request_uri = "/private/page"
local r = ng.get_request() local r = ng.class__request:current()
local profile = { local profile = sites.class__profile:build_from_lists("U", "P", nil, nil,
u = "U", {},
p = "P", {
ok = {},
ko = {
"^/private", "^/private",
} }
} )
-- when -- when
local resp = sites.handle_request(r, profile) local resp = sites.handle_request(r, profile)
-- then -- then