aboutsummaryrefslogtreecommitdiff
path: root/password.py
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2022-11-29 02:51:29 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2022-11-29 02:51:29 +0100
commit4587e5e124afcb13c81dea68e373420670d076b4 (patch)
tree91416ac5cb53dce9d80b466f66206a05c5a25a40 /password.py
parentAdd makefile for ctags file. (diff)
downloadmu4web-4587e5e124afcb13c81dea68e373420670d076b4.tar.gz
mu4web-4587e5e124afcb13c81dea68e373420670d076b4.tar.xz
Properly specify version requirements, and add directory layout.
Diffstat (limited to 'password.py')
-rwxr-xr-xpassword.py120
1 files changed, 0 insertions, 120 deletions
diff --git a/password.py b/password.py
deleted file mode 100755
index ff0df21..0000000
--- a/password.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Simple password store, backed by a JSON file.
-
-Also contains an entry point for managing the store.
-"""
-
-import hashlib
-import json
-import os
-import random
-from typing import (
- TypedDict,
-)
-
-
-def gen_salt(length: int = 10) -> str:
- # TODO is this a sufficient source of randomness
- return bytearray(random.randint(0, 256) for _ in range(length)).hex()
-
-
-# Manual list of entries, to stop someone from executing arbitrary
-# code by modyfying password database
-hash_methods = {
- 'sha256': hashlib.sha256
-}
-
-
-class PasswordEntry(TypedDict):
- hash: str
- salt: str
- # One of the keys of hash_methods
- method: str
-
-
-class Passwords:
- """
- Simple password store.
-
- [Parameters]
- fname - Path of json file to load and store from.
- """
- def __init__(self, fname: os.PathLike):
- self.fname = fname
- self.db: dict[str, PasswordEntry]
- try:
- with open(fname) as f:
- self.db = json.load(f)
- except Exception:
- self.db = {}
-
- def save(self) -> None:
- """Dump current data to disk."""
- try:
- with open(os.fspath(self.fname) + '.tmp', 'w') as f:
- json.dump(self.db, f)
- f.write('\n')
- os.rename(os.fspath(self.fname) + '.tmp', self.fname)
- except Exception as e:
- print(f'Saving password failed {e}')
-
- def add(self, username: str, password: str) -> None:
- """Add (or modify) entry in store."""
- if cur := self.db.get(username):
- salt = cur['salt']
- hashed = hashlib.sha256((salt + password).encode('UTF-8'))
- self.db[username] = {
- 'hash': hashed.hexdigest(),
- 'salt': salt,
- 'method': 'sha256',
- }
- else:
- salt = gen_salt()
- hashed = hashlib.sha256((salt + password).encode('UTF-8'))
- self.db[username] = {
- 'hash': hashed.hexdigest(),
- 'salt': salt,
- 'method': 'sha256'
- }
-
- def validate(self, username: str, password: str) -> bool:
- """Check if user exists, and if it has a correct password."""
- # These shall fail when key is missing
- data = self.db[username]
- proc = hash_methods[data['method']]
- digest = proc((data['salt'] + password).encode('UTF-8')).hexdigest()
- return data['hash'] == digest
-
-
-def main():
- import argparse
- parser = argparse.ArgumentParser()
-
- parser.add_argument('--file', default='passwords.json')
-
- subparsers = parser.add_subparsers(dest='cmd')
-
- add_parser = subparsers.add_parser('add')
- add_parser.add_argument('username')
- add_parser.add_argument('password')
-
- val_parser = subparsers.add_parser('validate')
- val_parser.add_argument('username')
- val_parser.add_argument('password')
-
- args = parser.parse_args()
-
- passwords = Passwords(args.file)
- if args.cmd == 'add':
- passwords.add(args.username, args.password)
- passwords.save()
- elif args.cmd == 'validate':
- print(passwords.validate(args.username, args.password))
- else:
- parser.print_help()
-
-
-if __name__ == '__main__':
- main()