[+] 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

25
api.py
View File

@ -4,6 +4,7 @@ import inspect
from flask import Flask, Response, jsonify, request
from flask_cors import CORS
import firebase_manager
import generic_executor
app = Flask(__name__)
@ -44,6 +45,19 @@ def parse_and_validate_request(parameters: [str]) -> list[str]:
data = request.get_json()
if not data:
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:
if name not in data:
raise Exception(f"Missing parameter {name}")
@ -73,13 +87,14 @@ def dynamic_route_handler(path):
route_fn = route_handlers[path]
parameters = []
sig = inspect.signature(route_fn)
for param in sig.parameters.values():
parameters.append(param.name)
mapped_parameters = parse_and_validate_request(parameters)
status: http.HTTPStatus = route_fn(*mapped_parameters)
return generic_response_maker(status)
try :
mapped_parameters = parse_and_validate_request(parameters)
status: http.HTTPStatus = route_fn(*mapped_parameters)
return generic_response_maker(status)
except Exception as e:
return generic_response_maker(http.HTTPStatus.BAD_REQUEST, str(e))
if __name__ == '__main__':

View File

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

View File

@ -1,10 +1,13 @@
from datetime import datetime
import jwt
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')
firebase_admin.initialize_app(cred)
firestore_database = firestore.client()
'''
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.
'''
def get_user_from_id(user_id):
return auth.get_user(user_id)
@ -28,3 +32,44 @@ def verify_jwt_token(token):
return False, None
except jwt.InvalidTokenError:
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 http import HTTPStatus
import file_manager
mc_manager: MinecraftServerManager = MinecraftServerManager()
@ -90,3 +91,6 @@ def run_command(port: str, command: str) -> HTTPStatus:
print(f"Error executing command: {e}")
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_Cors==4.0.1
plotly==5.22.0
psutil==5.9.8
PyJWT==2.8.0