7777from warehouse .events .tags import EventTag
7878from warehouse .metrics .interfaces import IMetricsService
7979from warehouse .oidc .forms import DeletePublisherForm
80+ from warehouse .oidc .forms .buildkite import PendingBuildkitePublisherForm
8081from warehouse .oidc .forms .github import PendingGitHubPublisherForm
8182from warehouse .oidc .interfaces import TooManyOIDCRegistrations
82- from warehouse .oidc .models import PendingGitHubPublisher , PendingOIDCPublisher
83+ from warehouse .oidc .models import (
84+ PendingBuildkitePublisher ,
85+ PendingGitHubPublisher ,
86+ PendingOIDCPublisher ,
87+ )
8388from warehouse .organizations .interfaces import IOrganizationService
8489from warehouse .organizations .models import OrganizationRole , OrganizationRoleType
8590from warehouse .packaging .models import (
@@ -1488,6 +1493,13 @@ def _check_ratelimits(self):
14881493 )
14891494 )
14901495
1496+ @property
1497+ def pending_buildkite_publisher_form (self ):
1498+ return PendingBuildkitePublisherForm (
1499+ self .request .POST ,
1500+ project_factory = self .project_factory
1501+ )
1502+
14911503 @property
14921504 def pending_github_publisher_form (self ):
14931505 return PendingGitHubPublisherForm (
@@ -1499,6 +1511,8 @@ def pending_github_publisher_form(self):
14991511 @property
15001512 def default_response (self ):
15011513 return {
1514+ "default_publisher_name" : "github" ,
1515+ "pending_buildkite_publisher_form" : self .pending_buildkite_publisher_form ,
15021516 "pending_github_publisher_form" : self .pending_github_publisher_form ,
15031517 }
15041518
@@ -1516,11 +1530,147 @@ def manage_publishing(self):
15161530
15171531 return self .default_response
15181532
1533+ @view_config (
1534+ route_name = "manage.account.publishing.buildkite" ,
1535+ request_method = "POST" ,
1536+ request_param = PendingBuildkitePublisherForm .__params__ ,
1537+ )
1538+ def add_pending_buildkite_oidc_publisher (self ):
1539+ response = self .default_response
1540+ response ["default_publisher_name" ] = "buildkite"
1541+
1542+ if self .request .flags .disallow_oidc (AdminFlagValue .DISALLOW_BUILDKITE_OIDC ):
1543+ self .request .session .flash (
1544+ self .request ._ (
1545+ "Buildkite-based trusted publishing is temporarily disabled. "
1546+ "See https://pypi.org/help#admin-intervention for details."
1547+ ),
1548+ queue = "error" ,
1549+ )
1550+ return response
1551+
1552+ self .metrics .increment (
1553+ "warehouse.oidc.add_pending_publisher.attempt" , tags = ["publisher:Buildkite" ]
1554+ )
1555+
1556+ if not self .request .user .has_primary_verified_email :
1557+ self .request .session .flash (
1558+ self .request ._ (
1559+ "You must have a verified email in order to register a "
1560+ "pending trusted publisher. "
1561+ "See https://pypi.org/help#openid-connect for details."
1562+ ),
1563+ queue = "error" ,
1564+ )
1565+ return response
1566+
1567+ # Separately from having permission to register pending OIDC publishers,
1568+ # we limit users to no more than 3 pending publishers at once.
1569+ if len (self .request .user .pending_oidc_publishers ) >= 3 :
1570+ self .request .session .flash (
1571+ self .request ._ (
1572+ "You can't register more than 3 pending trusted "
1573+ "publishers at once."
1574+ ),
1575+ queue = "error" ,
1576+ )
1577+ return response
1578+
1579+ try :
1580+ self ._check_ratelimits ()
1581+ except TooManyOIDCRegistrations as exc :
1582+ self .metrics .increment (
1583+ "warehouse.oidc.add_pending_publisher.ratelimited" ,
1584+ tags = ["publisher:Buildkite" ],
1585+ )
1586+ return HTTPTooManyRequests (
1587+ self .request ._ (
1588+ "There have been too many attempted trusted publisher "
1589+ "registrations. Try again later."
1590+ ),
1591+ retry_after = exc .resets_in .total_seconds (),
1592+ )
1593+
1594+ self ._hit_ratelimits ()
1595+
1596+ response = response
1597+ form = response ["pending_buildkite_publisher_form" ]
1598+
1599+ if not form .validate ():
1600+ self .request .session .flash (
1601+ self .request ._ ("The trusted publisher could not be registered" ),
1602+ queue = "error" ,
1603+ )
1604+ return response
1605+
1606+ publisher_already_exists = (
1607+ self .request .db .query (PendingBuildkitePublisher )
1608+ .filter_by (
1609+ organization_slug = form .organization_slug .data ,
1610+ pipeline_slug = form .pipeline_slug .data ,
1611+ )
1612+ .first ()
1613+ is not None
1614+ )
1615+
1616+ if publisher_already_exists :
1617+ self .request .session .flash (
1618+ self .request ._ (
1619+ "This trusted publisher has already been registered. "
1620+ "Please contact PyPI's admins if this wasn't intentional."
1621+ ),
1622+ queue = "error" ,
1623+ )
1624+ return response
1625+
1626+ pending_publisher = PendingBuildkitePublisher (
1627+ project_name = form .project_name .data ,
1628+ added_by = self .request .user ,
1629+ organization_slug = form .organization_slug .data ,
1630+ pipeline_slug = form .pipeline_slug .data ,
1631+ build_branch = form .build_branch .data ,
1632+ build_tag = form .build_tag .data ,
1633+ step_key = form .step_key .data ,
1634+ )
1635+
1636+ self .request .db .add (pending_publisher )
1637+ self .request .db .flush () # To get the new ID
1638+
1639+ self .request .user .record_event (
1640+ tag = EventTag .Account .PendingOIDCPublisherAdded ,
1641+ request = self .request ,
1642+ additional = {
1643+ "project" : pending_publisher .project_name ,
1644+ "publisher" : pending_publisher .publisher_name ,
1645+ "id" : str (pending_publisher .id ),
1646+ "specifier" : str (pending_publisher ),
1647+ "url" : pending_publisher .publisher_url (),
1648+ "submitted_by" : self .request .user .username ,
1649+ },
1650+ )
1651+
1652+ self .request .session .flash (
1653+ self .request ._ (
1654+ "Registered a new pending publisher to create "
1655+ f"the project '{ pending_publisher .project_name } '."
1656+ ),
1657+ queue = "success" ,
1658+ )
1659+
1660+ self .metrics .increment (
1661+ "warehouse.oidc.add_pending_publisher.ok" , tags = ["publisher:Buildkite" ]
1662+ )
1663+
1664+ return HTTPSeeOther (self .request .route_path ("manage.account.publishing" ))
1665+
15191666 @view_config (
15201667 request_method = "POST" ,
15211668 request_param = PendingGitHubPublisherForm .__params__ ,
15221669 )
15231670 def add_pending_github_oidc_publisher (self ):
1671+ response = self .default_response
1672+ response ["default_publisher_name" ] = "github"
1673+
15241674 if self .request .flags .disallow_oidc (AdminFlagValue .DISALLOW_GITHUB_OIDC ):
15251675 self .request .session .flash (
15261676 self .request ._ (
@@ -1529,7 +1679,7 @@ def add_pending_github_oidc_publisher(self):
15291679 ),
15301680 queue = "error" ,
15311681 )
1532- return self . default_response
1682+ return response
15331683
15341684 self .metrics .increment (
15351685 "warehouse.oidc.add_pending_publisher.attempt" , tags = ["publisher:GitHub" ]
0 commit comments