import argon2
import base64
import flask
import hmac
import re
from globals import *
class AuthenticationFailed(Exception):
pass
def valid_username(username):
return re.match("^[a-zA-Z0-9_]{1,32}$", username)
def hash_password(username, password):
salt = username[:32].ljust(32, "$") + "-salt:"
return salt.encode("utf-8") + base64.b64encode(argon2.argon2_hash(password, salt))
def check_password(username, password):
if not valid_username(username):
raise AuthenticationFailed()
phash_stored = r.get("apex:users:{}:password".format(username))
if phash_stored is None:
# the comparison to this will always fail, but we do the comparison
# anyway to maintain constant-time and avoid user enumeration
phash_compare = hash_password("", "")
else:
phash_compare = phash_stored
phash_request = hash_password(username, password)
return hmac.compare_digest(phash_compare, phash_request)
def add_user(username, password):
if not valid_username(username):
flask.abort(400)
return
hash = hash_password(username, password)
r.set("apex:users:{}:password".format(username), hash)
def session_user():
if "username" in flask.session:
return flask.session["username"]
return None
def set_session_user(username):
r.sadd("apex:users", username)
flask.session["username"] = username
def user_is_admin(username=None):
if username is None:
username = session_user()
if username is None:
return False
return bool(r.get("apex:users:{}:admin".format(username)))
def user_set_admin(username):
r.set("apex:users:{}:admin".format(username), True)