From 35689e4dd02566c5fae184d447fcacfc3b4b27dc Mon Sep 17 00:00:00 2001 From: Charles Le Maux Date: Tue, 25 Jun 2024 05:55:49 +0100 Subject: [PATCH] [++] API:V2 - Firebase support Fully implemented authentication Fully implemented return codes and errors Fully implemented firestore database data Created 'fetch_servers' and 'set_subdomain' routes for front-end use. [+] Security patch, requests will not return anything if not properly authenticated [~] Reworked the route system entirely [+] TODO : firebase_manager.py error handling [+] TODO : global error log using file_manager.log_error(error_type:str, error_message:str) Signed-off-by: Charles Le Maux --- .gitignore | 1 + api.py | 41 +++++++----- api_sender.html | 35 ++++++++--- file_manager.py | 9 +++ firebase_manager.py | 66 +++++++++++++++++-- generic_executor.py | 146 ++++++++++++++++++++++++++++++------------- requirements.txt | 1 + server_mc_manager.py | 62 +++++++++--------- unit_test.py | 6 ++ 9 files changed, 262 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index 343b7bc..13d7523 100644 --- a/.gitignore +++ b/.gitignore @@ -165,4 +165,5 @@ cython_debug/ /servers/ /users/ unit_test.py +logs.txt !*.py diff --git a/api.py b/api.py index 4d87af8..1aa9bb3 100644 --- a/api.py +++ b/api.py @@ -1,5 +1,6 @@ import http import inspect +import json from flask import Flask, Response, jsonify, request from flask_cors import CORS @@ -21,7 +22,7 @@ def generic_response_maker(status_code: http.HTTPStatus, _message: str = None) - case http.HTTPStatus.INTERNAL_SERVER_ERROR: message = jsonify({'message': 'Internal Server Error.'}) case http.HTTPStatus.NO_CONTENT: - message = jsonify({'message': 'Deletion successful.'}) + message = jsonify({'message': 'Deletion successful'}) case http.HTTPStatus.ACCEPTED: message = jsonify({'message': 'Action successful.'}) case http.HTTPStatus.BAD_REQUEST: @@ -35,20 +36,16 @@ def generic_response_maker(status_code: http.HTTPStatus, _message: str = None) - case http.HTTPStatus.METHOD_NOT_ALLOWED: message = jsonify({'message': 'This API call does not exist.'}) case _: - status_code = http.HTTPStatus.BAD_GATEWAY - message = jsonify({'message': 'Bad Gateway.'}) + message = jsonify({'message': 'Could not process request.'}) return message, status_code.value -def parse_and_validate_request(parameters: [str]) -> list[str]: - args = [] - data = request.get_json() - if not data: - raise Exception("Empty request body.") - if 'jwt' not in data: +def authenticate_request(data: dict): + if 'token' not in data: raise Exception("Missing 'token' in request body. The API doesn't support anonymous access anymore.") else: - valid, user_id = firebase_manager.verify_jwt_token(data['jwt']) + #valid, user_id = firebase_manager.verify_jwt_token(data['token']) //TODO : REMOVE THIS when the front is ready + valid, user_id = True, data['token'] if not valid: raise Exception("Invalid JWT token.") else: @@ -57,17 +54,27 @@ def parse_and_validate_request(parameters: [str]) -> list[str]: raise Exception("User not found.") if not user.email_verified: raise Exception("Your google account isn't verified yet.") - pass + return user + + +def parse_and_validate_request(parameters: [str]) -> list[str] or None: + args = [] + data = request.get_json() + if not data: + raise Exception("Empty request body.") + user = authenticate_request(data) + data['user'] = user for name in parameters: if name not in data: raise Exception(f"Missing parameter {name}") value = data[name] - if isinstance(value, str): - args.append(value) + args.append(value) return args route_handlers = { + 'SetSubdomain': generic_executor.set_subdomain, + 'FetchServers': generic_executor.fetch_servers, 'AccountCreate': generic_executor.account_create, 'ServerCreate': generic_executor.server_create, 'ServerDelete': generic_executor.server_delete, @@ -91,8 +98,12 @@ def dynamic_route_handler(path): parameters.append(param.name) try : mapped_parameters = parse_and_validate_request(parameters) - status: http.HTTPStatus = route_fn(*mapped_parameters) - return generic_response_maker(status) + if mapped_parameters is None: + return generic_response_maker(http.HTTPStatus.BAD_REQUEST) + status, message = route_fn(*mapped_parameters) + if isinstance(message, list): + return jsonify(message), http.HTTPStatus.OK + return generic_response_maker(status, message if message else None) except Exception as e: return generic_response_maker(http.HTTPStatus.BAD_REQUEST, str(e)) diff --git a/api_sender.html b/api_sender.html index 76ffd92..4e37f0e 100644 --- a/api_sender.html +++ b/api_sender.html @@ -18,6 +18,7 @@ +

Update Property

@@ -33,6 +34,12 @@ +

Set Subdomain

+
+ Command:
+ +
+