From 85ebc72e8af542d73909b6f58f9bfb3b4f40ccd3 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Mon, 8 Nov 2021 18:18:41 -0800 Subject: [PATCH] fix(fastapi): only elevated users are allowed to suspend accounts Signed-off-by: Kevin Morris --- aurweb/auth.py | 3 ++ aurweb/routers/accounts.py | 7 +++- po/aurweb.pot | 4 +++ templates/partials/account_form.html | 18 +++++----- test/test_accounts_routes.py | 49 ++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/aurweb/auth.py b/aurweb/auth.py index 5e45ee83..38754db0 100644 --- a/aurweb/auth.py +++ b/aurweb/auth.py @@ -51,6 +51,9 @@ class AnonymousUser: LangPreference = aurweb.config.get("options", "default_lang") Timezone = aurweb.config.get("options", "default_timezone") + Suspended = 0 + InactivityTS = 0 + # A stub ssh_pub_key relationship. ssh_pub_key = None diff --git a/aurweb/routers/accounts.py b/aurweb/routers/accounts.py index 152b0a15..498568ad 100644 --- a/aurweb/routers/accounts.py +++ b/aurweb/routers/accounts.py @@ -143,6 +143,10 @@ def process_account_form(request: Request, user: models.User, args: dict): if not email or not username: return (False, ["Missing a required field."]) + inactive = args.get("J", False) + if not request.user.is_elevated() and inactive != bool(user.InactivityTS): + return (False, ["You do not have permission to suspend accounts."]) + username_min_len = aurweb.config.getint("options", "username_min_len") username_max_len = aurweb.config.getint("options", "username_max_len") if not util.valid_username(args.get("U")): @@ -528,7 +532,8 @@ async def account_edit_post(request: Request, user.Homepage = HP or user.Homepage user.IRCNick = I or user.IRCNick user.PGPKey = K or user.PGPKey - user.InactivityTS = datetime.utcnow().timestamp() if J else 0 + user.Suspended = J + user.InactivityTS = int(datetime.utcnow().timestamp()) * int(J) # If we update the language, update the cookie as well. if L and L != user.LangPreference: diff --git a/po/aurweb.pot b/po/aurweb.pot index 721f874e..f4deee70 100644 --- a/po/aurweb.pot +++ b/po/aurweb.pot @@ -879,6 +879,10 @@ msgstr "" msgid "Account suspended" msgstr "" +#: aurweb/routers/accounts.py +msgid "You do not have permission to suspend accounts." +msgstr "" + #: lib/acctfuncs.inc.php #, php-format msgid "" diff --git a/templates/partials/account_form.html b/templates/partials/account_form.html index f166c230..2e47a932 100644 --- a/templates/partials/account_form.html +++ b/templates/partials/account_form.html @@ -42,14 +42,16 @@ "account is inactive." | tr }}

-

- - -

+ {% if request.user.is_elevated() %} +

+ + +

+ {% endif %} {% if request.user.has_credential("CRED_ACCOUNT_CHANGE_TYPE") %}

diff --git a/test/test_accounts_routes.py b/test/test_accounts_routes.py index 188f7048..5e855daf 100644 --- a/test/test_accounts_routes.py +++ b/test/test_accounts_routes.py @@ -780,6 +780,55 @@ def test_post_account_edit_error_invalid_password(): assert "Invalid password." in content +def test_post_account_edit_inactivity_unauthorized(): + cookies = {"AURSID": user.login(Request(), "testPassword")} + post_data = { + "U": "test", + "E": "test@example.org", + "J": True, + "passwd": "testPassword" + } + with client as request: + resp = request.post(f"/account/{user.Username}/edit", 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 suspend accounts." + assert errors[0].text.strip() == expected + + +def test_post_account_edit_inactivity(): + with db.begin(): + user.AccountTypeID = TRUSTED_USER_ID + assert not user.Suspended + + cookies = {"AURSID": user.login(Request(), "testPassword")} + post_data = { + "U": "test", + "E": "test@example.org", + "J": True, + "passwd": "testPassword" + } + with client as request: + resp = request.post(f"/account/{user.Username}/edit", data=post_data, + cookies=cookies) + assert resp.status_code == int(HTTPStatus.OK) + + # Make sure the user record got updated correctly. + assert user.Suspended + assert user.InactivityTS > 0 + + post_data.update({"J": False}) + with client as request: + resp = request.post(f"/account/{user.Username}/edit", data=post_data, + cookies=cookies) + assert resp.status_code == int(HTTPStatus.OK) + + assert not user.Suspended + assert user.InactivityTS == 0 + + def test_post_account_edit_error_unauthorized(): request = Request() sid = user.login(request, "testPassword")