Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from user_interface import *
- if __name__ == '__main__':
- main_menu()
- import bcrypt
- def encrypt_password(password: str) -> str:
- """
- Encrypt a password with a randomly generated salt then a hash.
- Cost rounds added to slow down the process in case of rainbow table/brute force attack.
- :param password: The password to encrypt in clear text.
- :return: The encrypted password as a unicode string.
- """
- encoded_password = password.encode('utf8')
- cost_rounds = 12
- random_salt = bcrypt.gensalt(cost_rounds)
- hashed_password = bcrypt.hashpw(encoded_password, random_salt).decode('utf8', 'strict')
- return hashed_password
- def check_password(password: str, password_hash: str) -> bool:
- """
- Check a password against its encrypted hash for a match.
- :param password: the password to check in clear text. (Unicode)
- :param password_hash: The encrypted hash to check against. (Unicode)
- :return: Whether the password and the hash match
- """
- encoded_password = password.encode('utf8')
- encoded_password_hash = password_hash.encode('utf8')
- password_matches = bcrypt.checkpw(encoded_password, encoded_password_hash)
- return password_matches
- if __name__ == '__main__':
- test_password = 'Password1'
- hashed_test_password = encrypt_password(test_password)
- print(f'hashed_password: {hashed_test_password}')
- password_matches_hash = check_password(test_password, hashed_test_password)
- print(f'password matches hash? {password_matches_hash}')
- import json
- from pathlib import Path
- from typing import List, Union
- def create_file_if_not_exists(file_path: str) -> None:
- """
- Checks if a file exists at the given path, and creates it if it doesn't.
- :param file_path: the path of the file to check/create, which can be relative or absolute.
- :return: None
- """
- Path(file_path).touch()
- def get_json_file_contents(file_path: str) -> Union[List, None]:
- """
- Reads and return the contents of a JSON file.
- :param file_path: The path where the JSON file is located.
- :return: The contents of the file, or None if there if the file is empty or not found.
- """
- try:
- json_file = open(file_path)
- except IOError:
- return None
- try:
- file_contents = json.load(json_file)
- except ValueError:
- file_contents = None
- json_file.close()
- return file_contents
- import datetime
- from typing import Dict
- from encryption import *
- from json_handling import *
- DEFAULT_FILE_PATH = 'data/users.json'
- def prepare_new_user_data(username: str, password: str) -> Dict:
- """
- Return user data ready for storage.
- :param username: The username for this user.
- :param password: The password for this user, in clear text.
- :return: A Dict containing user data ready to store, including encrypted password.
- """
- new_user = {
- 'username': username,
- 'password': encrypt_password(password),
- 'created': str(datetime.datetime.now()),
- 'active': True
- }
- return new_user
- def check_if_user_already_exists(username: str, json_file_path: str=DEFAULT_FILE_PATH) -> bool:
- """
- Queries a JSON file and returns whether it already exists.
- :param username: The username to check for duplication.
- :param json_file_path: The path where the JSON file is located.
- :return: Whether the username already exists.
- """
- all_users = get_json_file_contents(json_file_path)
- if not all_users:
- return False
- for user in all_users:
- if user['username'] == username:
- return True
- return False
- def add_user(username: str, password: str, json_file_path: str=DEFAULT_FILE_PATH) -> None:
- """
- Adds a user to a JSON file, unless it is a duplicate, in which case it raises a ValueError.
- :param username: The username of the user to add.
- :param password: The password of the user to add, in clear text.
- :param json_file_path: The path where the JSON file to add the user to is located.
- :return: None
- """
- create_file_if_not_exists(json_file_path)
- is_duplicate_user = check_if_user_already_exists(username, json_file_path)
- if is_duplicate_user:
- raise ValueError(f'Username "{username}" already exists.')
- new_user = prepare_new_user_data(username, password)
- all_users = get_json_file_contents(json_file_path)
- if not all_users:
- all_users = []
- all_users.append(new_user)
- with open(json_file_path, 'w') as users_file:
- json.dump(all_users, users_file, indent=2)
- def retrieve_user(username: str, json_filepath: str=DEFAULT_FILE_PATH) -> Union[Dict, None]:
- """
- Returns a single user record from the target JSON file.
- :param username: the username to search for.
- :param json_filepath: The path where the JSON file to retrieve the user from is located.
- :return: The user record as a Dict, or None if it is not found.
- """
- all_users = get_json_file_contents(json_filepath)
- for user in all_users:
- if user['username'] == username:
- return user
- return None
- def authenticate_username_and_password(username: str, password: str) -> bool:
- """
- Verify that the provided username and password match what is stored in the user data,
- for authentication purposes.
- :param username: The user's username.
- :param password: The user's password, in clear text.
- :return: Whether the authentication was successful.
- """
- user = retrieve_user(username)
- password_hash = user['password']
- if not user:
- return False
- if not check_password(password, password_hash):
- return False
- return True
- if __name__ == '__main__':
- test_username = 'test1'
- test_password = 'Password1'
- print(prepare_new_user_data(test_username, test_password))
- test_file_path = 'data/test_database.json'
- create_file_if_not_exists(test_file_path)
- add_user(test_username, test_password, test_file_path)
- print(get_json_file_contents(test_file_path))
- import getpass
- import re
- from user_storage import *
- def main_menu() -> None:
- """
- Displays the main menu of the application.
- :return: None
- """
- menu = 'n'.join([
- 'Select an option by entering its number and pressing Enter.',
- '1. Create a user account',
- '2. Log in to existing account',
- '---'
- ])
- print(menu)
- valid_selections = [1, 2]
- input_is_valid = False
- selection = None
- while not input_is_valid:
- try:
- selection = int(input('Selection: '))
- if selection in valid_selections:
- input_is_valid = True
- else:
- print('The number you entered is not a valid selection.')
- except ValueError:
- print('The value you entered is not a number.')
- handle_main_menu_selection(selection)
- def handle_main_menu_selection(selection: int) -> None:
- """
- Calls the function related to the selection the user made.
- :param selection: The user's selection.
- :return: None
- """
- if selection == 1:
- create_new_user_menu()
- elif selection == 2:
- user_login_menu()
- else:
- raise ValueError(f'Selection {selection} is invalid.')
- def create_new_user_menu() -> None:
- """
- Displays the account creation menu, including asking the user for username and password.
- :return: None
- """
- menu = 'n'.join([
- '---',
- 'Account creation',
- 'Username must...',
- 't- be at least 3 characters long',
- 't- contain only letters, numbers, and underscores',
- 'Password must...',
- 't- be at least 8 characters long',
- '---'
- ])
- print(menu)
- user_added_successfully = False
- username = ''
- while not user_added_successfully:
- try:
- username = get_username_input()
- password = get_password_input()
- user_added_successfully = try_adding_user(username, password)
- if not user_added_successfully:
- print(f'Username "{username}" already exists.')
- except ValueError as error:
- print(str(error))
- def try_adding_user(username: str, password: str) -> bool:
- """
- Attempts to add a user to the user database file.
- :param username: The username provided by the user.
- :param password: The password provided to the user, in clear text.
- :return: Whether the user was added successfully.
- """
- try:
- add_user(username, password)
- return True
- except ValueError:
- return False
- def user_login_menu() -> None:
- menu = 'n'.join([
- '---',
- 'User login',
- '---'
- ])
- print(menu)
- login_successful = False
- while not login_successful:
- username = get_username_input()
- password = get_password_input()
- login_successful = authenticate_username_and_password(username, password)
- if not login_successful:
- print('Incorrect username or password.')
- print('Login successful.')
- def get_username_input() -> str:
- """
- Request username input from the user.
- :return: The username entered by the user.
- """
- minimum_length = 3
- username = input('Enter username: ')
- if len(username) < minimum_length:
- raise ValueError('Username must be at least 3 characters.')
- # match upper & lower case letters, numbers, and underscores
- pattern = re.compile('^([a-zA-Z0-9_]+)$')
- if not pattern.match(username):
- raise ValueError('Username must consist of only letters, numbers, and underscores.')
- return username
- def get_password_input() -> str:
- """
- Request password input from the user.
- :return: The password entered by the user.
- """
- minimum_length = 8
- password = getpass.getpass('Enter password: ')
- if len(password) < minimum_length:
- raise ValueError('Password must be at least 8 characters.')
- return password
- if __name__ == '__main__':
- main_menu()
Add Comment
Please, Sign In to add comment