Continue working on CLI Implementation

This commit is contained in:
Lucas Mathews
2024-06-21 00:36:51 +02:00
parent a8f4a061ca
commit 2dfdd6d6a5
7 changed files with 208 additions and 124 deletions

View File

@@ -74,12 +74,19 @@ Depending on what you are making, it can be a good idea to include screenshots o
<br> <br>
## Configuration ## Configuration
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. 1. Clone and cd to repository
2. In /application, /cli, and /server create a copy of the template .ini file removing the leading template_
3. Generate sesssion key with generate_seesion_key.py and add to /server/bank.ini
4. Add SMTP details if required. If SMTP is set to False OTP will not work, but an OTP code is still required. In this case, just enter any 6 digit number in.
5. Run docker command: docker-compose up -d
<br> <br>
### Email Configuration ### Email Configuration
In order for OTP to work, an SMTP server is needed In order for OTP to work, an SMTP server is needed.
```ini ```ini
[smtp] [smtp]

View File

@@ -43,7 +43,6 @@ def logout_client():
response = requests.post(CONFIG["server"]["url"] + "/Client/Logout", cookies=session_data['session_cookie']) response = requests.post(CONFIG["server"]["url"] + "/Client/Logout", cookies=session_data['session_cookie'])
return response return response
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
response = Response() response = Response()
response.status_code = 500 response.status_code = 500
response._content = b'{"success": false, "message": "Could not connect to the server. Please try again later."}' response._content = b'{"success": false, "message": "Could not connect to the server. Please try again later."}'
@@ -58,7 +57,6 @@ def get_client(client_id):
response.raise_for_status() response.raise_for_status()
return response.json() return response.json()
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."} return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def update_client(client_id, otp_code, email=None, phone_number=None, address=None): def update_client(client_id, otp_code, email=None, phone_number=None, address=None):

View File

@@ -1,14 +1,26 @@
# Lucas Mathews - Fontys Student ID: 5023572 # Lucas Mathews - Fontys Student ID: 5023572
# Banking System CLI Utility # Banking System CLI Utility
import json
import argparse import argparse
import sys import sys
from connection import login, logout from config import CONFIG
from getpass import getpass
from connection import login, logout, get_client
from test_database_generator import generate_test_database
def show_menu():
print("\nAvailable options:")
print("1. Logout")
print("2. New user")
print("3. Add test users")
print("4. Exit")
def main(): def main():
parser = argparse.ArgumentParser(description='Banking System CLI Utility') parser = argparse.ArgumentParser(description='Banking System CLI Utility')
parser.add_argument('-u', '--username', type=str, help='Username for login') parser.add_argument('-u', '--username', type=str, default=CONFIG["client"]["default_id"], help='Username for login')
parser.add_argument('-p', '--password', type=str, help='Password for login') parser.add_argument('-p', '--password', type=str, default=CONFIG["client"]["default_password"], help='Password for login')
subparsers = parser.add_subparsers(dest='command') subparsers = parser.add_subparsers(dest='command')
@@ -17,7 +29,59 @@ def main():
args = parser.parse_args() args = parser.parse_args()
if args.command == 'login': if not args.command:
while True:
if not args.username:
args.username = input("Enter username: ")
if not args.password:
args.password = getpass("Enter password: ")
response = login(args.username, args.password)
print(response)
if response['success']:
print(f"Login successful: {response['message']}")
try:
with open('session_data.json', 'r') as f:
session_data = json.load(f)
client_id = session_data['client_id']
client_info = get_client(client_id)
if client_info.get('success'):
client = client_info['data']
name = client['name']
print(f"Welcome, {name}!")
except Exception as e:
print(f"Error loading client data: {str(e)}")
while True:
show_menu()
option = input("Choose an option: ")
if option == "1":
response = logout()
json_response = response.json()
if json_response['success']:
print(f"Logout successful: {json_response['message']}")
else:
print(f"Logout failed: {json_response['message']}")
args.username = None
args.password = None
elif option == "2":
print("New user option selected.")
# Implement new user functionality here
elif option == "3":
print("Add test users option selected.")
generate_test_database(args.username, args.password)
elif option == "4":
print("Exiting...")
break
else:
print("Invalid option. Please try again.")
break
else:
print(f"Login failed: {response['message']}. Please try again.")
args.username = None
args.password = None
elif args.command == 'login':
if not args.username or not args.password: if not args.username or not args.password:
print("Username and password are required for login.") print("Username and password are required for login.")
sys.exit(1) sys.exit(1)
@@ -25,13 +89,13 @@ def main():
if response['success']: if response['success']:
print(f"Login successful: {response['message']}") print(f"Login successful: {response['message']}")
else: else:
print(f"Login failed: {response['message']}") print(f"Login failed: {response['message']}. Please try again.")
elif args.command == 'logout': elif args.command == 'logout':
response = logout() response = logout()
if response['success']: if response['success']:
print(f"Logout successful: {response['message']}") print(f"Logout successful: {response['message']}")
else: else:
print(f"Check Credentials: {response['message']}") print(f"Logout failed: {response['message']}")
else: else:
print("Invalid command. Use 'login' or 'logout'.") print("Invalid command. Use 'login' or 'logout'.")

View File

@@ -1,10 +1,11 @@
# Lucas Mathews - Fontys Student ID: 5023572 # Lucas Mathews - Fontys Student ID: 5023572
# Banking System CLI Utility Connection File # Banking System CLI Utility Connection File
import requests
from config import CONFIG
import hashlib import hashlib
import json import json
import requests
from requests.models import Response
from config import CONFIG
############## ##############
### System ### ### System ###
@@ -14,7 +15,7 @@ def format_balance(balance):
"""Formats the balance as a currency string with comma separators.""" """Formats the balance as a currency string with comma separators."""
return f"{balance:,.2f}" return f"{balance:,.2f}"
def hash_password(password:str): def hash_password(password: str):
"""Hashes a password using the SHA-512 algorithm and returns the hexadecimal representation of the hash.""" """Hashes a password using the SHA-512 algorithm and returns the hexadecimal representation of the hash."""
return hashlib.sha512(password.encode()).hexdigest() return hashlib.sha512(password.encode()).hexdigest()
@@ -30,6 +31,12 @@ def login(client_id, password):
response.raise_for_status() response.raise_for_status()
response_content = json.loads(response.content) # Parse the JSON response response_content = json.loads(response.content) # Parse the JSON response
if response.status_code == 200 and response_content.get('success'): if response.status_code == 200 and response_content.get('success'):
session_data = {
'session_cookie': response.cookies.get_dict(),
'client_id': client_id
}
with open('session_data.json', 'w') as f:
json.dump(session_data, f)
return {'success': True, 'message': response_content.get('message')} return {'success': True, 'message': response_content.get('message')}
else: else:
return {'success': False, 'message': response_content.get('message')} return {'success': False, 'message': response_content.get('message')}
@@ -37,14 +44,50 @@ def login(client_id, password):
return {'success': False, 'message': str(e)} return {'success': False, 'message': str(e)}
def logout(): def logout():
url = f"{CONFIG['server']['url']}/logout" """Logs out the current client by deleting the session data."""
try: try:
response = requests.get(url) with open('session_data.json', 'r') as f:
response.raise_for_status() session_data = json.load(f)
response_content = json.loads(response.content) # Parse the JSON response response = requests.post(CONFIG["server"]["url"] + "/Client/Logout", cookies=session_data['session_cookie'])
if response.status_code == 200 and response_content.get('success'): return response
return {'success': True, 'message': response_content.get('message')}
else:
return {'success': False, 'message': response_content.get('message')}
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return {'success': False, 'message': str(e)} response = Response()
response.status_code = 500
response._content = b'{"success": false, "message": "Could not connect to the server. Please try again later."}'
return response
def get_client(client_id):
"""Retrieves the client details for the given client_id."""
try:
with open('session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(CONFIG["server"]["url"] + "/Client", 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."}
def add_client(name, birthdate, address, phone_number, email, password, notes):
data = {
"name": name,
"birthdate": birthdate,
"address": address,
"phone_number": phone_number,
"email": email,
"password": password,
"notes": notes
}
try:
with open('session_data.json', 'r') as f:
session_data = json.load(f)
response = requests.get(CONFIG["server"]["url"] + "/Client", cookies=session_data['session_cookie'], params=data)
response.raise_for_status()
if response.status_code == 200:
print("Client retrieved successfully.")
else:
print(f"Failed to retrieve client. 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."}

View File

@@ -4,5 +4,5 @@ port = 8066
url = http://127.0.0.1:8066 url = http://127.0.0.1:8066
[client] [client]
default_id = default_id = None
default_password = default_password = None

View File

@@ -7,122 +7,93 @@
# creates a new SQLite database called test_database.db and writes the test data to the database. The client ID of the administrator account and the # creates a new SQLite database called test_database.db and writes the test data to the database. The client ID of the administrator account and the
# password for the administrator account. # password for the administrator account.
ADMIN_EMAIL = "lmath56@hotmail.com" # Email address of the administrator account
from faker import Faker from faker import Faker
import class_account from connection import login, add_client, logout
import class_client
import class_transaction
from connection import login, add_client, add_account, add_transaction, logout
import argparse
import random import random
import datetime import datetime
import hashlib import hashlib
import uuid import uuid
ADMIN_EMAIL = "lmath56@hotmail.com" # Email address of the administrator account
def generate_hash(): # Creates a hash for a password def generate_hash(): # Creates a hash for a password
seed = str(random.random()).encode('utf-8') seed = str(random.random()).encode('utf-8')
return hashlib.sha512(seed).hexdigest() return hashlib.sha512(seed).hexdigest()
def generate_uuid(): # Generates a unique identifier for transactions
return str(uuid.uuid4())
def generate_uuid_short(): # Generates a short uuid for accounts and clients
return str(uuid.uuid4())[:8]
def timestamp(): # Returns the current timestamp def timestamp(): # Returns the current timestamp
return (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) return (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
fake = Faker() # Create a Faker instance def generate_test_database(client_id, password):
fake = Faker() # Create a Faker instance
all_account_ids = [] # List to store all account IDs all_account_ids = [] # List to store all account IDs
# Set up argument parsing # Log in as the admin using provided username and password
parser = argparse.ArgumentParser(description="Generate test database for the banking system.") client_hash = hashlib.sha512(password.encode()).hexdigest()
parser.add_argument('-u', '--username', required=True, help="Username for admin login") client = login(client_id, client_hash)
parser.add_argument('-p', '--password', required=True, help="Password for admin login")
args = parser.parse_args() if client is not None: # Check if login was successful
print("Admin logged in successfully")
# Log in as the admin using provided username and password for i in range(50): # Generate 50 clients
client_id = args.username is_administrator = 1 if i == 0 else 0 # Set the first client as an administrator
client_hash = hashlib.sha512(args.password.encode()).hexdigest() # Set the password hash for the first account so that the password is "Happymeal1"
client = login(client_id, client_hash) password = "Happymeal1" if i == 0 else generate_hash()
name = "ADMIN" if i == 0 else fake.name()
birthdate = "ADMIN" if i == 0 else fake.date_of_birth(minimum_age=18, maximum_age=90)
address = "ADMIN" if i == 0 else fake.address()
phone_number = "ADMIN" if i == 0 else fake.phone_number()
email = ADMIN_EMAIL if i == 0 else fake.email()
notes = fake.text(max_nb_chars=50)
if client is not None: # Check if login was successful # Add client using add_client function
print("Admin logged in successfully") response = add_client(name, birthdate, address, phone_number, email, password, notes)
print(response) # Print the response message
for i in range(50): # Generate 50 clients for j in range(2): # Each client has 2 accounts
is_administrator = 1 if i == 0 else 0 # Set the first client as an administrator balance = 1000 # Initialize balance to 1000
# Set the password hash for the first account so that the password is "Happymeal1" account_type = random.choice(['Spending', 'Savings'])
password = "Happymeal1" if i == 0 else generate_hash() account_notes = fake.text(max_nb_chars=50)
client_id = generate_uuid_short()
client_name = "ADMIN" if i == 0 else fake.name()
birthdate = "ADMIN" if i == 0 else fake.date_of_birth(minimum_age=18, maximum_age=90)
opening_timestamp = timestamp() if i == 0 else fake.date_this_century()
address = "ADMIN" if i == 0 else fake.address()
phone_number = "ADMIN" if i == 0 else fake.phone_number()
email = ADMIN_EMAIL if i == 0 else fake.email()
notes = fake.text(max_nb_chars=50)
# Add client using add_client function # Add account using add_account function
client_response = add_client( account_response = add_account(
name=client_name, client_id=client_id,
birthdate=birthdate, description=fake.text(max_nb_chars=200),
address=address, account_type=account_type,
phone_number=phone_number, notes=account_notes
email=email,
password=password,
notes=notes
)
print(client_response[1]) # Print the response message
for j in range(2): # Each client has 2 accounts
account_id = generate_uuid_short()
balance = 1000 # Initialize balance to 1000
account_type = random.choice(['Spending', 'Savings'])
account_notes = fake.text(max_nb_chars=50)
# Add account using add_account function
account_response = add_account(
client_id=client_id,
description=fake.text(max_nb_chars=200),
account_type=account_type,
notes=account_notes
)
print(account_response[1]) # Print the response message
for k in range(40): # 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)
if transaction_type == 'Withdrawal' and balance - amount < 0: # Skip withdrawal if it would make balance negative
continue
if transaction_type == 'Deposit': # Update balance based on transaction type
balance += amount
elif transaction_type == 'Withdrawal':
balance -= amount
transaction_description = fake.text(max_nb_chars=50)
recipient_account_id = random.choice(all_account_ids)
# Add transaction using add_transaction function
transaction_response = add_transaction(
amount=amount,
account_id=account_id,
recipient_account_id=recipient_account_id,
otp_code=123456, # Replace with actual OTP verification code
description=transaction_description
) )
print(transaction_response[1]) # Print the response message print(account_response[1]) # Print the response message
all_account_ids.append(account_id) for k in range(40): # Each account has 40 transactions
logout() # Log out of the admin account if not all_account_ids: # Skip creating a transaction if there are no accounts yet
print(f"The client_id of the administrator account of this test database is: {all_account_ids[0]}. The password is: Happymeal1") continue
else:
print("Admin login failed.") transaction_type = random.choice(['Deposit', 'Withdrawal'])
amount = random.randint(1, 200)
if transaction_type == 'Withdrawal' and balance - amount < 0: # Skip withdrawal if it would make balance negative
continue
if transaction_type == 'Deposit': # Update balance based on transaction type
balance += amount
elif transaction_type == 'Withdrawal':
balance -= amount
transaction_description = fake.text(max_nb_chars=50)
recipient_account_id = random.choice(all_account_ids)
# Add transaction using add_transaction function
transaction_response = add_transaction(
amount=amount,
recipient_account_id=recipient_account_id,
otp_code=123456, # Replace with actual OTP verification code
description=transaction_description
)
print(transaction_response[1]) # Print the response message
all_account_ids.append(account_id)
logout() # Log out of the admin account
print(f"The client_id of the administrator account of this test database is: {all_account_ids[0]}. The password is: Happymeal1")
else:
print("Admin login failed.")

View File

@@ -349,9 +349,10 @@ def add_transaction(amount: float, account_id: str, recipient_account_id: str, o
current_client_id, is_admin = get_current_client() current_client_id, is_admin = get_current_client()
if not is_admin and account_id != current_client_id: if not is_admin and account_id != current_client_id:
return format_response(False, "You can only view your own client information."), 403 return format_response(False, "You can only view your own client information."), 403
otp_verified = verify_otp(current_client_id, otp_code) if not is_admin:
if not otp_verified: otp_verified = verify_otp(current_client_id, otp_code)
return format_response(False, "Invalid OTP."), 400 if not otp_verified:
return format_response(False, "Invalid OTP."), 400
transaction_id = generate_uuid() transaction_id = generate_uuid()
account_from = session.query(Account).filter_by(account_id=account_id).one_or_none() account_from = session.query(Account).filter_by(account_id=account_id).one_or_none()