This is an automated email from the ASF dual-hosted git repository.

arm pushed a commit to branch arm
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


The following commit(s) were added to refs/heads/arm by this push:
     new 08c65a52 Add check_access back into routes which require it and remove 
from GET generally.
08c65a52 is described below

commit 08c65a52ebfca77b1d8070d4a3ba224f18ce05d6
Author: Alastair McFarlane <[email protected]>
AuthorDate: Mon Mar 2 16:24:09 2026 +0000

    Add check_access back into routes which require it and remove from GET 
generally.
---
 atr/blueprints/get.py      |  6 ------
 atr/get/announce.py        |  1 +
 atr/get/compose.py         |  1 +
 atr/get/draft.py           |  1 +
 atr/get/file.py            |  2 ++
 atr/get/manual.py          |  2 ++
 atr/get/release.py         |  3 ++-
 atr/get/report.py          |  1 +
 atr/get/result.py          |  1 +
 atr/get/revisions.py       |  1 +
 atr/get/start.py           |  3 ++-
 atr/get/upload.py          |  1 +
 atr/get/voting.py          |  1 +
 atr/shared/distribution.py | 11 +++++++----
 atr/web.py                 |  5 +++--
 15 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/atr/blueprints/get.py b/atr/blueprints/get.py
index cd586505..ea48245a 100644
--- a/atr/blueprints/get.py
+++ b/atr/blueprints/get.py
@@ -26,7 +26,6 @@ import quart
 
 import atr.blueprints.common as common
 import atr.log as log
-import atr.models.safe as safe
 import atr.web as web
 
 _BLUEPRINT_NAME = "get_blueprint"
@@ -63,17 +62,12 @@ def typed(func: Callable[..., Any]) -> 
web.RouteFunction[Any]:
     - check_access is called automatically for committer routes with 
project_name
     """
     path, validated_params, literal_params, _, public = common.build_path(func)
-    project_name_var = next((name for name, type_name in validated_params if 
type_name is safe.ProjectName), None)
-    check_access = not public and (project_name_var is not None)
 
     async def wrapper(*_args: Any, **kwargs: Any) -> Any:
         enhanced_session = await common.authenticate_public() if public else 
await common.authenticate()
         await common.run_validators(kwargs, validated_params)
         kwargs.update(literal_params)
 
-        if check_access and (enhanced_session is not None) and 
(project_name_var is not None):
-            await enhanced_session.check_access(str(kwargs[project_name_var]))
-
         start_time_ns = time.perf_counter_ns()
         response = await func(enhanced_session, **kwargs)
         end_time_ns = time.perf_counter_ns()
diff --git a/atr/get/announce.py b/atr/get/announce.py
index 022ef3e5..29c0cfed 100644
--- a/atr/get/announce.py
+++ b/atr/get/announce.py
@@ -47,6 +47,7 @@ async def selected(
     URL: /announce/<project_name>/<version_name>
     Allow the user to announce a release preview.
     """
+    await session.check_access(project_name)
     release = await _get_page_data(session, project_name, version_name)
 
     latest_revision_number = release.latest_revision_number
diff --git a/atr/get/compose.py b/atr/get/compose.py
index 18d9ce69..38d2e1fb 100644
--- a/atr/get/compose.py
+++ b/atr/get/compose.py
@@ -39,6 +39,7 @@ async def selected(
     URL: /compose/<project_name>/<version_name>
     Show the contents of the release candidate draft.
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         release = await data.release(
             project_name=str(project_name),
diff --git a/atr/get/draft.py b/atr/get/draft.py
index 04ce860b..c5a2155e 100644
--- a/atr/get/draft.py
+++ b/atr/get/draft.py
@@ -47,6 +47,7 @@ async def tools(
     URL: /draft/tools/<project_name>/<version_name>/<path:file_path>
     Show the tools for a specific file.
     """
+    await session.check_access(project_name)
     validated_path = form.to_relpath(str(file_path))
     if validated_path is None:
         raise base.ASFQuartException("Invalid file path", errorcode=400)
diff --git a/atr/get/file.py b/atr/get/file.py
index c0f3f9c4..40b9081a 100644
--- a/atr/get/file.py
+++ b/atr/get/file.py
@@ -46,6 +46,7 @@ async def selected(
     URL: /file/<project_name>/<version_name>
     View all the files in a release (any phase).
     """
+    await session.check_access(project_name)
     release = await session.release(str(project_name), str(version_name), 
phase=None)
 
     revision_number = release.latest_revision_number
@@ -149,6 +150,7 @@ async def selected_path(
     URL: /file/<project_name>/<version_name>/<path:file_path>
     View the content of a specific file in a release (any phase).
     """
+    await session.check_access(project_name)
     validated_path = form.to_relpath(str(file_path))
     if validated_path is None:
         raise web.FlashError("Invalid file path")
diff --git a/atr/get/manual.py b/atr/get/manual.py
index 76786aa2..0f774ae1 100644
--- a/atr/get/manual.py
+++ b/atr/get/manual.py
@@ -45,6 +45,7 @@ async def resolve_selected(
     URL: /manual/resolve/<project_name>/<version_name>
     Get the manual vote resolution page.
     """
+    await session.check_access(project_name)
     release = await session.release(
         str(project_name),
         str(version_name),
@@ -75,6 +76,7 @@ async def start_selected_revision(
     """
     URL: /manual/start/<project_name>/<version_name>/<revision>
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         match await interaction.release_ready_for_vote(
             session, str(project_name), str(version_name), revision, data, 
manual_vote=True
diff --git a/atr/get/release.py b/atr/get/release.py
index 2a535fc2..cf610872 100644
--- a/atr/get/release.py
+++ b/atr/get/release.py
@@ -89,12 +89,13 @@ async def releases(_session: web.Public, _releases: 
Literal["releases"]) -> str:
 
 @get.typed
 async def select(
-    _session: web.Committer, _release_select: Literal["release/select"], 
project_name: safe.ProjectName
+    session: web.Committer, _release_select: Literal["release/select"], 
project_name: safe.ProjectName
 ) -> str:
     """
     URL: /release/select/<project_name>
     Show releases in progress for a project.
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         project = await data.project(name=str(project_name), 
status=sql.ProjectStatus.ACTIVE, _releases=True).demand(
             base.ASFQuartException(f"Project {project_name} not found", 
errorcode=404)
diff --git a/atr/get/report.py b/atr/get/report.py
index 33b99686..995cb098 100644
--- a/atr/get/report.py
+++ b/atr/get/report.py
@@ -45,6 +45,7 @@ async def selected_path(
     URL: /report/<project_name>/<version_name>/<rel_path>
     Show the report for a specific file.
     """
+    await session.check_access(project_name)
     validated_path = form.to_relpath(rel_path)
     if validated_path is None:
         raise base.ASFQuartException("Invalid file path", errorcode=400)
diff --git a/atr/get/result.py b/atr/get/result.py
index 607987bd..63387f92 100644
--- a/atr/get/result.py
+++ b/atr/get/result.py
@@ -39,6 +39,7 @@ async def data(
     URL: /result/data/<project_name>/<version_name>/<check_id>
     Show a check result as formatted JSON.
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         release = await data.release(
             project_name=str(project_name),
diff --git a/atr/get/revisions.py b/atr/get/revisions.py
index be0d7b02..8bbb575a 100644
--- a/atr/get/revisions.py
+++ b/atr/get/revisions.py
@@ -60,6 +60,7 @@ async def selected(
     URL: /revisions/<project_name>/<version_name>
     Show the revision history for a release candidate draft or release preview.
     """
+    await session.check_access(project_name)
     try:
         release = await session.release(str(project_name), str(version_name))
         phase_key = "draft"
diff --git a/atr/get/start.py b/atr/get/start.py
index 17ff9e98..7e33a0fb 100644
--- a/atr/get/start.py
+++ b/atr/get/start.py
@@ -35,10 +35,11 @@ import atr.web as web
 
 
 @get.typed
-async def selected(_session: web.Committer, _start: Literal["start"], 
project_name: safe.ProjectName) -> str:
+async def selected(session: web.Committer, _start: Literal["start"], 
project_name: safe.ProjectName) -> str:
     """
     URL: /start/<project_name>
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         project = await data.project(name=str(project_name), 
status=sql.ProjectStatus.ACTIVE).demand(
             base.ASFQuartException(f"Project {project_name} not found", 
errorcode=404)
diff --git a/atr/get/upload.py b/atr/get/upload.py
index c4b36454..ef2e6777 100644
--- a/atr/get/upload.py
+++ b/atr/get/upload.py
@@ -47,6 +47,7 @@ async def selected(
     """
     URL: /upload/<project_name>/<version_name>
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         release = await session.release(str(project_name), str(version_name), 
data=data)
         user_ssh_keys = await data.ssh_key(asf_uid=session.uid).all()
diff --git a/atr/get/voting.py b/atr/get/voting.py
index c678fbc6..8c72cac8 100644
--- a/atr/get/voting.py
+++ b/atr/get/voting.py
@@ -52,6 +52,7 @@ async def selected_revision(
     """
     URL: /voting/<project_name>/<version_name>/<revision>
     """
+    await session.check_access(project_name)
     async with db.session() as data:
         match await interaction.release_ready_for_vote(
             session, str(project_name), str(version_name), revision, data, 
manual_vote=False
diff --git a/atr/shared/distribution.py b/atr/shared/distribution.py
index 023bed39..6408f94f 100644
--- a/atr/shared/distribution.py
+++ b/atr/shared/distribution.py
@@ -19,6 +19,7 @@ from __future__ import annotations
 
 import datetime
 import enum
+import urllib.parse
 
 import aiohttp
 import pydantic
@@ -242,18 +243,20 @@ def distribution_web_url(  # noqa: C901
 
 def get_api_url(dd: distribution.Data, staging: bool | None = None):
     template_url = _template_url(dd, staging)
+    package = urllib.parse.quote(dd.package)
+    version = urllib.parse.quote(dd.version)
     api_url = template_url.format(
         owner_namespace=dd.owner_namespace,
-        package=dd.package,
-        version=dd.version,
+        package=package,
+        version=version,
     )
     if dd.platform == sql.DistributionPlatform.MAVEN:
         # We do this here because the CDNs break the namespace up into a / 
delimited URL
         owner = (dd.owner_namespace or "").replace(".", "/")
         api_url = template_url.format(
             owner_namespace=owner,
-            package=dd.package,
-            version=dd.version,
+            package=package,
+            version=version,
         )
     return api_url
 
diff --git a/atr/web.py b/atr/web.py
index e7a999f5..e319d3de 100644
--- a/atr/web.py
+++ b/atr/web.py
@@ -32,6 +32,7 @@ import atr.config as config
 import atr.db as db
 import atr.form as form
 import atr.htm as htm
+import atr.models.safe as safe
 import atr.models.sql as sql
 import atr.user as user
 import atr.util as util
@@ -87,8 +88,8 @@ class Committer:
     def is_admin(self) -> bool:
         return user.is_admin(self.uid)
 
-    async def check_access(self, project_name: str) -> None:
-        if not any((p.name == str(project_name)) for p in (await 
self.user_projects)):
+    async def check_access(self, project_name: str | safe.ProjectName) -> None:
+        if not any((p.name == project_name) for p in (await 
self.user_projects)):
             if self.is_admin:
                 # Admins can view all projects
                 # But we must warn them when the project is not one of their 
own


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to