diff options
context:
space:
mode:
Diffstat (limited to 'custom/extensions/auth-inline.lua')
-rw-r--r--custom/extensions/auth-inline.lua66
1 file changed, 25 insertions, 41 deletions
diff --git a/custom/extensions/auth-inline.lua b/custom/extensions/auth-inline.lua
index aa4b9f1..5489189 100644
--- a/custom/extensions/auth-inline.lua
+++ b/custom/extensions/auth-inline.lua
@@ -11,11 +11,9 @@
-- be the same Lua that cgit was built against. Lua 5.5 will not do, because
-- luaossl has no 5.5 build.
--
--- Serve cgit over HTTPS and terminate TLS in the web server in front of it.
--- The session cookie is marked Secure by default, so a browser only sends it
--- back over HTTPS, and on an instance served over plain HTTP with no TLS
--- anywhere the cookie never comes back and login appears to loop until
--- cookie_insecure below is set.
+-- The session cookie is marked Secure by default, so a browser only sends
+-- it back over HTTPS. On an instance served over plain HTTP login loops
+-- until cookie_insecure below is set.
--
-- Two libraries are needed. luaossl provides openssl.rand and openssl.hmac and
-- lives at <https://github.com/wahern/luaossl>, and luaposix provides
@@ -42,16 +40,10 @@
-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
-- luarocks install luaposix
--
--- Some distributions package both as well, for example lua-luaossl and
--- lua-posix on Debian, and such a package has to be built for the same Lua
--- version as cgit.
---
--- The cookie carries only a user name and there is no server-side session
--- store, so deleting an account does not revoke a cookie already issued until
--- it expires, and instances that share a secret file accept each other's
--- cookies. The login form carries no CSRF token. Both are acceptable for
--- gating read access to a git browser, so weigh them before guarding anything
--- more sensitive.
+-- There is no server-side session store and no CSRF token, so a deleted
+-- account's cookie stays valid until it expires and instances sharing a
+-- secret file accept each other's cookies. That is acceptable for gating
+-- read access to a git browser, so weigh it before guarding anything more.
local sysstat = require("posix.sys.stat")
local unistd = require("posix.unistd")
@@ -59,13 +51,11 @@ local rand = require("openssl.rand")
local hmac = require("openssl.hmac")
-- The values that follow are the configuration and are meant to be edited.
--- Nothing below them needs changing for ordinary use.
-- Protected repositories and the users allowed into each. A repository named
-- here is protected and one that is not named is public. The repository key
--- has to match exactly, while user names match whatever their case. Replace
--- the examples below with your own. They are commented out, so an unedited
--- copy protects nothing and grants no accounts.
+-- has to match exactly, while user names match regardless of case. The
+-- examples are commented out, so an unedited copy protects nothing.
local protected_repos = {
-- ["secret-repo"] = { alice = true, bob = true },
-- ["another"] = { alice = true },
@@ -73,8 +63,6 @@ local protected_repos = {
-- Accounts as name and hash. Generate a hash with
-- mkpasswd -m sha-512 -R 300000
--- Replace the examples below. They are not real credentials and must not be
--- deployed as they stand.
local users = {
-- alice = "$6$rounds=300000$REPLACE_THIS_SALT$REPLACE_THIS_HASH",
-- bob = "$6$rounds=300000$REPLACE_THIS_SALT$REPLACE_THIS_HASH",
@@ -95,8 +83,7 @@ local cookie_name = "cgitauth"
-- more tightly.
local cookie_path = "/"
--- Leave this false so the cookie is marked Secure and only travels over HTTPS.
--- Set it true only if cgit is served over plain HTTP with no TLS anywhere.
+-- Set this true only if cgit is served over plain HTTP with no TLS anywhere.
local cookie_insecure = false
-- A throwaway hash of the documented shape, used only to spend the same work
@@ -108,9 +95,8 @@ local dummy_hash = "$6$rounds=300000$0000000000000000$"
-- open decodes is kept here for the calls that follow.
local action, http, cgit, post
--- The two lookups below, account_hash and repo_userset, are the only part of
--- this script that differs from auth-file.lua. Replacing them is all it takes
--- to keep accounts somewhere else.
+-- The two lookups below, account_hash and repo_userset, are the only part
+-- of this script that differs from auth-file.lua.
-- The configured tables are folded to lowercased user names once, so that a
-- lookup matches whatever case the login form was filled in with, the way
@@ -186,14 +172,9 @@ local function pattern_escape(s)
return (string.gsub(s, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"))
end
--- The stored token was already URL encoded by secure_value, so it comes back
--- verbatim and the write path in set_cookie stays symmetric with this read
--- path. Decoding it here would break the signature check for any value
--- carrying a percent escape.
---
--- The name is escaped because it lands in a pattern. A cookie_name holding a
--- magic character, say "cgit-auth", would otherwise read as a pattern and stop
--- matching its own cookie while matching names nobody configured.
+-- The token comes back still URL encoded by secure_value. Decoding it here
+-- would break the signature check for any value carrying a percent escape.
+-- The name is escaped because it lands in a Lua pattern.
function get_cookie(cookies, name)
cookies = string.gsub(";" .. (cookies or "") .. ";", "%s*;%s*", ";")
return string.match(cookies, ";" .. pattern_escape(name) .. "=(.-);")
@@ -478,18 +459,21 @@ function body()
html_attr(secure_value("redirect", target, 0))
html("'>")
html("<table>")
- html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autocomplete='username' autofocus></td></tr>")
- html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' autocomplete='current-password'></td></tr>")
- html("<tr><td colspan='2'><input value='Login' type='submit'></td></tr>")
+ html("<tr><td><label for='username'>Username:</label></td><td>")
+ html("<input id='username' name='username'")
+ html(" autocomplete='username' autofocus></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td>")
+ html("<input id='password' name='password' type='password'")
+ html(" autocomplete='current-password'></td></tr>")
+ html("<tr><td colspan='2'>")
+ html("<input value='Login' type='submit'></td></tr>")
html("</table></form>")
return 0
end
--- cgit calls filter_open with the action name followed by the request fields
--- in a fixed order, so they are unpacked here into the tables the functions
--- above read. Only a post reaches filter_write, carrying the form body, and
--- filter_close is where the action finally runs and answers cgit.
+-- filter_open unpacks the action and request fields cgit passes in fixed
+-- order, filter_write collects a post body, and filter_close runs the action.
local actions = {}
actions["authenticate-post"] = authenticate_post