11
11
# limitations under the License.
12
12
13
13
from http import HTTPStatus
14
+ from pathlib import Path
14
15
15
- from ...common .db .packaging import ProjectFactory , ReleaseFactory
16
+ import pymacaroons
17
+
18
+ from warehouse .macaroons import caveats
19
+
20
+ from ...common .db .accounts import EmailFactory , UserFactory
21
+ from ...common .db .macaroons import MacaroonFactory
22
+ from ...common .db .oidc import GitHubPublisherFactory
23
+ from ...common .db .packaging import ProjectFactory , ReleaseFactory , RoleFactory
24
+
25
+ _HERE = Path (__file__ ).parent
26
+ _ASSETS = _HERE .parent / "_fixtures"
16
27
17
28
18
29
def test_simple_api_html (webtest ):
@@ -31,3 +42,88 @@ def test_simple_api_detail(webtest):
31
42
assert resp .content_type == "text/html"
32
43
assert "X-PyPI-Last-Serial" in resp .headers
33
44
assert f"Links for { project .normalized_name } " in resp .text
45
+
46
+
47
+ def test_simple_attestations_from_upload (webtest ):
48
+ user = UserFactory .create (
49
+ password = ( # 'password'
50
+ "$argon2id$v=19$m=1024,t=6,p=6$EiLE2Nsbo9S6N+acs/beGw$ccyZDCZstr1/+Y/1s3BVZ"
51
+ "HOJaqfBroT0JCieHug281c"
52
+ )
53
+ )
54
+ EmailFactory .create (user = user , verified = True )
55
+ project = ProjectFactory .create (name = "sampleproject" )
56
+ RoleFactory .create (user = user , project = project , role_name = "Owner" )
57
+ publisher = GitHubPublisherFactory .create (projects = [project ])
58
+
59
+ # Construct the macaroon. This needs to be based on a Trusted Publisher, which is
60
+ # required to upload attestations
61
+ dm = MacaroonFactory .create (
62
+ oidc_publisher_id = publisher .id ,
63
+ caveats = [
64
+ caveats .OIDCPublisher (oidc_publisher_id = str (publisher .id )),
65
+ caveats .ProjectID (project_ids = [str (p .id ) for p in publisher .projects ]),
66
+ ],
67
+ additional = {"oidc" : {"ref" : "someref" , "sha" : "somesha" }},
68
+ )
69
+
70
+ m = pymacaroons .Macaroon (
71
+ location = "localhost" ,
72
+ identifier = str (dm .id ),
73
+ key = dm .key ,
74
+ version = pymacaroons .MACAROON_V2 ,
75
+ )
76
+ for caveat in dm .caveats :
77
+ m .add_first_party_caveat (caveats .serialize (caveat ))
78
+ serialized_macaroon = f"pypi-{ m .serialize ()} "
79
+
80
+ with open (_ASSETS / "sampleproject-3.0.0.tar.gz" , "rb" ) as f :
81
+ content = f .read ()
82
+
83
+ with open (
84
+ _ASSETS / "sampleproject-3.0.0.tar.gz.publish.attestation" ,
85
+ ) as f :
86
+ attestation = f .read ()
87
+
88
+ webtest .set_authorization (("Basic" , ("__token__" , serialized_macaroon )))
89
+ webtest .post (
90
+ "/legacy/?:action=file_upload" ,
91
+ params = {
92
+ "name" : "sampleproject" ,
93
+ "sha256_digest" : (
94
+ "117ed88e5db073bb92969a7545745fd977ee85b7019706dd256a64058f70963d"
95
+ ),
96
+ "filetype" : "sdist" ,
97
+ "metadata_version" : "2.1" ,
98
+ "version" : "3.0.0" ,
99
+ "attestations" : f"[{ attestation } ]" ,
100
+ },
101
+ upload_files = [("content" , "sampleproject-3.0.0.tar.gz" , content )],
102
+ status = HTTPStatus .OK ,
103
+ )
104
+
105
+ assert len (project .releases ) == 1
106
+ assert project .releases [0 ].files .count () == 1
107
+ assert project .releases [0 ].files [0 ].provenance is not None
108
+
109
+ expected_provenance = project .releases [0 ].files [0 ].provenance .provenance_digest
110
+ expected_filename = "sampleproject-3.0.0.tar.gz"
111
+
112
+ response = webtest .get ("/simple/sampleproject/" , status = HTTPStatus .OK )
113
+ link = response .html .find ("a" , text = expected_filename )
114
+
115
+ assert "data-provenance" in link .attrs
116
+ assert link .get ("data-provenance" ) == expected_provenance
117
+
118
+ response = webtest .get (
119
+ "/simple/sampleproject/" ,
120
+ headers = {"Accept" : "application/vnd.pypi.simple.v1+json" },
121
+ status = HTTPStatus .OK ,
122
+ )
123
+
124
+ assert response .content_type == "application/vnd.pypi.simple.v1+json"
125
+
126
+ json_content = response .json
127
+ assert len (json_content ["files" ]) == 1
128
+ assert json_content ["files" ][0 ]["filename" ] == expected_filename
129
+ assert json_content ["files" ][0 ]["provenance" ] == expected_provenance
0 commit comments