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]