diff --git a/tests/unit/oidc/models/test_activestate.py b/tests/unit/oidc/models/test_activestate.py
index a1d178735566..a0bd7e128911 100644
--- a/tests/unit/oidc/models/test_activestate.py
+++ b/tests/unit/oidc/models/test_activestate.py
@@ -93,6 +93,21 @@ def test_stored_claims(self):
assert publisher.stored_claims() == {}
+ def test_admin_details(self):
+ publisher = ActiveStatePublisher(
+ organization="fakeorg",
+ activestate_project_name="fakeproject",
+ actor="fakeactor",
+ actor_id="fakeactorid",
+ )
+
+ assert publisher.admin_details == [
+ ("Organization", "fakeorg"),
+ ("Project", "fakeproject"),
+ ("Actor", "fakeactor"),
+ ("Actor ID", "fakeactorid"),
+ ]
+
def test_stringifies_as_project_url(self):
org_name = "fakeorg"
project_name = "fakeproject"
diff --git a/tests/unit/oidc/models/test_core.py b/tests/unit/oidc/models/test_core.py
index 9c2c4dfc03cb..671cb7072d18 100644
--- a/tests/unit/oidc/models/test_core.py
+++ b/tests/unit/oidc/models/test_core.py
@@ -47,6 +47,10 @@ def test_attestation_identity(self):
publisher = _core.OIDCPublisher(projects=[])
assert not publisher.attestation_identity
+ def test_admin_details_default(self):
+ publisher = _core.OIDCPublisher(projects=[])
+ assert publisher.admin_details == []
+
@pytest.mark.parametrize(
("url", "publisher_url", "expected"),
[
diff --git a/tests/unit/oidc/models/test_github.py b/tests/unit/oidc/models/test_github.py
index 10a569f180fb..7e187b6c9746 100644
--- a/tests/unit/oidc/models/test_github.py
+++ b/tests/unit/oidc/models/test_github.py
@@ -238,6 +238,37 @@ def test_github_publisher_computed_properties(self):
"ref": "someref",
}
+ def test_github_publisher_admin_details_with_environment(self):
+ publisher = github.GitHubPublisher(
+ repository_name="fakerepo",
+ repository_owner="fakeowner",
+ repository_owner_id="fakeid",
+ workflow_filename="fakeworkflow.yml",
+ environment="fakeenv",
+ )
+
+ assert publisher.admin_details == [
+ ("Repository", "fakeowner/fakerepo"),
+ ("Workflow", "fakeworkflow.yml"),
+ ("Owner ID", "fakeid"),
+ ("Environment", "fakeenv"),
+ ]
+
+ def test_github_publisher_admin_details_without_environment(self):
+ publisher = github.GitHubPublisher(
+ repository_name="fakerepo",
+ repository_owner="fakeowner",
+ repository_owner_id="fakeid",
+ workflow_filename="fakeworkflow.yml",
+ environment="",
+ )
+
+ assert publisher.admin_details == [
+ ("Repository", "fakeowner/fakerepo"),
+ ("Workflow", "fakeworkflow.yml"),
+ ("Owner ID", "fakeid"),
+ ]
+
def test_github_publisher_unaccounted_claims(self, monkeypatch):
scope = pretend.stub()
sentry_sdk = pretend.stub(
diff --git a/tests/unit/oidc/models/test_gitlab.py b/tests/unit/oidc/models/test_gitlab.py
index 8e23e14521b2..07ce44574133 100644
--- a/tests/unit/oidc/models/test_gitlab.py
+++ b/tests/unit/oidc/models/test_gitlab.py
@@ -249,6 +249,33 @@ def test_gitlab_publisher_computed_properties(self):
"ref_path": "someref",
}
+ def test_gitlab_publisher_admin_details_with_environment(self):
+ publisher = gitlab.GitLabPublisher(
+ project="fakerepo",
+ namespace="fakeowner",
+ workflow_filepath="subfolder/fakeworkflow.yml",
+ environment="fakeenv",
+ )
+
+ assert publisher.admin_details == [
+ ("Project", "fakeowner/fakerepo"),
+ ("Workflow", "subfolder/fakeworkflow.yml"),
+ ("Environment", "fakeenv"),
+ ]
+
+ def test_gitlab_publisher_admin_details_without_environment(self):
+ publisher = gitlab.GitLabPublisher(
+ project="fakerepo",
+ namespace="fakeowner",
+ workflow_filepath="subfolder/fakeworkflow.yml",
+ environment="",
+ )
+
+ assert publisher.admin_details == [
+ ("Project", "fakeowner/fakerepo"),
+ ("Workflow", "subfolder/fakeworkflow.yml"),
+ ]
+
def test_gitlab_publisher_unaccounted_claims(self, monkeypatch):
scope = pretend.stub()
sentry_sdk = pretend.stub(
diff --git a/tests/unit/oidc/models/test_google.py b/tests/unit/oidc/models/test_google.py
index 5bb77d516f84..2ec702204872 100644
--- a/tests/unit/oidc/models/test_google.py
+++ b/tests/unit/oidc/models/test_google.py
@@ -34,6 +34,27 @@ def test_stringifies_as_email(self):
assert str(publisher) == publisher.email
+ def test_google_publisher_admin_details_with_sub(self):
+ publisher = google.GooglePublisher(
+ email="fake@example.com",
+ sub="fakesubject",
+ )
+
+ assert publisher.admin_details == [
+ ("Email", "fake@example.com"),
+ ("Subject", "fakesubject"),
+ ]
+
+ def test_google_publisher_admin_details_without_sub(self):
+ publisher = google.GooglePublisher(
+ email="fake@example.com",
+ sub=None,
+ )
+
+ assert publisher.admin_details == [
+ ("Email", "fake@example.com"),
+ ]
+
def test_google_publisher_all_known_claims(self):
assert google.GooglePublisher.all_known_claims() == {
# verifiable claims
diff --git a/warehouse/admin/templates/admin/projects/detail.html b/warehouse/admin/templates/admin/projects/detail.html
index 070e004503b6..28a812daea1f 100644
--- a/warehouse/admin/templates/admin/projects/detail.html
+++ b/warehouse/admin/templates/admin/projects/detail.html
@@ -129,7 +129,7 @@
|
- Upload limit |
+ Upload limit |
{# Calculate effective limit #}
{% set effective_upload_limit = MAX_FILESIZE %}
@@ -142,16 +142,16 @@
{% set effective_upload_limit = project.organization.upload_limit %}
{% set limit_source = "organization" %}
{% endif %}
-
-
+
+
Effective limit: {{ effective_upload_limit|filesizeformat(binary=True) }} (from {{ limit_source }})
-
-
+
+
- System default: |
- {{ MAX_FILESIZE|filesizeformat(binary=True) }} |
- Not configurable |
+ System default: |
+ {{ MAX_FILESIZE|filesizeformat(binary=True) }} |
+ Not configurable |
Project limit: |
@@ -193,7 +193,7 @@
{% else %}
{% set upload_limit_value = '' %}
{% endif %}
-
+
MiB
{% if project.upload_limit and limit_source != "project" %}
@@ -204,7 +204,7 @@
- Total size limit |
+ Total size limit |
{# Calculate effective limit #}
{% set effective_total_size_limit = MAX_PROJECT_SIZE %}
@@ -217,16 +217,16 @@
{% set effective_total_size_limit = project.organization.total_size_limit %}
{% set size_limit_source = "organization" %}
{% endif %}
-
-
+
+
Effective limit: {{ effective_total_size_limit|filesizeformat(binary=True) }} (from {{ size_limit_source }})
-
-
+
+
- System default: |
- {{ MAX_PROJECT_SIZE|filesizeformat(binary=True) }} |
- Not configurable |
+ System default: |
+ {{ MAX_PROJECT_SIZE|filesizeformat(binary=True) }} |
+ Not configurable |
Project limit: |
@@ -268,7 +268,7 @@
{% else %}
{% set total_size_limit_value = '' %}
{% endif %}
-
+
GiB
{% if project.total_size_limit and size_limit_source != "project" %}
@@ -480,21 +480,35 @@ Releases
- Publisher name |
+ Publisher |
+ Configuration |
URL |
- repr |
{% for pub in oidc_publishers %}
- {{ pub.publisher_name }} |
- {% if pub.publisher_url() %}
- {{ pub.publisher_url() }} |
- {% else %}
- N/A |
- {% endif %}
- {{ pub }} |
+ {{ pub.publisher_name }} |
+
+ {% if pub.admin_details %}
+
+ {% for label, value in pub.admin_details %}
+ - {{ label }}:
+ {{ value }}
+
+ {% endfor %}
+
+ {% else %}
+ {{ pub }}
+ {% endif %}
+ |
+
+ {% if pub.publisher_url() %}
+ {{ pub.publisher_url() }}
+ {% else %}
+ N/A
+ {% endif %}
+ |
{% endfor %}
diff --git a/warehouse/oidc/models/_core.py b/warehouse/oidc/models/_core.py
index 6908492ac46d..6ae8ab43bc7e 100644
--- a/warehouse/oidc/models/_core.py
+++ b/warehouse/oidc/models/_core.py
@@ -346,6 +346,14 @@ def exists(self, session) -> bool: # pragma: no cover
# Only concrete subclasses are constructed.
raise NotImplementedError
+ @property
+ def admin_details(self) -> list[tuple[str, str]]:
+ """
+ Returns a list of (label, value) tuples for display in admin interface.
+ Each publisher should override this to provide its configuration details.
+ """
+ return []
+
class OIDCPublisher(OIDCPublisherMixin, db.Model):
__tablename__ = "oidc_publishers"
diff --git a/warehouse/oidc/models/activestate.py b/warehouse/oidc/models/activestate.py
index 91cb10822ed5..650568517bc4 100644
--- a/warehouse/oidc/models/activestate.py
+++ b/warehouse/oidc/models/activestate.py
@@ -124,6 +124,16 @@ def exists(self, session) -> bool:
)
).scalar()
+ @property
+ def admin_details(self) -> list[tuple[str, str]]:
+ """Returns ActiveState publisher configuration details for admin display."""
+ return [
+ ("Organization", self.organization),
+ ("Project", self.activestate_project_name),
+ ("Actor", self.actor),
+ ("Actor ID", self.actor_id),
+ ]
+
@classmethod
def lookup_by_claims(cls, session, signed_claims: SignedClaims) -> Self:
query: Query = Query(cls).filter_by(
diff --git a/warehouse/oidc/models/github.py b/warehouse/oidc/models/github.py
index 017666f4c048..0f984e32918b 100644
--- a/warehouse/oidc/models/github.py
+++ b/warehouse/oidc/models/github.py
@@ -292,6 +292,18 @@ def exists(self, session) -> bool:
)
).scalar()
+ @property
+ def admin_details(self) -> list[tuple[str, str]]:
+ """Returns GitHub publisher configuration details for admin display."""
+ details = [
+ ("Repository", self.repository),
+ ("Workflow", self.workflow_filename),
+ ("Owner ID", self.repository_owner_id),
+ ]
+ if self.environment:
+ details.append(("Environment", self.environment))
+ return details
+
class GitHubPublisher(GitHubPublisherMixin, OIDCPublisher):
__tablename__ = "github_oidc_publishers"
diff --git a/warehouse/oidc/models/gitlab.py b/warehouse/oidc/models/gitlab.py
index 078f89e8ff07..446d0138026c 100644
--- a/warehouse/oidc/models/gitlab.py
+++ b/warehouse/oidc/models/gitlab.py
@@ -282,6 +282,17 @@ def exists(self, session) -> bool:
)
).scalar()
+ @property
+ def admin_details(self) -> list[tuple[str, str]]:
+ """Returns GitLab publisher configuration details for admin display."""
+ details = [
+ ("Project", self.project_path),
+ ("Workflow", self.workflow_filepath),
+ ]
+ if self.environment:
+ details.append(("Environment", self.environment))
+ return details
+
class GitLabPublisher(GitLabPublisherMixin, OIDCPublisher):
__tablename__ = "gitlab_oidc_publishers"
diff --git a/warehouse/oidc/models/google.py b/warehouse/oidc/models/google.py
index 13251e76a142..f5b55b44e92b 100644
--- a/warehouse/oidc/models/google.py
+++ b/warehouse/oidc/models/google.py
@@ -113,6 +113,16 @@ def exists(self, session) -> bool:
)
).scalar()
+ @property
+ def admin_details(self) -> list[tuple[str, str]]:
+ """Returns Google publisher configuration details for admin display."""
+ details = [
+ ("Email", self.email),
+ ]
+ if self.sub:
+ details.append(("Subject", self.sub))
+ return details
+
class GooglePublisher(GooglePublisherMixin, OIDCPublisher):
__tablename__ = "google_oidc_publishers"
| |