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
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()
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()
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()
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()
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()
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()