From d19eed66eab8ef08c8d51a3c2f72303697fafc6f Mon Sep 17 00:00:00 2001 From: Alexis Date: Thu, 29 Aug 2024 16:55:13 +0200 Subject: [PATCH 1/3] Functional tests for attestations --- tests/conftest.py | 22 ++- .../assets/rfc8785-0.1.2-py3-none-any.whl | Bin 0 -> 9172 bytes tests/functional/test_attestations.py | 153 ++++++++++++++++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl create mode 100644 tests/functional/test_attestations.py diff --git a/tests/conftest.py b/tests/conftest.py index 1bc388f06875..b0ad48520a4e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,7 +57,7 @@ from warehouse.organizations import services as organization_services from warehouse.organizations.interfaces import IOrganizationService from warehouse.packaging import services as packaging_services -from warehouse.packaging.interfaces import IProjectService +from warehouse.packaging.interfaces import IFileStorage, IProjectService from warehouse.subscriptions import services as subscription_services from warehouse.subscriptions.interfaces import IBillingService, ISubscriptionService @@ -112,6 +112,14 @@ def metrics(): ) +@pytest.fixture +def storage_service(tmp_path): + """ + A good-enough local file storage service. + """ + return packaging_services.LocalArchiveFileStorage(tmp_path) + + @pytest.fixture def remote_addr(): return "1.2.3.4" @@ -173,6 +181,7 @@ def pyramid_services( project_service, github_oidc_service, activestate_oidc_service, + storage_service, macaroon_service, helpdesk_service, ): @@ -195,6 +204,7 @@ def pyramid_services( activestate_oidc_service, IOIDCPublisherService, None, name="activestate" ) services.register_service(macaroon_service, IMacaroonService, None, name="") + services.register_service(storage_service, IFileStorage, None, name="archive") services.register_service(helpdesk_service, IHelpDeskService, None) return services @@ -683,7 +693,7 @@ def xmlrpc(self, path, method, *args): @pytest.fixture -def webtest(app_config_dbsession_from_env): +def webtest(app_config_dbsession_from_env, tmp_path): """ This fixture yields a test app with an alternative Pyramid configuration, injecting the database session and transaction manager into the app. @@ -695,7 +705,13 @@ def webtest(app_config_dbsession_from_env): """ # We want to disable anything that relies on TLS here. - app_config_dbsession_from_env.add_settings(enforce_https=False) + # app_config_dbsession_from_env.add_settings(enforce_https=False) + app_config_dbsession_from_env.add_settings( + { + "enforce_https": False, + "archive_files.path": tmp_path, + } + ) app = app_config_dbsession_from_env.make_wsgi_app() diff --git a/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl b/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..00225acb8a344bb16015f9c172c21f3faa557f19 GIT binary patch literal 9172 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 a?LT*riUJh$KkQ-tTsME#W0ve6Z~qSo5WoQd literal 0 HcmV?d00001 diff --git a/tests/functional/test_attestations.py b/tests/functional/test_attestations.py new file mode 100644 index 000000000000..18a330e413fa --- /dev/null +++ b/tests/functional/test_attestations.py @@ -0,0 +1,153 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import collections +import hashlib +import time + +from datetime import datetime +from http import HTTPStatus +from pathlib import Path + +import pretend + +from pypi_attestations import Attestation, Envelope, VerificationMaterial +from sigstore.verify import Verifier +from webtest.forms import Upload + +from tests.common.db.oidc import GitHubPublisherFactory +from tests.common.db.packaging import ProjectFactory +from warehouse.macaroons import caveats +from warehouse.macaroons.services import DatabaseMacaroonService + + +def get_macaraoon(macaroon_service, publisher): + not_before = int(time.time()) + expires_at = not_before + 900 + claims = {"sha": "some-sha", "ref": "some-ref"} + + serialized, dm = macaroon_service.create_macaroon( + "localhost", + (f"OpenID token: TODO " f"({datetime.fromtimestamp(not_before).isoformat()})"), + [ + caveats.OIDCPublisher( + oidc_publisher_id=str(publisher.id), + ), + caveats.ProjectID(project_ids=[str(p.id) for p in publisher.projects]), + caveats.Expiration(expires_at=expires_at, not_before=not_before), + ], + oidc_publisher_id=str(publisher.id), + additional={"oidc": publisher.stored_claims(claims)}, + ) + + return serialized + + +_HERE = Path(__file__).parent +_ASSETS = _HERE / "assets" +_WHEEL_PACKAGE = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl" + + +class TestPackageWithAttestations: + + def upload_package(self, webtest, monkeypatch, project, publisher): + + filename = "rfc8785-0.1.2-py3-none-any.whl" + attestation = Attestation( + version=1, + verification_material=VerificationMaterial( + certificate="some_cert", transparency_entries=[dict()] + ), + envelope=Envelope( + statement="somebase64string", + signature="somebase64string", + ), + ) + + md5_digest = hashlib.file_digest(open(_WHEEL_PACKAGE, "rb"), "md5").hexdigest() + + data = collections.OrderedDict( + { + ":action": "file_upload", + "protocol_version": "1", # TODO(dm) + "metadata_version": "1.2", + "name": project.name, + "attestations": f"[{attestation.model_dump_json()}]", + "version": "0.1.2", + "filetype": "bdist_wheel", + "pyversion": "cp312", + "md5_digest": md5_digest, + "content": ( + Upload( + filename, open(_WHEEL_PACKAGE, "rb").read(), "application/tar" + ) + ), + } + ) + + macaroon_service = DatabaseMacaroonService( + webtest.extra_environ["warehouse.db_session"] + ) + webtest.set_authorization( + ("Basic", ("__token__", get_macaraoon(macaroon_service, publisher))) + ) + + verify = pretend.call_recorder( + lambda _self, _verifier, _policy, _dist: ( + "https://docs.pypi.org/attestations/publish/v1", + None, + ) + ) + monkeypatch.setattr(Attestation, "verify", verify) + monkeypatch.setattr(Verifier, "production", lambda: pretend.stub()) + + resp = webtest.post( + "/legacy/", + params=data, + ) + + # TODO(DM) Improve testing results here + + assert resp.status_code == HTTPStatus.OK + + def view_package(self, webtest, project): + resp = webtest.get(f"/simple/{project.normalized_name}/") + + release_version = project.releases[0].version + release_filename = project.releases[0].files[0].filename + + assert resp.status_code == HTTPStatus.OK + assert release_filename in resp.text + + # Use simple JSON API + resp = webtest.get( + f"/simple/{project.normalized_name}/", + headers={"Accept": "application/vnd.pypi.simple.v1+json"}, + ) + assert resp.status_code == HTTPStatus.OK + assert resp.json["files"][0]["filename"] == release_filename + + # Use soon-to-be deprecated legacy API + # Remove me once deprecated + resp = webtest.get(f"/pypi/{project.normalized_name}/json") + assert resp.status_code == HTTPStatus.OK + assert resp.content_type == "application/json" + + content = resp.json + assert content["releases"][release_version][0]["filename"] == release_filename + + def test_package(self, webtest, monkeypatch): + + project = ProjectFactory.create(name="rfc8785") + publisher = GitHubPublisherFactory.create(projects=[project]) + + self.upload_package(webtest, monkeypatch, project, publisher) + self.view_package(webtest, project) From a4148bfbb1b3351bb4a962e3605f23619a51b65a Mon Sep 17 00:00:00 2001 From: Alexis Date: Thu, 29 Aug 2024 16:55:13 +0200 Subject: [PATCH 2/3] Functional tests for attestations --- tests/conftest.py | 10 +- .../assets/rfc8785-0.1.2-py3-none-any.whl | Bin 0 -> 9172 bytes tests/functional/test_attestations.py | 153 ++++++++++++++++++ 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl create mode 100644 tests/functional/test_attestations.py diff --git a/tests/conftest.py b/tests/conftest.py index 42f52aebcce5..ce03125625fa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -702,7 +702,7 @@ def xmlrpc(self, path, method, *args): @pytest.fixture -def webtest(app_config_dbsession_from_env): +def webtest(app_config_dbsession_from_env, tmp_path): """ This fixture yields a test app with an alternative Pyramid configuration, injecting the database session and transaction manager into the app. @@ -714,7 +714,13 @@ def webtest(app_config_dbsession_from_env): """ # We want to disable anything that relies on TLS here. - app_config_dbsession_from_env.add_settings(enforce_https=False) + # app_config_dbsession_from_env.add_settings(enforce_https=False) + app_config_dbsession_from_env.add_settings( + { + "enforce_https": False, + "archive_files.path": tmp_path, + } + ) app = app_config_dbsession_from_env.make_wsgi_app() diff --git a/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl b/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..00225acb8a344bb16015f9c172c21f3faa557f19 GIT binary patch literal 9172 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 a?LT*riUJh$KkQ-tTsME#W0ve6Z~qSo5WoQd literal 0 HcmV?d00001 diff --git a/tests/functional/test_attestations.py b/tests/functional/test_attestations.py new file mode 100644 index 000000000000..18a330e413fa --- /dev/null +++ b/tests/functional/test_attestations.py @@ -0,0 +1,153 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import collections +import hashlib +import time + +from datetime import datetime +from http import HTTPStatus +from pathlib import Path + +import pretend + +from pypi_attestations import Attestation, Envelope, VerificationMaterial +from sigstore.verify import Verifier +from webtest.forms import Upload + +from tests.common.db.oidc import GitHubPublisherFactory +from tests.common.db.packaging import ProjectFactory +from warehouse.macaroons import caveats +from warehouse.macaroons.services import DatabaseMacaroonService + + +def get_macaraoon(macaroon_service, publisher): + not_before = int(time.time()) + expires_at = not_before + 900 + claims = {"sha": "some-sha", "ref": "some-ref"} + + serialized, dm = macaroon_service.create_macaroon( + "localhost", + (f"OpenID token: TODO " f"({datetime.fromtimestamp(not_before).isoformat()})"), + [ + caveats.OIDCPublisher( + oidc_publisher_id=str(publisher.id), + ), + caveats.ProjectID(project_ids=[str(p.id) for p in publisher.projects]), + caveats.Expiration(expires_at=expires_at, not_before=not_before), + ], + oidc_publisher_id=str(publisher.id), + additional={"oidc": publisher.stored_claims(claims)}, + ) + + return serialized + + +_HERE = Path(__file__).parent +_ASSETS = _HERE / "assets" +_WHEEL_PACKAGE = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl" + + +class TestPackageWithAttestations: + + def upload_package(self, webtest, monkeypatch, project, publisher): + + filename = "rfc8785-0.1.2-py3-none-any.whl" + attestation = Attestation( + version=1, + verification_material=VerificationMaterial( + certificate="some_cert", transparency_entries=[dict()] + ), + envelope=Envelope( + statement="somebase64string", + signature="somebase64string", + ), + ) + + md5_digest = hashlib.file_digest(open(_WHEEL_PACKAGE, "rb"), "md5").hexdigest() + + data = collections.OrderedDict( + { + ":action": "file_upload", + "protocol_version": "1", # TODO(dm) + "metadata_version": "1.2", + "name": project.name, + "attestations": f"[{attestation.model_dump_json()}]", + "version": "0.1.2", + "filetype": "bdist_wheel", + "pyversion": "cp312", + "md5_digest": md5_digest, + "content": ( + Upload( + filename, open(_WHEEL_PACKAGE, "rb").read(), "application/tar" + ) + ), + } + ) + + macaroon_service = DatabaseMacaroonService( + webtest.extra_environ["warehouse.db_session"] + ) + webtest.set_authorization( + ("Basic", ("__token__", get_macaraoon(macaroon_service, publisher))) + ) + + verify = pretend.call_recorder( + lambda _self, _verifier, _policy, _dist: ( + "https://docs.pypi.org/attestations/publish/v1", + None, + ) + ) + monkeypatch.setattr(Attestation, "verify", verify) + monkeypatch.setattr(Verifier, "production", lambda: pretend.stub()) + + resp = webtest.post( + "/legacy/", + params=data, + ) + + # TODO(DM) Improve testing results here + + assert resp.status_code == HTTPStatus.OK + + def view_package(self, webtest, project): + resp = webtest.get(f"/simple/{project.normalized_name}/") + + release_version = project.releases[0].version + release_filename = project.releases[0].files[0].filename + + assert resp.status_code == HTTPStatus.OK + assert release_filename in resp.text + + # Use simple JSON API + resp = webtest.get( + f"/simple/{project.normalized_name}/", + headers={"Accept": "application/vnd.pypi.simple.v1+json"}, + ) + assert resp.status_code == HTTPStatus.OK + assert resp.json["files"][0]["filename"] == release_filename + + # Use soon-to-be deprecated legacy API + # Remove me once deprecated + resp = webtest.get(f"/pypi/{project.normalized_name}/json") + assert resp.status_code == HTTPStatus.OK + assert resp.content_type == "application/json" + + content = resp.json + assert content["releases"][release_version][0]["filename"] == release_filename + + def test_package(self, webtest, monkeypatch): + + project = ProjectFactory.create(name="rfc8785") + publisher = GitHubPublisherFactory.create(projects=[project]) + + self.upload_package(webtest, monkeypatch, project, publisher) + self.view_package(webtest, project) From ad651df65e2f6ead28f0f2849fa1d4b5174dc0dd Mon Sep 17 00:00:00 2001 From: Alexis Date: Thu, 29 Aug 2024 18:27:56 +0200 Subject: [PATCH 3/3] Fix wrapping issue --- .../pypi_hello_world-0.1.0-py3-none-any.whl | Bin 0 -> 1145 bytes ...0.1.0-py3-none-any.whl.publish.attestation | 1 + .../assets/rfc8785-0.1.2-py3-none-any.whl | Bin 9172 -> 0 bytes tests/functional/test_attestations.py | 58 +++++++++--------- 4 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl create mode 100644 tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl.publish.attestation delete mode 100644 tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl diff --git a/tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl b/tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..50b7e12c14b2788c52761598c32408b0eedd15d9 GIT binary patch literal 1145 zcmWIWW@Zs#U|`??VyA!?p(2KsK$aa4%K&jfWkF_qMruw@etda;QBI0}e0*kJW=VX! zUO}a|r*H5HkFzIFUcV4>VQa94p5~@UK@3fcu1vbL$c9ywf5IH6sKB~)PL8RL%k(1F z=2g}O`gJW55@!fNvGUV**}^qItDXU|CKfAo4fG844D?bmi%WDf^V0J5eO*HwT^vIk zZTmLz9Wvl?DX(u;ea6BSts0TQs<vPx?yO{S zp?Jf@Nw?Bp+Fia{v~aa$jz;Wa-k^f4vvDW7vx+pr7Ebr!{(kY(3r4jHliGqWkC{#8 z`qoExzKK2dDC^>{O@5R1$?H9Sf5de^BdV9%Zv8I_19~+ah}DSka=3@9s}J{i58tyF zdA)VD&Yd~GImqCO@q;4&v);O1Iw$ov1)cEF@jQ3pvFNv8yXl-WmY7~z$jlzd?d0`Cc={ZfFCN^CyzU2=zkhP+sQ8b4iIPh) zUTu4OG`~K3^;UtPlYtz?5e<2I70&m(_kNjvCL`XH|8(hF;Ql;2KkbDs15)2U~B z_x|LV_~DyLkE+dT=6;5{#`Dd89A180xHvUD^pi+Q@WcGYN;~^(Wc*v6$nm_pmhPzP zTRm-=v3|qW+P>mf2Je_Q{9+F9W@Hj!#+@~Qp#ug>8bK7kjDl_wdWwgb!@#hFu^5X< zIMYA6vFND^VQd;OHqjl3VJtX}p__@G9uQ_SFcD!UBy|LMv$BCSvH+nM&?%D4ARYkS CIg#uD literal 0 HcmV?d00001 diff --git a/tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl.publish.attestation b/tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl.publish.attestation new file mode 100644 index 000000000000..8b5938494214 --- /dev/null +++ b/tests/functional/assets/pypi_hello_world-0.1.0-py3-none-any.whl.publish.attestation @@ -0,0 +1 @@ +{"version":1,"verification_material":{"certificate":"MIIC4TCCAmagAwIBAgIUeQoQZze+KsP364q6eOH72BFWDWswCgYIKoZIzj0EAwMwNzEVMBMGA1UE\nChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQwODI5\nMTU0MTQ0WhcNMjQwODI5MTU1MTQ0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3fRY5RIr\n32h2fFSK5z7vJdOgDFRz5A5EWpotg+HMal4ckviSLhxjoiry9s3Vg1e+e0QylSNAej1YcjWUCOe4\nTqOCAYUwggGBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU\n+bvTYk4hsJDJIzvyo3ZaH52JkyAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wLgYD\nVR0RAQH/BCQwIoEgYWxleGlzLmNoYWxsYW5kZUB0cmFpbG9mYml0cy5jb20wLAYKKwYBBAGDvzAB\nAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6\nLy9naXRodWIuY29tL2xvZ2luL29hdXRoMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJj\nGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGRnsy+6gAABAMASDBGAiEAj64pq6I1lpTTUi9WjLLs\nr3Wg+OkLyhcgdulND14mHOICIQD/6sl1bhnb1jS0gUvmv5YRqrNusu6kobNJSnJi20KR7DAKBggq\nhkjOPQQDAwNpADBmAjEAzUhENnoHABt1b25dsencHlh7Uokc7xc2XiaxDxG6SKZZe3OAIlb8hmaB\nhL38DSSUAjEAzXqP2QGTsWaMdrw6EYLikl0qlgcuF7+GEEKJfRWpMeuH2tSsC7C8VOkU0tLVJ1wr\n","transparency_entries":[{"logIndex":"125944014","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"dsse","version":"0.0.1"},"integratedTime":"1724946104","inclusionPromise":{"signedEntryTimestamp":"MEUCIFZW7Zwr8LJB3HCkjx4uONIcoVwYE7lgv4PdOe7ENEoQAiEA8bc30td4qkVif87E8hCmVV1I6T0G+230gW2gbtVO+fw="},"inclusionProof":{"logIndex":"4039752","rootHash":"t83MAvlWEwV0+yXgbPZCpOsty6Ga7sOa2cccJENLcF8=","treeSize":"4039754","hashes":["xCpt/4yLxw2aBLfSF3FOHBtlaOwL8+dvz0dfIdqPc7g=","556wQ9lOG6dxmxa3hIHs/hplIMZaHxFZHsH+Sa0p788=","4/0qj7mIIRUcc+dvIeGJ74ascEKzfQqI6kmaggI82Xg=","eN0MvdrB+SfjJwud+DGOBD5IFlxn0FYIv3o1564EsgI=","Zxi2oScQDP3DT9KkFyLVlL4njUBfCzzDTvN+4GyEM2U=","4sPpQZyQ44A9/q/TCcuTgkokrSzXc3wIf7rDUQAX5jk=","XsQC4gZUvk32uYOw0Da8IPeWwX5tzmHvzK/opehwQR8=","RrZsD/RSxNoujlvq/MsCEvLSkKZfv0jmQM9Kp7qbJec=","QxmVWsbTp4cClxuAkuT51UH2EY7peHMVGKq7+b+cGwQ=","Q2LAtNzOUh+3PfwfMyNxYb06fTQmF3VeTT6Fr6Upvfc=","ftwAu6v62WFDoDmcZ1JKfrRPrvuiIw5v3BvRsgQj7N8="],"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n4039754\nt83MAvlWEwV0+yXgbPZCpOsty6Ga7sOa2cccJENLcF8=\n\n— rekor.sigstore.dev wNI9ajBEAiA3nqaxIpDg/TAcYsWUkXtZiW1rXMnKSmMq7LU5yMb9JgIgO4032Z1JX6ZlFuypMUt/ZevDWoESS243vMALXkQsv2w=\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNmM4MmE1NGZmYzZlMGFlYWVmZTZkYWI1NDM0NjRkN2Y4MGNmYjcwM2NhMzQ0NmRkNTY2M2U2ZTg2NzlkZWVhMyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjA2YzJjNTc5NThiMWVjOTI2MTM4MjI5MGY3ZmM4OWEwNDRiNzY1MjA4NzdmZTBjMGNmMDJkMGUwM2Q4OWNiNzgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRQ1E0THR1TjdYUjdxYkVRbG1qWFVjcHVIUmtXVUgxVHJpdTlyd1dJVnZlUndJaEFMc1VyK05kRHVBSzlkTTF5VlpHU09TRkIwWEx5RDBjbm9JTTRhbWVVd2pZIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNMFZFTkRRVzFoWjBGM1NVSkJaMGxWWlZGdlVWcDZaU3RMYzFBek5qUnhObVZQU0RjeVFrWlhSRmR6ZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmQwOUVTVFZOVkZVd1RWUlJNRmRvWTA1TmFsRjNUMFJKTlUxVVZURk5WRkV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVXpabEpaTlZKSmNqTXlhREptUmxOTE5YbzNka3BrVDJkRVJsSjZOVUUxUlZkd2IzUUtaeXRJVFdGc05HTnJkbWxUVEdoNGFtOXBjbms1Y3pOV1p6RmxLMlV3VVhsc1UwNUJaV294V1dOcVYxVkRUMlUwVkhGUFEwRlpWWGRuWjBkQ1RVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVXJZblpVQ2xsck5HaHpTa1JLU1hwMmVXOHpXbUZJTlRKS2EzbEJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMHhuV1VSV1VqQlNRVkZJTDBKRFVYZEpiMFZuV1ZkNGJHVkhiSHBNYlU1dldWZDRjMWxYTld0YVZVSXdZMjFHY0dKSE9XMVpiV3d3WTNrMWFncGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRa0ZSVVdWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZDazFETkVkRGFYTkhRVkZSUW1jM09IZEJVV2RGU1VGM1pXRklVakJqU0UwMlRIazVibUZZVW05a1YwbDFXVEk1ZEV3eWVIWmFNbXgxVERJNWFHUllVbThLVFVsSFRFSm5iM0pDWjBWRlFXUmFOVUZuVVVOQ1NEQkZaWGRDTlVGSVkwRXpWREIzWVhOaVNFVlVTbXBIVWpSamJWZGpNMEZ4U2t0WWNtcGxVRXN6THdwb05IQjVaME00Y0Rkdk5FRkJRVWRTYm5ONUt6Wm5RVUZDUVUxQlUwUkNSMEZwUlVGcU5qUndjVFpKTVd4d1ZGUlZhVGxYYWt4TWMzSXpWMmNyVDJ0TUNubG9ZMmRrZFd4T1JERTBiVWhQU1VOSlVVUXZObk5zTVdKb2JtSXhhbE13WjFWMmJYWTFXVkp4Y2s1MWMzVTJhMjlpVGtwVGJrcHBNakJMVWpkRVFVc0tRbWRuY1docmFrOVFVVkZFUVhkT2NFRkVRbTFCYWtWQmVsVm9SVTV1YjBoQlFuUXhZakkxWkhObGJtTkliR2czVlc5cll6ZDRZekpZYVdGNFJIaEhOZ3BUUzFwYVpUTlBRVWxzWWpob2JXRkNhRXd6T0VSVFUxVkJha1ZCZWxoeFVESlJSMVJ6VjJGTlpISjNOa1ZaVEdscmJEQnhiR2RqZFVZM0swZEZSVXRLQ21aU1YzQk5aWFZJTW5SVGMwTTNRemhXVDJ0Vk1IUk1Wa294ZDNJS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9XX19"}]},"envelope":{"statement":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJu\nYW1lIjoicHlwaV9oZWxsb193b3JsZC0wLjEuMC1weTMtbm9uZS1hbnkud2hsIiwiZGlnZXN0Ijp7\nInNoYTI1NiI6ImQzOWMwZjY1ZThlYWEyZDVlY2YwOThkOWEzZDM4ZjEzNWYyODFjNDgwYjY1Y2Vj\nYTZlODk1Y2E1MDliYzFmNDUifX1dLCJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9kb2NzLnB5cGku\nb3JnL2F0dGVzdGF0aW9ucy9wdWJsaXNoL3YxIiwicHJlZGljYXRlIjpudWxsfQ==\n","signature":"MEYCIQCQ4LtuN7XR7qbEQlmjXUcpuHRkWUH1Triu9rwWIVveRwIhALsUr+NdDuAK9dM1yVZGSOSF\nB0XLyD0cnoIM4ameUwjY\n"}} \ No newline at end of file diff --git a/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl b/tests/functional/assets/rfc8785-0.1.2-py3-none-any.whl deleted file mode 100644 index 00225acb8a344bb16015f9c172c21f3faa557f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9172 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 a?LT*riUJh$KkQ-tTsME#W0ve6Z~qSo5WoQd diff --git a/tests/functional/test_attestations.py b/tests/functional/test_attestations.py index 18a330e413fa..8554efadd10b 100644 --- a/tests/functional/test_attestations.py +++ b/tests/functional/test_attestations.py @@ -17,9 +17,10 @@ from http import HTTPStatus from pathlib import Path +import packaging.utils import pretend -from pypi_attestations import Attestation, Envelope, VerificationMaterial +from pypi_attestations import Attestation from sigstore.verify import Verifier from webtest.forms import Upload @@ -27,6 +28,7 @@ from tests.common.db.packaging import ProjectFactory from warehouse.macaroons import caveats from warehouse.macaroons.services import DatabaseMacaroonService +from warehouse.packaging import Release def get_macaraoon(macaroon_service, publisher): @@ -53,49 +55,46 @@ def get_macaraoon(macaroon_service, publisher): _HERE = Path(__file__).parent _ASSETS = _HERE / "assets" -_WHEEL_PACKAGE = _ASSETS / "rfc8785-0.1.2-py3-none-any.whl" +_WHEEL_PACKAGE = _ASSETS / "pypi_hello_world-0.1.0-py3-none-any.whl" class TestPackageWithAttestations: - def upload_package(self, webtest, monkeypatch, project, publisher): + def upload_package( + self, webtest, monkeypatch, project, wheel_filename: Path, publisher + ): - filename = "rfc8785-0.1.2-py3-none-any.whl" - attestation = Attestation( - version=1, - verification_material=VerificationMaterial( - certificate="some_cert", transparency_entries=[dict()] - ), - envelope=Envelope( - statement="somebase64string", - signature="somebase64string", - ), + name, version, _, __ = packaging.utils.parse_wheel_filename(wheel_filename.name) + attestation = Attestation.model_validate_json( + ( + wheel_filename.parent / f"{wheel_filename.name}.publish.attestation" + ).read_text() ) - md5_digest = hashlib.file_digest(open(_WHEEL_PACKAGE, "rb"), "md5").hexdigest() + md5_digest = hashlib.file_digest(wheel_filename.open("rb"), "md5").hexdigest() data = collections.OrderedDict( { ":action": "file_upload", - "protocol_version": "1", # TODO(dm) "metadata_version": "1.2", "name": project.name, "attestations": f"[{attestation.model_dump_json()}]", - "version": "0.1.2", + "version": str(version), "filetype": "bdist_wheel", "pyversion": "cp312", "md5_digest": md5_digest, "content": ( Upload( - filename, open(_WHEEL_PACKAGE, "rb").read(), "application/tar" + wheel_filename.name, + wheel_filename.read_bytes(), + "application/tar", ) ), } ) - macaroon_service = DatabaseMacaroonService( - webtest.extra_environ["warehouse.db_session"] - ) + db_session = webtest.extra_environ["warehouse.db_session"] + macaroon_service = DatabaseMacaroonService(db_session) webtest.set_authorization( ("Basic", ("__token__", get_macaraoon(macaroon_service, publisher))) ) @@ -109,15 +108,15 @@ def upload_package(self, webtest, monkeypatch, project, publisher): monkeypatch.setattr(Attestation, "verify", verify) monkeypatch.setattr(Verifier, "production", lambda: pretend.stub()) - resp = webtest.post( - "/legacy/", - params=data, - ) - - # TODO(DM) Improve testing results here + resp = webtest.post("/legacy/", params=data) assert resp.status_code == HTTPStatus.OK + release_db = db_session.query(Release).filter(Release.project == project).one() + release_file = release_db.files[0] + assert release_file.filename == wheel_filename.name + + def view_package(self, webtest, project): resp = webtest.get(f"/simple/{project.normalized_name}/") @@ -146,8 +145,11 @@ def view_package(self, webtest, project): def test_package(self, webtest, monkeypatch): - project = ProjectFactory.create(name="rfc8785") + project_name = "pypi_hello_world" + project = ProjectFactory.create(name=project_name) + + # Shortcut here: we directly associate our publisher to the project publisher = GitHubPublisherFactory.create(projects=[project]) - self.upload_package(webtest, monkeypatch, project, publisher) + self.upload_package(webtest, monkeypatch, project, _WHEEL_PACKAGE, publisher) self.view_package(webtest, project)