From 19fa91d319b02a42cad6be06fd8faf53475b1795 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 12 May 2023 00:17:15 +0100 Subject: [PATCH] Add changes to edit package audit log entry --- app/logic/packages.py | 15 +++++++++--- app/utils/__init__.py | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/app/logic/packages.py b/app/logic/packages.py index 64b5ff6e..f5fab6c2 100644 --- a/app/logic/packages.py +++ b/app/logic/packages.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . - +import json import re import validators from flask_babel import lazy_gettext @@ -22,7 +22,7 @@ from flask_babel import lazy_gettext from app.logic.LogicError import LogicError from app.models import User, Package, PackageType, MetaPackage, Tag, ContentWarning, db, Permission, AuditSeverity, \ License, UserRank, PackageDevState -from app.utils import addAuditLog, has_blocked_domains +from app.utils import addAuditLog, has_blocked_domains, diff_dictionaries, describe_difference from app.utils.url import clean_youtube_url @@ -113,6 +113,8 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool, not package.checkPerm(user, Permission.CHANGE_NAME): raise LogicError(403, lazy_gettext("You don't have permission to change the package name")) + before_dict = package.getAsDictionary("/") + for alias, to in ALIASES.items(): if alias in data: data[to] = data[alias] @@ -188,14 +190,21 @@ def do_edit_package(user: User, package: Package, was_new: bool, was_web: bool, raise LogicError(400, "Unknown warning: " + warning_id) package.content_warnings.append(warning) + after_dict = package.getAsDictionary("/") + diff = diff_dictionaries(before_dict, after_dict) + if not was_new: if reason is None: msg = "Edited {}".format(package.title) else: msg = "Edited {} ({})".format(package.title, reason) + diff_desc = describe_difference(diff, 100 - len(msg) - 3) + if diff_desc: + msg += " [" + diff_desc + "]" + severity = AuditSeverity.NORMAL if user in package.maintainers else AuditSeverity.EDITOR - addAuditLog(severity, user, msg, package.getURL("packages.view"), package) + addAuditLog(severity, user, msg, package.getURL("packages.view"), package, json.dumps(diff, indent=4)) db.session.commit() diff --git a/app/utils/__init__.py b/app/utils/__init__.py index 7469c0bf..a897f058 100644 --- a/app/utils/__init__.py +++ b/app/utils/__init__.py @@ -16,6 +16,9 @@ import re import secrets +from typing import Dict + +import typing from .flask import * from .models import * @@ -68,3 +71,57 @@ def has_blocked_domains(text: str, username: str, location: str) -> bool: return True return False + + +def diff_dictionaries(one: Dict, two: Dict): + if len(set(one.keys()).difference(set(two.keys()))) != 0: + raise "Mismatching keys" + + retval = [] + + for key, before in one.items(): + after = two[key] + + if before is dict: + diff = diff_dictionaries(before, after) + if len(diff) != 0: + retval.append({ + "key": key, + "changes": diff, + }) + elif before != after: + retval.append({ + "key": key, + "before": before, + "after": after, + }) + + return retval + + +def describe_difference(diff: List, available_space: int) -> typing.Optional[str]: + if len(diff) == 0 or available_space <= 0: + return None + + if len(diff) == 1 and "before" in diff[0] and "after" in diff[0]: + key = diff[0]["key"] + before = diff[0]["before"] + after = diff[0]["after"] + + if isinstance(before, str) and isinstance(after, str): + return f"{key}: {before} -> {after}" + + if isinstance(before, list) and isinstance(after, list): + removed = [] + added = [] + for x in before: + if x not in after: + removed.append(x) + for x in after: + if x not in before: + added.append(x) + + parts = ["-" + str(x) for x in removed] + ["+" + str(x) for x in added] + return f"{key}: {', '.join(parts)}" + + return ", ".join([x["key"] for x in diff])