You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ar...@apache.org on 2026/03/16 17:10:19 UTC
(tooling-trusted-releases) 01/02: #840: safe committee name type - without validation
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 763eceb6e9e7270a94428172b87525a7116a0b3f
Author: Alastair McFarlane <ar...@apache.org>
AuthorDate: Thu Mar 12 11:16:53 2026 +0000
#840: safe committee name type - without validation
---
atr/api/__init__.py | 6 +++---
atr/blueprints/common.py | 1 +
atr/get/committees.py | 8 ++++----
atr/get/keys.py | 3 ++-
atr/get/projects.py | 17 ++++++++---------
atr/models/safe.py | 5 +++++
atr/post/projects.py | 5 ++---
atr/storage/writers/project.py | 9 ++++-----
atr/web.py | 4 ++--
9 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/atr/api/__init__.py b/atr/api/__init__.py
index cbc0065e..820f9b60 100644
--- a/atr/api/__init__.py
+++ b/atr/api/__init__.py
@@ -180,7 +180,7 @@ async def checks_ongoing(
@quart_schema.validate_response(models.api.CommitteeGetResults, 200)
async def committee_get(
_committee_get: Literal["committee/get"],
- name: unsafe.UnsafeStr,
+ name: safe.CommitteeKey,
) -> DictResponse:
"""
URL: GET /committee/get/<name>
@@ -206,7 +206,7 @@ async def committee_get(
@quart_schema.validate_response(models.api.CommitteeKeysResults, 200)
async def committee_keys(
_committee_keys: Literal["committee/keys"],
- name: unsafe.UnsafeStr,
+ name: safe.CommitteeKey,
) -> DictResponse:
"""
URL: GET /committee/keys/<name>
@@ -232,7 +232,7 @@ async def committee_keys(
@quart_schema.validate_response(models.api.CommitteeProjectsResults, 200)
async def committee_projects(
_committee_projects: Literal["committee/projects"],
- name: unsafe.UnsafeStr,
+ name: safe.CommitteeKey,
) -> DictResponse:
"""
URL: GET /committee/projects/<name>
diff --git a/atr/blueprints/common.py b/atr/blueprints/common.py
index 0fd2073b..1382e90c 100644
--- a/atr/blueprints/common.py
+++ b/atr/blueprints/common.py
@@ -40,6 +40,7 @@ QUART_CONVERTERS: dict[Any, str] = {
VALIDATED_TYPES: set[Any] = {
safe.Alphanumeric,
+ safe.CommitteeKey,
safe.ProjectName,
safe.RevisionNumber,
safe.VersionName,
diff --git a/atr/get/committees.py b/atr/get/committees.py
index 1ab70af9..03089197 100644
--- a/atr/get/committees.py
+++ b/atr/get/committees.py
@@ -23,8 +23,8 @@ import asfquart.base as base
import atr.blueprints.get as get
import atr.db as db
import atr.form as form
+import atr.models.safe as safe
import atr.models.sql as sql
-import atr.models.unsafe as unsafe
import atr.post as post
import atr.shared as shared
import atr.template as template
@@ -49,7 +49,7 @@ async def directory(_session: web.Public, _committees: Literal["committees"]) ->
@get.typed
-async def view(_session: web.Public, _committees: Literal["committees"], name: unsafe.UnsafeStr) -> str:
+async def view(_session: web.Public, _committees: Literal["committees"], name: safe.CommitteeKey) -> str:
"""
URL: /committees/<name>
"""
@@ -75,8 +75,8 @@ async def view(_session: web.Public, _committees: Literal["committees"], name: u
model_cls=shared.keys.UpdateCommitteeKeysForm,
action=util.as_url(post.keys.keys),
submit_label="Regenerate KEYS file",
- defaults={"committee_name": str(name)},
+ defaults={"committee_name": committee.name},
empty=True,
),
- is_standing=util.committee_is_standing(str(name)),
+ is_standing=util.committee_is_standing(committee.name),
)
diff --git a/atr/get/keys.py b/atr/get/keys.py
index d0fba17f..dab0082e 100644
--- a/atr/get/keys.py
+++ b/atr/get/keys.py
@@ -26,6 +26,7 @@ import atr.blueprints.get as get
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.models.unsafe as unsafe
import atr.post as post
@@ -183,7 +184,7 @@ async def details(session: web.Committer, _keys_details: Literal["keys/details"]
@get.typed
async def export(
- _session: web.Committer, _keys_export: Literal["keys/export"], committee_name: unsafe.UnsafeStr
+ _session: web.Committer, _keys_export: Literal["keys/export"], committee_name: safe.CommitteeKey
) -> web.TextResponse:
"""
URL: /keys/export/<committee_name>
diff --git a/atr/get/projects.py b/atr/get/projects.py
index d57622cf..20cb2a77 100644
--- a/atr/get/projects.py
+++ b/atr/get/projects.py
@@ -35,7 +35,6 @@ import atr.get.start as start
import atr.htm as htm
import atr.models.safe as safe
import atr.models.sql as sql
-import atr.models.unsafe as unsafe
import atr.post as post
import atr.registry as registry
import atr.shared as shared
@@ -47,12 +46,12 @@ import atr.web as web
@get.typed
async def add_project(
- session: web.Committer, _project_add: Literal["project/add"], committee_name: unsafe.UnsafeStr
+ session: web.Committer, _project_add: Literal["project/add"], committee_name: safe.CommitteeKey
) -> web.WerkzeugResponse | str:
"""
URL: /project/add/<committee_name>
"""
- await session.check_access_committee(str(committee_name))
+ await session.check_access_committee(committee_name)
async with db.session() as data:
committee = await data.committee(name=str(committee_name)).demand(
@@ -60,20 +59,20 @@ async def add_project(
)
page = htm.Block()
- page.p[htm.a(".atr-back-link", href=util.as_url(committees.view, name=str(committee_name)))["← Back to committee"]]
+ page.p[htm.a(".atr-back-link", href=util.as_url(committees.view, name=committee.name))["← Back to committee"]]
page.h1["Add project"]
page.p[f"Add a new project to the {committee.display_name} committee."]
- committee_display_name = committee.full_name or str(committee_name).title()
+ committee_display_name = committee.full_name or committee.name.title()
form.render_block(
page,
model_cls=shared.projects.AddProjectForm,
- action=util.as_url(post.projects.add_project, committee_name=str(committee_name)),
+ action=util.as_url(post.projects.add_project, committee_name=committee.name),
submit_label="Add project",
- cancel_url=util.as_url(committees.view, name=str(committee_name)),
+ cancel_url=util.as_url(committees.view, name=committee.name),
defaults={
- "committee_name": str(committee_name),
+ "committee_name": committee.name,
},
)
@@ -81,7 +80,7 @@ async def add_project(
page.append(
htpy.div(
"#projects-add-config.d-none",
- data_committee_name=str(committee_name),
+ data_committee_name=committee.name,
data_committee_display_name=committee_display_name,
)
)
diff --git a/atr/models/safe.py b/atr/models/safe.py
index 799ac5dc..e72cfb21 100644
--- a/atr/models/safe.py
+++ b/atr/models/safe.py
@@ -90,6 +90,11 @@ class Alphanumeric(SafeType):
return _ALPHANUM
+class CommitteeKey(Alphanumeric):
+ def _additional_validations(self, value: str):
+ pass
+
+
class Numeric(SafeType):
@classmethod
def _valid_chars(cls) -> frozenset[str]:
diff --git a/atr/post/projects.py b/atr/post/projects.py
index 6763bf48..9896834a 100644
--- a/atr/post/projects.py
+++ b/atr/post/projects.py
@@ -37,7 +37,7 @@ import atr.web as web
async def add_project(
session: web.Committer,
_project_add: Literal["project/add"],
- committee_name: unsafe.UnsafeStr,
+ committee_name: safe.CommitteeKey,
project_form: shared.projects.AddProjectForm,
) -> web.WerkzeugResponse:
"""
@@ -46,11 +46,10 @@ async def add_project(
display_name = project_form.display_name
label = project_form.label
- # TODO: Is this right? Name is unvalidated
async with storage.write(session) as write:
wacm = await write.as_project_committee_member(safe.ProjectName(str(committee_name)))
try:
- await wacm.project.create(str(committee_name), display_name, label)
+ await wacm.project.create(committee_name, display_name, label)
except storage.AccessError as e:
return await session.redirect(
get.projects.add_project, committee_name=str(committee_name), error=f"Error adding project: {e}"
diff --git a/atr/storage/writers/project.py b/atr/storage/writers/project.py
index 0580a2bc..031be6d7 100644
--- a/atr/storage/writers/project.py
+++ b/atr/storage/writers/project.py
@@ -132,15 +132,14 @@ class CommitteeMember(CommitteeParticipant):
return True
return False
- async def create(self, committee_name: str, display_name: str, label: str) -> None:
+ async def create(self, committee_name: safe.CommitteeKey, display_name: str, label: str) -> None:
super_project = None
- # Wrap the name in safe.ProjectName so we can create it in the database
# TODO: Do we need to do any additional validation on the string value?
# Get the base project to derive from
# We're allowing derivation from a retired project here
# TODO: Should we disallow this instead?
committee_projects = await self.__data.project(
- committee_name=committee_name, _committee=True, _release_policy=True
+ committee_name=str(committee_name), _committee=True, _release_policy=True
).all()
for committee_project in committee_projects:
if label.startswith(str(committee_project.name) + "-"):
@@ -160,7 +159,7 @@ class CommitteeMember(CommitteeParticipant):
description=super_project.description if super_project else None,
category=super_project.category if super_project else None,
programming_languages=super_project.programming_languages if super_project else None,
- committee_name=committee_name,
+ committee_name=str(committee_name),
created=datetime.datetime.now(datetime.UTC),
created_by=self.__asf_uid,
)
@@ -172,7 +171,7 @@ class CommitteeMember(CommitteeParticipant):
await self.__data.commit()
self.__write_as.append_to_audit_log(
asf_uid=self.__asf_uid,
- committee_name=committee_name,
+ committee_name=str(committee_name),
project_name=label,
)
diff --git a/atr/web.py b/atr/web.py
index e8cbd8e0..2d79589d 100644
--- a/atr/web.py
+++ b/atr/web.py
@@ -99,8 +99,8 @@ class Committer:
return
raise base.ASFQuartException("You do not have access to this project", errorcode=403)
- async def check_access_committee(self, committee_name: str) -> None:
- if committee_name not in self.committees:
+ async def check_access_committee(self, committee_name: safe.CommitteeKey) -> None:
+ if str(committee_name) not in self.committees:
if self.is_admin:
# Admins can view all committees
# But we must warn them when the committee is not one of their own
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org