diff --git a/cli/cli.ini b/cli/cli.ini deleted file mode 100644 index c9dfe03..0000000 --- a/cli/cli.ini +++ /dev/null @@ -1,8 +0,0 @@ -[server] -ip = 0.0.0.0 -port = 8066 -url = http://127.0.0.1:8066 - -[client] -default_id = 4f0b6dce -default_password = Happymeal1 \ No newline at end of file diff --git a/cli/cli.py b/cli/cli.py index 17ebb7f..57c05fb 100644 --- a/cli/cli.py +++ b/cli/cli.py @@ -6,7 +6,7 @@ import argparse import sys from config import CONFIG from getpass import getpass -from connection import login, logout, get_client, add_client +from connection import login, logout, get_client, add_client, promote, demote, initialise from test_database_generator import generate_test_database @@ -14,17 +14,18 @@ def show_menu(): print("\nAvailable options:") print("1. Logout and exit") print("2. New client") - print("3. Add test data to database (50 clients, 2 accounts each, 40 transactions each)") - print("4. Initialise Database") - #print("5. Promote to Admin") - #print("6. Demote from Admin") - #print("7. Delete user")S + print("3. Add test data to database (3 clients, 2 accounts each, 3 transactions each)") + print("4. Promote to Admin") + print("5. Demote from Admin") + #print("6. Delete user") print("\n") def main(): parser = argparse.ArgumentParser(description='Banking System CLI Utility') parser.add_argument('-u', '--username', type=str, default=CONFIG["client"]["default_id"], help='Username for login') parser.add_argument('-p', '--password', type=str, default=CONFIG["client"]["default_password"], help='Password for login') + parser.add_argument('-e', '--email', type=str, help='Email for initialisation') + parser.add_argument('-init', '--initialise', action='store_true', help='Initialise the system') subparsers = parser.add_subparsers(dest='command') @@ -33,6 +34,17 @@ def main(): args = parser.parse_args() + if args.initialise: + if not args.email or not args.password: + print("Email and password are required for initialisation.") + sys.exit(1) + response = initialise(args.password, args.email) + if response['success']: + print(f"Initialisation successful: {response['message']}") + else: + print(f"Initialisation failed: {response['message']}") + return + if not args.command: while True: if not args.username: @@ -78,21 +90,34 @@ def main(): password = input("Enter password: ") notes = input("Enter notes: ") response = add_client(name, birthdate, address, phone_number, email, password, notes) + if response['success']: + print(f"Client added successfully: {response['message']}") + else: + print(f"Client addition failed: {response['message']}") elif option == "3": # Menu option 3 - Add test data to database print("Add test users option selected.") generate_test_database(args.username, args.password) - elif option == "4": # Menu option 4 - Initialise Database - print("Not implemented yet, exiting...") - break - elif option == "5": # Menu option 5 - Promote to Admin - print("Not implemented yet, exiting...") - break - elif option == "6": # Menu option 6 - Demote from Admin - print("Not implemented yet, exiting...") - break - elif option == "7": # Menu option 7 - Delete user + elif option == "4": # Menu option 4 - Promote to Admin + print("Enter the client ID to promote to Admin:") + client_id = input("Enter client ID: ") + response = promote(client_id) + if response['success']: + print(f"Promotion successful: {response['message']}") + else: + print(f"Promotion failed: {response['message']}") + + elif option == "5": # Menu option 5 - Demote from Admin + print("Enter the client ID to demote from Admin:") + client_id = input("Enter client ID: ") + response = demote(client_id) + if response['success']: + print(f"Demotion successful: {response['message']}") + else: + print(f"Demotion failed: {response['message']}") + + elif option == "6": # Menu option 6 - Delete user print("Not implemented yet, exiting...") break else: diff --git a/cli/connection.py b/cli/connection.py index 6437ee2..092f6b8 100644 --- a/cli/connection.py +++ b/cli/connection.py @@ -132,7 +132,6 @@ def add_account(client_id, description, account_type, notes): else: print(f"Failed to create account. Status code: {response.status_code}, message: {response.text}") except requests.exceptions.RequestException as e: - print(f"RequestException: {e}") return {'success': False, 'message': "Could not connect to the server. Please try again later."} def add_transaction(amount, account_id, recipient_account_id, otp_code, description): @@ -149,9 +148,72 @@ def add_transaction(amount, account_id, recipient_account_id, otp_code, descript response = requests.post(CONFIG["server"]["url"] + "/Transaction", cookies=session_data['session_cookie'], params=params) response.raise_for_status() if response.status_code == 200: - print("Transaction created successfully.") + pass else: print(f"Failed to create transaction. Status code: {response.status_code}, message: {response.text}") except requests.exceptions.RequestException as e: - print(f"RequestException: {e}") - return {'success': False, 'message': "Could not connect to the server. Please try again later."} \ No newline at end of file + return {'success': False, 'message': {e}} + +def modify_balance(account_id, balance): + """Modifies the balance of the account with the given account_id.""" + params = { + "account_id": account_id, + "balance": balance + } + try: + with open('session_data.json', 'r') as f: + session_data = json.load(f) + response = requests.put(CONFIG["server"]["url"] + "/Admin/Balance", cookies=session_data['session_cookie'], params=params) + response.raise_for_status() + if response.status_code == 200: + print("Balance modified successfully.") + else: + print(f"Failed to modify balance. Status code: {response.status_code}, message: {response.text}") + except requests.exceptions.RequestException as e: + return {'success': False, 'message': {e}} + +def promote(client_id): + """Promotes the client with the given client_id to an admin.""" + params = { + "client_id": client_id + } + try: + with open('session_data.json', 'r') as f: + session_data = json.load(f) + response = requests.put(CONFIG["server"]["url"] + "/Admin/Promote", cookies=session_data['session_cookie'], params=params) + response.raise_for_status() + return {'success': True, 'message': 'Promotion successful.'} + except Exception as e: + return {'success': False, 'message': str(e)} + +def demote(client_id): + """Demotes the client with the given client_id from an admin.""" + params = { + "client_id": client_id + } + try: + with open('session_data.json', 'r') as f: + session_data = json.load(f) + response = requests.put(CONFIG["server"]["url"] + "/Admin/Demote", cookies=session_data['session_cookie'], params=params) + response.raise_for_status() + return {'success': True, 'message': 'Promotion successful.'} + except Exception as e: + return {'success': False, 'message': str(e)} + +def initialise(password, email): + """Initialises the database with a default admin account.""" + params = { + "password": password, + "email": email + } + try: + response = requests.get(CONFIG["server"]["url"] + "/System/Initialise", params=params) + response.raise_for_status() + return response.json() + except requests.exceptions.HTTPError as e: + if response.status_code == 400: + return {'success': False, 'message': 'Database not empty, this function cannot be used'} + else: + return {'success': False, 'message': str(e)} + except requests.exceptions.RequestException as e: + return {'success': False, 'message': str(e)} \ No newline at end of file diff --git a/cli/test_database_generator.py b/cli/test_database_generator.py index acf3382..d811e46 100644 --- a/cli/test_database_generator.py +++ b/cli/test_database_generator.py @@ -5,15 +5,13 @@ # This adds 50 clients, each with 2 accounts. Each account has 40 transactions. from faker import Faker -from connection import add_client, add_account, add_transaction +from connection import add_client, add_account, add_transaction, modify_balance import random import datetime -import hashlib import time -def generate_hash(): # Creates a hash for a password - seed = str(random.random()).encode('utf-8') - return hashlib.sha512(seed).hexdigest() +passwd = "Happymeal1" + def timestamp(): # Returns the current timestamp return (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) @@ -23,8 +21,8 @@ def generate_test_database(client_id, password): all_account_ids = [] # List to store all account IDs - for i in range(50): # Generate 50 clients - password = generate_hash() + for i in range(3): # Generate 50 clients + password = passwd name = fake.name() birthdate = fake.date_of_birth(minimum_age=18, maximum_age=90) address = fake.address() @@ -34,8 +32,9 @@ def generate_test_database(client_id, password): client_response = add_client(name, birthdate, address, phone_number, email, password, notes) client_id = client_response['message'] + print(f"Client {client_id} added successfully. Password: {password}") for j in range(2): # Each client has 2 accounts - balance = 1000 # Initialize balance to 1000 + account_type = random.choice(['Spending', 'Savings']) account_notes = fake.text(max_nb_chars=50) @@ -43,14 +42,16 @@ def generate_test_database(client_id, password): response_dict = account_response account_id = response_dict['message'] + print(f"Account {account_id} added successfully.") + balance = float(1000) + balance_response = modify_balance(account_id, balance) - - for k in range(40): # Each account has 40 transactions + for k in range(3): # Each account has 40 transactions if not all_account_ids: # Skip creating a transaction if there are no accounts yet continue transaction_type = random.choice(['Deposit', 'Withdrawal']) - amount = random.randint(1, 200) + amount = float(random.randint(1, 200)) if transaction_type == 'Withdrawal' and balance - amount < 0: # Skip withdrawal if it would make balance negative continue @@ -64,9 +65,8 @@ def generate_test_database(client_id, password): recipient_account_id = random.choice(all_account_ids) transaction_response = add_transaction(amount=amount, account_id=account_id, recipient_account_id=recipient_account_id, otp_code=123456, description=transaction_description) - print(f"Transaction response: {transaction_response}") - time.sleep(1) + time.sleep(0.1) all_account_ids.append(account_id) - print("Test database generated successfully.") + print("Test data added successfully.") diff --git a/server/api.yml b/server/api.yml index 03cc0dc..ba3f7b3 100644 --- a/server/api.yml +++ b/server/api.yml @@ -564,6 +564,34 @@ paths: description: Invalid input '404': description: No accounts found + /Admin/Balance: + put: + tags: + - admin + summary: Modify account balance + description: Modify account balance + operationId: manager.modify_balance + parameters: + - name: account_id + in: query + description: ID of account to modify + required: true + schema: + type: string + - name: balance + in: query + description: Amount to modify balance by + required: true + schema: + type: number + format: float + responses: + '200': + description: Successful operation + '400': + description: Invalid input + '404': + description: Account not found /Admin/Transactions: get: tags: @@ -625,7 +653,7 @@ paths: description: Invalid input '401': description: Unauthorised - /Admin/Balance: + /Admin/TestBalance: get: tags: - admin diff --git a/server/manager.py b/server/manager.py index f0f45d8..bd33d44 100644 --- a/server/manager.py +++ b/server/manager.py @@ -159,7 +159,6 @@ def generate_otp(client_id: str): send_email(email, "Luxbank One Time Password", f"Your one-time password is: {password}") otps[client_id] = (password, time.time()) # Store the OTP and the current time event_logger(f"OTP Code {password} emailed to {email}") - print(f"OTP Code {password} emailed to {email}") return format_response(True, "OTP generated and sent successfully."), 200 except EmailSendingError as e: event_logger(f"Error sending email: {str(e)}") @@ -477,7 +476,7 @@ def delete_transaction(transaction_id:int): return format_response(False, "Transaction not found."), 404 @admin_required -def modify_balance(transaction_id:int, amount:int): +def modify_transaction_amount(transaction_id:str, amount:float): """Modifies the amount of a transaction in the database. If the transaction is not found, returns an error message.""" for transaction in session.query(Transaction).all(): if transaction.transaction_id == transaction_id: @@ -487,6 +486,17 @@ def modify_balance(transaction_id:int, amount:int): return format_response(True, f"Transaction ID: {transaction_id} has been modified."), 200 return format_response(False, "Transaction not found."), 404 +@admin_required +def modify_balance(account_id:str, balance:float): + """Modifies the balance of an account in the database. If the account is not found, returns an error message.""" + for account in session.query(Account).all(): + if account.account_id == account_id: + account.balance = balance + session.commit() + event_logger(f"Account ID: {account_id} has been modified by {flask_session['client_id']}.") + return format_response(True, f"Account ID: {account_id} has been modified."), 200 + return format_response(False, "Account not found."), 404 + @admin_required def test_account_balances(): """Checks all account balances in the database and returns a list of discrepancies."""