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