Skip to content

Commit

Permalink
display subject as list, add issuer to display and unify CA/cert display
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasertl committed Aug 24, 2023
1 parent bd24530 commit 46d3044
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 18 deletions.
46 changes: 28 additions & 18 deletions ca/django_ca/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,6 @@ def _download_response(self, request: HttpRequest, pk: int, bundle: bool = False
response["Content-Disposition"] = f"attachment; filename={filename}"
return response

def distinguished_name(self, obj: X509CertMixinTypeVar) -> str:
"""The certificates distinguished name formatted as string."""
return format_name(obj.pub.loaded.subject)

distinguished_name.short_description = _("Distinguished Name") # type: ignore[attr-defined]

def download_view(self, request: HttpRequest, pk: int) -> HttpResponse:
"""A view that allows the user to download a certificate in PEM or DER/ASN1 format."""

Expand Down Expand Up @@ -234,13 +228,21 @@ def hpkp_pin(self, obj: X509CertMixinTypeVar) -> str:

hpkp_pin.short_description = _("HPKP pin") # type: ignore[attr-defined] # django standard

def cn_display(self, obj: X509CertMixinTypeVar) -> "StrOrPromise":
"""Display the common name or ``<none>``."""
@admin.display(description=_("Primary name"))
def primary_name(self, obj: X509CertMixinTypeVar) -> "StrOrPromise":
extensions = obj.x509_extensions
if san := extensions.get(ExtensionOID.SUBJECT_ALTERNATIVE_NAME):
# NOTE: Do not format the general name here, as this should be obvious from the list display.
return san.value[0].value # type: ignore[no-any-return,index]
if obj.cn:
return obj.cn
return _("<none>")
# COVERAGE NOTE: Should not happen, certs have either a subject or a Subject Alternative Name
return _("<none>") # pragma: no cover

cn_display.short_description = _("CommonName") # type: ignore[attr-defined] # django standard
@admin.display(description=_("Issuer"))
def issuer_field(self, obj: X509CertMixinTypeVar) -> str:
name = [(constants.NAME_OID_DISPLAY_NAMES[attr.oid], attr.value) for attr in obj.issuer]
return render_to_string("django_ca/admin/x509_name.html", context={"name": name})

def serial_field(self, obj: X509CertMixinTypeVar) -> str:
"""Display the serial (with colons added)."""
Expand All @@ -249,6 +251,11 @@ def serial_field(self, obj: X509CertMixinTypeVar) -> str:
serial_field.short_description = _("Serial") # type: ignore[attr-defined] # django standard
serial_field.admin_order_field = "serial" # type: ignore[attr-defined] # django standard

@admin.display(description=_("Subject"))
def subject_field(self, obj: X509CertMixinTypeVar) -> str:
name = [(constants.NAME_OID_DISPLAY_NAMES[attr.oid], attr.value) for attr in obj.subject]
return render_to_string("django_ca/admin/x509_name.html", context={"name": name})

def get_search_results(
self, request: HttpRequest, queryset: QuerySet, search_term: str
) -> Tuple[QuerySet, bool]:
Expand Down Expand Up @@ -343,8 +350,10 @@ class CertificateAuthorityAdmin(CertificateMixin[CertificateAuthority], Certific
"fields": [
"name",
"enabled",
"cn_display",
"subject_field",
"serial_field",
"parent",
"issuer_field",
"hpkp_pin",
"caa_identity",
"website",
Expand Down Expand Up @@ -373,7 +382,7 @@ class CertificateAuthorityAdmin(CertificateMixin[CertificateAuthority], Certific
(
_("Certificate"),
{
"fields": ["serial_field", "pub_pem", "expires"],
"fields": ["pub_pem", "expires"],
# The "as-code" class is used so CSS can only match this section (and only in an
# existing cert).
"classes": ("as-code",),
Expand Down Expand Up @@ -402,10 +411,11 @@ class CertificateAuthorityAdmin(CertificateMixin[CertificateAuthority], Certific
"serial_field",
]
readonly_fields = (
"issuer_field",
"serial_field",
"subject_field",
"pub_pem",
"parent",
"cn_display",
"expires",
"hpkp_pin",
)
Expand Down Expand Up @@ -574,18 +584,18 @@ class CertificateAdmin(DjangoObjectActions, CertificateMixin[Certificate], Certi
)
add_form_template = "admin/django_ca/certificate/add_form.html"
change_form_template = "admin/django_ca/certificate/change_form.html"
list_display = ("cn_display", "profile", "serial_field", "status", "expires_date")
list_display = ("primary_name", "profile", "serial_field", "status", "expires_date")
list_filter = ("profile", AutoGeneratedFilter, StatusListFilter, "ca")
readonly_fields = [
"expires",
"issuer_field",
"csr_pem",
"pub_pem",
"cn_display",
"serial_field",
"subject_field",
"revoked",
"revoked_date",
"revoked_reason",
"distinguished_name",
"ca",
"hpkp_pin",
"profile",
Expand All @@ -601,11 +611,11 @@ class CertificateAdmin(DjangoObjectActions, CertificateMixin[Certificate], Certi
None,
{
"fields": [
"cn_display",
"subject_field",
"oid_2_5_29_17", # SubjectAlternativeName
"distinguished_name",
"serial_field",
"ca",
"issuer_field",
("expires", "autogenerated"),
"watchers",
"hpkp_pin",
Expand Down
12 changes: 12 additions & 0 deletions ca/django_ca/templates/django_ca/admin/x509_name.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% load i18n %}
<span class="django-ca-extension">
<div class="django-ca-extension-value">
<ul>
{% for key, value in name %}
<li>{{ key }}: {{ value }}</li>
{% empty %}
{% translate "Empty name" %}
{% endfor %}
</ul>
</div>
</span>
2 changes: 2 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ ChangeLog
valid (fixes `issue 102 <https://github.com/mathiasertl/django-ca/issues/102>`_).
* The web interface now allows creating certificates with arbitrary or even empty subjects (fixes `issue 77
<https://github.com/mathiasertl/django-ca/issues/77>`_).
* The certificate subject is now displayed as a unambiguous list instead of a string. The issuer is now also
shown in the same way.

Backwards incompatible changes
==============================
Expand Down

0 comments on commit 46d3044

Please sign in to comment.