From 965c201d8af4b8123edeb2e1b0adf9a033c3b913 Mon Sep 17 00:00:00 2001 From: Mike Fiedler Date: Fri, 29 Sep 2023 13:23:03 -0400 Subject: [PATCH 1/3] feat: introduce property on File Signed-off-by: Mike Fiedler --- tests/unit/packaging/test_models.py | 26 ++++++++++++++++++++++++++ warehouse/packaging/models.py | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/tests/unit/packaging/test_models.py b/tests/unit/packaging/test_models.py index de24269da8a3..31e2fe7daa91 100644 --- a/tests/unit/packaging/test_models.py +++ b/tests/unit/packaging/test_models.py @@ -32,6 +32,7 @@ ) from ...common.db.packaging import ( DependencyFactory as DBDependencyFactory, + FileEventFactory as DBFileEventFactory, FileFactory as DBFileFactory, ProjectFactory as DBProjectFactory, ReleaseFactory as DBReleaseFactory, @@ -579,3 +580,28 @@ def test_query_paths(self, db_session): ) assert results == (expected, expected + ".metadata") + + def test_published_via_trusted_publisher(self, db_session): + project = DBProjectFactory.create() + release = DBReleaseFactory.create(project=project) + rfile = DBFileFactory.create( + release=release, + filename=f"{project.name}-{release.version}.tar.gz", + python_version="source", + ) + DBFileEventFactory.create( + source=rfile, + tag="fake:event", + additional={}, + ) + + # Without the `publisher_url` key, not considered trusted published + assert not rfile.uploaded_via_trusted_publisher + + DBFileEventFactory.create( + source=rfile, + tag="fake:event", + additional={"publisher_url": "https://fake/url"}, + ) + + assert rfile.uploaded_via_trusted_publisher diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index d0211bbc8f3d..da34ebc964cc 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -670,6 +670,16 @@ def __table_args__(cls): # noqa comment="If True, the object has been archived to our archival bucket.", ) + @property + def uploaded_via_trusted_publisher(self) -> bool: + """Return True if the file was uploaded via a trusted publisher.""" + return ( + self.events.where( + self.Event.additional.has_key("publisher_url") # type: ignore[attr-defined] # noqa E501 + ).count() + > 0 + ) + @hybrid_property def metadata_path(self): return self.path + ".metadata" From 97686291f6710765165a5181b1653bb340deff14 Mon Sep 17 00:00:00 2001 From: Mike Fiedler Date: Fri, 29 Sep 2023 13:36:37 -0400 Subject: [PATCH 2/3] feat: introduce property on Release Signed-off-by: Mike Fiedler --- tests/unit/packaging/test_models.py | 54 +++++++++++++++++++++++++++++ warehouse/packaging/models.py | 11 ++++++ 2 files changed, 65 insertions(+) diff --git a/tests/unit/packaging/test_models.py b/tests/unit/packaging/test_models.py index 31e2fe7daa91..470100fdf487 100644 --- a/tests/unit/packaging/test_models.py +++ b/tests/unit/packaging/test_models.py @@ -516,6 +516,60 @@ def test_github_open_issue_info_url(self, db_session, home_page, expected): release = DBReleaseFactory.create(home_page=home_page) assert release.github_open_issue_info_url == expected + def test_trusted_published_none(self, db_session): + release = DBReleaseFactory.create() + + assert not release.trusted_published + + def test_trusted_published_all(self, db_session): + release = DBReleaseFactory.create() + release_file = DBFileFactory.create( + release=release, + filename=f"{release.project.name}-{release.version}.tar.gz", + python_version="source", + ) + DBFileEventFactory.create( + source=release_file, + tag="fake:event", + additional={}, + ) + + # Without the `publisher_url` key, not considered trusted published + assert not release.trusted_published + + DBFileEventFactory.create( + source=release_file, + tag="fake:event", + additional={"publisher_url": "https://fake/url"}, + ) + + assert release.trusted_published + + def test_trusted_published_mixed(self, db_session): + release = DBReleaseFactory.create() + rfile_1 = DBFileFactory.create( + release=release, + filename=f"{release.project.name}-{release.version}.tar.gz", + python_version="source", + ) + rfile_2 = DBFileFactory.create( + release=release, + filename=f"{release.project.name}-{release.version}.whl", + python_version="bdist_wheel", + ) + DBFileEventFactory.create( + source=rfile_1, + tag="fake:event", + additional={}, + ) + DBFileEventFactory.create( + source=rfile_2, + tag="fake:event", + additional={"publisher_url": "https://fake/url"}, + ) + + assert not release.trusted_published + class TestFile: def test_requires_python(self, db_session): diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index da34ebc964cc..8c97f4a08717 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -603,6 +603,17 @@ def has_meta(self): ] ) + @property + def trusted_published(self) -> bool: + """ + A Release can be considered published via a trusted publisher if + **all** the Files in the release are published via a trusted publisher. + """ + files = self.files.all() # type: ignore[attr-defined] + if not files: + return False + return all(file.uploaded_via_trusted_publisher for file in files) + class PackageType(str, enum.Enum): bdist_dmg = "bdist_dmg" From 0d245b89221c6edac724b07580d7ab66a3de139d Mon Sep 17 00:00:00 2001 From: Mike Fiedler Date: Fri, 29 Sep 2023 14:27:17 -0400 Subject: [PATCH 3/3] feat(admin): surface trusted publisher via admin Signed-off-by: Mike Fiedler --- .../admin/projects/release_detail.html | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/warehouse/admin/templates/admin/projects/release_detail.html b/warehouse/admin/templates/admin/projects/release_detail.html index 93f172a99930..33235efde9d5 100644 --- a/warehouse/admin/templates/admin/projects/release_detail.html +++ b/warehouse/admin/templates/admin/projects/release_detail.html @@ -51,6 +51,18 @@

Details

Created via {{ release.uploaded_via }} + + Trusted Published? + + {% if release.trusted_published %} + + {% else %} + + + + + {% endif %} + Yanked {{ release.yanked }} @@ -70,6 +82,7 @@

Files

Package Type Python Version Uploaded via + Trusted Publisher? @@ -79,6 +92,13 @@

Files

{{ file.packagetype }} {{ file.python_version }} {{ file.uploaded_via }} + + {% if file.uploaded_via_trusted_publisher %} + + {% else %} + + {% endif %} + {% endfor %}