Batch certificate summary lookups
CI / Build and push docker image (push) Successful in 3m14s
CI / Build and push OCSP image (push) Successful in 40s

This commit is contained in:
2026-10-27 14:09:00 +00:00
parent a2457ab1f4
commit 0301cbff6f
3 changed files with 129 additions and 7 deletions
@@ -39,7 +39,7 @@ from ..service import (
save_key_once,
search_text,
serialize_certificate_detail,
serialize_certificate_summary,
serialize_certificate_summaries,
serialize_revocation,
serialize_template_summary,
serialize_crl_detail,
@@ -74,7 +74,7 @@ def register(app: KayaApp, kca: "Kca") -> None:
if vault is None:
return
rows = await Certificate.filter(vault_id=vault.id).order_by("name", "id")
await send_json(ctx, 200, {"results": [await serialize_certificate_summary(c) for c in rows]})
await send_json(ctx, 200, {"results": await serialize_certificate_summaries(rows)})
@app.POST("/api/vaults/${vault_id}/certificates")
@operation(summary="Issue a certificate", tags=["certificates"])
+56 -5
View File
@@ -220,23 +220,27 @@ async def revoked_for(cert: Certificate) -> Optional[CertificateRevocation]:
return await CertificateRevocation.get_or_none(ca_id=cert.issuer_id, serial_hex=cert.serial_hex)
async def serialize_certificate_summary(cert: Certificate) -> Dict[str, Any]:
revoked = await revoked_for(cert)
def _certificate_summary(
cert: Certificate,
revoked: Optional[CertificateRevocation],
has_private_key: bool,
) -> Dict[str, Any]:
parsed = cert_core.parse_certificate(bytes(cert.cert_der))
return {
"id": str(cert.id),
"name": cert.name,
"comment": cert.comment,
"source": cert.source,
"subject": name_core.one_line(cert_core.parse_certificate(bytes(cert.cert_der)).subject),
"subject": name_core.one_line(parsed.subject),
"issuer_id": str(cert.issuer_id) if cert.issuer_id else None,
"issuer_name": name_core.one_line(cert_core.parse_certificate(bytes(cert.cert_der)).issuer),
"issuer_name": name_core.one_line(parsed.issuer),
"serial_hex": cert.serial_hex,
"not_before": iso(cert.not_before),
"not_after": iso(cert.not_after),
"no_expiration": cert.no_expiration,
"is_ca": cert.is_ca,
"path_length": cert.path_length,
"has_private_key": bool(cert.key_id and await Key.filter(id=cert.key_id, has_private=True).exists()),
"has_private_key": has_private_key,
"revoked": revoked is not None,
"revoked_at": iso(revoked.revoked_at) if revoked else None,
"fingerprint_sha256": cert.fingerprint_sha256,
@@ -247,6 +251,52 @@ async def serialize_certificate_summary(cert: Certificate) -> Dict[str, Any]:
}
async def serialize_certificate_summary(cert: Certificate) -> Dict[str, Any]:
revoked = await revoked_for(cert)
has_private_key = bool(
cert.key_id and await Key.filter(id=cert.key_id, has_private=True).exists()
)
return _certificate_summary(cert, revoked, has_private_key)
async def serialize_certificate_summaries(
certificates: Sequence[Certificate],
) -> List[Dict[str, Any]]:
"""Serialize a certificate list with constant-count revocation/key queries."""
if not certificates:
return []
issuer_ids = {cert.issuer_id for cert in certificates if cert.issuer_id is not None}
serial_hexes = {cert.serial_hex for cert in certificates if cert.issuer_id is not None}
revocations = (
await CertificateRevocation.filter(
ca_id__in=issuer_ids,
serial_hex__in=serial_hexes,
)
if issuer_ids
else []
)
revoked_by_certificate = {
(revocation.ca_id, revocation.serial_hex): revocation
for revocation in revocations
}
key_ids = {cert.key_id for cert in certificates if cert.key_id is not None}
private_key_ids = (
set(await Key.filter(id__in=key_ids, has_private=True).values_list("id", flat=True))
if key_ids
else set()
)
return [
_certificate_summary(
cert,
revoked_by_certificate.get((cert.issuer_id, cert.serial_hex)),
cert.key_id in private_key_ids,
)
for cert in certificates
]
async def chain_for_certificate(cert: Certificate, vault: Vault) -> List[Certificate]:
chain = [cert]
current = cert
@@ -1591,6 +1641,7 @@ __all__ = [
"save_key",
"save_key_once",
"serialize_certificate_detail",
"serialize_certificate_summaries",
"serialize_certificate_summary",
"serialize_crl_detail",
"serialize_crl_summary",