|
99 | 99 | user_projects,
|
100 | 100 | )
|
101 | 101 | from warehouse.metrics.interfaces import IMetricsService
|
102 |
| -from warehouse.oidc.forms import DeletePublisherForm |
103 |
| -from warehouse.oidc.forms.github import GitHubPublisherForm |
| 102 | +from warehouse.oidc.forms import ( |
| 103 | + DeletePublisherForm, |
| 104 | + GitHubPublisherForm, |
| 105 | + GooglePublisherForm, |
| 106 | +) |
104 | 107 | from warehouse.oidc.interfaces import TooManyOIDCRegistrations
|
105 |
| -from warehouse.oidc.models import GitHubPublisher, OIDCPublisher |
| 108 | +from warehouse.oidc.models import GitHubPublisher, GooglePublisher, OIDCPublisher |
106 | 109 | from warehouse.organizations.interfaces import IOrganizationService
|
107 | 110 | from warehouse.organizations.models import (
|
108 | 111 | OrganizationProject,
|
@@ -1135,6 +1138,7 @@ def __init__(self, project, request):
|
1135 | 1138 | self.request.POST,
|
1136 | 1139 | api_token=self.request.registry.settings.get("github.token"),
|
1137 | 1140 | )
|
| 1141 | + self.google_publisher_form = GooglePublisherForm(self.request.POST) |
1138 | 1142 |
|
1139 | 1143 | @property
|
1140 | 1144 | def _ratelimiters(self):
|
@@ -1171,6 +1175,7 @@ def default_response(self):
|
1171 | 1175 | return {
|
1172 | 1176 | "project": self.project,
|
1173 | 1177 | "github_publisher_form": self.github_publisher_form,
|
| 1178 | + "google_publisher_form": self.google_publisher_form, |
1174 | 1179 | }
|
1175 | 1180 |
|
1176 | 1181 | @view_config(request_method="GET")
|
@@ -1299,6 +1304,114 @@ def add_github_oidc_publisher(self):
|
1299 | 1304 |
|
1300 | 1305 | return HTTPSeeOther(self.request.path)
|
1301 | 1306 |
|
| 1307 | + @view_config( |
| 1308 | + request_method="POST", |
| 1309 | + request_param=GooglePublisherForm.__params__, |
| 1310 | + ) |
| 1311 | + def add_google_oidc_publisher(self): |
| 1312 | + if self.request.flags.disallow_oidc(AdminFlagValue.DISALLOW_GOOGLE_OIDC): |
| 1313 | + self.request.session.flash( |
| 1314 | + self.request._( |
| 1315 | + "Google-based trusted publishing is temporarily disabled. " |
| 1316 | + "See https://pypi.org/help#admin-intervention for details." |
| 1317 | + ), |
| 1318 | + queue="error", |
| 1319 | + ) |
| 1320 | + return self.default_response |
| 1321 | + |
| 1322 | + self.metrics.increment( |
| 1323 | + "warehouse.oidc.add_publisher.attempt", tags=["publisher:Google"] |
| 1324 | + ) |
| 1325 | + |
| 1326 | + try: |
| 1327 | + self._check_ratelimits() |
| 1328 | + except TooManyOIDCRegistrations as exc: |
| 1329 | + self.metrics.increment( |
| 1330 | + "warehouse.oidc.add_publisher.ratelimited", tags=["publisher:Google"] |
| 1331 | + ) |
| 1332 | + return HTTPTooManyRequests( |
| 1333 | + self.request._( |
| 1334 | + "There have been too many attempted trusted publisher " |
| 1335 | + "registrations. Try again later." |
| 1336 | + ), |
| 1337 | + retry_after=exc.resets_in.total_seconds(), |
| 1338 | + ) |
| 1339 | + |
| 1340 | + self._hit_ratelimits() |
| 1341 | + |
| 1342 | + response = self.default_response |
| 1343 | + form = response["google_publisher_form"] |
| 1344 | + |
| 1345 | + if not form.validate(): |
| 1346 | + self.request.session.flash( |
| 1347 | + self.request._("The trusted publisher could not be registered"), |
| 1348 | + queue="error", |
| 1349 | + ) |
| 1350 | + return response |
| 1351 | + |
| 1352 | + # Google OIDC publishers are unique on the tuple of (email, sub), so we |
| 1353 | + # check for an already registered one before creating. |
| 1354 | + publisher = ( |
| 1355 | + self.request.db.query(GooglePublisher) |
| 1356 | + .filter( |
| 1357 | + GooglePublisher.email == form.email.data, |
| 1358 | + GooglePublisher.sub == form.sub.data, |
| 1359 | + ) |
| 1360 | + .one_or_none() |
| 1361 | + ) |
| 1362 | + if publisher is None: |
| 1363 | + publisher = GooglePublisher( |
| 1364 | + email=form.email.data, |
| 1365 | + sub=form.sub.data, |
| 1366 | + ) |
| 1367 | + |
| 1368 | + self.request.db.add(publisher) |
| 1369 | + |
| 1370 | + # Each project has a unique set of OIDC publishers; the same |
| 1371 | + # publisher can't be registered to the project more than once. |
| 1372 | + if publisher in self.project.oidc_publishers: |
| 1373 | + self.request.session.flash( |
| 1374 | + self.request._( |
| 1375 | + f"{publisher} is already registered with {self.project.name}" |
| 1376 | + ), |
| 1377 | + queue="error", |
| 1378 | + ) |
| 1379 | + return response |
| 1380 | + |
| 1381 | + for user in self.project.users: |
| 1382 | + send_trusted_publisher_added_email( |
| 1383 | + self.request, |
| 1384 | + user, |
| 1385 | + project_name=self.project.name, |
| 1386 | + publisher=publisher, |
| 1387 | + ) |
| 1388 | + |
| 1389 | + self.project.oidc_publishers.append(publisher) |
| 1390 | + |
| 1391 | + self.project.record_event( |
| 1392 | + tag=EventTag.Project.OIDCPublisherAdded, |
| 1393 | + ip_address=self.request.remote_addr, |
| 1394 | + request=self.request, |
| 1395 | + additional={ |
| 1396 | + "publisher": publisher.publisher_name, |
| 1397 | + "id": str(publisher.id), |
| 1398 | + "specifier": str(publisher), |
| 1399 | + "url": publisher.publisher_url(), |
| 1400 | + "submitted_by": self.request.user.username, |
| 1401 | + }, |
| 1402 | + ) |
| 1403 | + |
| 1404 | + self.request.session.flash( |
| 1405 | + f"Added {publisher} in {publisher.publisher_url()} to {self.project.name}", |
| 1406 | + queue="success", |
| 1407 | + ) |
| 1408 | + |
| 1409 | + self.metrics.increment( |
| 1410 | + "warehouse.oidc.add_publisher.ok", tags=["publisher:GitHub"] |
| 1411 | + ) |
| 1412 | + |
| 1413 | + return HTTPSeeOther(self.request.path) |
| 1414 | + |
1302 | 1415 | @view_config(
|
1303 | 1416 | request_method="POST",
|
1304 | 1417 | request_param=DeletePublisherForm.__params__,
|
|
0 commit comments