示例#1
0
def check_cdc(confirmed_data: dict, email: str, db_path: str = None) -> bool:
    """ Check if user's data is found in CDC Database to prevent fraud vaccine cards

    Usage
    -----
    >>> from cvp.app.utils import check_cdc
    >>> bool_ = check_cdc(confirmed_data, email)

    Args:
        confirmed_data (dict): Data from users' vaccine cards
        email (str): user's email that associate with CDC Database

    Returns:
        flag (bool): False if account email not found in CDC Database or data does not match. True otherwise

    """
    db_path = db_path or cdc_db_path
    if not os.path.exists(db_path):
        raise FileNotFoundError(
            f"File {db_path} was not found. Current dir: {os.getcwd()}")

    db = Database(db_path)
    try:
        acc = db.select('*', profile_table, f'Email = "{email}"')
        if not acc:
            return False  # Account was not found.
        acc_dict = {
            'first_name': acc[0][3],
            'mid_initial': acc[0][4],
            'dob': acc[0][5],
            'first_dose': acc[0][6],
            'second_dose': acc[0][9],
            'last_name': acc[0][2],
            'patient_num': acc[0][1],
            'clinic_site': acc[0][8],
            'date_first': acc[0][7],
            'date_second': acc[0][10]
        }
        for key, value in acc_dict.items():
            # acc_dict will return `none` from database if the column in null,
            # and confirmed_data will have '' if column is empty or not filled
            # Hence we need to fill the empty string with `none` before comparing them
            if not str(confirmed_data[key]):
                confirmed_data[key] = 'none'

            if re.sub(r'\s+', '', str(acc_dict[key])).lower() == re.sub(
                    r'\s+', '', str(confirmed_data[key])).lower():
                continue
            else:
                return False

    finally:
        db.close_connection()
    return True
示例#2
0
def is_user(email):
    """
    Check if this email is in the database.
    :param email: email of the user to be searched.
    :return: account information if found else False
    """
    db = Database(db_path)
    try:
        acc = db.select('*', account_table, f'Email = \"{email}\"')
        if not acc:  # account was not found with this email
            return f'Account was not found with this email.'
        else:
            return acc[0]

    finally:
        db.close_connection()
示例#3
0
def authenticate(password, email=None, account_id=None):
    """
    Authenticate user's email and password with database.
    Has to have email or account_id to find account information. Only one of them is required.
    :param email: email of user
    :param password: password of user
    :param account_id: user's account_id
    return tuple of account information if succeeded else return error message
    """
    if not password or (not email and not account_id):
        return 'Incorrect input.'
    db = Database(db_path)
    try:
        if email:
            acc = db.select('*', account_table, f'Email = \"{email}\"')
        elif account_id:
            acc = db.select('*', account_table,
                            f'User_Account_ID = \"{account_id}\"')
        else:
            acc = None

        if not acc:  # account was not found with this email
            return 'Account was not found.'
        if acc:  # account with this email is in our database
            # handle incorrect input
            db_password, db_salt = b64decode(acc[0][1]), b64decode(acc[0][3])
            hashed_pass, _ = generate_hash(password=password, salt=db_salt)
            if hmac.compare_digest(hashed_pass, db_password):  # login
                return acc[0]
            else:
                return 'Password did not match'

    finally:
        db.close_connection()
示例#4
0
def update_account(account_id, uname=None, email=None):
    """
    Update account of username or email.
    :param uname: new username.
    :param email: new email.
    """
    db = Database(db_path)
    try:
        if uname:
            db.update((uname, ), ('Username', ), account_table,
                      f'User_Account_ID = \"{account_id}\"')
        if email:
            db.update((email, ), ('Email', ), account_table,
                      f'User_Account_ID = \"{account_id}\"')
    finally:
        db.close_connection()
示例#5
0
def generate_account(session, profile_data):
    """
    Generate account for registration.
    :param session: dict of session from flask.
    :param profile_data: dict of profile data from flask.
    :return: True if successfully generated.
    """
    db = Database(db_path)
    try:
        # Get cur_hash from last record in database
        last_record_hash = \
        db.select('Block_Hash', profile_table, 'User_Account_ID = (SELECT max(User_Account_ID) FROM profile)')[0][0]

        # Get largest account_id in database and increment account_id by 1
        new_account_id = db.select(values='max(User_Account_ID)',
                                   table_name=account_table)[0][0] + 1

        hashed_pass, temp_hashed_salt = generate_hash(session['password'])
        hashed_pass = b64encode(hashed_pass).decode('utf-8')
        hashed_salt = b64encode(temp_hashed_salt).decode('utf-8')
        username = profile_data['first_name'] + ' ' + profile_data['last_name']
        account_value = (session['email'], hashed_pass, new_account_id,
                         hashed_salt, username)

        items = [
            last_record_hash, new_account_id, profile_data['patient_num'],
            profile_data['last_name'], profile_data['first_name'],
            profile_data['mid_initial'], profile_data['dob'],
            profile_data['first_dose'], profile_data['date_first'],
            profile_data['clinic_site'], profile_data['second_dose'],
            profile_data['date_second'], last_record_hash
        ]
        new_hash = generate_block_hash(items, temp_hashed_salt)
        with open(HISTORY_LOG_PATH, 'a') as hist_log_file:
            hist_log_file.write(f'{new_account_id}\t{new_hash}\n')

        items.pop(len(items) - 1)
        items.pop(0)
        items.append(new_hash)

        profile_value = tuple(items)

        db.insert(account_value, account_table)
        db.insert(profile_value, profile_table)
        return new_account_id
    finally:
        db.close_connection()
示例#6
0
def update_password(new_password, email=None, acc=None):
    """
    Update account's password.
    :param email: email
    :param new_password: new password
    :param acc: account id
    :return: True if succeeded else False.
    """
    db = Database(db_path)

    try:
        if acc:
            salt = db.select('Salt', account_table,
                             f'''User_Account_ID = {acc}''')
        elif email:
            salt = db.select('Salt', account_table, f'Email = \"{email}\"')
        else:
            salt = None

        if not salt:  # account was not found with this email
            return 'Account was not found.'

        salt = b64decode(salt[0][0])
        hashed_pass, _ = generate_hash(new_password, salt)
        hashed_pass = b64encode(hashed_pass).decode('utf-8')
        if acc:
            db.update((hashed_pass, ), ('Password', ), account_table,
                      f'User_Account_ID = \"{acc}\"')
        elif email and type(is_user(email)) == tuple:
            db.update((hashed_pass, ), ('Password', ), account_table,
                      f'Email = \"{email}\"')
        else:
            return False
        return True

    finally:
        db.close_connection()
示例#7
0
def get_profile(account_id):
    """
    Get profile with account id.
    :param account_id: account id.
    :return: dictionary of the account and record.
    """
    db = Database(db_path)
    try:
        acc = db.select('*', account_table,
                        f'User_Account_ID = \"{account_id}\"')[0]
        record = db.select('*', profile_table,
                           f'User_Account_ID = \"{account_id}\"')[0]

        # Get block_hash from previous block
        if record[0] == 1:
            prev_hash = os.environ['hash_0']
        else:
            prev_hash = db.select(
                'Block_Hash', profile_table,
                f'User_Account_ID = \"{account_id - 1}\"')[0][0]

        salt = b64decode(acc[3])
        block_hash = b64decode(record[len(record) - 1])

        # Deep copy record and add prev_block_hash to front and back of the list
        items = copy.deepcopy(list(record[:-1]))
        items.insert(0, prev_hash)
        items.append(prev_hash)

        new_block_hash = generate_block_hash(items, salt)
        new_block_hash_decoded = b64decode(new_block_hash)
        correct_hash = hmac.compare_digest(block_hash, new_block_hash_decoded)

        # Double checking method, check if this_block_hash is stored in local history logs
        if correct_hash:
            hist_log_df = pd.read_csv(HISTORY_LOG_PATH, sep='\t')
            hist_log_block_hash = hist_log_df[hist_log_df['User_Account_ID'] ==
                                              account_id]['Block_Hash'].values
            if hist_log_block_hash == new_block_hash:
                correct_hash = True
            else:
                correct_hash = False

        # True if correct_hash is wrong (is tampered), False otherwise (is not tampered)
        is_tampered = not correct_hash
        return __form_dict(acc, record), is_tampered
    finally:
        db.close_connection()