login

<     >

2022-02-27 18:48:19 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

add config and auth support

diff --git a/app.lua b/app.lua
index 3359006..14f3a57 100644
--- a/app.lua
+++ b/app.lua
@@ -1,5 +1,6 @@
 local git = require "git"
 local scan = require "scan"
+local hash = require "hash"
 
 local lud = require "ludweb"
 
@@ -45,50 +46,124 @@ local function diff_cb(line_type, line)
     return line .. "\n"
 end
 
-local path = "/tmp/cogit"
-local limit = 20
+local function allowed(user, gname)
+    if gname == "public" then
+        return true
+    elseif user == nil then
+        return false
+    end
+    for _, ok in ipairs(user.groups) do
+        if gname == ok then
+            return true
+        end
+    end
+    return false
+end
+
+local LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG = 0, 1, 2, 3
+
+local path = arg[1]
+local conf = dofile(path.."/conf.lua")
 
 git.init()
 local groups = scan.scanrepos(path)
 
+local me
+
 local routes = {
     {"GET", "/?",
     function (req)
+        local user = conf.users[me]
         local gnames = {}
         for gname in pairs(groups) do
-            table.insert(gnames, gname)
+            if allowed(user, gname) then
+                table.insert(gnames, gname)
+            end
         end
-        return lud.template.render_file("view/home.html", {gnames=gnames})
+        return lud.template.render_file("view/home.html", {user=user, gnames=gnames})
+    end},
+    {"GET", "/login",
+    function (req)
+        return lud.template.render_file("view/login.html", {})
+    end},
+    {"POST", "/login",
+    function (req)
+        local uname = req.form.username
+        local pass = req.form.password
+        local user = conf.users[uname]
+        local salt, h
+        if user == nil then
+            -- hash something as if we're trying to login anyway
+            salt = hash.get_salt()
+            h = hash.hash_pass(pass, salt)
+            conf.log(LOG_INFO, "invalid username")
+        else
+            salt = lud.crypt.b64_dec(user.salt)
+            h = hash.hash_pass(pass, salt)
+            if h == lud.crypt.b64_dec(user.hash) then
+                me = uname
+                conf.log(LOG_INFO, "logged in as "..uname)
+                return "/", 303
+            else
+                conf.log(LOG_INFO, "invalid password")
+            end
+        end
+        return "/login", 303
+    end},
+    {"GET", "/logout",
+    function (req)
+        me = nil
+        return "/", 303
     end},
     {"GET", "/group/([%w_-]+)",
     function (req, gname)
+        local user = conf.users[me]
+        if not allowed(user, gname) then
+            return "/login", 303
+        end
         local rnames = {}
         for rname in pairs(groups[gname]) do
             table.insert(rnames, rname)
         end
-        return lud.template.render_file("view/group.html", {gname=gname, rnames=rnames})
+        local env = {user=user, gname=gname, rnames=rnames}
+        return lud.template.render_file("view/group.html", env)
     end},
     {"GET", "/group/([%w_-]+)/repo/([%w_-]+)",
     function (req, gname, rname)
+        local user = conf.users[me]
+        if not allowed(user, gname) then
+            return "/login", 303
+        end
         local repo = groups[gname][rname]
         local bnames = repo:branches()
         local tnames = repo:tags()
-        local env = {repo=repo, gname=gname, rname=rname, bnames=bnames, tnames=tnames}
+        local env = {
+            user=user, repo=repo, gname=gname,
+            rname=rname, bnames=bnames, tnames=tnames,
+        }
         return lud.template.render_file("view/repo.html", env)
     end},
     {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/history/([%w_-]+)",
     function (req, gname, rname, first)
+        local user = conf.users[me]
+        if not allowed(user, gname) then
+            return "/login", 303
+        end
         local repo = groups[gname][rname]
         local commit = repo:commit(first)
-        local prev = repo:find_prev(commit:id(), limit)
+        local prev = repo:find_prev(commit:id(), conf.limit)
         local env = {
-            gname=gname, rname=rname, bname=bname, commit=commit,
-            limit=limit, prev=prev, first=first,
+            user=user, gname=gname, rname=rname, bname=bname,
+            commit=commit, limit=conf.limit, prev=prev, first=first,
         }
         return lud.template.render_file("view/history.html", env)
     end},
     {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)",
     function (req, gname, rname, cid)
+        local user = conf.users[me]
+        if not allowed(user, gname) then
+            return "/login", 303
+        end
         local repo = groups[gname][rname]
         local commit = repo:commit(cid)
         local prev = repo:find_prev(commit:id(), 1)
@@ -96,13 +171,17 @@ local routes = {
         local time_str = time_fmt(sig)
         local diff = repo:diff(commit, diff_cb)
         local env = {
-            gname=gname, rname=rname, bname=bname, commit=commit,
+            user=user, gname=gname, rname=rname, bname=bname, commit=commit,
             time_str=time_str, sig=sig, cid=cid, prev=prev, diff=diff,
         }
         return lud.template.render_file("view/commit.html", env)
     end},
     {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)/tree/(.*)",
     function (req, gname, rname, cid, path)
+        local user = conf.users[me]
+        if not allowed(user, gname) then
+            return "/login", 303
+        end
         local repo = groups[gname][rname]
         local commit = repo:commit(cid)
         local node = commit:tree_entry(path)
@@ -118,8 +197,8 @@ local routes = {
             base = base .. "/"
         end
         local env = {
-            gname=gname, rname=rname, cid=cid, path=path,
-            base=base, parts=parts, node=node,
+            user=user, gname=gname, rname=rname, cid=cid,
+            path=path, base=base, parts=parts, node=node,
         }
         if node.type_ == "dir" then
             return lud.template.render_file("view/dir.html", env)

diff --git a/conf.lua b/conf.lua
new file mode 100644
index 0000000..09cc124
--- /dev/null
+++ b/conf.lua
@@ -0,0 +1,25 @@
+--[[
+Put this file on the same folder as public/ and customize it.
+]]
+
+local LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG = 0, 1, 2, 3
+local level_str = {"ERROR", "WARN", "INFO", "DEBUG"}
+local log_level = LOG_DEBUG
+
+return {
+    ["log"] = function (level, msg)  -- logging function
+        if log_level >= level then
+            print(("[%s] %s"):format(level_str[level+1], msg))
+        end
+    end,
+    limit = 20, -- page size, for pagination
+    users = {
+        ["guest"] = {
+            salt = "GfwhQ/F6HYnv6g5qrpv58NgMWmOF6nsQXc8RVr6C8Fc=",
+            hash = "uxfQEiPSWAuu96rYpqYfi0kcue0ZiTvSCDX3ngFjC3RqLa7v9OouFd5UglJ7vh52nNDh2E9cG/f0RlVrLzIE9Q==",
+            nick = "guest",
+            name = "Guest",
+            groups = {"guests"},
+        },
+    },
+}

diff --git a/hash.lua b/hash.lua
new file mode 100644
index 0000000..c396c66
--- /dev/null
+++ b/hash.lua
@@ -0,0 +1,38 @@
+local lud = require "ludweb"
+
+local function get_pass(prompt)
+    io.write(prompt)
+    os.execute("stty -echo")
+    local pass = io.read()
+    os.execute("stty echo")
+    io.write("\n")
+    return pass
+end
+
+local function get_salt()
+    return lud.crypt.urandom(32)
+end
+
+local function hash_pass(pass, salt)
+    return lud.crypt.pbkdf2(pass, salt, 10000, 64)
+end
+
+if arg[0] ~= "hash.lua" then
+    return {get_salt=get_salt, hash_pass=hash_pass}
+end
+
+local pass, pass2
+
+repeat
+    pass  = get_pass("password: ")
+    pass2 = get_pass("repeat  : ")
+until pass2 == pass
+
+local salt = get_salt()
+local hash = hash_pass(pass, salt)
+
+local salt_b64 = lud.crypt.b64_enc(salt)
+local hash_b64 = lud.crypt.b64_enc(hash)
+
+print("salt: " .. salt_b64)
+print("hash: " .. hash_b64)

diff --git a/view/commit.html b/view/commit.html
index 2fe374f..cdf216c 100644
--- a/view/commit.html
+++ b/view/commit.html
@@ -4,6 +4,8 @@
   <meta charset="utf-8">
   <title>cogit - {{$gname}} - {{$rname}} - {{$cid}}</title>
   <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
     .diff_file_hdr { font-weight: bold; }
     .diff_hunk_hdr { font-weight: bold; color: blue; }
     .diff_add { color: green; }
@@ -13,7 +15,7 @@
   </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     <a href="/group/{{$gname}}">{{$gname}}</a>
@@ -25,7 +27,16 @@
     {{$cid}}
     &gt;
     <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$cid}}/tree/">tree</a>
-  </p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   % if $prev then
   <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$prev:id()}}">&lt</a>
   % else

diff --git a/view/dir.html b/view/dir.html
index 109390c..c53d6a6 100644
--- a/view/dir.html
+++ b/view/dir.html
@@ -3,9 +3,13 @@
 <head>
   <meta charset="utf-8">
   <title>cogit - {{$gname}} - {{$rname}} - {{$cid}} - {{$path}}</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     <a href="/group/{{$gname}}">{{$gname}}</a>
@@ -21,7 +25,16 @@
     &gt;
     <a href="{{$partial}}">{{$part}}</a>
     % end
-  </p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   <ul>
     % for child in $node.children do
     <li><a href="{{$base..$child}}">{{$child}}</a></li>

diff --git a/view/file.html b/view/file.html
index b4ea240..1190b83 100644
--- a/view/file.html
+++ b/view/file.html
@@ -3,9 +3,13 @@
 <head>
   <meta charset="utf-8">
   <title>cogit - {{$gname}} - {{$rname}} - {{$cid}} - {{$path}}</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     <a href="/group/{{$gname}}">{{$gname}}</a>
@@ -21,7 +25,16 @@
     &gt;
     <a href="{{$partial}}">{{$part}}</a>
     % end
-  </p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   % if $node.bin then
   <p><em>binary file</em></p>
   % else

diff --git a/view/group.html b/view/group.html
index 6579b54..2d37c75 100644
--- a/view/group.html
+++ b/view/group.html
@@ -3,13 +3,26 @@
 <head>
   <meta charset="utf-8">
   <title>cogit - {{$gname}}</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     {{$gname}}
-  </p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   <p>Repos:</p>
   <ul>
     % for rname in $rnames do

diff --git a/view/history.html b/view/history.html
index 310b4d2..118229e 100644
--- a/view/history.html
+++ b/view/history.html
@@ -3,9 +3,13 @@
 <head>
   <meta charset="utf-8">
   <title>cogit - {{$gname}} - {{$rname}} - history</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     <a href="/group/{{$gname}}">{{$gname}}</a>
@@ -15,7 +19,16 @@
     history
     &gt;
     <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$first}}/tree/">tree</a>
-  <p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   % if $prev then
   <a href="/group/{{$gname}}/repo/{{$rname}}/history/{{$prev:id()}}">&lt</a>
   % else

diff --git a/view/home.html b/view/home.html
index be3f067..74253d2 100644
--- a/view/home.html
+++ b/view/home.html
@@ -3,9 +3,22 @@
 <head>
   <meta charset="utf-8">
   <title>cogit</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>home</p>
+  <div id="nav-bar">home</div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   <p>Groups:</p>
   <ul>
     % for gname in $gnames do

diff --git a/view/login.html b/view/login.html
new file mode 100644
index 0000000..28dd562
--- /dev/null
+++ b/view/login.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>cogit - login</title>
+  <style>
+    .centered { text-align: center; }
+    .ul-form {
+      padding: 0;
+      list-style-type: none;
+    }
+    .flat-field {
+      margin: 5px;
+      font-size: 18px;
+      background: white;
+      border-width: 1px;
+      border-style: solid;
+      border-color: #B4C2AA;
+      border-radius: 0px;
+    }
+    .flat-field:invalid {
+      border-color: lightcoral;
+    }
+    .flat-button {
+      margin: 10px;
+      box-shadow: none;
+      background: none;
+      background-color: #F5FFE2;
+      border-radius: 6px;
+      border: 3px solid #B4C2AA;
+      font-weight: bold;
+      padding: 5px 20px;
+      cursor: default;
+      color: black;
+      font-size: 16px;
+      text-decoration: none;
+      text-shadow: 1px 1px 0px white;
+    }
+
+    .flat-button:hover {
+      background-color: var(--color-2);
+    }
+  </style>
+</head>
+<body>
+  <h1 class="centered">Login</h1>
+  <form action="/login" method="post">
+    <ul class="centered ul-form">
+      <li><input type="text" class="flat-field" name="username" placeholder="User" autofocus></li>
+      <li><input type="password" class="flat-field" name="password" placeholder="Password"></li>
+      <li><input type="submit" class="flat-button" value="Login"></li>
+    </ul>
+  </form>
+</body>
+</html>

diff --git a/view/repo.html b/view/repo.html
index 926994d..b0b6808 100644
--- a/view/repo.html
+++ b/view/repo.html
@@ -3,15 +3,28 @@
 <head>
   <meta charset="utf-8">
   <title>cogit - {{$gname}} - {{$rname}}</title>
+  <style>
+    #nav-bar { float: left; }
+    #auth-bar { float: right; }
+  </style>
 </head>
 <body>
-  <p>
+  <div id="nav-bar">
     <a href="/">home</a>
     &gt;
     <a href="/group/{{$gname}}">{{$gname}}</a>
     &gt;
     {{$rname}}
-  </p>
+  </div>
+  <div id="auth-bar">
+    % if $user == nil then
+    <a href="/login">login</a>
+    % else
+    <strong>{{$user.name}}</strong>
+    <a href="/logout">(logout)</a>
+    % end
+  </div>
+  <br>
   <p>Branches:</p>
   <ul>
     % for bname in $bnames do