1818import pytest
1919
2020from tests .common .db .accounts import UserFactory
21- from tests .common .db .oidc import PendingGitHubPublisherFactory
21+ from tests .common .db .oidc import GitHubPublisherFactory , PendingGitHubPublisherFactory
2222from tests .common .db .packaging import ProjectFactory
2323from warehouse .events .tags import EventTag
2424from warehouse .macaroons import caveats
2525from warehouse .macaroons .interfaces import IMacaroonService
2626from warehouse .oidc import errors , views
2727from warehouse .oidc .interfaces import IOIDCPublisherService
28- from warehouse .oidc .models import github
29- from warehouse .packaging .models import Project
3028from warehouse .rate_limiting .interfaces import IRateLimiter
3129
3230
@@ -111,7 +109,7 @@ def test_mint_token_from_github_oidc_not_enabled():
111109 {"token" : {}},
112110 ],
113111)
114- def test_mint_token_from_github_oidc_invalid_payload (body ):
112+ def test_mint_token_oidc_invalid_payload (body ):
115113 class Request :
116114 def __init__ (self ):
117115 self .response = pretend .stub (status = None )
@@ -202,7 +200,9 @@ def test_mint_token_from_trusted_publisher_lookup_fails():
202200
203201def test_mint_token_from_oidc_pending_publisher_project_already_exists (db_request ):
204202 project = ProjectFactory .create ()
205- pending_publisher = PendingGitHubPublisherFactory .create (project_name = project .name )
203+ pending_publisher = PendingGitHubPublisherFactory .create (
204+ project_name = project .name ,
205+ )
206206
207207 db_request .flags .disallow_oidc = lambda f = None : False
208208 db_request .body = json .dumps ({"token" : "faketoken" })
@@ -325,22 +325,25 @@ def test_mint_token_from_pending_trusted_publisher_invalidates_others(
325325 )
326326
327327 db_request .flags .oidc_enabled = lambda f : False
328- token = (
329- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZTY3YjFjYi0yYjhkLTRi"
330- "ZTUtOTFjYi03NTdlZGIyZWM5NzAiLCJzdWIiOiJyZXBvOmZvby9iYXIiLCJhdWQiOiJwe"
331- "XBpIiwicmVmIjoiZmFrZSIsInNoYSI6ImZha2UiLCJyZXBvc2l0b3J5IjoiZm9vL2Jhci"
332- "IsInJlcG9zaXRvcnlfb3duZXIiOiJmb28iLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiMTI"
333- "zIiwicnVuX2lkIjoiZmFrZSIsInJ1bl9udW1iZXIiOiJmYWtlIiwicnVuX2F0dGVtcHQi"
334- "OiIxIiwicmVwb3NpdG9yeV9pZCI6ImZha2UiLCJhY3Rvcl9pZCI6ImZha2UiLCJhY3Rvc"
335- "iI6ImZvbyIsIndvcmtmbG93IjoiZmFrZSIsImhlYWRfcmVmIjoiZmFrZSIsImJhc2Vfcm"
336- "VmIjoiZmFrZSIsImV2ZW50X25hbWUiOiJmYWtlIiwicmVmX3R5cGUiOiJmYWtlIiwiZW5"
337- "2aXJvbm1lbnQiOiJmYWtlIiwiam9iX3dvcmtmbG93X3JlZiI6ImZvby9iYXIvLmdpdGh1"
338- "Yi93b3JrZmxvd3MvZXhhbXBsZS55bWxAZmFrZSIsImlzcyI6Imh0dHBzOi8vdG9rZW4uY"
339- "WN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20iLCJuYmYiOjE2NTA2NjMyNjUsImV4cC"
340- "I6MTY1MDY2NDE2NSwiaWF0IjoxNjUwNjYzODY1fQ.f-FMv5FF5sdxAWeUilYDt9NoE7Et"
341- "0vbdNhK32c2oC-E"
328+ db_request .body = json .dumps (
329+ {
330+ "token" : (
331+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZTY3YjFjYi0yYjhkLTRi"
332+ "ZTUtOTFjYi03NTdlZGIyZWM5NzAiLCJzdWIiOiJyZXBvOmZvby9iYXIiLCJhdWQiOiJwe"
333+ "XBpIiwicmVmIjoiZmFrZSIsInNoYSI6ImZha2UiLCJyZXBvc2l0b3J5IjoiZm9vL2Jhci"
334+ "IsInJlcG9zaXRvcnlfb3duZXIiOiJmb28iLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiMTI"
335+ "zIiwicnVuX2lkIjoiZmFrZSIsInJ1bl9udW1iZXIiOiJmYWtlIiwicnVuX2F0dGVtcHQi"
336+ "OiIxIiwicmVwb3NpdG9yeV9pZCI6ImZha2UiLCJhY3Rvcl9pZCI6ImZha2UiLCJhY3Rvc"
337+ "iI6ImZvbyIsIndvcmtmbG93IjoiZmFrZSIsImhlYWRfcmVmIjoiZmFrZSIsImJhc2Vfcm"
338+ "VmIjoiZmFrZSIsImV2ZW50X25hbWUiOiJmYWtlIiwicmVmX3R5cGUiOiJmYWtlIiwiZW5"
339+ "2aXJvbm1lbnQiOiJmYWtlIiwiam9iX3dvcmtmbG93X3JlZiI6ImZvby9iYXIvLmdpdGh1"
340+ "Yi93b3JrZmxvd3MvZXhhbXBsZS55bWxAZmFrZSIsImlzcyI6Imh0dHBzOi8vdG9rZW4uY"
341+ "WN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20iLCJuYmYiOjE2NTA2NjMyNjUsImV4cC"
342+ "I6MTY1MDY2NDE2NSwiaWF0IjoxNjUwNjYzODY1fQ.f-FMv5FF5sdxAWeUilYDt9NoE7Et"
343+ "0vbdNhK32c2oC-E"
344+ )
345+ }
342346 )
343- db_request .body = json .dumps ({"token" : token })
344347 db_request .remote_addr = "0.0.0.0"
345348
346349 ratelimiter = pretend .stub (clear = pretend .call_recorder (lambda id : None ))
@@ -350,9 +353,7 @@ def test_mint_token_from_pending_trusted_publisher_invalidates_others(
350353 }
351354 monkeypatch .setattr (views , "_ratelimiters" , lambda r : ratelimiters )
352355
353- oidc_service = db_request .find_service (IOIDCPublisherService , name = "github" )
354-
355- resp = views .mint_token (oidc_service , db_request )
356+ resp = views .mint_token_from_oidc_github (db_request )
356357 assert resp ["success" ]
357358 assert resp ["token" ].startswith ("pypi-" )
358359
@@ -370,6 +371,73 @@ def test_mint_token_from_pending_trusted_publisher_invalidates_others(
370371 ]
371372
372373
374+ def test_mint_token_from_oidc_only_pending_publisher_fail (monkeypatch , db_request ):
375+ pending_publisher = PendingGitHubPublisherFactory ()
376+
377+ def _find_publisher (claims , pending = False ):
378+ return pending_publisher
379+
380+ oidc_service = pretend .stub (
381+ verify_jwt_signature = pretend .call_recorder (
382+ lambda token : {"ref" : "someref" , "sha" : "somesha" }
383+ ),
384+ find_publisher = pretend .call_recorder (_find_publisher ),
385+ reify_pending_publisher = pretend .call_recorder (
386+ lambda * a , ** kw : pending_publisher
387+ ),
388+ )
389+
390+ db_request .body = json .dumps (
391+ {
392+ "token" : (
393+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZTY3YjFjYi0yYjhkLTRi"
394+ "ZTUtOTFjYi03NTdlZGIyZWM5NzAiLCJzdWIiOiJyZXBvOmZvby9iYXIiLCJhdWQiOiJwe"
395+ "XBpIiwicmVmIjoiZmFrZSIsInNoYSI6ImZha2UiLCJyZXBvc2l0b3J5IjoiZm9vL2Jhci"
396+ "IsInJlcG9zaXRvcnlfb3duZXIiOiJmb28iLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiMTI"
397+ "zIiwicnVuX2lkIjoiZmFrZSIsInJ1bl9udW1iZXIiOiJmYWtlIiwicnVuX2F0dGVtcHQi"
398+ "OiIxIiwicmVwb3NpdG9yeV9pZCI6ImZha2UiLCJhY3Rvcl9pZCI6ImZha2UiLCJhY3Rvc"
399+ "iI6ImZvbyIsIndvcmtmbG93IjoiZmFrZSIsImhlYWRfcmVmIjoiZmFrZSIsImJhc2Vfcm"
400+ "VmIjoiZmFrZSIsImV2ZW50X25hbWUiOiJmYWtlIiwicmVmX3R5cGUiOiJmYWtlIiwiZW5"
401+ "2aXJvbm1lbnQiOiJmYWtlIiwiam9iX3dvcmtmbG93X3JlZiI6ImZvby9iYXIvLmdpdGh1"
402+ "Yi93b3JrZmxvd3MvZXhhbXBsZS55bWxAZmFrZSIsImlzcyI6Imh0dHBzOi8vdG9rZW4uY"
403+ "WN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20iLCJuYmYiOjE2NTA2NjMyNjUsImV4cC"
404+ "I6MTY1MDY2NDE2NSwiaWF0IjoxNjUwNjYzODY1fQ.f-FMv5FF5sdxAWeUilYDt9NoE7Et"
405+ "0vbdNhK32c2oC-E"
406+ )
407+ }
408+ )
409+
410+ db_request .remote_addr = "0.0.0.0"
411+
412+ ratelimiter = pretend .stub (clear = pretend .call_recorder (lambda id : None ))
413+ ratelimiters = {
414+ "user.oidc" : ratelimiter ,
415+ "ip.oidc" : ratelimiter ,
416+ }
417+ monkeypatch .setattr (views , "_ratelimiters" , lambda r : ratelimiters )
418+
419+ send_pending_trusted_publisher_invalidated_email = pretend .call_recorder (
420+ lambda * a , ** kw : None
421+ )
422+ monkeypatch .setattr (
423+ views ,
424+ "send_pending_trusted_publisher_invalidated_email" ,
425+ send_pending_trusted_publisher_invalidated_email ,
426+ )
427+
428+ response = views .mint_token (oidc_service , db_request )
429+
430+ assert response == {
431+ "message" : "Token request failed" ,
432+ "errors" : [
433+ {
434+ "code" : "invalid-publisher" ,
435+ "description" : ("valid token, but no corresponding publisher" ),
436+ }
437+ ],
438+ }
439+
440+
373441@pytest .mark .parametrize (
374442 ("claims_in_token" , "claims_input" ),
375443 [
@@ -379,30 +447,25 @@ def test_mint_token_from_pending_trusted_publisher_invalidates_others(
379447 ],
380448)
381449def test_mint_token_from_oidc_no_pending_publisher_ok (
382- monkeypatch , claims_in_token , claims_input
450+ monkeypatch , db_request , claims_in_token , claims_input
383451):
384452 time = pretend .stub (time = pretend .call_recorder (lambda : 0 ))
385453 monkeypatch .setattr (views , "time" , time )
386454
387- project = Project ( id = "fakeprojectid" )
388- monkeypatch . setattr (
389- project , " record_event" , pretend .call_recorder (lambda ** kw : None )
455+ project = pretend . stub (
456+ id = "fakeprojectid" ,
457+ record_event = pretend .call_recorder (lambda ** kw : None ),
390458 )
391459
392- publisher = github .GitHubPublisher (
393- repository_name = "fakerepo" ,
394- repository_owner = "fakeowner" ,
395- repository_owner_id = "fakeid" ,
396- workflow_filename = "fakeworkflow.yml" ,
397- environment = "fakeenv" ,
398- )
399- publisher .projects = [project ]
460+ publisher = GitHubPublisherFactory ()
461+ monkeypatch .setattr (publisher .__class__ , "projects" , [project ])
462+ publisher .publisher_url = pretend .call_recorder (lambda ** kw : "https://fake/url" )
400463 # NOTE: Can't set __str__ using pretend.stub()
401- monkeypatch .setattr (publisher , "id " , "fakepublisherid " )
464+ monkeypatch .setattr (publisher . __class__ , "__str__ " , lambda s : "fakespecifier " )
402465
403466 def _find_publisher (claims , pending = False ):
404467 if pending :
405- raise errors . InvalidPublisherError
468+ return None
406469 else :
407470 return publisher
408471
@@ -425,16 +488,19 @@ def find_service(iface, **kw):
425488 return macaroon_service
426489 assert False , iface
427490
428- request = pretend .stub (
429- response = pretend .stub (status = None ),
430- body = json .dumps ({"token" : "faketoken" }),
431- find_service = find_service ,
432- domain = "fakedomain" ,
433- remote_addr = "0.0.0.0" ,
434- flags = pretend .stub (disallow_oidc = lambda * a : False ),
435- )
436-
437- response = views .mint_token (oidc_service , request )
491+ # request = pretend.stub(
492+ # response=pretend.stub(status=None),
493+ # body=json.dumps({"token": "faketoken"}),
494+ # find_service=find_service,
495+ # domain="fakedomain",
496+ # remote_addr="0.0.0.0",
497+ # flags=pretend.stub(disallow_oidc=lambda *a: False),
498+ # )
499+ monkeypatch .setattr (db_request , "find_service" , find_service )
500+ monkeypatch .setattr (db_request , "body" , json .dumps ({"token" : "faketoken" }))
501+ monkeypatch .setattr (db_request , "domain" , "fakedomain" )
502+
503+ response = views .mint_token (oidc_service , db_request )
438504 assert response == {
439505 "success" : True ,
440506 "token" : "raw-macaroon" ,
@@ -445,29 +511,30 @@ def find_service(iface, **kw):
445511 pretend .call (claims_in_token , pending = True ),
446512 pretend .call (claims_in_token , pending = False ),
447513 ]
514+
448515 assert macaroon_service .create_macaroon .calls == [
449516 pretend .call (
450517 "fakedomain" ,
451- f"OpenID token: fakeworkflow.yml ({ datetime .fromtimestamp (0 ).isoformat ()} )" ,
518+ f"OpenID token: fakespecifier ({ datetime .fromtimestamp (0 ).isoformat ()} )" ,
452519 [
453520 caveats .OIDCPublisher (
454- oidc_publisher_id = "fakepublisherid" ,
521+ oidc_publisher_id = str ( publisher . id ) ,
455522 ),
456523 caveats .ProjectID (project_ids = ["fakeprojectid" ]),
457524 caveats .Expiration (expires_at = 900 , not_before = 0 ),
458525 ],
459- oidc_publisher_id = "fakepublisherid" ,
526+ oidc_publisher_id = str ( publisher . id ) ,
460527 additional = {"oidc" : claims_input },
461528 )
462529 ]
463530 assert project .record_event .calls == [
464531 pretend .call (
465532 tag = EventTag .Project .ShortLivedAPITokenAdded ,
466- request = request ,
533+ request = db_request ,
467534 additional = {
468535 "expires" : 900 ,
469536 "publisher_name" : "GitHub" ,
470- "publisher_url" : f "https://github.com/ { publisher . repository_owner } / { publisher . repository_name } " , # noqa
537+ "publisher_url" : "https://fake/url" ,
471538 },
472539 )
473540 ]
0 commit comments