fix(fastapi): support "Account Type:" input for account edit

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-10-25 21:56:15 -07:00
parent 7e7a1ead88
commit 65be8b8e07
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
4 changed files with 153 additions and 5 deletions

View file

@ -47,3 +47,6 @@ ACCOUNT_TYPE_ID = {
DEVELOPER: DEVELOPER_ID,
TRUSTED_USER_AND_DEV: TRUSTED_USER_AND_DEV_ID
}
# Reversed ACCOUNT_TYPE_ID mapping.
ACCOUNT_TYPE_NAME = {v: k for k, v in ACCOUNT_TYPE_ID.items()}

View file

@ -15,6 +15,8 @@ from aurweb.auth import account_type_required, auth_required
from aurweb.captcha import get_captcha_answer, get_captcha_salts, get_captcha_token
from aurweb.l10n import get_translator_for_request
from aurweb.models import account_type
from aurweb.models.account_type import (DEVELOPER, DEVELOPER_ID, TRUSTED_USER, TRUSTED_USER_AND_DEV, TRUSTED_USER_AND_DEV_ID,
TRUSTED_USER_ID, USER_ID)
from aurweb.models.ssh_pub_key import get_fingerprint
from aurweb.scripts.notify import ResetKeyNotification, WelcomeNotification
from aurweb.templates import make_context, make_variable_context, render_template
@ -226,6 +228,29 @@ def process_account_form(request: Request, user: models.User, args: dict):
"<strong>", fingerprint, "</strong>")
])
T = int(args.get("T", user.AccountTypeID))
if T != user.AccountTypeID:
if T not in account_type.ACCOUNT_TYPE_NAME:
return (False,
["Invalid account type provided."])
elif not request.user.is_elevated():
return (False,
["You do not have permission to change account types."])
credential_checks = {
DEVELOPER_ID: request.user.is_developer,
TRUSTED_USER_AND_DEV_ID: request.user.is_developer,
TRUSTED_USER_ID: request.user.is_elevated,
USER_ID: request.user.is_elevated
}
credential_check = credential_checks.get(T)
if not credential_check():
name = account_type.ACCOUNT_TYPE_NAME.get(T)
error = _("You do not have permission to change "
"this user's account type to %s.") % name
return (False, [error])
captcha_salt = args.get("captcha_salt", None)
if captcha_salt and captcha_salt not in get_captcha_salts():
return (False, ["This CAPTCHA has expired. Please try again."])
@ -255,15 +280,16 @@ def make_account_form_context(context: dict,
context = copy.copy(context)
context["account_types"] = [
(1, "Normal User"),
(2, "Trusted User")
(USER_ID, "Normal User"),
(TRUSTED_USER_ID, TRUSTED_USER)
]
user_account_type_id = context.get("account_types")[0][0]
if request.user.has_credential("CRED_ACCOUNT_EDIT_DEV"):
context["account_types"].append((3, "Developer"))
context["account_types"].append((4, "Trusted User & Developer"))
context["account_types"].append((DEVELOPER_ID, DEVELOPER))
context["account_types"].append((TRUSTED_USER_AND_DEV_ID,
TRUSTED_USER_AND_DEV))
if request.user.is_authenticated():
context["username"] = args.get("U", user.Username)
@ -465,6 +491,7 @@ async def account_edit_post(request: Request,
CN: bool = Form(default=False), # Comment Notify
UN: bool = Form(default=False), # Update Notify
ON: bool = Form(default=False), # Owner Notify
T: int = Form(default=None),
passwd: str = Form(default=str())):
from aurweb.db import session
@ -545,6 +572,10 @@ async def account_edit_post(request: Request,
# Else, if the user has a public key already, delete it.
session.delete(user.ssh_pub_key)
if T and T != user.AccountTypeID:
with db.begin():
user.AccountTypeID = T
if P and not user.valid_password(P):
# Remove the fields we consumed for passwords.
context["P"] = context["C"] = str()

View file

@ -2280,3 +2280,15 @@ msgid ""
"Please remember to cast your vote on proposal {id} [1]. The voting period "
"ends in less than 48 hours."
msgstr ""
#: aurweb/routers/accounts.py
msgid "Invalid account type provided."
msgstr ""
#: aurweb/routers/accounts.py
msgid "You do not have permission to change account types."
msgstr ""
#: aurweb/routers/accounts.py
msgid "You do not have permission to change this user's account type to %s."
msgstr ""

View file

@ -14,13 +14,14 @@ from aurweb import captcha, db, logging
from aurweb.asgi import app
from aurweb.db import create, query
from aurweb.models.accepted_term import AcceptedTerm
from aurweb.models.account_type import DEVELOPER_ID, TRUSTED_USER_AND_DEV_ID, TRUSTED_USER_ID, AccountType
from aurweb.models.account_type import DEVELOPER_ID, TRUSTED_USER_AND_DEV_ID, TRUSTED_USER_ID, USER_ID, AccountType
from aurweb.models.ban import Ban
from aurweb.models.session import Session
from aurweb.models.ssh_pub_key import SSHPubKey, get_fingerprint
from aurweb.models.term import Term
from aurweb.models.user import User
from aurweb.testing import setup_test_db
from aurweb.testing.html import get_errors
from aurweb.testing.requests import Request
# Some test global constants.
@ -909,6 +910,107 @@ def test_post_account_edit_password():
assert user.valid_password("newPassword")
def test_post_account_edit_account_types():
request = Request()
sid = user.login(request, "testPassword")
cookies = {"AURSID": sid}
endpoint = f"/account/{user.Username}/edit"
# As a normal user, we cannot see the "Account Type:" input.
with client as request:
resp = request.get(endpoint, cookies=cookies)
assert resp.status_code == int(HTTPStatus.OK)
assert "id_type" not in resp.text
# Invalid account types return an error.
post_data = {
"U": user.Username,
"E": user.Email,
"T": 0, # Invalid type ID
"passwd": "testPassword"
}
with client as request:
resp = request.post(endpoint, data=post_data, cookies=cookies)
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
errors = get_errors(resp.text)
expected = "Invalid account type provided."
assert errors[0].text.strip() == expected
# Nor can we change any account types.
post_data = {
"U": user.Username,
"E": user.Email,
"T": TRUSTED_USER_ID,
"passwd": "testPassword"
}
with client as request:
resp = request.post(endpoint, data=post_data, cookies=cookies)
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
errors = get_errors(resp.text)
expected = "You do not have permission to change account types."
assert errors[0].text.strip() == expected
# Change user from USER_ID to TRUSTED_USER_ID.
with db.begin():
user.AccountTypeID = TRUSTED_USER_ID
# As a trusted user, we can see the "Account Type:" input.
with client as request:
resp = request.get(endpoint, cookies=cookies)
assert resp.status_code == int(HTTPStatus.OK)
assert "id_type" in resp.text
# As a trusted user, we cannot change account type to DEVELOPER_ID.
post_data = {
"U": user.Username,
"E": user.Email,
"T": DEVELOPER_ID,
"passwd": "testPassword"
}
with client as request:
resp = request.post(endpoint, data=post_data, cookies=cookies)
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
errors = get_errors(resp.text)
expected = ("You do not have permission to change "
"this user's account type to Developer.")
assert errors[0].text.strip() == expected
# However, we can modify our account type to USER_ID.
post_data = {
"U": user.Username,
"E": user.Email,
"T": USER_ID,
"passwd": "testPassword"
}
with client as request:
resp = request.post(endpoint, data=post_data, cookies=cookies)
assert resp.status_code == int(HTTPStatus.OK)
# No errors should be displayed.
errors = get_errors(resp.text)
assert not errors
# Make sure it got changed to USER_ID as we intended.
assert user.AccountTypeID == USER_ID
# Change user to a Developer.
with db.begin():
user.AccountTypeID = DEVELOPER_ID
# As a developer, we can absolutely change all account types.
# For example, from DEVELOPER_ID to TRUSTED_USER_AND_DEV_ID:
post_data = {
"U": user.Username,
"E": user.Email,
"T": TRUSTED_USER_AND_DEV_ID,
"passwd": "testPassword"
}
with client as request:
resp = request.post(endpoint, data=post_data, cookies=cookies)
assert resp.status_code == int(HTTPStatus.OK)
assert user.AccountTypeID == TRUSTED_USER_AND_DEV_ID
def test_get_account():
request = Request()
sid = user.login(request, "testPassword")