Skip to content

Commit 657a33c

Browse files
authored
feat(admin): display more publisher details (#18775)
1 parent 0a15b69 commit 657a33c

File tree

11 files changed

+190
-27
lines changed

11 files changed

+190
-27
lines changed

tests/unit/oidc/models/test_activestate.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ def test_stored_claims(self):
9393

9494
assert publisher.stored_claims() == {}
9595

96+
def test_admin_details(self):
97+
publisher = ActiveStatePublisher(
98+
organization="fakeorg",
99+
activestate_project_name="fakeproject",
100+
actor="fakeactor",
101+
actor_id="fakeactorid",
102+
)
103+
104+
assert publisher.admin_details == [
105+
("Organization", "fakeorg"),
106+
("Project", "fakeproject"),
107+
("Actor", "fakeactor"),
108+
("Actor ID", "fakeactorid"),
109+
]
110+
96111
def test_stringifies_as_project_url(self):
97112
org_name = "fakeorg"
98113
project_name = "fakeproject"

tests/unit/oidc/models/test_core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ def test_attestation_identity(self):
4747
publisher = _core.OIDCPublisher(projects=[])
4848
assert not publisher.attestation_identity
4949

50+
def test_admin_details_default(self):
51+
publisher = _core.OIDCPublisher(projects=[])
52+
assert publisher.admin_details == []
53+
5054
@pytest.mark.parametrize(
5155
("url", "publisher_url", "expected"),
5256
[

tests/unit/oidc/models/test_github.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,37 @@ def test_github_publisher_computed_properties(self):
238238
"ref": "someref",
239239
}
240240

241+
def test_github_publisher_admin_details_with_environment(self):
242+
publisher = github.GitHubPublisher(
243+
repository_name="fakerepo",
244+
repository_owner="fakeowner",
245+
repository_owner_id="fakeid",
246+
workflow_filename="fakeworkflow.yml",
247+
environment="fakeenv",
248+
)
249+
250+
assert publisher.admin_details == [
251+
("Repository", "fakeowner/fakerepo"),
252+
("Workflow", "fakeworkflow.yml"),
253+
("Owner ID", "fakeid"),
254+
("Environment", "fakeenv"),
255+
]
256+
257+
def test_github_publisher_admin_details_without_environment(self):
258+
publisher = github.GitHubPublisher(
259+
repository_name="fakerepo",
260+
repository_owner="fakeowner",
261+
repository_owner_id="fakeid",
262+
workflow_filename="fakeworkflow.yml",
263+
environment="",
264+
)
265+
266+
assert publisher.admin_details == [
267+
("Repository", "fakeowner/fakerepo"),
268+
("Workflow", "fakeworkflow.yml"),
269+
("Owner ID", "fakeid"),
270+
]
271+
241272
def test_github_publisher_unaccounted_claims(self, monkeypatch):
242273
scope = pretend.stub()
243274
sentry_sdk = pretend.stub(

tests/unit/oidc/models/test_gitlab.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,33 @@ def test_gitlab_publisher_computed_properties(self):
249249
"ref_path": "someref",
250250
}
251251

252+
def test_gitlab_publisher_admin_details_with_environment(self):
253+
publisher = gitlab.GitLabPublisher(
254+
project="fakerepo",
255+
namespace="fakeowner",
256+
workflow_filepath="subfolder/fakeworkflow.yml",
257+
environment="fakeenv",
258+
)
259+
260+
assert publisher.admin_details == [
261+
("Project", "fakeowner/fakerepo"),
262+
("Workflow", "subfolder/fakeworkflow.yml"),
263+
("Environment", "fakeenv"),
264+
]
265+
266+
def test_gitlab_publisher_admin_details_without_environment(self):
267+
publisher = gitlab.GitLabPublisher(
268+
project="fakerepo",
269+
namespace="fakeowner",
270+
workflow_filepath="subfolder/fakeworkflow.yml",
271+
environment="",
272+
)
273+
274+
assert publisher.admin_details == [
275+
("Project", "fakeowner/fakerepo"),
276+
("Workflow", "subfolder/fakeworkflow.yml"),
277+
]
278+
252279
def test_gitlab_publisher_unaccounted_claims(self, monkeypatch):
253280
scope = pretend.stub()
254281
sentry_sdk = pretend.stub(

tests/unit/oidc/models/test_google.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ def test_stringifies_as_email(self):
3434

3535
assert str(publisher) == publisher.email
3636

37+
def test_google_publisher_admin_details_with_sub(self):
38+
publisher = google.GooglePublisher(
39+
40+
sub="fakesubject",
41+
)
42+
43+
assert publisher.admin_details == [
44+
("Email", "[email protected]"),
45+
("Subject", "fakesubject"),
46+
]
47+
48+
def test_google_publisher_admin_details_without_sub(self):
49+
publisher = google.GooglePublisher(
50+
51+
sub=None,
52+
)
53+
54+
assert publisher.admin_details == [
55+
("Email", "[email protected]"),
56+
]
57+
3758
def test_google_publisher_all_known_claims(self):
3859
assert google.GooglePublisher.all_known_claims() == {
3960
# verifiable claims

warehouse/admin/templates/admin/projects/detail.html

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ <h3 class="card-title">
129129
<td></td>
130130
</tr>
131131
<tr>
132-
<td rowspan="2" style="vertical-align: top;">Upload limit</td>
132+
<td rowspan="2">Upload limit</td>
133133
<td colspan="3">
134134
{# Calculate effective limit #}
135135
{% set effective_upload_limit = MAX_FILESIZE %}
@@ -142,16 +142,16 @@ <h3 class="card-title">
142142
{% set effective_upload_limit = project.organization.upload_limit %}
143143
{% set limit_source = "organization" %}
144144
{% endif %}
145-
146-
<div class="alert alert-info" style="margin-bottom: 10px;">
145+
146+
<div class="alert alert-info">
147147
<strong>Effective limit: {{ effective_upload_limit|filesizeformat(binary=True) }}</strong> (from {{ limit_source }})
148148
</div>
149-
150-
<table class="table table-sm table-bordered" style="margin-bottom: 0;">
149+
150+
<table class="table table-sm table-bordered">
151151
<tr>
152-
<td style="width: 40%;">System default:</td>
153-
<td style="width: 30%;">{{ MAX_FILESIZE|filesizeformat(binary=True) }}</td>
154-
<td style="width: 30%;"><small class="text-muted">Not configurable</small></td>
152+
<td>System default:</td>
153+
<td>{{ MAX_FILESIZE|filesizeformat(binary=True) }}</td>
154+
<td><small class="text-muted">Not configurable</small></td>
155155
</tr>
156156
<tr>
157157
<td>Project limit:</td>
@@ -193,7 +193,7 @@ <h3 class="card-title">
193193
{% else %}
194194
{% set upload_limit_value = '' %}
195195
{% endif %}
196-
<input type="number" name="upload_limit" id="uploadLimit" class="form-control form-control-sm mr-2" min="{{ MAX_FILESIZE / ONE_MIB }}" max="{{ UPLOAD_LIMIT_CAP / ONE_MIB }}" step=1 value="{{ upload_limit_value|int }}" style="width: 100px;">
196+
<input type="number" name="upload_limit" id="uploadLimit" class="form-control form-control-sm mr-2" min="{{ MAX_FILESIZE / ONE_MIB }}" max="{{ UPLOAD_LIMIT_CAP / ONE_MIB }}" step=1 value="{{ upload_limit_value|int }}">
197197
<span class="mr-3">MiB</span>
198198
<button type="submit" class="btn btn-primary btn-sm">Update</button>
199199
{% if project.upload_limit and limit_source != "project" %}
@@ -204,7 +204,7 @@ <h3 class="card-title">
204204
</form>
205205
</tr>
206206
<tr>
207-
<td rowspan="2" style="vertical-align: top;">Total size limit</td>
207+
<td rowspan="2">Total size limit</td>
208208
<td colspan="3">
209209
{# Calculate effective limit #}
210210
{% set effective_total_size_limit = MAX_PROJECT_SIZE %}
@@ -217,16 +217,16 @@ <h3 class="card-title">
217217
{% set effective_total_size_limit = project.organization.total_size_limit %}
218218
{% set size_limit_source = "organization" %}
219219
{% endif %}
220-
221-
<div class="alert alert-info" style="margin-bottom: 10px;">
220+
221+
<div class="alert alert-info">
222222
<strong>Effective limit: {{ effective_total_size_limit|filesizeformat(binary=True) }}</strong> (from {{ size_limit_source }})
223223
</div>
224-
225-
<table class="table table-sm table-bordered" style="margin-bottom: 0;">
224+
225+
<table class="table table-sm table-bordered">
226226
<tr>
227-
<td style="width: 40%;">System default:</td>
228-
<td style="width: 30%;">{{ MAX_PROJECT_SIZE|filesizeformat(binary=True) }}</td>
229-
<td style="width: 30%;"><small class="text-muted">Not configurable</small></td>
227+
<td>System default:</td>
228+
<td>{{ MAX_PROJECT_SIZE|filesizeformat(binary=True) }}</td>
229+
<td><small class="text-muted">Not configurable</small></td>
230230
</tr>
231231
<tr>
232232
<td>Project limit:</td>
@@ -268,7 +268,7 @@ <h3 class="card-title">
268268
{% else %}
269269
{% set total_size_limit_value = '' %}
270270
{% endif %}
271-
<input type="number" name="total_size_limit" id="totalSizeLimit" class="form-control form-control-sm mr-2" min="{{ MAX_PROJECT_SIZE // ONE_GIB }}" value="{{total_size_limit_value}}" style="width: 100px;">
271+
<input type="number" name="total_size_limit" id="totalSizeLimit" class="form-control form-control-sm mr-2" min="{{ MAX_PROJECT_SIZE // ONE_GIB }}" value="{{total_size_limit_value}}">
272272
<span class="mr-3">GiB</span>
273273
<button type="submit" class="btn btn-primary btn-sm">Update</button>
274274
{% if project.total_size_limit and size_limit_source != "project" %}
@@ -480,21 +480,35 @@ <h3 class="card-title">Releases</h3>
480480
<table class="table table-hover table-striped">
481481
<thead>
482482
<tr>
483-
<th>Publisher name</th>
483+
<th>Publisher</th>
484+
<th>Configuration</th>
484485
<th>URL</th>
485-
<th>repr</th>
486486
</tr>
487487
</thead>
488488
<tbody>
489489
{% for pub in oidc_publishers %}
490490
<tr>
491-
<td>{{ pub.publisher_name }}</td>
492-
{% if pub.publisher_url() %}
493-
<td><a href="{{ pub.publisher_url() }}">{{ pub.publisher_url() }}</a></td>
494-
{% else %}
495-
<td>N/A</td>
496-
{% endif %}
497-
<td><code>{{ pub }}</code></td>
491+
<td><strong>{{ pub.publisher_name }}</strong></td>
492+
<td>
493+
{% if pub.admin_details %}
494+
<dl class="mb-0">
495+
{% for label, value in pub.admin_details %}
496+
<dt class="small d-inline-block">{{ label }}:</dt>
497+
<dd class="small d-inline"><code>{{ value }}</code></dd>
498+
<br>
499+
{% endfor %}
500+
</dl>
501+
{% else %}
502+
<code>{{ pub }}</code>
503+
{% endif %}
504+
</td>
505+
<td>
506+
{% if pub.publisher_url() %}
507+
<a href="{{ pub.publisher_url() }}" target="_blank">{{ pub.publisher_url() }}</a>
508+
{% else %}
509+
<span class="text-muted">N/A</span>
510+
{% endif %}
511+
</td>
498512
</tr>
499513
{% endfor %}
500514
</tbody>

warehouse/oidc/models/_core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,14 @@ def exists(self, session) -> bool: # pragma: no cover
346346
# Only concrete subclasses are constructed.
347347
raise NotImplementedError
348348

349+
@property
350+
def admin_details(self) -> list[tuple[str, str]]:
351+
"""
352+
Returns a list of (label, value) tuples for display in admin interface.
353+
Each publisher should override this to provide its configuration details.
354+
"""
355+
return []
356+
349357

350358
class OIDCPublisher(OIDCPublisherMixin, db.Model):
351359
__tablename__ = "oidc_publishers"

warehouse/oidc/models/activestate.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ def exists(self, session) -> bool:
124124
)
125125
).scalar()
126126

127+
@property
128+
def admin_details(self) -> list[tuple[str, str]]:
129+
"""Returns ActiveState publisher configuration details for admin display."""
130+
return [
131+
("Organization", self.organization),
132+
("Project", self.activestate_project_name),
133+
("Actor", self.actor),
134+
("Actor ID", self.actor_id),
135+
]
136+
127137
@classmethod
128138
def lookup_by_claims(cls, session, signed_claims: SignedClaims) -> Self:
129139
query: Query = Query(cls).filter_by(

warehouse/oidc/models/github.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,18 @@ def exists(self, session) -> bool:
292292
)
293293
).scalar()
294294

295+
@property
296+
def admin_details(self) -> list[tuple[str, str]]:
297+
"""Returns GitHub publisher configuration details for admin display."""
298+
details = [
299+
("Repository", self.repository),
300+
("Workflow", self.workflow_filename),
301+
("Owner ID", self.repository_owner_id),
302+
]
303+
if self.environment:
304+
details.append(("Environment", self.environment))
305+
return details
306+
295307

296308
class GitHubPublisher(GitHubPublisherMixin, OIDCPublisher):
297309
__tablename__ = "github_oidc_publishers"

warehouse/oidc/models/gitlab.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,17 @@ def exists(self, session) -> bool:
282282
)
283283
).scalar()
284284

285+
@property
286+
def admin_details(self) -> list[tuple[str, str]]:
287+
"""Returns GitLab publisher configuration details for admin display."""
288+
details = [
289+
("Project", self.project_path),
290+
("Workflow", self.workflow_filepath),
291+
]
292+
if self.environment:
293+
details.append(("Environment", self.environment))
294+
return details
295+
285296

286297
class GitLabPublisher(GitLabPublisherMixin, OIDCPublisher):
287298
__tablename__ = "gitlab_oidc_publishers"

0 commit comments

Comments
 (0)