implement OTP
This commit is contained in:
51
api.yml
51
api.yml
@@ -15,8 +15,8 @@ tags:
|
|||||||
description: Operations for Bank Accounts
|
description: Operations for Bank Accounts
|
||||||
- name: transaction
|
- name: transaction
|
||||||
description: Operations for Transactions
|
description: Operations for Transactions
|
||||||
- name: login
|
- name: auth
|
||||||
description: Operations for Logging in
|
description: Operations for Authentication
|
||||||
- name: system
|
- name: system
|
||||||
description: Operations for System
|
description: Operations for System
|
||||||
- name: admin
|
- name: admin
|
||||||
@@ -25,7 +25,7 @@ paths:
|
|||||||
/Client/Login:
|
/Client/Login:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- login
|
- auth
|
||||||
summary: Log in to the system
|
summary: Log in to the system
|
||||||
description: Log in to the system
|
description: Log in to the system
|
||||||
operationId: manager.login
|
operationId: manager.login
|
||||||
@@ -50,7 +50,7 @@ paths:
|
|||||||
/Client/Logout:
|
/Client/Logout:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- login
|
- auth
|
||||||
summary: Log out from the system
|
summary: Log out from the system
|
||||||
description: Log out from the system
|
description: Log out from the system
|
||||||
operationId: manager.logout
|
operationId: manager.logout
|
||||||
@@ -62,7 +62,7 @@ paths:
|
|||||||
/Client/Status:
|
/Client/Status:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- login
|
- auth
|
||||||
summary: Get login status
|
summary: Get login status
|
||||||
description: Get login status
|
description: Get login status
|
||||||
operationId: manager.status
|
operationId: manager.status
|
||||||
@@ -74,7 +74,7 @@ paths:
|
|||||||
/Client/Password:
|
/Client/Password:
|
||||||
put:
|
put:
|
||||||
tags:
|
tags:
|
||||||
- client
|
- auth
|
||||||
summary: Change password
|
summary: Change password
|
||||||
description: Change password
|
description: Change password
|
||||||
operationId: manager.change_password
|
operationId: manager.change_password
|
||||||
@@ -84,8 +84,7 @@ paths:
|
|||||||
description: ID of client to change password
|
description: ID of client to change password
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: string
|
||||||
format: int32
|
|
||||||
- name: password
|
- name: password
|
||||||
in: query
|
in: query
|
||||||
description: New password
|
description: New password
|
||||||
@@ -98,6 +97,13 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- name: otp
|
||||||
|
in: query
|
||||||
|
description: OTP to verify
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Password changed successfully
|
description: Password changed successfully
|
||||||
@@ -105,6 +111,29 @@ paths:
|
|||||||
description: Old password incorrect
|
description: Old password incorrect
|
||||||
'404':
|
'404':
|
||||||
description: client_id not found
|
description: client_id not found
|
||||||
|
/OTP/Generate:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- auth
|
||||||
|
summary: Generate OTP
|
||||||
|
description: Generate OTP
|
||||||
|
operationId: manager.generate_otp
|
||||||
|
parameters:
|
||||||
|
- name: client_id
|
||||||
|
in: query
|
||||||
|
description: ID of client to generate OTP
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OTP generated
|
||||||
|
'401':
|
||||||
|
description: Unauthorised
|
||||||
|
'400':
|
||||||
|
description: OTP not valid
|
||||||
|
'404':
|
||||||
|
description: client_id not found
|
||||||
/Client/Client:
|
/Client/Client:
|
||||||
put:
|
put:
|
||||||
tags:
|
tags:
|
||||||
@@ -551,6 +580,12 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- name: email
|
||||||
|
in: query
|
||||||
|
description: Email to initialise the system
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Successful operation
|
description: Successful operation
|
||||||
|
|||||||
80
manager.py
80
manager.py
@@ -5,11 +5,14 @@ from class_client import Client
|
|||||||
from class_account import Account
|
from class_account import Account
|
||||||
from class_transaction import Transaction
|
from class_transaction import Transaction
|
||||||
from flask import jsonify, session as flask_session # Imports the Flask modules
|
from flask import jsonify, session as flask_session # Imports the Flask modules
|
||||||
import hashlib # hashlib for password hashing
|
import hashlib # For password hashing
|
||||||
import datetime # datetime for timestamps
|
import datetime # For timestamps
|
||||||
import uuid # uuid for unique identifiers
|
import uuid # For unique identifiers
|
||||||
from functools import wraps # functools for decorators / user login
|
import random # For OTP generation
|
||||||
|
import time # For OTP generation
|
||||||
|
from functools import wraps # For decorators / user login
|
||||||
from database import * # Importing the database connection
|
from database import * # Importing the database connection
|
||||||
|
from emailer import send_email # Importing the emailer function
|
||||||
|
|
||||||
##############
|
##############
|
||||||
### System ###
|
### System ###
|
||||||
@@ -27,9 +30,15 @@ def generate_uuid(): # Generates a unique identifier for transactions
|
|||||||
def generate_uuid_short(): # Generates a short uuid
|
def generate_uuid_short(): # Generates a short uuid
|
||||||
return str(uuid.uuid4())[:8]
|
return str(uuid.uuid4())[:8]
|
||||||
|
|
||||||
#############
|
def get_email(client_id:str):
|
||||||
### Login ###
|
for client in session.query(Client).all():
|
||||||
#############
|
if client.client_id == client_id:
|
||||||
|
return client.email
|
||||||
|
return None
|
||||||
|
|
||||||
|
######################
|
||||||
|
### Authentication ###
|
||||||
|
######################
|
||||||
|
|
||||||
def login(client_id:str, password:str): # Logs in a user
|
def login(client_id:str, password:str): # Logs in a user
|
||||||
password_hash = hash_password(password)
|
password_hash = hash_password(password)
|
||||||
@@ -76,6 +85,35 @@ def get_current_client():
|
|||||||
is_admin = session.query(Client).filter_by(client_id=client).one_or_none().administrator
|
is_admin = session.query(Client).filter_by(client_id=client).one_or_none().administrator
|
||||||
return client, is_admin
|
return client, is_admin
|
||||||
|
|
||||||
|
otps = {} # Dictionary to store OTPs and their creation time
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def generate_otp(client_id:str): # Generates a one time password for a client
|
||||||
|
current_client_id, is_admin = get_current_client()
|
||||||
|
if not is_admin and client_id != current_client_id:
|
||||||
|
return jsonify({"error": "You can only view your own client information."}), 403
|
||||||
|
email = get_email(client_id)
|
||||||
|
if email:
|
||||||
|
password = int(random.randint(100000, 999999)) # Generate a 6-digit OTP
|
||||||
|
send_email(email, "Luxbank One Time Password", f"Your one time password is: {password}"), 200
|
||||||
|
otps[client_id] = (password, time.time()) # Store the OTP and the current time
|
||||||
|
return jsonify({"error": "Client not found"}), 404
|
||||||
|
|
||||||
|
def verify_otp(client_id:str, otp:int): # Verifies a one time password for a client
|
||||||
|
if client_id in otps and otps[client_id][0] == otp:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_otp(client_id:str): # Deletes a one time password for a client
|
||||||
|
if client_id in otps:
|
||||||
|
del otps[client_id]
|
||||||
|
|
||||||
|
def check_expired_otps(): # Checks and deletes expired OTPs
|
||||||
|
current_time = time.time()
|
||||||
|
expired_otps = [client_id for client_id, (otp, creation_time) in otps.items() if current_time - creation_time > 300] # Find OTPs older than 5 minutes
|
||||||
|
for client_id in expired_otps:
|
||||||
|
delete_otp(client_id)
|
||||||
|
|
||||||
|
|
||||||
##############
|
##############
|
||||||
### Client ###
|
### Client ###
|
||||||
@@ -121,10 +159,12 @@ def update_client(client_id:str, **kwargs): # Updates a client in the database
|
|||||||
return f"Client ID: {client_id} is not found." , 400
|
return f"Client ID: {client_id} is not found." , 400
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def change_password(client_id:str, password:str, new_password:str): # Changes the password of a client
|
def change_password(client_id:str, password:str, new_password:str, otp:int): # Changes the password of a client
|
||||||
current_client_id, is_admin = get_current_client()
|
current_client_id, is_admin = get_current_client()
|
||||||
if not is_admin and client_id != current_client_id:
|
if not is_admin and client_id != current_client_id:
|
||||||
return jsonify({"error": "You can only update your own password."}), 403
|
return jsonify({"error": "You can only update your own password."}), 403
|
||||||
|
if not verify_otp(client_id, otp):
|
||||||
|
return jsonify({"error": "Invalid OTP"}), 400
|
||||||
old_hash = hash_password(password)
|
old_hash = hash_password(password)
|
||||||
new_hash = hash_password(new_password)
|
new_hash = hash_password(new_password)
|
||||||
for client in session.query(Client).all():
|
for client in session.query(Client).all():
|
||||||
@@ -132,6 +172,7 @@ def change_password(client_id:str, password:str, new_password:str): # Changes th
|
|||||||
if client.hash == old_hash:
|
if client.hash == old_hash:
|
||||||
client.hash = new_hash
|
client.hash = new_hash
|
||||||
session.commit()
|
session.commit()
|
||||||
|
delete_otp(client_id)
|
||||||
return "Password changed successfully.", 200
|
return "Password changed successfully.", 200
|
||||||
return "Incorrect old password.", 400
|
return "Incorrect old password.", 400
|
||||||
return f"client_id: {client_id} is not found.", 404
|
return f"client_id: {client_id} is not found.", 404
|
||||||
@@ -321,8 +362,21 @@ def apply_fee(account_id:int, fee:float):
|
|||||||
|
|
||||||
@admin_required
|
@admin_required
|
||||||
def delete_transaction(transaction_id:int):
|
def delete_transaction(transaction_id:int):
|
||||||
DELETE_TRANSACTION = "DELETE FROM transaction WHERE transaction_id=?"
|
for transaction in session.query(Transaction).all():
|
||||||
return
|
if transaction.transaction_id == transaction_id:
|
||||||
|
session.delete(transaction)
|
||||||
|
session.commit()
|
||||||
|
return f"Transaction ID: {transaction_id} has been removed.", 400
|
||||||
|
return f"Transaction ID: {transaction_id} is not found.", 404
|
||||||
|
|
||||||
|
@admin_required
|
||||||
|
def modify_balance(transaction_id:int, amount:int):
|
||||||
|
for transaction in session.query(Transaction).all():
|
||||||
|
if transaction.transaction_id == transaction_id:
|
||||||
|
transaction.amount = amount
|
||||||
|
session.commit()
|
||||||
|
return f"Transaction ID: {transaction_id} has been modified.", 200
|
||||||
|
return f"Transaction ID: {transaction_id} is not found.", 404
|
||||||
|
|
||||||
@admin_required
|
@admin_required
|
||||||
def test_account_balances():
|
def test_account_balances():
|
||||||
@@ -369,10 +423,10 @@ def add_client(name:str, birthdate:str, address:str, phone_number:str, email:str
|
|||||||
session.commit()
|
session.commit()
|
||||||
return client_id, 200
|
return client_id, 200
|
||||||
|
|
||||||
def initialise_database(password:str):
|
def initialise_database(password:str, email:str):
|
||||||
existing_clients = session.query(Client).all() # Check if any clients exist in the database
|
existing_clients = session.query(Client).all() # Check if any clients exist in the database
|
||||||
if not existing_clients: # If no clients exist, create an administrator client
|
if not existing_clients: # If no clients exist, create an administrator client
|
||||||
add_client('ADMINISTRATOR', 'ADMINISTRATOR', 'ADMINISTRATOR', 'ADMINISTRATOR', 'ADMINISTRATOR', password) # Add the administrator client
|
add_client('ADMINISTRATOR', 'ADMINISTRATOR', 'ADMINISTRATOR', 'ADMINISTRATOR', email, password) # Add the administrator client
|
||||||
session.commit()
|
session.commit()
|
||||||
admin_client = session.query(Client).filter_by(name='ADMINISTRATOR').one() # Retrieve the administrator client
|
admin_client = session.query(Client).filter_by(name='ADMINISTRATOR').one() # Retrieve the administrator client
|
||||||
admin_client.administrator = 1 # Set the new client as an administrator
|
admin_client.administrator = 1 # Set the new client as an administrator
|
||||||
|
|||||||
Reference in New Issue
Block a user