[+] Added basic firebase authentication checks, and added firestore basic functions.

Signed-off-by: Charles Le Maux <charles.le-maux@epitech.eu>
This commit is contained in:
Charles Le Maux 2024-06-24 15:42:41 +01:00
parent acd72ffa8a
commit 9ccef01999
5 changed files with 82 additions and 17 deletions

19
api.py
View File

@ -4,6 +4,7 @@ import inspect
from flask import Flask, Response, jsonify, request from flask import Flask, Response, jsonify, request
from flask_cors import CORS from flask_cors import CORS
import firebase_manager
import generic_executor import generic_executor
app = Flask(__name__) app = Flask(__name__)
@ -44,6 +45,19 @@ def parse_and_validate_request(parameters: [str]) -> list[str]:
data = request.get_json() data = request.get_json()
if not data: if not data:
raise Exception("Empty request body.") raise Exception("Empty request body.")
if 'jwt' 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'])
if not valid:
raise Exception("Invalid JWT token.")
else:
user = firebase_manager.get_user_from_id(user_id)
if not user:
raise Exception("User not found.")
if not user.email_verified:
raise Exception("Your google account isn't verified yet.")
pass
for name in parameters: for name in parameters:
if name not in data: if name not in data:
raise Exception(f"Missing parameter {name}") raise Exception(f"Missing parameter {name}")
@ -73,13 +87,14 @@ def dynamic_route_handler(path):
route_fn = route_handlers[path] route_fn = route_handlers[path]
parameters = [] parameters = []
sig = inspect.signature(route_fn) sig = inspect.signature(route_fn)
for param in sig.parameters.values(): for param in sig.parameters.values():
parameters.append(param.name) parameters.append(param.name)
try :
mapped_parameters = parse_and_validate_request(parameters) mapped_parameters = parse_and_validate_request(parameters)
status: http.HTTPStatus = route_fn(*mapped_parameters) status: http.HTTPStatus = route_fn(*mapped_parameters)
return generic_response_maker(status) return generic_response_maker(status)
except Exception as e:
return generic_response_maker(http.HTTPStatus.BAD_REQUEST, str(e))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -42,6 +42,7 @@ document.addEventListener('DOMContentLoaded', () => {
button.addEventListener('click', async event => { button.addEventListener('click', async event => {
const form = event.target.closest('form'); const form = event.target.closest('form');
const action = button.dataset.action; const action = button.dataset.action;
const jwt = "0";
const email = document.getElementById('accountEmail').value; const email = document.getElementById('accountEmail').value;
const port = document.getElementById('accountPort').value; const port = document.getElementById('accountPort').value;
const name = document.getElementById('serverName').value; const name = document.getElementById('serverName').value;
@ -52,28 +53,28 @@ document.addEventListener('DOMContentLoaded', () => {
var data = {} var data = {}
switch(action) { switch(action) {
case 'AccountCreate': case 'AccountCreate':
data = {email, port} data = {email, port, jwt}
break; break;
case 'AccountDelete': case 'AccountDelete':
data = {email, port} data = {email, port, jwt}
break; break;
case 'ServerCreate': case 'ServerCreate':
data = {port, name, version} data = {port, name, version, jwt}
break; break;
case 'ServerDelete': case 'ServerDelete':
data = {port, name} data = {port, name, jwt}
break; break;
case 'ServerRun': case 'ServerRun':
data = {port, name} data = {port, name, jwt}
break; break;
case 'ServerStop': case 'ServerStop':
data = {port, name} data = {port, name, jwt}
break; break;
case 'UpdateProperty': case 'UpdateProperty':
data = {port, name, prop, value} data = {port, name, prop, value, jwt}
break; break;
case 'Command': case 'Command':
data = {port, name, command} data = {port, name, command, jwt}
break; break;
} }
sendRequest(action, data) sendRequest(action, data)

View File

@ -1,10 +1,13 @@
from datetime import datetime
import jwt import jwt
import firebase_admin import firebase_admin
from firebase_admin import credentials, auth from firebase_admin import credentials, auth, firestore
from google.api_core.exceptions import NotFound, PermissionDenied, Aborted, ResourceExhausted, OutOfRange, DataLoss
cred = credentials.Certificate('servii.json') cred = credentials.Certificate('servii.json')
firebase_admin.initialize_app(cred) firebase_admin.initialize_app(cred)
firestore_database = firestore.client()
''' '''
TODO TODO
@ -15,6 +18,7 @@ Tell the database they have now stopped running.
Also ensure the program can add an additional argument to avoid this checking for scalability. Also ensure the program can add an additional argument to avoid this checking for scalability.
''' '''
def get_user_from_id(user_id): def get_user_from_id(user_id):
return auth.get_user(user_id) return auth.get_user(user_id)
@ -28,3 +32,44 @@ def verify_jwt_token(token):
return False, None return False, None
except jwt.InvalidTokenError: except jwt.InvalidTokenError:
return False, None return False, None
def create_firestore(user_id: str, data: dict) -> bool:
doc_ref = firestore_database.collection('users').document(user_id)
try:
doc_ref.create(data)
return True
except (NotFound, PermissionDenied, Aborted, ResourceExhausted,
OutOfRange, DataLoss, TypeError, Exception, ValueError) as e:
log_exception_to_firestore(e, user_id, data)
return False
def update_firestore(user_id: str, data: dict) -> bool:
doc_ref = firestore_database.collection('users').document(user_id)
try:
doc_ref.update(data)
return True
except (NotFound, PermissionDenied, Aborted, ResourceExhausted,
OutOfRange, DataLoss, TypeError, Exception, ValueError) as e:
log_exception_to_firestore(e, user_id, data)
return False
def log_exception_to_firestore(exception: Exception = None, user_id: str = None, data: dict = None):
new_id: str = datetime.now().strftime('%Y-%m-%d %H:%M:%S %Z%z')
log_entry = {
'exception_name': str(type(exception).__name__),
'exception': str(exception) if exception else 'No exception',
'user_id': str(user_id) if user_id else 'No user_id',
'data': str(data) if data else 'No data provided',
}
try:
firestore_database.collection('firebase.logs').document(new_id).create(log_entry)
print("Log entry added successfully.")
except Exception as e:
print(f"Failed to add log entry: {e}")
if __name__ == "__main__":
pass

View File

@ -1,5 +1,6 @@
from server_mc_manager import MinecraftServerManager from server_mc_manager import MinecraftServerManager
from http import HTTPStatus from http import HTTPStatus
import file_manager import file_manager
mc_manager: MinecraftServerManager = MinecraftServerManager() mc_manager: MinecraftServerManager = MinecraftServerManager()
@ -90,3 +91,6 @@ def run_command(port: str, command: str) -> HTTPStatus:
print(f"Error executing command: {e}") print(f"Error executing command: {e}")
return HTTPStatus.INTERNAL_SERVER_ERROR return HTTPStatus.INTERNAL_SERVER_ERROR
if __name__ == "__main__":
pass

View File

@ -1,4 +1,4 @@
firebase_admin==6.5.0
Flask==3.0.3 Flask==3.0.3
Flask_Cors==4.0.1 Flask_Cors==4.0.1
plotly==5.22.0 PyJWT==2.8.0
psutil==5.9.8