diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-11-29 02:51:29 +0100 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-11-29 02:51:29 +0100 |
commit | 4587e5e124afcb13c81dea68e373420670d076b4 (patch) | |
tree | 91416ac5cb53dce9d80b466f66206a05c5a25a40 /password.py | |
parent | Add makefile for ctags file. (diff) | |
download | mu4web-4587e5e124afcb13c81dea68e373420670d076b4.tar.gz mu4web-4587e5e124afcb13c81dea68e373420670d076b4.tar.xz |
Properly specify version requirements, and add directory layout.
Diffstat (limited to 'password.py')
-rwxr-xr-x | password.py | 120 |
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() |