From 16d516c221112d36ead6ce36b5beb6a54015c8a2 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Wed, 6 Oct 2021 22:07:20 -0700 Subject: [PATCH] feat(FastAPI): add /pkgbase/{name}/disown (get, post) Signed-off-by: Kevin Morris --- aurweb/routers/packages.py | 63 +++++++++++++++++++++++ templates/packages/disown.html | 55 ++++++++++++++++++++ templates/partials/packages/actions.html | 10 ++-- test/test_packages_routes.py | 65 ++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 templates/packages/disown.html diff --git a/aurweb/routers/packages.py b/aurweb/routers/packages.py index 8bfb680e..af1ebe46 100644 --- a/aurweb/routers/packages.py +++ b/aurweb/routers/packages.py @@ -807,3 +807,66 @@ async def pkgbase_unvote(request: Request, name: str): return RedirectResponse(f"/pkgbase/{name}", status_code=int(HTTPStatus.SEE_OTHER)) + + +def disown_pkgbase(pkgbase: PackageBase, disowner: User): + conn = db.ConnectionExecutor(db.get_engine().raw_connection()) + notif = notify.DisownNotification(conn, disowner.ID, pkgbase.ID) + + if disowner != pkgbase.Maintainer: + with db.begin(): + pkgbase.Maintainer = None + else: + co = pkgbase.comaintainers.order_by( + PackageComaintainer.Priority.asc() + ).limit(1).first() + + if co: + with db.begin(): + pkgbase.Maintainer = co.User + db.session.delete(co) + else: + pkgbase.Maintainer = None + + notif.send() + + +@router.get("/pkgbase/{name}/disown") +@auth_required(True, redirect="/pkgbase/{name}") +async def pkgbase_disown_get(request: Request, name: str): + pkgbase = get_pkg_or_base(name, PackageBase) + + has_cred = request.user.has_credential("CRED_PKGBASE_DISOWN", + approved=[pkgbase.Maintainer]) + if not has_cred: + return RedirectResponse(f"/pkgbase/{name}", + int(HTTPStatus.SEE_OTHER)) + + context = make_context(request, "Disown Package") + context["pkgbase"] = pkgbase + return render_template(request, "packages/disown.html", context) + + +@router.post("/pkgbase/{name}/disown") +@auth_required(True, redirect="/pkgbase/{name}") +async def pkgbase_disown_post(request: Request, name: str, + confirm: bool = Form(default=False)): + pkgbase = get_pkg_or_base(name, PackageBase) + + has_cred = request.user.has_credential("CRED_PKGBASE_DISOWN", + approved=[pkgbase.Maintainer]) + if not has_cred: + return RedirectResponse(f"/pkgbase/{name}", + int(HTTPStatus.SEE_OTHER)) + + if not confirm: + context = make_context(request, "Disown Package") + context["pkgbase"] = pkgbase + context["errors"] = [("The selected packages have not been disowned, " + "check the confirmation checkbox.")] + return render_template(request, "packages/disown.html", context, + status_code=int(HTTPStatus.EXPECTATION_FAILED)) + + disown_pkgbase(pkgbase, request.user) + return RedirectResponse(f"/pkgbase/{name}", + status_code=int(HTTPStatus.SEE_OTHER)) diff --git a/templates/packages/disown.html b/templates/packages/disown.html new file mode 100644 index 00000000..8d5a8574 --- /dev/null +++ b/templates/packages/disown.html @@ -0,0 +1,55 @@ +{% extends "partials/layout.html" %} + +{% block pageContent %} + + {% if errors %} + + {% endif %} + +
+

{{ "Disown Package" | tr }}: {{ pkgbase.Name }}

+ +

+ {{ + "Use this form to disown the package base %s%s%s which " + "includes the following packages: " + | tr | format("", pkgbase.Name, "") | safe + }} +

+ +
    + {% for package in pkgbase.packages.all() %} +
  • {{ package.Name }}
  • + {% endfor %} +
+ +

+ {{ + "By selecting the checkbox, you confirm that you want to " + "disown the package." | tr + }} +

+ +
+
+

+ +

+

+ +

+
+
+ +
+{% endblock %} diff --git a/templates/partials/packages/actions.html b/templates/partials/packages/actions.html index f1863663..2b26144e 100644 --- a/templates/partials/packages/actions.html +++ b/templates/partials/packages/actions.html @@ -121,13 +121,9 @@ {% endif %} {% if request.user.has_credential("CRED_PKGBASE_DISOWN", approved=[pkgbase.Maintainer]) %}
  • -
    - -
    + + {{ "Disown Package" | tr }} +
  • {% endif %} diff --git a/test/test_packages_routes.py b/test/test_packages_routes.py index a03c5920..c9622431 100644 --- a/test/test_packages_routes.py +++ b/test/test_packages_routes.py @@ -1780,3 +1780,68 @@ def test_pkgbase_vote(client: TestClient, user: User, package: Package): vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first() assert vote is None + + +def test_pkgbase_disown_as_tu(client: TestClient, tu_user: User, + package: Package): + cookies = {"AURSID": tu_user.login(Request(), "testPassword")} + pkgbase = package.PackageBase + endpoint = f"/pkgbase/{pkgbase.Name}/disown" + + # But we do here. + with client as request: + resp = request.post(endpoint, data={"confirm": True}, cookies=cookies) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + +def test_pkgbase_disown_as_sole_maintainer(client: TestClient, + maintainer: User, + package: Package): + cookies = {"AURSID": maintainer.login(Request(), "testPassword")} + pkgbase = package.PackageBase + endpoint = f"/pkgbase/{pkgbase.Name}/disown" + + # But we do here. + with client as request: + resp = request.post(endpoint, data={"confirm": True}, cookies=cookies) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + +def test_pkgbase_disown(client: TestClient, user: User, maintainer: User, + package: Package): + cookies = {"AURSID": maintainer.login(Request(), "testPassword")} + user_cookies = {"AURSID": user.login(Request(), "testPassword")} + pkgbase = package.PackageBase + endpoint = f"/pkgbase/{pkgbase.Name}/disown" + + with db.begin(): + db.create(PackageComaintainer, + User=user, + PackageBase=pkgbase, + Priority=1) + + # GET as a normal user, which is rejected for lack of credentials. + with client as request: + resp = request.get(endpoint, cookies=user_cookies, + allow_redirects=False) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + # GET as the maintainer. + with client as request: + resp = request.get(endpoint, cookies=cookies) + assert resp.status_code == int(HTTPStatus.OK) + + # POST as a normal user, which is rejected for lack of credentials. + with client as request: + resp = request.post(endpoint, cookies=user_cookies) + assert resp.status_code == int(HTTPStatus.SEE_OTHER) + + # POST as the maintainer without "confirm". + with client as request: + resp = request.post(endpoint, cookies=cookies) + assert resp.status_code == int(HTTPStatus.EXPECTATION_FAILED) + + # POST as the maintainer with "confirm". + with client as request: + resp = request.post(endpoint, data={"confirm": True}, cookies=cookies) + assert resp.status_code == int(HTTPStatus.SEE_OTHER)