HTTP basic auth
parent
5fde1663f5
commit
5f44ced065
|
@ -6,7 +6,7 @@
|
|||
<h1>Single Sign-On for <code>example.org</code></h1>
|
||||
<!--MESSAGES-->
|
||||
<form method="POST" action="login">
|
||||
<input type="hidden" value="BACK_URL">
|
||||
<input type="hidden" name="back" value="BACK_URL">
|
||||
<label>User-name <input type="text" name="login"></label>
|
||||
<label>Password <input type="password" name="password"></label>
|
||||
<button type="submit">Log in</button>
|
||||
|
|
|
@ -10,9 +10,12 @@ end
|
|||
|
||||
if not b64["encode_base64"] then
|
||||
b64.encode_base64 = function(plaintext)
|
||||
local plain = b64.encode_base64url(plaintext)
|
||||
local plain, err = b64.encode_base64url(plaintext)
|
||||
if not plain then
|
||||
return nil, err
|
||||
end
|
||||
plain = plain:gsub("_", "/")
|
||||
return plain:gsub("%-", "+")
|
||||
return plain:gsub("%-", "+"), nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -47,15 +47,23 @@ local function inject_data(html, req_data, warnings)
|
|||
return html
|
||||
end
|
||||
|
||||
local function check_login(req_data)
|
||||
req_data = nginx.with_post_parameters(req_data)
|
||||
local user = req_data.query_params["login"] or ""
|
||||
local password = req_data.query_params["password"] or ""
|
||||
local function check_credentials_and_get_profile(user, password)
|
||||
local user_data = auth.read_user(user, password)
|
||||
if user_data then
|
||||
log.debug("Credentials accepted for user " .. user_data.name)
|
||||
local profile = prof.build_profile(user, password, user_data.name, user_data.email)
|
||||
profile = sites.with_sites(profile, user)
|
||||
return sites.with_sites(profile, user)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function check_login(req_data)
|
||||
req_data = nginx.with_post_parameters(req_data)
|
||||
local user = req_data.query_params["login"] or ""
|
||||
local password = req_data.query_params["password"] or ""
|
||||
local profile = check_credentials_and_get_profile(user, password)
|
||||
if profile then
|
||||
log.debug("Building JWS")
|
||||
local jws, tslimit = crypto.get_jws_and_tslimit(profile)
|
||||
if not jws then
|
||||
|
@ -100,5 +108,6 @@ end
|
|||
|
||||
return {
|
||||
answer_request = answer_request,
|
||||
check_credentials_and_get_profile = check_credentials_and_get_profile,
|
||||
set_root = set_root,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local ngx = require("ngx")
|
||||
local b64 = require("ssso_base64")
|
||||
local util = require("ssso_util")
|
||||
local conf = require("ssso_config")
|
||||
|
||||
|
@ -43,6 +44,36 @@ local function with_post_parameters(req_data)
|
|||
return req_data
|
||||
end
|
||||
|
||||
local function str_starts_with(str, begin)
|
||||
local i, _ = str:find(util.str_to_pattern(begin))
|
||||
return 1 == i
|
||||
end
|
||||
|
||||
local function get_basic_auth()
|
||||
local header = ngx.var.Authentication
|
||||
if (not header) or (#header < 7) or (not str_starts_with(header, "Basic ")) then
|
||||
return nil, nil
|
||||
end
|
||||
local text, _ = b64.decode_base64(header:sub(7))
|
||||
if not text then
|
||||
ngx.log(ngx.DEBUG, "Invalid Authentication header: " .. header)
|
||||
return nil, nil
|
||||
end
|
||||
local colon
|
||||
colon, _ = text:find(":")
|
||||
if not colon then
|
||||
return nil, nil
|
||||
end
|
||||
local login, password = "", ""
|
||||
if colon > 1 then
|
||||
login = text:sub(1, colon - 1)
|
||||
end
|
||||
if colon < #text then
|
||||
password = text:sub(colon + 1, #text)
|
||||
end
|
||||
return login, password
|
||||
end
|
||||
|
||||
local function get_jws_cookie()
|
||||
return ngx.var.cookie_SSSO_TOKEN
|
||||
end
|
||||
|
@ -85,8 +116,7 @@ local function matches(req_data, lua_pattern)
|
|||
end
|
||||
|
||||
local function starts_with(req_data, prefix)
|
||||
local i, _ = req_data.target:find(util.str_to_pattern(prefix))
|
||||
return 1 == i
|
||||
return str_starts_with(req_data.target, prefix)
|
||||
end
|
||||
|
||||
local function has_param(req_data, param, value)
|
||||
|
@ -138,6 +168,7 @@ return {
|
|||
add_header = add_header,
|
||||
answer_not_found = answer_not_found,
|
||||
forward_request = forward_request,
|
||||
get_basic_auth = get_basic_auth,
|
||||
get_jws_cookie = get_jws_cookie,
|
||||
get_request = get_request,
|
||||
get_seconds_since_epoch = get_seconds_since_epoch,
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
local crypto = require("ssso_crypto")
|
||||
local login = require("ssso_login")
|
||||
local nginx = require("ssso_nginx")
|
||||
|
||||
local function get_session()
|
||||
local cookie = nginx.get_jws_cookie()
|
||||
local session, jws, tslimit
|
||||
local user, password = nginx.get_basic_auth()
|
||||
|
||||
if user and password then
|
||||
session = login.check_credentials_and_get_profile(user, password)
|
||||
if session then
|
||||
jws, tslimit = crypto.get_jws_and_tslimit(session)
|
||||
end
|
||||
end
|
||||
|
||||
if not session then
|
||||
local cookie = nginx.get_jws_cookie()
|
||||
if not cookie or cookie == "" then
|
||||
return nil, 401
|
||||
end
|
||||
|
||||
local session, jws, tslimit = crypto.get_data_and_new_jws(cookie)
|
||||
session, jws, tslimit = crypto.get_data_and_new_jws(cookie)
|
||||
end
|
||||
|
||||
if session then
|
||||
nginx.set_jws_cookie(jws, tslimit)
|
||||
|
|
|
@ -310,4 +310,80 @@ function test_with_post_parameters_merges_post_parameters_to_request_data()
|
|||
})
|
||||
end
|
||||
|
||||
function test_get_basic_auth_with_no_header_returns_nil()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertNil(u)
|
||||
lu.assertNil(p)
|
||||
end
|
||||
|
||||
function test_get_basic_auth_with_non_basic_header_returns_nil()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Bearer uuid"
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertNil(u)
|
||||
lu.assertNil(p)
|
||||
end
|
||||
|
||||
function test_get_basic_auth_with_basic_header_but_no_base64_returns_nil()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Basic "
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertNil(u)
|
||||
lu.assertNil(p)
|
||||
end
|
||||
|
||||
function test_get_basic_auth_with_basic_header_but_invalid_base64_returns_nil()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Basic !!!!"
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertNil(u)
|
||||
lu.assertNil(p)
|
||||
end
|
||||
|
||||
function test_get_basic_auth_with_valid_basic_header_returns_auth()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Basic dTpw"
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertEquals(u, "u")
|
||||
lu.assertEquals(p, "p")
|
||||
end
|
||||
|
||||
function test_get_basic_auth_works_with_an_empty_login()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Basic OnA="
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertEquals(u, "")
|
||||
lu.assertEquals(p, "p")
|
||||
end
|
||||
|
||||
function test_get_basic_auth_works_with_an_empty_password()
|
||||
-- given
|
||||
ngx.reset_header()
|
||||
ngx.var.Authentication = "Basic dTo="
|
||||
-- when
|
||||
local u, p = ng.get_basic_auth()
|
||||
-- then
|
||||
lu.assertEquals(u, "u")
|
||||
lu.assertEquals(p, "")
|
||||
end
|
||||
|
||||
os.exit(lu.LuaUnit.run())
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
local lu = require("luaunit")
|
||||
local sess = require("ssso_sessions")
|
||||
local ngx = require("ngx")
|
||||
local b64 = require("ssso_base64")
|
||||
local conf = require("ssso_config")
|
||||
local crypt = require("ssso_crypto")
|
||||
local ngx = require("ngx")
|
||||
local login = require("ssso_login")
|
||||
local sess = require("ssso_sessions")
|
||||
local sites = require("ssso_sites")
|
||||
|
||||
local here = debug.getinfo(1).source:sub(2, -20)
|
||||
conf.load_conf(here)
|
||||
sites.load_sites(here)
|
||||
|
||||
function test_no_session_and_hint_401_if_no_cookie()
|
||||
-- given
|
||||
|
@ -45,6 +49,7 @@ end
|
|||
function test_session_and_cookie_renewal_if_good_cookie()
|
||||
-- given
|
||||
ngx.req.reset()
|
||||
ngx.reset_header()
|
||||
ngx.reset_var()
|
||||
local data = {u = "bob"}
|
||||
local c, _ = crypt.get_jws_and_tslimit(data)
|
||||
|
@ -58,4 +63,56 @@ function test_session_and_cookie_renewal_if_good_cookie()
|
|||
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
|
||||
end
|
||||
|
||||
function test_good_basic_auth_credentials_generate_a_session_and_a_cookie()
|
||||
-- given
|
||||
ngx.req.reset()
|
||||
ngx.reset_header()
|
||||
ngx.reset_var()
|
||||
ngx.var.Authentication = "Basic " .. b64.encode_base64("bob:goodpassword")
|
||||
local expected = login.check_credentials_and_get_profile("bob", "goodpassword")
|
||||
-- when
|
||||
local s, h = sess.get_session()
|
||||
-- then
|
||||
lu.assertEquals(h, 200)
|
||||
lu.assertEquals(s, expected)
|
||||
lu.assertNil(ngx.header["Set-Cookie"].link)
|
||||
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
|
||||
end
|
||||
|
||||
function test_basic_auth_takes_precedence_over_cookie()
|
||||
-- given
|
||||
ngx.req.reset()
|
||||
ngx.reset_header()
|
||||
ngx.reset_var()
|
||||
local data = {u = "forget me"}
|
||||
local c, _ = crypt.get_jws_and_tslimit(data)
|
||||
ngx.var.cookie_SSSO_TOKEN = c
|
||||
ngx.var.Authentication = "Basic " .. b64.encode_base64("bob:goodpassword")
|
||||
-- when
|
||||
local s, h = sess.get_session()
|
||||
-- then
|
||||
lu.assertEquals(h, 200)
|
||||
lu.assertEquals(s.u, "bob")
|
||||
lu.assertNil(ngx.header["Set-Cookie"].link)
|
||||
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
|
||||
end
|
||||
|
||||
function test_basic_auth_ignored_if_invalid()
|
||||
-- given
|
||||
ngx.req.reset()
|
||||
ngx.reset_header()
|
||||
ngx.reset_var()
|
||||
local data = {u = "do not forget me"}
|
||||
local c, _ = crypt.get_jws_and_tslimit(data)
|
||||
ngx.var.cookie_SSSO_TOKEN = c
|
||||
ngx.var.Authentication = "Basic !!!!"
|
||||
-- when
|
||||
local s, h = sess.get_session()
|
||||
-- then
|
||||
lu.assertEquals(h, 200)
|
||||
lu.assertEquals(s.u, "do not forget me")
|
||||
lu.assertNil(ngx.header["Set-Cookie"].link)
|
||||
lu.assertStrMatches(ngx.header["Set-Cookie"].v, "SSSO_TOKEN=[^%.]+%.[^%.]+%.[^%.]+; Path=/; Expires=1626550390; Secure")
|
||||
end
|
||||
|
||||
os.exit(lu.LuaUnit.run())
|
||||
|
|
Loading…
Reference in New Issue