From 0f6d2b1219ad4282bbe32463b2dd251e1effbdb4 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:42:07 +0100 Subject: [PATCH] [add]: appId and apiKey validation --- .gitignore | 1 - app.log | 6 ++++++ app/middlewares/__init__.py | 1 + app/middlewares/app_id_checker.py | 28 +++++++++++++++++----------- app/middlewares/verify_api_key.py | 30 ++++++++++++++++++------------ app/routes/routes.py | 27 +++++++++++++++++++++++---- 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 78b08ab..1541da5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -# Byte-compiled / optimized / DLL files __pycache__/ .env \ No newline at end of file diff --git a/app.log b/app.log index 907c315..ef7a054 100644 --- a/app.log +++ b/app.log @@ -125,3 +125,9 @@ TypeError: require_api_key() missing 1 required positional argument: 'f' 2025-03-21 09:16:24,496 - INFO - before_request middleware triggered 2025-03-21 09:16:24,496 - INFO - Processing Disbursement request 2025-03-21 09:16:24,498 - ERROR - Validation Error: {'productId': ['Missing data for required field.'], 'debtId': ['Missing data for required field.'], 'collectAmountMgtFee': ['Missing data for required field.'], 'collectAmountVAT': ['Missing data for required field.'], 'provideAmount': ['Missing data for required field.'], 'requestId': ['Missing data for required field.'], 'collectAmountInsurance': ['Missing data for required field.'], 'countryId': ['Missing data for required field.'], 'channel': ['Unknown field.'], '$type': ['Unknown field.'], 'msisdn': ['Unknown field.'], 'countryCode': ['Unknown field.'], 'lienAmount': ['Unknown field.']} +2025-03-21 09:40:40,601 - ERROR - Unauthorized access: Invalid API key. +2025-03-21 09:41:09,390 - INFO - Processing Disbursement request +2025-03-21 09:41:09,396 - ERROR - Validation Error: {'requestId': ['Missing data for required field.'], 'productId': ['Missing data for required field.'], 'collectAmountMgtFee': ['Missing data for required field.'], 'collectAmountVAT': ['Missing data for required field.'], 'provideAmount': ['Missing data for required field.'], 'countryId': ['Missing data for required field.'], 'debtId': ['Missing data for required field.'], 'collectAmountInsurance': ['Missing data for required field.'], 'channel': ['Unknown field.'], 'lienAmount': ['Unknown field.'], '$type': ['Unknown field.'], 'msisdn': ['Unknown field.'], 'countryCode': ['Unknown field.']} +2025-03-21 09:41:13,582 - ERROR - Unauthorized access: Missing App-ID. +2025-03-21 09:41:16,993 - INFO - Processing Disbursement request +2025-03-21 09:41:16,995 - ERROR - Validation Error: {'requestId': ['Missing data for required field.'], 'productId': ['Missing data for required field.'], 'collectAmountMgtFee': ['Missing data for required field.'], 'collectAmountVAT': ['Missing data for required field.'], 'provideAmount': ['Missing data for required field.'], 'countryId': ['Missing data for required field.'], 'debtId': ['Missing data for required field.'], 'collectAmountInsurance': ['Missing data for required field.'], 'channel': ['Unknown field.'], 'lienAmount': ['Unknown field.'], '$type': ['Unknown field.'], 'msisdn': ['Unknown field.'], 'countryCode': ['Unknown field.']} diff --git a/app/middlewares/__init__.py b/app/middlewares/__init__.py index f00c7b7..8a0d8bb 100644 --- a/app/middlewares/__init__.py +++ b/app/middlewares/__init__.py @@ -1 +1,2 @@ from .verify_api_key import require_api_key +from .app_id_checker import require_app_id \ No newline at end of file diff --git a/app/middlewares/app_id_checker.py b/app/middlewares/app_id_checker.py index a826aac..dd74af5 100644 --- a/app/middlewares/app_id_checker.py +++ b/app/middlewares/app_id_checker.py @@ -1,20 +1,26 @@ +from functools import wraps from flask import request from app.helpers.response_helper import ResponseHelper from app.utils.logger import logger import os -VALID_APP_IDS = os.getenv("VALID_APP_ID", "") +# Load valid App-IDs from environment variables (comma-separated list) +VALID_APP_ID = os.getenv("VALID_APP_ID", "app1,app2,app3").split(",") -def require_app_id(): - """Middleware to check if App-ID is present and valid.""" - app_id = request.headers.get("App-ID") +def require_app_id(f): + """Decorator to enforce App-ID validation.""" + @wraps(f) + def decorated_function(*args, **kwargs): + app_id = request.headers.get("App-ID") - if not app_id: - logger.error("Unauthorized access: Missing App-ID.") - return ResponseHelper.unauthorized("Missing App-ID") + if not app_id: + logger.error("Unauthorized access: Missing App-ID.") + return ResponseHelper.unauthorized("Missing App-ID") - if app_id not in VALID_APP_IDS: - logger.error(f"Unauthorized access: Invalid App-ID {app_id}.") - return ResponseHelper.unauthorized("Invalid App-ID") + if app_id not in VALID_APP_ID: + logger.error(f"Unauthorized access: Invalid App-ID {app_id}.") + return ResponseHelper.unauthorized("Invalid App-ID") - return None + return f(*args, **kwargs) + + return decorated_function diff --git a/app/middlewares/verify_api_key.py b/app/middlewares/verify_api_key.py index 6ed8a9d..12099e0 100644 --- a/app/middlewares/verify_api_key.py +++ b/app/middlewares/verify_api_key.py @@ -1,20 +1,26 @@ +from functools import wraps from flask import request from app.helpers.response_helper import ResponseHelper from app.utils.logger import logger +import os -# Define a valid API key (store securely in environment variables) -VALID_API_KEY = "your-secure-api-key" +# Load valid API key from environment variables (fallback for testing) +VALID_API_KEY = os.getenv("VALID_API_KEY", "test-api-key-12345") -def require_api_key(): - """Middleware to check if API key is present and valid.""" - api_key = request.headers.get("X-API-KEY") +def require_api_key(f): + """Decorator to enforce API key authentication.""" + @wraps(f) + def decorated_function(*args, **kwargs): + api_key = request.headers.get("X-API-KEY") - if not api_key: - logger.error("Unauthorized access: Missing API key.") - return ResponseHelper.unauthorized("Missing API key") + if not api_key: + logger.error("Unauthorized access: Missing API key.") + return ResponseHelper.unauthorized("Missing API key") - if api_key != VALID_API_KEY: - logger.error("Unauthorized access: Invalid API key.") - return ResponseHelper.unauthorized("Invalid API key") + if api_key != VALID_API_KEY: + logger.error("Unauthorized access: Invalid API key.") + return ResponseHelper.unauthorized("Invalid API key") - return None \ No newline at end of file + return f(*args, **kwargs) + + return decorated_function diff --git a/app/routes/routes.py b/app/routes/routes.py index 144f047..86df5e2 100644 --- a/app/routes/routes.py +++ b/app/routes/routes.py @@ -12,20 +12,23 @@ from app.blueprints import ( ) from app.utils.logger import logger from app.middlewares import require_api_key +from app.middlewares import require_app_id api = Blueprint("api", __name__) -@api.before_request -def require_api_key_middleware(): - """Middleware applied globally to all API routes in this blueprint""" - return require_api_key() +# @api.before_request +# def require_api_key_middleware(): +# """Middleware applied globally to all API routes in this blueprint""" +# return require_api_key() # RACCheck Endpoint @api.route('/RACCheck', methods=['POST']) +@require_api_key +@require_app_id def rac_check(): data = request.get_json() # logger.info(f"RACCheck request received: {data}") @@ -34,6 +37,8 @@ def rac_check(): # Disbursement Endpoint @api.route('/Disbursement', methods=['POST']) +@require_api_key +@require_app_id def disbursement(): data = request.get_json() # logger.info(f"Disbursement request received: {data}") @@ -42,6 +47,8 @@ def disbursement(): # CollectLoan Endpoint @api.route('/CollectLoan', methods=['POST']) +@require_api_key +@require_app_id def collect_loan(): data = request.get_json() # logger.info(f"CollectLoan request received: {data}") @@ -50,6 +57,8 @@ def collect_loan(): # TransactionVerify Endpoint @api.route('/TransactionVerify', methods=['POST']) +@require_api_key +@require_app_id def transaction_verify(): data = request.get_json() # logger.info(f"TransactionVerify request received: {data}") @@ -58,6 +67,8 @@ def transaction_verify(): # PenalCharge Endpoint @api.route('/PenalCharge', methods=['POST']) +@require_api_key +@require_app_id def penal_charge(): data = request.get_json() # logger.info(f"PenalCharge request received: {data}") @@ -66,6 +77,8 @@ def penal_charge(): # RevokeEnableConsent Endpoint @api.route('/RevokeEnableConsent', methods=['POST']) +@require_api_key +@require_app_id def revoke_enable_consent(): data = request.get_json() # logger.info(f"RevokeEnableConsent request received: {data}") @@ -74,6 +87,8 @@ def revoke_enable_consent(): # TokenValidation Endpoint @api.route('/TokenValidation', methods=['POST']) +@require_api_key +@require_app_id def token_validation(): data = request.get_json() # logger.info(f"TokenValidation request received: {data}") @@ -82,6 +97,8 @@ def token_validation(): # LienCheck Endpoint @api.route('/LienCheck', methods=['POST']) +@require_api_key +@require_app_id def lien_check(): data = request.get_json() # logger.info(f"LienCheck request received: {data}") @@ -90,6 +107,8 @@ def lien_check(): # NewTransactionCheck Endpoint @api.route('/NewTransactionCheck', methods=['POST']) +@require_api_key +@require_app_id def new_transaction_check(): data = request.get_json() # logger.info(f"NewTransactionCheck request received: {data}")