implement OTP
This commit is contained in:
51
api.yml
51
api.yml
@@ -15,8 +15,8 @@ tags:
|
||||
description: Operations for Bank Accounts
|
||||
- name: transaction
|
||||
description: Operations for Transactions
|
||||
- name: login
|
||||
description: Operations for Logging in
|
||||
- name: auth
|
||||
description: Operations for Authentication
|
||||
- name: system
|
||||
description: Operations for System
|
||||
- name: admin
|
||||
@@ -25,7 +25,7 @@ paths:
|
||||
/Client/Login:
|
||||
post:
|
||||
tags:
|
||||
- login
|
||||
- auth
|
||||
summary: Log in to the system
|
||||
description: Log in to the system
|
||||
operationId: manager.login
|
||||
@@ -50,7 +50,7 @@ paths:
|
||||
/Client/Logout:
|
||||
post:
|
||||
tags:
|
||||
- login
|
||||
- auth
|
||||
summary: Log out from the system
|
||||
description: Log out from the system
|
||||
operationId: manager.logout
|
||||
@@ -62,7 +62,7 @@ paths:
|
||||
/Client/Status:
|
||||
get:
|
||||
tags:
|
||||
- login
|
||||
- auth
|
||||
summary: Get login status
|
||||
description: Get login status
|
||||
operationId: manager.status
|
||||
@@ -74,7 +74,7 @@ paths:
|
||||
/Client/Password:
|
||||
put:
|
||||
tags:
|
||||
- client
|
||||
- auth
|
||||
summary: Change password
|
||||
description: Change password
|
||||
operationId: manager.change_password
|
||||
@@ -84,8 +84,7 @@ paths:
|
||||
description: ID of client to change password
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
type: string
|
||||
- name: password
|
||||
in: query
|
||||
description: New password
|
||||
@@ -98,6 +97,13 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: otp
|
||||
in: query
|
||||
description: OTP to verify
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: Password changed successfully
|
||||
@@ -105,6 +111,29 @@ paths:
|
||||
description: Old password incorrect
|
||||
'404':
|
||||
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:
|
||||
put:
|
||||
tags:
|
||||
@@ -551,6 +580,12 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: email
|
||||
in: query
|
||||
description: Email to initialise the system
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
|
||||
78
manager.py
78
manager.py
@@ -5,11 +5,14 @@ from class_client import Client
|
||||
from class_account import Account
|
||||
from class_transaction import Transaction
|
||||
from flask import jsonify, session as flask_session # Imports the Flask modules
|
||||
import hashlib # hashlib for password hashing
|
||||
import datetime # datetime for timestamps
|
||||
import uuid # uuid for unique identifiers
|
||||
from functools import wraps # functools for decorators / user login
|
||||
import hashlib # For password hashing
|
||||
import datetime # For timestamps
|
||||
import uuid # For unique identifiers
|
||||
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 emailer import send_email # Importing the emailer function
|
||||
|
||||
##############
|
||||
### System ###
|
||||
@@ -27,9 +30,15 @@ def generate_uuid(): # Generates a unique identifier for transactions
|
||||
def generate_uuid_short(): # Generates a short uuid
|
||||
return str(uuid.uuid4())[:8]
|
||||
|
||||
#############
|
||||
### Login ###
|
||||
#############
|
||||
def get_email(client_id:str):
|
||||
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
|
||||
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
|
||||
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 ###
|
||||
@@ -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
|
||||
|
||||
@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()
|
||||
if not is_admin and client_id != current_client_id:
|
||||
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)
|
||||
new_hash = hash_password(new_password)
|
||||
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:
|
||||
client.hash = new_hash
|
||||
session.commit()
|
||||
delete_otp(client_id)
|
||||
return "Password changed successfully.", 200
|
||||
return "Incorrect old password.", 400
|
||||
return f"client_id: {client_id} is not found.", 404
|
||||
@@ -321,8 +362,21 @@ def apply_fee(account_id:int, fee:float):
|
||||
|
||||
@admin_required
|
||||
def delete_transaction(transaction_id:int):
|
||||
DELETE_TRANSACTION = "DELETE FROM transaction WHERE transaction_id=?"
|
||||
return
|
||||
for transaction in session.query(Transaction).all():
|
||||
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
|
||||
def test_account_balances():
|
||||
@@ -369,10 +423,10 @@ def add_client(name:str, birthdate:str, address:str, phone_number:str, email:str
|
||||
session.commit()
|
||||
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
|
||||
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()
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user