Implemented login page and new jwt-based sessions. Watching cleanup.

This commit is contained in:
Leo Vasanko
2023-10-18 01:06:27 +03:00
committed by Leo Vasanko
parent bd680e3668
commit 429a7dfb16
6 changed files with 166 additions and 59 deletions

View File

@@ -1,26 +1,19 @@
from __future__ import annotations
import hmac
import re
import secrets
from functools import wraps
from hashlib import sha256
from pathlib import Path, PurePath
from time import time
from unicodedata import normalize
import argon2
import msgspec
_argon = argon2.PasswordHasher()
_droppyhash = re.compile(r'^([a-f0-9]{64})\$([a-f0-9]{8})$')
class Config(msgspec.Struct):
path: Path = Path.cwd()
secret: str = secrets.token_hex(12)
public: bool = False
users: dict[str, User] = {}
sessions: dict[str, Session] = {}
links: dict[str, Link] = {}
class User(msgspec.Struct, omit_defaults=True):
@@ -28,13 +21,6 @@ class User(msgspec.Struct, omit_defaults=True):
hash: str = ""
lastSeen: int = 0
def set_password(self, password: str):
self.hash = _argon.hash(_pwnorm(password))
class Session(msgspec.Struct):
username: str
lastSeen: int
class Link(msgspec.Struct, omit_defaults=True):
location: str
creator: str = ""
@@ -54,39 +40,6 @@ def derived_secret(*params, len=8) -> bytes:
# Output a bytes of the desired length
return sha256(combined).digest()[:len]
def _pwnorm(password):
return normalize('NFC', password).strip().encode()
def login(username: str, password: str):
un = _pwnorm(username)
pw = _pwnorm(password)
try:
u = config.users[un.decode()]
except KeyError:
raise ValueError("Invalid username")
# Verify password
if not u.hash:
raise ValueError("Account disabled")
if (m := _droppyhash.match(u.hash)) is not None:
h, s = m.groups()
h2 = hmac.digest(pw + s.encode() + un, b"", "sha256").hex()
if not hmac.compare_digest(h, h2):
raise ValueError("Invalid password")
# Droppy hashes are weak, do a hash update
u.set_password(password)
else:
try:
_argon.verify(u.hash, pw)
except Exception:
raise ValueError("Invalid password")
if _argon.check_needs_rehash(u.hash):
u.set_password(password)
# Login successful
now = int(time())
u.lastSeen = now
sid = secrets.token_urlsafe(12)
config.sessions[sid] = Session(username, now)
return u, sid
def enc_hook(obj):
if isinstance(obj, PurePath):