diff --git a/partitioncloud/__init__.py b/partitioncloud/__init__.py index 76369e6..7d77f2d 100644 --- a/partitioncloud/__init__.py +++ b/partitioncloud/__init__.py @@ -12,7 +12,7 @@ from flask import Flask, g, redirect, render_template, request, send_file, flash from werkzeug.security import generate_password_hash from .modules.utils import User, Album, get_all_albums -from .modules import albums, auth, partition, admin, groupe, thumbnails +from .modules import albums, auth, partition, admin, groupe, thumbnails, logging from .modules.auth import admin_required, login_required from .modules.db import get_db @@ -33,6 +33,11 @@ def load_config(): ".", os.path.join(app.instance_path, "config.py") ) + + if spec is None: + print("[ERROR] Failed to load $INSTANCE_PATH/config.py") + sys.exit(1) + user_config = importlib.util.module_from_spec(spec) spec.loader.exec_module(user_config) @@ -50,6 +55,8 @@ def load_config(): DATABASE=os.path.join(app.instance_path, f"{__name__}.sqlite"), ) + logging.log_file = os.path.join(app.instance_path, "logs.txt") + def get_version(): try: @@ -71,6 +78,8 @@ app.register_blueprint(thumbnails.bp) __version__ = get_version() +logging.log([], logging.LogEntry.SERVER_RESTART) + @app.route("/") def home(): @@ -96,6 +105,12 @@ def add_user(): if error is None: # Success, go to the login page. user = User(name=username) + + logging.log( + [user.username, user.id, True, current_user.username], + logging.LogEntry.NEW_USER + ) + try: if album_uuid != "": user.join_album(album_uuid) diff --git a/partitioncloud/modules/albums.py b/partitioncloud/modules/albums.py index ddeaa5e..40ca12a 100644 --- a/partitioncloud/modules/albums.py +++ b/partitioncloud/modules/albums.py @@ -13,7 +13,7 @@ from flask import (Blueprint, abort, flash, redirect, render_template, from .auth import login_required from .db import get_db from .utils import User, Album -from . import search, utils +from . import search, utils, logging bp = Blueprint("albums", __name__, url_prefix="/albums") @@ -116,6 +116,8 @@ def create_album_req(): db = get_db() error = None + user = User(user_id=session["user_id"]) + if not name or name.strip() == "": error = "Un nom est requis. L'album n'a pas été créé" @@ -131,6 +133,8 @@ def create_album_req(): ) db.commit() + logging.log([album.name, album.uuid, user.username], logging.LogEntry.NEW_ALBUM) + if "response" in request.args and request.args["response"] == "json": return { "status": "ok", @@ -217,7 +221,7 @@ def delete_album(uuid): @login_required def add_partition(album_uuid): """ - Ajouter une partition à un album (par upload) + Ajouter une partition à un album (nouveau fichier) """ T = TypeVar("T") def get_opt_string(dictionary: dict[T, str], key: T): @@ -265,6 +269,7 @@ def add_partition(album_uuid): author = get_opt_string(request.form, "author") body = get_opt_string(request.form, "body") + partition_uuid: str while True: try: partition_uuid = str(uuid4()) @@ -307,6 +312,11 @@ def add_partition(album_uuid): except db.IntegrityError: pass + logging.log( + [request.form["name"], partition_uuid, user.username], + logging.LogEntry.NEW_PARTITION + ) + if "response" in request.args and request.args["response"] == "json": return { "status": "ok", @@ -320,7 +330,7 @@ def add_partition(album_uuid): @login_required def add_partition_from_search(): """ - Ajout d'une partition (depuis la recherche) + Ajout d'une partition (depuis la recherche locale) """ user = User(user_id=session.get("user_id")) error = None diff --git a/partitioncloud/modules/auth.py b/partitioncloud/modules/auth.py index b53fe64..8d08c6e 100644 --- a/partitioncloud/modules/auth.py +++ b/partitioncloud/modules/auth.py @@ -12,6 +12,7 @@ from werkzeug.security import check_password_hash, generate_password_hash from .db import get_db from .utils import User +from . import logging bp = Blueprint("auth", __name__, url_prefix="/auth") @@ -120,8 +121,15 @@ def register(): if error is not None: flash(error) else: + user = User(name=username) + flash("Utilisateur créé avec succès. Vous pouvez vous connecter.") + logging.log( + [user.username, user.id, False], + logging.LogEntry.NEW_USER + ) + return render_template("auth/register.html") @@ -139,12 +147,16 @@ def login(): ).fetchone() if (user is None) or not check_password_hash(user["password"], password): + logging.log([username], logging.LogEntry.FAILED_LOGIN) error = "Nom d'utilisateur ou mot de passe incorrect." if error is None: # store the user id in a new session and return to the index session.clear() session["user_id"] = user["id"] + + logging.log([username], logging.LogEntry.LOGIN) + return redirect(url_for("albums.index")) flash(error) diff --git a/partitioncloud/modules/groupe.py b/partitioncloud/modules/groupe.py index fe03429..6991f3a 100644 --- a/partitioncloud/modules/groupe.py +++ b/partitioncloud/modules/groupe.py @@ -9,6 +9,7 @@ from .auth import login_required from .db import get_db from .utils import User, Album, Groupe from . import utils +from . import logging bp = Blueprint("groupe", __name__, url_prefix="/groupe") @@ -63,6 +64,8 @@ def create_groupe(): db = get_db() error = None + user = User(user_id=session["user_id"]) + if not name or name.strip() == "": error = "Un nom est requis. Le groupe n'a pas été créé" @@ -93,6 +96,8 @@ def create_groupe(): except db.IntegrityError: pass + logging.log([name, uuid, user.username], logging.LogEntry.NEW_GROUPE) + if "response" in request.args and request.args["response"] == "json": return { "status": "ok", @@ -194,6 +199,8 @@ def create_album_req(groupe_uuid): ) db.commit() + logging.log([album.name, album.uuid, user.username], logging.LogEntry.NEW_ALBUM) + if "response" in request.args and request.args["response"] == "json": return { "status": "ok", diff --git a/partitioncloud/modules/logging.py b/partitioncloud/modules/logging.py new file mode 100644 index 0000000..4cd0e0f --- /dev/null +++ b/partitioncloud/modules/logging.py @@ -0,0 +1,61 @@ +from datetime import datetime +from typing import Union +from enum import Enum + +global log_file +global enabled + + +class LogEntry(Enum): + LOGIN = 1 + NEW_GROUPE = 2 + NEW_ALBUM = 3 + NEW_PARTITION = 4 + NEW_USER = 5 + SERVER_RESTART = 6 + FAILED_LOGIN = 7 + + +def add_entry(entry: str) -> None: + date = datetime.now().strftime("%y-%b-%Y %H:%M:%S") + + with open(log_file, 'a', encoding="utf8") as f: + f.write(f"[{date}] {entry}\n") + + +def log(content: list[Union[str, bool, int]], log_type: LogEntry) -> None: + description: str = "" + + if log_type not in enabled: + return + + match log_type: + case LogEntry.LOGIN: # content = (user.name) + description = f"Successful login for {content[0]}" + + case LogEntry.NEW_GROUPE: # content = (groupe.name, groupe.id, user.name) + description = f"{content[2]} added groupe '{content[0]}' ({content[1]})" + + case LogEntry.NEW_ALBUM: # content = (album.name, album.id, user.name) + description = f"{content[2]} added album '{content[0]}' ({content[1]})" + + case LogEntry.NEW_PARTITION: # content = (partition.name, partition.uuid, user.name) + description = f"{content[2]} added partition '{content[0]}' ({content[1]})" + + case LogEntry.NEW_USER: # content = (user.name, user.id, from_register_page, admin.name if relevant) + if not content[2]: + description = f"New user {content[0]}[{content[1]}]" + else: + description = f"New user {content[0]}[{content[1]}] added by {content[3]}" + + case LogEntry.SERVER_RESTART: # content = () + description = "Server just restarted" + + case LogEntry.FAILED_LOGIN: # content = (user.name) + description = f"Failed login for {content[0]}" + + add_entry(description) + + +log_file = "logs.txt" +enabled = [i for i in LogEntry] \ No newline at end of file