mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
feature: allow co-maintainers to disown their pkg
Derived off of original work done by Leonidas Spyropoulos at https://gitlab.archlinux.org/archlinux/aurweb/-/merge_requests/503 This revision of that original work finishes off the inconsistencies mentioned in the original MR and adds a small bit of testing for more regression checks. Fixes: #360 Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
ab2956eef7
commit
08d485206c
5 changed files with 123 additions and 35 deletions
|
@ -50,6 +50,12 @@ def pkgbase_disown_instance(request: Request, pkgbase: PackageBase) -> None:
|
|||
notifs = [notify.DisownNotification(disowner.ID, pkgbase.ID)]
|
||||
|
||||
is_maint = disowner == pkgbase.Maintainer
|
||||
|
||||
comaint = pkgbase.comaintainers.filter(
|
||||
PackageComaintainer.User == disowner
|
||||
).one_or_none()
|
||||
is_comaint = comaint is not None
|
||||
|
||||
if is_maint:
|
||||
with db.begin():
|
||||
# Comaintainer with the lowest Priority value; next-in-line.
|
||||
|
@ -63,6 +69,11 @@ def pkgbase_disown_instance(request: Request, pkgbase: PackageBase) -> None:
|
|||
else:
|
||||
# Otherwise, just orphan the package completely.
|
||||
pkgbase.Maintainer = None
|
||||
elif is_comaint:
|
||||
# This disown request is from a Comaintainer
|
||||
with db.begin():
|
||||
notif = pkgbaseutil.remove_comaintainer(comaint)
|
||||
notifs.append(notif)
|
||||
elif request.user.has_credential(creds.PKGBASE_DISOWN):
|
||||
# Otherwise, the request user performing this disownage is a
|
||||
# Trusted User and we treat it like a standard orphan request.
|
||||
|
|
|
@ -545,15 +545,18 @@ async def pkgbase_disown_get(request: Request, name: str,
|
|||
next: str = Query(default=str())):
|
||||
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||
|
||||
comaints = {c.User for c in pkgbase.comaintainers}
|
||||
approved = [pkgbase.Maintainer] + list(comaints)
|
||||
has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
|
||||
approved=[pkgbase.Maintainer])
|
||||
approved=approved)
|
||||
if not has_cred:
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
HTTPStatus.SEE_OTHER)
|
||||
return RedirectResponse(f"/pkgbase/{name}", HTTPStatus.SEE_OTHER)
|
||||
|
||||
context = templates.make_context(request, "Disown Package")
|
||||
context["pkgbase"] = pkgbase
|
||||
context["next"] = next or "/pkgbase/{name}"
|
||||
context["is_maint"] = request.user == pkgbase.Maintainer
|
||||
context["is_comaint"] = request.user in comaints
|
||||
return render_template(request, "pkgbase/disown.html", context)
|
||||
|
||||
|
||||
|
@ -566,8 +569,10 @@ async def pkgbase_disown_post(request: Request, name: str,
|
|||
next: str = Form(default=str())):
|
||||
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||
|
||||
comaints = {c.User for c in pkgbase.comaintainers}
|
||||
approved = [pkgbase.Maintainer] + list(comaints)
|
||||
has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
|
||||
approved=[pkgbase.Maintainer])
|
||||
approved=approved)
|
||||
if not has_cred:
|
||||
return RedirectResponse(f"/pkgbase/{name}",
|
||||
HTTPStatus.SEE_OTHER)
|
||||
|
@ -580,8 +585,9 @@ async def pkgbase_disown_post(request: Request, name: str,
|
|||
return render_template(request, "pkgbase/disown.html", context,
|
||||
status_code=HTTPStatus.BAD_REQUEST)
|
||||
|
||||
with db.begin():
|
||||
update_closure_comment(pkgbase, ORPHAN_ID, comments)
|
||||
if request.user != pkgbase.Maintainer and request.user not in comaints:
|
||||
with db.begin():
|
||||
update_closure_comment(pkgbase, ORPHAN_ID, comments)
|
||||
|
||||
try:
|
||||
actions.pkgbase_disown_instance(request, pkgbase)
|
||||
|
@ -862,7 +868,6 @@ async def pkgbase_merge_post(request: Request, name: str,
|
|||
comments: str = Form(default=str()),
|
||||
confirm: bool = Form(default=False),
|
||||
next: str = Form(default=str())):
|
||||
|
||||
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||
context = await make_variable_context(request, "Package Merging")
|
||||
context["pkgbase"] = pkgbase
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
/>
|
||||
</form>
|
||||
</li>
|
||||
{% elif request.user.has_credential(creds.PKGBASE_DISOWN, approved=[pkgbase.Maintainer]) %}
|
||||
{% elif request.user.has_credential(creds.PKGBASE_DISOWN, approved=[pkgbase.Maintainer] + comaintainers) %}
|
||||
<li>
|
||||
<a href="/pkgbase/{{ pkgbase.Name }}/disown?{{ {'next': '/pkgbase/%s' | format(pkgbase.Name)} | urlencode }}">
|
||||
{{ "Disown Package" | tr }}
|
||||
|
|
|
@ -27,14 +27,16 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
{{
|
||||
"This action will close any pending package requests "
|
||||
"related to it. If %sComments%s are omitted, a closure "
|
||||
"comment will be autogenerated."
|
||||
| tr | format("<em>", "</em>") | safe
|
||||
}}
|
||||
</p>
|
||||
{% if not is_maint and not is_comaint %}
|
||||
<p>
|
||||
{{
|
||||
"This action will close any pending package requests "
|
||||
"related to it. If %sComments%s are omitted, a closure "
|
||||
"comment will be autogenerated."
|
||||
| tr | format("<em>", "</em>") | safe
|
||||
}}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
{{
|
||||
|
@ -47,14 +49,18 @@
|
|||
<fieldset>
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
|
||||
<p>
|
||||
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
||||
<textarea id="id_comments"
|
||||
name="comments"
|
||||
rows="5" cols="50"
|
||||
placeholder="Related package request closure comments..."
|
||||
></textarea>
|
||||
</p>
|
||||
{% if not is_maint and not is_comaint %}
|
||||
<p>
|
||||
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
||||
<textarea id="id_comments"
|
||||
name="comments"
|
||||
rows="5" cols="50"
|
||||
placeholder="Related package request closure comments..."
|
||||
></textarea>
|
||||
</p>
|
||||
{% else %}
|
||||
<input type="hidden" name="comments" value="">
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<label class="confirmation">
|
||||
|
|
|
@ -98,6 +98,18 @@ def maintainer() -> User:
|
|||
yield maintainer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def comaintainer() -> User:
|
||||
""" Yield a specific User used to maintain packages. """
|
||||
account_type = db.query(AccountType, AccountType.ID == USER_ID).first()
|
||||
with db.begin():
|
||||
comaintainer = db.create(User, Username="test_comaintainer",
|
||||
Email="test_comaintainer@example.org",
|
||||
Passwd="testPassword",
|
||||
AccountType=account_type)
|
||||
yield comaintainer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tu_user():
|
||||
tu_type = db.query(AccountType,
|
||||
|
@ -1037,16 +1049,47 @@ def test_pkgbase_disown_as_sole_maintainer(client: TestClient,
|
|||
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")}
|
||||
def test_pkgbase_disown_as_maint_with_comaint(client: TestClient,
|
||||
user: User,
|
||||
maintainer: User,
|
||||
package: Package):
|
||||
""" When disowning as a maintainer, the lowest priority comaintainer
|
||||
is promoted to maintainer. """
|
||||
pkgbase = package.PackageBase
|
||||
endpoint = f"/pkgbase/{pkgbase.Name}/disown"
|
||||
endp = f"/pkgbase/{pkgbase.Name}/disown"
|
||||
post_data = {"confirm": True}
|
||||
|
||||
with db.begin():
|
||||
db.create(PackageComaintainer,
|
||||
PackageBase=pkgbase,
|
||||
User=user,
|
||||
Priority=1)
|
||||
|
||||
maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")}
|
||||
with client as request:
|
||||
resp = request.post(endp, data=post_data, cookies=maint_cookies,
|
||||
allow_redirects=True)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
package = db.refresh(package)
|
||||
pkgbase = package.PackageBase
|
||||
|
||||
assert pkgbase.Maintainer == user
|
||||
assert pkgbase.comaintainers.count() == 0
|
||||
|
||||
|
||||
def test_pkgbase_disown(client: TestClient, user: User, maintainer: User,
|
||||
comaintainer: User, package: Package):
|
||||
maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")}
|
||||
comaint_cookies = {"AURSID": comaintainer.login(Request(), "testPassword")}
|
||||
user_cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||
pkgbase = package.PackageBase
|
||||
pkgbase_endp = f"/pkgbase/{pkgbase.Name}"
|
||||
endpoint = f"{pkgbase_endp}/disown"
|
||||
|
||||
with db.begin():
|
||||
db.create(PackageComaintainer,
|
||||
User=comaintainer,
|
||||
PackageBase=pkgbase,
|
||||
Priority=1)
|
||||
|
||||
|
@ -1056,24 +1099,50 @@ def test_pkgbase_disown(client: TestClient, user: User, maintainer: User,
|
|||
allow_redirects=False)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# GET as a comaintainer.
|
||||
with client as request:
|
||||
resp = request.get(endpoint, cookies=comaint_cookies,
|
||||
allow_redirects=False)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Ensure that the comaintainer can see "Disown Package" link
|
||||
with client as request:
|
||||
resp = request.get(pkgbase_endp, cookies=comaint_cookies)
|
||||
assert "Disown Package" in resp.text
|
||||
|
||||
# GET as the maintainer.
|
||||
with client as request:
|
||||
resp = request.get(endpoint, cookies=cookies)
|
||||
resp = request.get(endpoint, cookies=maint_cookies)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Ensure that the maintainer can see "Disown Package" link
|
||||
with client as request:
|
||||
resp = request.get(pkgbase_endp, cookies=maint_cookies)
|
||||
assert "Disown Package" in resp.text
|
||||
|
||||
# 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 comaintainer without "confirm".
|
||||
with client as request:
|
||||
resp = request.post(endpoint, cookies=comaint_cookies)
|
||||
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||
|
||||
# POST as the maintainer without "confirm".
|
||||
with client as request:
|
||||
resp = request.post(endpoint, cookies=cookies)
|
||||
resp = request.post(endpoint, cookies=maint_cookies)
|
||||
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||
|
||||
# POST as the comaintainer with "confirm".
|
||||
with client as request:
|
||||
resp = request.post(endpoint, data={"confirm": True}, cookies=comaint_cookies)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# POST as the maintainer with "confirm".
|
||||
with client as request:
|
||||
resp = request.post(endpoint, data={"confirm": True}, cookies=cookies)
|
||||
resp = request.post(endpoint, data={"confirm": True}, cookies=maint_cookies)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
|
||||
|
@ -1190,7 +1259,6 @@ def test_pkgbase_delete_with_request(client: TestClient, tu_user: User,
|
|||
|
||||
def test_packages_post_unknown_action(client: TestClient, user: User,
|
||||
package: Package):
|
||||
|
||||
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||
with client as request:
|
||||
resp = request.post("/packages", data={"action": "unknown"},
|
||||
|
@ -1199,7 +1267,6 @@ def test_packages_post_unknown_action(client: TestClient, user: User,
|
|||
|
||||
|
||||
def test_packages_post_error(client: TestClient, user: User, package: Package):
|
||||
|
||||
async def stub_action(request: Request, **kwargs):
|
||||
return (False, ["Some error."])
|
||||
|
||||
|
@ -1217,7 +1284,6 @@ def test_packages_post_error(client: TestClient, user: User, package: Package):
|
|||
|
||||
|
||||
def test_packages_post(client: TestClient, user: User, package: Package):
|
||||
|
||||
async def stub_action(request: Request, **kwargs):
|
||||
return (True, ["Some success."])
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue