diff --git a/__pycache__/class_account.cpython-312.pyc b/__pycache__/class_account.cpython-312.pyc new file mode 100644 index 0000000..98737b1 Binary files /dev/null and b/__pycache__/class_account.cpython-312.pyc differ diff --git a/__pycache__/class_base.cpython-312.pyc b/__pycache__/class_base.cpython-312.pyc new file mode 100644 index 0000000..462fd02 Binary files /dev/null and b/__pycache__/class_base.cpython-312.pyc differ diff --git a/__pycache__/class_client.cpython-312.pyc b/__pycache__/class_client.cpython-312.pyc new file mode 100644 index 0000000..8544a71 Binary files /dev/null and b/__pycache__/class_client.cpython-312.pyc differ diff --git a/__pycache__/class_transaction.cpython-312.pyc b/__pycache__/class_transaction.cpython-312.pyc new file mode 100644 index 0000000..3b10f0d Binary files /dev/null and b/__pycache__/class_transaction.cpython-312.pyc differ diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..10caa87 Binary files /dev/null and b/__pycache__/config.cpython-312.pyc differ diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000..7fae991 Binary files /dev/null and b/__pycache__/database.cpython-312.pyc differ diff --git a/__pycache__/emailer.cpython-312.pyc b/__pycache__/emailer.cpython-312.pyc new file mode 100644 index 0000000..208fc96 Binary files /dev/null and b/__pycache__/emailer.cpython-312.pyc differ diff --git a/__pycache__/manager.cpython-312.pyc b/__pycache__/manager.cpython-312.pyc new file mode 100644 index 0000000..b56aeaf Binary files /dev/null and b/__pycache__/manager.cpython-312.pyc differ diff --git a/api.yml b/api.yml index 061a0fb..1153eaa 100644 --- a/api.yml +++ b/api.yml @@ -112,7 +112,7 @@ paths: '404': description: client_id not found /OTP/Generate: - get: + post: tags: - auth summary: Generate OTP @@ -148,6 +148,13 @@ paths: required: true schema: type: string + - name: otp_code + in: query + description: OTP to verify + required: true + schema: + type: integer + format: int32 - name: name in: query description: Client Name @@ -187,8 +194,12 @@ paths: responses: '200': description: Successful operation - '400': + '404': description: Invalid Client ID supplied + '403': + description: Unauthorised + '405': + description: OTP not valid get: tags: - client diff --git a/application/__pycache__/config.cpython-312.pyc b/application/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..fb7b6f6 Binary files /dev/null and b/application/__pycache__/config.cpython-312.pyc differ diff --git a/application/__pycache__/connection.cpython-312.pyc b/application/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000..e78b922 Binary files /dev/null and b/application/__pycache__/connection.cpython-312.pyc differ diff --git a/application/connection.py b/application/connection.py index d9b5f72..155a70e 100644 --- a/application/connection.py +++ b/application/connection.py @@ -53,12 +53,12 @@ def get_client(client_id): print(f"RequestException: {e}") return {'success': False, 'message': "Could not connect to the server. Please try again later."} -def update_client(client_id, email=None, phone_number=None, address=None): +def update_client(client_id, otp_code, email=None, phone_number=None, address=None): """Updates the client details for the given client_id.""" try: with open('application\\session_data.json', 'r') as f: session_data = json.load(f) - params = {'client_id': client_id} + params = {'client_id': client_id, 'otp_code': otp_code} if email is not None: params['email'] = email if phone_number is not None: @@ -147,3 +147,16 @@ def new_transaction(account): 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 generate_otp(): + """Generates a new OTP for the current client, which is sent by Email.""" + try: + with open('application\\session_data.json', 'r') as f: + session_data = json.load(f) + client_id = session_data['client_id'] + response = requests.post(CONFIG["server"]["url"] + "/OTP/Generate", cookies=session_data['session_cookie'], params={'client_id': client_id}) + response.raise_for_status() + return response.json() + 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 diff --git a/application/dashboard.py b/application/dashboard.py index 7d1217f..3bce439 100644 --- a/application/dashboard.py +++ b/application/dashboard.py @@ -3,12 +3,13 @@ import customtkinter import json import os from config import CONFIG -from connection import logout_client, get_client, update_client, get_accounts, format_balance +from connection import logout_client, get_client, update_client, get_accounts, format_balance, generate_otp # Global variables email_entry = None phone_entry = None address_entry = None +otp_entry = None frame = None ################# @@ -66,10 +67,11 @@ def display_client_info(): def edit_details(): """Opens a new window for editing client details.""" - global edit_window, email_entry, phone_entry, address_entry + global edit_window, email_entry, phone_entry, address_entry, otp_entry edit_window = customtkinter.CTkToplevel(root) edit_window.title("Edit Details") - edit_window.geometry("300x200") + edit_window.geometry("300x300") + edit_window.iconbitmap("application/luxbank.ico") edit_window.attributes('-topmost', True) email_label = customtkinter.CTkLabel(edit_window, text="Email: ") @@ -87,31 +89,38 @@ def edit_details(): address_label.pack() address_entry.pack() - save_button = customtkinter.CTkButton(edit_window, text="Save", command=save_details) + # Add MFA verify button and text box + mfa_button = customtkinter.CTkButton(edit_window, text="Get OTP Code", command=generate_otp) + mfa_button.pack() + + mfa_label = customtkinter.CTkLabel(edit_window, text="OTP Code: ") + otp_entry = customtkinter.CTkEntry(edit_window) + mfa_label.pack() + otp_entry.pack() + + save_button = customtkinter.CTkButton(edit_window, text="Verify MFA and Save", command=save_details) save_button.pack() edit_window.lift() def save_details(): """Saves the updated client details.""" - global edit_window, email_entry, phone_entry, address_entry + global edit_window, otp_entry, email_entry, phone_entry, address_entry new_email = email_entry.get() if email_entry.get() != '' else None new_phone = phone_entry.get() if phone_entry.get() != '' else None new_address = address_entry.get() if address_entry.get() != '' else None - try: - with open('application\\session_data.json', 'r') as f: - session_data = json.load(f) - client_id = session_data['client_id'] - if not messagebox.askyesno("Confirmation", "Are you sure you want to update the details?"): - return - result = update_client(client_id, new_email, new_phone, new_address) - if result['success']: - display_client_info() - else: - messagebox.showerror("Update Failed", result.get('message', 'Unknown error')) - edit_window.destroy() - except Exception as e: - messagebox.showerror("Error", f"Could not update details: {e}") - + otp_code = otp_entry.get() + with open('application\\session_data.json', 'r') as f: + session_data = json.load(f) + client_id = session_data['client_id'] + if not messagebox.askyesno("Confirmation", "Are you sure you want to update the details?"): + return + result = update_client(client_id, otp_code, new_email, new_phone, new_address) + if result['success']: + display_client_info() + else: + messagebox.showerror("Update Failed", result.get('message', 'Unknown error')) + edit_window.destroy() + def populate_table(): """Populates the accounts table with client accounts.""" try: @@ -160,9 +169,14 @@ welcome_label.pack(pady=20) display_client_info() +# Create a logout button logout_button = customtkinter.CTkButton(root, text="Logout", command=logout) logout_button.pack(pady=15) +# Create the MFA button +mfa_button = customtkinter.CTkButton(root, text="MFA", command=generate_otp) +mfa_button.pack(pady=15, side='left') + # Create a frame for the table table_frame = ttk.Frame(root) table_frame.pack(side='right', fill='both', expand=True) diff --git a/application/login.py b/application/login.py index a9ad6db..4a02854 100644 --- a/application/login.py +++ b/application/login.py @@ -53,10 +53,13 @@ def change_dark_theme(): ############## # Set appearance mode based on configuration -if CONFIG["preferences"]["dark_theme"] == "dark": - customtkinter.set_appearance_mode("dark") +if "preferences" in CONFIG and "dark_theme" in CONFIG["preferences"]: + if CONFIG["preferences"]["dark_theme"] == "dark": + customtkinter.set_appearance_mode("dark") + else: + customtkinter.set_appearance_mode("light") else: - customtkinter.set_appearance_mode("light") + customtkinter.set_appearance_mode("dark") # Initialize the main window root = customtkinter.CTk() diff --git a/application/session_data.json b/application/session_data.json index 9087467..084acf3 100644 --- a/application/session_data.json +++ b/application/session_data.json @@ -1 +1 @@ -{"session_cookie": {"session": "UHtXXDwzE9iVXmMpFHD9BYA-ztYORiBP-xfRImGQDJQ"}, "client_id": "31d90aad"} \ No newline at end of file +{"session_cookie": {"session": "mMTYW-c_n0BE-l7MENT8A1h2Rg4UUrNRJYl7NvXTcS4"}, "client_id": "31d90aad"} \ No newline at end of file diff --git a/flask_session/0f8e743989515f4aaf958d76f346210f b/flask_session/0f8e743989515f4aaf958d76f346210f new file mode 100644 index 0000000..dfe006c Binary files /dev/null and b/flask_session/0f8e743989515f4aaf958d76f346210f differ diff --git a/flask_session/1c0bd59593392e52337d73665704d133 b/flask_session/1c0bd59593392e52337d73665704d133 new file mode 100644 index 0000000..96d2935 Binary files /dev/null and b/flask_session/1c0bd59593392e52337d73665704d133 differ diff --git a/flask_session/2029240f6d1128be89ddc32729463129 b/flask_session/2029240f6d1128be89ddc32729463129 new file mode 100644 index 0000000..5e692b0 Binary files /dev/null and b/flask_session/2029240f6d1128be89ddc32729463129 differ diff --git a/flask_session/2892d6f928e4706b8b53976f24e16df9 b/flask_session/2892d6f928e4706b8b53976f24e16df9 new file mode 100644 index 0000000..78da41c Binary files /dev/null and b/flask_session/2892d6f928e4706b8b53976f24e16df9 differ diff --git a/flask_session/2aeb459d854ed6b531157b1f07263701 b/flask_session/2aeb459d854ed6b531157b1f07263701 new file mode 100644 index 0000000..d6497be Binary files /dev/null and b/flask_session/2aeb459d854ed6b531157b1f07263701 differ diff --git a/flask_session/4d439b134cc69940f885e01fb69fa2af b/flask_session/4d439b134cc69940f885e01fb69fa2af new file mode 100644 index 0000000..04c5181 Binary files /dev/null and b/flask_session/4d439b134cc69940f885e01fb69fa2af differ diff --git a/flask_session/543f6931cd6b44c3c94cadb682baadb6 b/flask_session/543f6931cd6b44c3c94cadb682baadb6 new file mode 100644 index 0000000..9739f27 Binary files /dev/null and b/flask_session/543f6931cd6b44c3c94cadb682baadb6 differ diff --git a/flask_session/5bd955d64e05f26ba5ef180a04500110 b/flask_session/5bd955d64e05f26ba5ef180a04500110 new file mode 100644 index 0000000..3f74ccc Binary files /dev/null and b/flask_session/5bd955d64e05f26ba5ef180a04500110 differ diff --git a/flask_session/5d5c04d15f0f8cbf4721083899098bc5 b/flask_session/5d5c04d15f0f8cbf4721083899098bc5 new file mode 100644 index 0000000..745db08 Binary files /dev/null and b/flask_session/5d5c04d15f0f8cbf4721083899098bc5 differ diff --git a/flask_session/6dbbe99eb2379d46adb417f7cb73998a b/flask_session/6dbbe99eb2379d46adb417f7cb73998a new file mode 100644 index 0000000..beb9d02 Binary files /dev/null and b/flask_session/6dbbe99eb2379d46adb417f7cb73998a differ diff --git a/flask_session/93cd60fca3662b3b62fa48162b37f0fc b/flask_session/93cd60fca3662b3b62fa48162b37f0fc new file mode 100644 index 0000000..ba18594 Binary files /dev/null and b/flask_session/93cd60fca3662b3b62fa48162b37f0fc differ diff --git a/flask_session/b374a533064d4f726057d2daf5465b06 b/flask_session/b374a533064d4f726057d2daf5465b06 new file mode 100644 index 0000000..bd95bf9 Binary files /dev/null and b/flask_session/b374a533064d4f726057d2daf5465b06 differ diff --git a/flask_session/c2ca2782adffe80ef938353324e18126 b/flask_session/c2ca2782adffe80ef938353324e18126 new file mode 100644 index 0000000..9c4b9c4 Binary files /dev/null and b/flask_session/c2ca2782adffe80ef938353324e18126 differ diff --git a/flask_session/d1a91b81953dba7179a749524fd7b5d8 b/flask_session/d1a91b81953dba7179a749524fd7b5d8 new file mode 100644 index 0000000..b266ccf Binary files /dev/null and b/flask_session/d1a91b81953dba7179a749524fd7b5d8 differ diff --git a/flask_session/e80c3442482b04dffebe9de33c2adace b/flask_session/e80c3442482b04dffebe9de33c2adace new file mode 100644 index 0000000..bd7b030 Binary files /dev/null and b/flask_session/e80c3442482b04dffebe9de33c2adace differ diff --git a/flask_session/e9087f19cb0516ef0a65e6899b4121b2 b/flask_session/e9087f19cb0516ef0a65e6899b4121b2 new file mode 100644 index 0000000..8bee529 Binary files /dev/null and b/flask_session/e9087f19cb0516ef0a65e6899b4121b2 differ diff --git a/manager.py b/manager.py index 08f7429..c870691 100644 --- a/manager.py +++ b/manager.py @@ -162,9 +162,11 @@ def get_client(client_id:str): return format_response(False, "Client not found."), 404 @login_required -def update_client(client_id:str, **kwargs): +def update_client(client_id:str, otp_code:int, **kwargs): """Updates a client in the database. If the client is not found, returns an error message.""" current_client_id, is_admin = get_current_client() + if not verify_otp(current_client_id, otp_code): + return format_response(False, "Invalid OTP."), 405 if not is_admin and client_id != current_client_id: return format_response(False, "You can only view your own client information."), 403 for client in session.query(Client).all(): diff --git a/test_database.db b/test_database.db index a9cfd26..4129f7e 100644 Binary files a/test_database.db and b/test_database.db differ