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
commit b53e4015eb5673892905560c0d94a25234c9a73a 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 | 3 ++- 15 files changed, 25 insertions(+), 13 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..d5499972 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,7 +88,7 @@ class Committer: def is_admin(self) -> bool: return user.is_admin(self.uid) - async def check_access(self, project_name: str) -> None: + async def check_access(self, project_name: str | safe.ProjectName) -> None: if not any((p.name == str(project_name)) for p in (await self.user_projects)): if self.is_admin: # Admins can view all projects --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
