This is an automated email from the ASF dual-hosted git repository. arm pushed a commit to branch safe_committee_type in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
commit 1a4dbeea31a4780af704e8a50fbfbec2b6897683 Author: Alastair McFarlane <[email protected]> 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..8b6058f6 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.CommitteeName, ) -> 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.CommitteeName, ) -> 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.CommitteeName, ) -> DictResponse: """ URL: GET /committee/projects/<name> diff --git a/atr/blueprints/common.py b/atr/blueprints/common.py index 0fd2073b..719c7dc0 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.CommitteeName, safe.ProjectName, safe.RevisionNumber, safe.VersionName, diff --git a/atr/get/committees.py b/atr/get/committees.py index 1ab70af9..1512fd46 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.CommitteeName) -> 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..fdb7333a 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.CommitteeName ) -> web.TextResponse: """ URL: /keys/export/<committee_name> diff --git a/atr/get/projects.py b/atr/get/projects.py index d57622cf..e47224ca 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.CommitteeName ) -> 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 c71daf27..2f9bc0f5 100644 --- a/atr/models/safe.py +++ b/atr/models/safe.py @@ -86,6 +86,11 @@ class Alphanumeric(SafeType): return _ALPHANUM +class CommitteeName(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..f01c3117 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.CommitteeName, 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..cf6dbe30 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.CommitteeName, 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..6ef7ee9b 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.CommitteeName) -> 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: [email protected] For additional commands, e-mail: [email protected]
