15_crypt/util.py

"""Utilities."""

from hashlib import blake2b
import os
import secrets


DIGEST_SIZE = 16

HTTP_200_OK = 200
HTTP_400_BAD_REQUEST = 400
HTTP_403_NOT_AUTHORIZED = 403
HTTP_500_INTERNAL = 500

NUM_RANDOM_BITS = 30
SECRET_VAR = "SECRET"


class AppException(Exception):
    """Root of exception hierarchy."""

    def __init__(self, msg):
        self._msg = msg

    def __str__(self):
        return self._msg


class ModelException(AppException):
    pass


class SecurityException(AppException):
    pass


class ViewException(AppException):
    pass


def dict_factory(cursor, row):
    """Convert row to dictionary."""
    fields = [column[0] for column in cursor.description]
    return {key: value for key, value in zip(fields, row)}


def encrypt_password(secret, plain):
    """Encrypt a password."""
    combined = f"{secret}:{plain}"
    h = blake2b(bytes(combined, "utf-8"), digest_size=DIGEST_SIZE)
    return h.hexdigest()


def get_secret():
    """Get the secret value from an environment variable."""
    secret = os.getenv(SECRET_VAR, None)
    if secret is None:
        raise SecurityException(f"secret not set: use {SECRET_VAR} environment variable")
    return secret


def make_secret():
    """Make a secret value."""
    return str(secrets.randbits(NUM_RANDOM_BITS))