From 7f981b9ed7d0ce90d23d4051d743accd0228b515 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Thu, 18 Nov 2021 21:15:57 -0800 Subject: [PATCH] fix(fastapi): utilize auto_{orphan,deletion}_age Didn't get this in when the initial request port went down; here it is. Auto-accept orphan requests when the package has been out of date for longer than auto_orphan_age. Auto-accept deletion requests by the package's maintainer if the package has been uploaded within auto_deletion_age seconds ago. Signed-off-by: Kevin Morris --- aurweb/packages/validate.py | 34 +++++++++++++++++++ aurweb/routers/packages.py | 65 ++++++++++++++++++------------------ test/test_packages_routes.py | 43 +++++++++++++++++++++++- 3 files changed, 109 insertions(+), 33 deletions(-) create mode 100644 aurweb/packages/validate.py diff --git a/aurweb/packages/validate.py b/aurweb/packages/validate.py new file mode 100644 index 00000000..e730e98b --- /dev/null +++ b/aurweb/packages/validate.py @@ -0,0 +1,34 @@ +from typing import Any, Dict + +from aurweb import db, models +from aurweb.exceptions import ValidationError + + +def request(pkgbase: models.PackageBase, + type: str, comments: str, merge_into: str, + context: Dict[str, Any]) -> None: + if not comments: + raise ValidationError(["The comment field must not be empty."]) + + if type == "merge": + # Perform merge-related checks. + if not merge_into: + # TODO: This error needs to be translated. + raise ValidationError( + ['The "Merge into" field must not be empty.']) + + target = db.query(models.PackageBase).filter( + models.PackageBase.Name == merge_into + ).first() + if not target: + # TODO: This error needs to be translated. + raise ValidationError([ + "The package base you want to merge into does not exist." + ]) + + db.refresh(target) + if target.ID == pkgbase.ID: + # TODO: This error needs to be translated. + raise ValidationError([ + "You cannot merge a package base into itself." + ]) diff --git a/aurweb/routers/packages.py b/aurweb/routers/packages.py index c8ceb275..dfb8e108 100644 --- a/aurweb/routers/packages.py +++ b/aurweb/routers/packages.py @@ -11,9 +11,11 @@ import aurweb.packages.util from aurweb import db, defaults, l10n, logging, models, util from aurweb.auth import auth_required +from aurweb.exceptions import ValidationError from aurweb.models.package_request import ACCEPTED_ID, PENDING_ID, REJECTED_ID from aurweb.models.relation_type import CONFLICTS_ID, PROVIDES_ID, REPLACES_ID from aurweb.models.request_type import DELETION_ID, MERGE, MERGE_ID +from aurweb.packages import validate from aurweb.packages.search import PackageSearch from aurweb.packages.util import get_pkg_or_base, get_pkgbase_comment, get_pkgreq_by_id, query_notified, query_voted from aurweb.scripts import notify, popupdate @@ -153,7 +155,7 @@ def delete_package(deleter: models.User, package: models.Package): with db.begin(): pkgreq = create_request_if_missing( requests, reqtype, deleter, package) - db.refresh(pkgreq) + pkgreq.Status = ACCEPTED_ID bases_to_delete.append(package.PackageBase) @@ -707,35 +709,13 @@ async def pkgbase_request_post(request: Request, name: str, return render_template(request, "pkgbase/request.html", context, status_code=HTTPStatus.BAD_REQUEST) - if not comments: - context["errors"] = ["The comment field must not be empty."] + try: + validate.request(pkgbase, type, comments, merge_into, context) + except ValidationError as exc: + logger.error(f"Request Validation Error: {str(exc.data)}") + context["errors"] = exc.data return render_template(request, "pkgbase/request.html", context) - if type == "merge": - # Perform merge-related checks. - if not merge_into: - # TODO: This error needs to be translated. - context["errors"] = ['The "Merge into" field must not be empty.'] - return render_template(request, "pkgbase/request.html", context) - - target = db.query(models.PackageBase).filter( - models.PackageBase.Name == merge_into - ).first() - if not target: - # TODO: This error needs to be translated. - context["errors"] = [ - "The package base you want to merge into does not exist." - ] - return render_template(request, "pkgbase/request.html", context) - - db.refresh(target) - if target.ID == pkgbase.ID: - # TODO: This error needs to be translated. - context["errors"] = [ - "You cannot merge a package base into itself." - ] - return render_template(request, "pkgbase/request.html", context) - # All good. Create a new PackageRequest based on the given type. now = int(datetime.utcnow().timestamp()) reqtype = db.query(models.RequestType).filter( @@ -748,16 +728,37 @@ async def pkgbase_request_post(request: Request, name: str, PackageBase=pkgbase, PackageBaseName=pkgbase.Name, MergeBaseName=merge_into, - Comments=comments, ClosureComment=str()) + Comments=comments, + ClosureComment=str()) - # Prepare notification object. conn = db.ConnectionExecutor(db.get_engine().raw_connection()) - notify_ = notify.RequestOpenNotification( + # Prepare notification object. + notif = notify.RequestOpenNotification( conn, request.user.ID, pkgreq.ID, reqtype.Name, pkgreq.PackageBase.ID, merge_into=merge_into or None) # Send the notification now that we're out of the DB scope. - notify_.send() + notif.send() + + auto_orphan_age = aurweb.config.getint("options", "auto_orphan_age") + auto_delete_age = aurweb.config.getint("options", "auto_delete_age") + + flagged = pkgbase.OutOfDateTS and pkgbase.OutOfDateTS >= auto_orphan_age + is_maintainer = pkgbase.Maintainer == request.user + outdated = now - pkgbase.SubmittedTS <= auto_delete_age + + if type == "orphan" and flagged: + with db.begin(): + pkgbase.Maintainer = None + pkgreq.Status = ACCEPTED_ID + db.refresh(pkgreq) + notif = notify.RequestCloseNotification( + conn, request.user.ID, pkgreq.ID, pkgreq.status_display()) + notif.send() + elif type == "deletion" and is_maintainer and outdated: + packages = pkgbase.packages.all() + for package in packages: + delete_package(request.user, package) # Redirect the submitting user to /packages. return RedirectResponse("/packages", diff --git a/test/test_packages_routes.py b/test/test_packages_routes.py index 02c22d9d..64ee38d0 100644 --- a/test/test_packages_routes.py +++ b/test/test_packages_routes.py @@ -10,7 +10,7 @@ import pytest from fastapi.testclient import TestClient from sqlalchemy import and_ -from aurweb import asgi, db, defaults +from aurweb import asgi, config, db, defaults from aurweb.models import License, PackageLicense from aurweb.models.account_type import USER_ID, AccountType from aurweb.models.dependency_type import DependencyType @@ -1536,6 +1536,24 @@ def test_pkgbase_request_post_deletion(client: TestClient, user: User, assert pkgreq.Comments == "We want to delete this." +def test_pkgbase_request_post_maintainer_deletion( + client: TestClient, maintainer: User, package: Package): + pkgbasename = package.PackageBase.Name + endpoint = f"/pkgbase/{package.PackageBase.Name}/request" + cookies = {"AURSID": maintainer.login(Request(), "testPassword")} + with client as request: + resp = request.post(endpoint, data={ + "type": "deletion", + "comments": "We want to delete this." + }, cookies=cookies, allow_redirects=False) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + pkgreq = db.query(PackageRequest).filter( + PackageRequest.PackageBaseName == pkgbasename + ).first() + assert pkgreq.Status == ACCEPTED_ID + + def test_pkgbase_request_post_orphan(client: TestClient, user: User, package: Package): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" @@ -1556,6 +1574,29 @@ def test_pkgbase_request_post_orphan(client: TestClient, user: User, assert pkgreq.Comments == "We want to disown this." +def test_pkgbase_request_post_auto_orphan(client: TestClient, user: User, + package: Package): + now = int(datetime.utcnow().timestamp()) + auto_orphan_age = config.getint("options", "auto_orphan_age") + with db.begin(): + package.PackageBase.OutOfDateTS = now - auto_orphan_age - 1 + + endpoint = f"/pkgbase/{package.PackageBase.Name}/request" + cookies = {"AURSID": user.login(Request(), "testPassword")} + with client as request: + resp = request.post(endpoint, data={ + "type": "orphan", + "comments": "We want to disown this." + }, cookies=cookies, allow_redirects=False) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + pkgreq = db.query(PackageRequest).filter( + PackageRequest.PackageBaseID == package.PackageBase.ID + ).first() + assert pkgreq is not None + assert pkgreq.Status == ACCEPTED_ID + + def test_pkgbase_request_post_merge(client: TestClient, user: User, package: Package): with db.begin():