""" Service to handle authentication """ import uuid from datetime import datetime from typing import Optional, Tuple from nacl import pwhash from nacl.exceptions import InvalidkeyError from atheneum.model import User, UserToken from atheneum.service import user_service, user_token_service def generate_token() -> uuid.UUID: """ Generate a unique token :return: """ return uuid.uuid4() def get_password_hash(password: str) -> Tuple[str, int]: """ Retrieve argon2id password hash. :param password: plaintext password to convert :return: Tuple[password_hash, password_revision] """ return pwhash.argon2id.str(password.encode('utf8')).decode('utf8'), 1 def is_valid_password(user: User, password: str) -> bool: """ User password must pass pwhash verify :param user: :param password: :return: """ assert user try: return pwhash.verify( user.password_hash.encode('utf8'), password.encode('utf8')) except InvalidkeyError: pass return False def is_valid_token(user_token: Optional[UserToken]) -> bool: """ Token must be enabled and if it has an expiration, it must be greater than now. :param user_token: :return: """ if user_token is None: return False if not user_token.enabled: return False if (user_token.expiration_time is not None and user_token.expiration_time < datetime.utcnow()): return False return True def bump_login(user: Optional[User]) -> None: """ Update the last login time for the user :param user: :return: """ if user is not None: user_service.update_last_login_time(user) def logout(user_token: Optional[UserToken] = None) -> None: """ Remove a user_token associated with a client session :param user_token: :return: """ if user_token is not None: user_token_service.delete(user_token)