diff --git a/aurweb/asgi.py b/aurweb/asgi.py index ad0b7ca0..fa2526ed 100644 --- a/aurweb/asgi.py +++ b/aurweb/asgi.py @@ -74,6 +74,10 @@ async def app_startup(): if not session_secret: raise Exception("[fastapi] session_secret must not be empty") + if not os.environ.get("PROMETHEUS_MULTIPROC_DIR", None): + logger.warning("$PROMETHEUS_MULTIPROC_DIR is not set, the /metrics " + "endpoint is disabled.") + app.mount("/static/css", StaticFiles(directory="web/html/css"), name="static_css") diff --git a/aurweb/config.py b/aurweb/config.py index 61b60402..b7aa3027 100644 --- a/aurweb/config.py +++ b/aurweb/config.py @@ -6,7 +6,7 @@ from typing import Any # Publicly visible version of aurweb. This is used to display # aurweb versioning in the footer and must be maintained. # Todo: Make this dynamic/automated. -AURWEB_VERSION = "v6.0.17" +AURWEB_VERSION = "v6.0.18" _parser = None diff --git a/aurweb/routers/html.py b/aurweb/routers/html.py index b9d291d2..d31a32c7 100644 --- a/aurweb/routers/html.py +++ b/aurweb/routers/html.py @@ -13,7 +13,7 @@ from sqlalchemy import and_, case, or_ import aurweb.config import aurweb.models.package_request -from aurweb import cookies, db, models, time, util +from aurweb import cookies, db, logging, models, time, util from aurweb.cache import db_count_cache from aurweb.exceptions import handle_form_exceptions from aurweb.models.account_type import TRUSTED_USER_AND_DEV_ID, TRUSTED_USER_ID @@ -21,6 +21,7 @@ from aurweb.models.package_request import PENDING_ID from aurweb.packages.util import query_notified, query_voted, updated_packages from aurweb.templates import make_context, render_template +logger = logging.get_logger(__name__) router = APIRouter() @@ -230,9 +231,12 @@ async def archive_sha256(request: Request, archive: str): @router.get("/metrics") async def metrics(request: Request): + if not os.environ.get("PROMETHEUS_MULTIPROC_DIR", None): + return Response("Prometheus metrics are not enabled.", + status_code=HTTPStatus.SERVICE_UNAVAILABLE) + registry = CollectorRegistry() - if os.environ.get("PROMETHEUS_MULTIPROC_DIR", None): # pragma: no cover - multiprocess.MultiProcessCollector(registry) + multiprocess.MultiProcessCollector(registry) data = generate_latest(registry) headers = { "Content-Type": CONTENT_TYPE_LATEST, diff --git a/aurweb/users/update.py b/aurweb/users/update.py index 8e42765e..5a32fd01 100644 --- a/aurweb/users/update.py +++ b/aurweb/users/update.py @@ -19,11 +19,11 @@ def simple(U: str = str(), E: str = str(), H: bool = False, user.Username = U or user.Username user.Email = E or user.Email user.HideEmail = strtobool(H) - user.BackupEmail = BE or user.BackupEmail - user.RealName = R or user.RealName - user.Homepage = HP or user.Homepage - user.IRCNick = I or user.IRCNick - user.PGPKey = K or user.PGPKey + user.BackupEmail = user.BackupEmail if BE is None else BE + user.RealName = user.RealName if R is None else R + user.Homepage = user.Homepage if HP is None else HP + user.IRCNick = user.IRCNick if I is None else I + user.PGPKey = user.PGPKey if K is None else K user.Suspended = strtobool(S) user.InactivityTS = now * int(strtobool(J)) user.CommentNotify = strtobool(CN) diff --git a/aurweb/users/validate.py b/aurweb/users/validate.py index 26f6eec6..de51e3ff 100644 --- a/aurweb/users/validate.py +++ b/aurweb/users/validate.py @@ -15,6 +15,7 @@ from aurweb.captcha import get_captcha_answer, get_captcha_salts, get_captcha_to from aurweb.exceptions import ValidationError from aurweb.models.account_type import ACCOUNT_TYPE_NAME from aurweb.models.ssh_pub_key import get_fingerprint +from aurweb.util import strtobool logger = logging.get_logger(__name__) @@ -26,9 +27,9 @@ def invalid_fields(E: str = str(), U: str = str(), **kwargs) -> None: def invalid_suspend_permission(request: Request = None, user: models.User = None, - J: bool = False, + S: str = "False", **kwargs) -> None: - if not request.user.is_elevated() and J != bool(user.InactivityTS): + if not request.user.is_elevated() and strtobool(S) != bool(user.Suspended): raise ValidationError([ "You do not have permission to suspend accounts."]) diff --git a/pyproject.toml b/pyproject.toml index 89b149a8..0b21a643 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ # [tool.poetry] name = "aurweb" -version = "v6.0.17" +version = "v6.0.18" license = "GPL-2.0-only" description = "Source code for the Arch User Repository's website" homepage = "https://aur.archlinux.org" diff --git a/test/test_accounts_routes.py b/test/test_accounts_routes.py index e532e341..37b3d130 100644 --- a/test/test_accounts_routes.py +++ b/test/test_accounts_routes.py @@ -916,13 +916,13 @@ def test_post_account_edit_error_invalid_password(client: TestClient, assert "Invalid password." in content -def test_post_account_edit_inactivity_unauthorized(client: TestClient, - user: User): +def test_post_account_edit_suspend_unauthorized(client: TestClient, + user: User): cookies = {"AURSID": user.login(Request(), "testPassword")} post_data = { "U": "test", "E": "test@example.org", - "J": True, + "S": True, "passwd": "testPassword" } with client as request: diff --git a/test/test_asgi.py b/test/test_asgi.py index 667ae871..c693a3a9 100644 --- a/test/test_asgi.py +++ b/test/test_asgi.py @@ -104,6 +104,17 @@ async def test_asgi_app_unsupported_backends(): await aurweb.asgi.app_startup() +@pytest.mark.asyncio +async def test_asgi_app_disabled_metrics(caplog: pytest.LogCaptureFixture): + env = {"PROMETHEUS_MULTIPROC_DIR": str()} + with mock.patch.dict(os.environ, env): + await aurweb.asgi.app_startup() + + expected = ("$PROMETHEUS_MULTIPROC_DIR is not set, the /metrics " + "endpoint is disabled.") + assert expected in caplog.text + + @pytest.fixture def use_traceback(): config_getboolean = aurweb.config.getboolean diff --git a/test/test_html.py b/test/test_html.py index b97d3571..ffe2a9f2 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -160,12 +160,23 @@ def test_archive_sig_404(client: TestClient): def test_metrics(client: TestClient): - with client as request: - resp = request.get("/metrics") + with tempfile.TemporaryDirectory() as tmpdir: + env = {"PROMETHEUS_MULTIPROC_DIR": tmpdir} + with mock.patch.dict(os.environ, env): + with client as request: + resp = request.get("/metrics") assert resp.status_code == int(HTTPStatus.OK) assert resp.headers.get("Content-Type").startswith("text/plain") +def test_disabled_metrics(client: TestClient): + env = {"PROMETHEUS_MULTIPROC_DIR": str()} + with mock.patch.dict(os.environ, env): + with client as request: + resp = request.get("/metrics") + assert resp.status_code == int(HTTPStatus.SERVICE_UNAVAILABLE) + + def test_rtl(client: TestClient): responses = {} expected = [