Skip to content

Commit f37d932

Browse files
dimiketheman
andauthored
Always remove pending publishers when project is created (#15020)
* Always remove pending publishers when project is created * Fix failing organization test * Update tests/unit/manage/views/test_organizations.py Co-authored-by: Mike Fiedler <[email protected]> * Linting --------- Co-authored-by: Mike Fiedler <[email protected]>
1 parent 7a0409a commit f37d932

File tree

4 files changed

+39
-29
lines changed

4 files changed

+39
-29
lines changed

tests/unit/manage/views/test_organizations.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,11 +1590,13 @@ def test_add_organization_project_new_project(
15901590
view = org_views.ManageOrganizationProjectsViews(organization, db_request)
15911591
result = view.add_organization_project()
15921592

1593-
# The project was created, and belongs to the organization.
1594-
project = (
1595-
db_request.db.query(Project).filter_by(name="fakepackage").one_or_none()
1596-
)
1597-
assert project is not None
1593+
# The project was created
1594+
project = db_request.db.query(Project).filter_by(name="fakepackage").one()
1595+
1596+
# Refresh the project in the DB session to ensure it is not stale
1597+
db_request.db.refresh(project)
1598+
1599+
# The project belongs to the organization.
15981600
assert project.organization == organization
15991601

16001602
assert isinstance(result, HTTPSeeOther)

tests/unit/oidc/test_views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from warehouse.macaroons.interfaces import IMacaroonService
2626
from warehouse.oidc import errors, views
2727
from warehouse.oidc.interfaces import IOIDCPublisherService
28+
from warehouse.packaging import services
2829
from warehouse.rate_limiting.interfaces import IRateLimiter
2930

3031

@@ -322,7 +323,7 @@ def test_mint_token_from_pending_trusted_publisher_invalidates_others(
322323
lambda *a, **kw: None
323324
)
324325
monkeypatch.setattr(
325-
views,
326+
services,
326327
"send_pending_trusted_publisher_invalidated_email",
327328
send_pending_trusted_publisher_invalidated_email,
328329
)

warehouse/oidc/views.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
from pydantic import BaseModel, StrictStr, ValidationError
1818
from pyramid.response import Response
1919
from pyramid.view import view_config
20-
from sqlalchemy import func
2120

2221
from warehouse.admin.flags import AdminFlagValue
23-
from warehouse.email import send_pending_trusted_publisher_invalidated_email
2422
from warehouse.events.tags import EventTag
2523
from warehouse.macaroons import caveats
2624
from warehouse.macaroons.interfaces import IMacaroonService
2725
from warehouse.oidc.errors import InvalidPublisherError
2826
from warehouse.oidc.interfaces import IOIDCPublisherService
29-
from warehouse.oidc.models import PendingOIDCPublisher
3027
from warehouse.packaging.interfaces import IProjectService
3128
from warehouse.packaging.models import ProjectFactory
3229
from warehouse.rate_limiting.interfaces import IRateLimiter
@@ -126,14 +123,18 @@ def _invalid(errors):
126123
]
127124
)
128125

129-
# Create the new project, and reify the pending publisher against it.
126+
# Create the new project
130127
project_service = request.find_service(IProjectService)
131128
new_project = project_service.create_project(
132129
pending_publisher.project_name,
133130
pending_publisher.added_by,
134131
request,
135132
ratelimited=False,
136133
)
134+
135+
# Creating the project will remove all pending publishers, EXCEPT the
136+
# pending publisher created by the uploader. If such a pending
137+
# publisher exists, reify it against the newly created project.
137138
oidc_service.reify_pending_publisher(pending_publisher, new_project)
138139

139140
# Successfully converting a pending publisher into a normal publisher
@@ -142,25 +143,6 @@ def _invalid(errors):
142143
ratelimiters["user.oidc"].clear(pending_publisher.added_by.id)
143144
ratelimiters["ip.oidc"].clear(request.remote_addr)
144145

145-
# There might be other pending publishers for the same project name,
146-
# which we've now invalidated by creating the project. These would
147-
# be disposed of on use, but we explicitly dispose of them here while
148-
# also sending emails to their owners.
149-
stale_pending_publishers = (
150-
request.db.query(PendingOIDCPublisher)
151-
.filter(
152-
func.normalize_pep426_name(PendingOIDCPublisher.project_name)
153-
== func.normalize_pep426_name(pending_publisher.project_name)
154-
)
155-
.all()
156-
)
157-
for stale_publisher in stale_pending_publishers:
158-
send_pending_trusted_publisher_invalidated_email(
159-
request,
160-
stale_publisher.added_by,
161-
project_name=stale_publisher.project_name,
162-
)
163-
request.db.delete(stale_publisher)
164146
except InvalidPublisherError:
165147
# If the claim set isn't valid for a pending publisher, it's OK, we
166148
# will try finding a regular publisher

warehouse/packaging/services.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@
2525
import google.api_core.retry
2626
import sentry_sdk
2727

28+
from sqlalchemy import func
2829
from zope.interface import implementer
2930

31+
from warehouse.email import send_pending_trusted_publisher_invalidated_email
3032
from warehouse.events.tags import EventTag
3133
from warehouse.metrics import IMetricsService
34+
from warehouse.oidc.models import PendingOIDCPublisher
3235
from warehouse.packaging.interfaces import (
3336
IDocsStorage,
3437
IFileStorage,
@@ -458,6 +461,28 @@ def create_project(
458461
},
459462
)
460463

464+
# Remove all pending publishers not owned by the creator.
465+
# There might be other pending publishers for the same project name,
466+
# which we've now invalidated by creating the project. These would
467+
# be disposed of on use, but we explicitly dispose of them here while
468+
# also sending emails to their owners.
469+
stale_pending_publishers = (
470+
request.db.query(PendingOIDCPublisher)
471+
.filter(
472+
func.normalize_pep426_name(PendingOIDCPublisher.project_name)
473+
== func.normalize_pep426_name(project.name),
474+
PendingOIDCPublisher.added_by != creator,
475+
)
476+
.all()
477+
)
478+
for stale_publisher in stale_pending_publishers:
479+
send_pending_trusted_publisher_invalidated_email(
480+
request,
481+
stale_publisher.added_by,
482+
project_name=stale_publisher.project_name,
483+
)
484+
request.db.delete(stale_publisher)
485+
461486
if ratelimited:
462487
self._hit_ratelimits(request, creator)
463488
return project

0 commit comments

Comments
 (0)