Add transient NPM certificate material

This commit is contained in:
Lyncoln
2026-09-20 00:27:47 -04:00
parent 381119a6b5
commit 2b4a6eea79
3 changed files with 344 additions and 10 deletions
+138 -7
View File
@@ -1,10 +1,14 @@
import os
from io import BytesIO
from pathlib import PurePosixPath
from typing import Any
from zipfile import BadZipFile, ZipFile
import requests
import urllib3
from .adapters import Endpoint
from .certificates.material import CertificateMaterial
class NpmClient:
@@ -236,6 +240,138 @@ class NpmClient:
)
return data
@staticmethod
def _pem_bytes(value: Any, label: str) -> bytes:
if not isinstance(value, str) or not value.strip():
raise RuntimeError(
f"NPM certificate is missing {label}"
)
return value.encode("utf-8")
@staticmethod
def _zip_pem_member(
archive: ZipFile,
prefix: str,
) -> bytes:
matches = []
for info in archive.infolist():
if info.is_dir():
continue
basename = PurePosixPath(info.filename).name
if not basename.endswith(".pem"):
continue
stem = basename[:-4]
if not stem.startswith(prefix):
continue
suffix = stem[len(prefix):]
if not suffix.isdigit():
continue
matches.append(info)
if len(matches) != 1:
raise RuntimeError(
"NPM certificate download must contain exactly one "
f"{prefix}N.pem member"
)
data = archive.read(matches[0])
if not data:
raise RuntimeError(
f"NPM certificate download {prefix} member is empty"
)
return data
def certificate_material(
self,
local_id: str | int,
) -> CertificateMaterial:
certificate_id = self._local_id(
local_id,
"certificate",
)
detail = self.certificate(certificate_id)
provider = str(
detail.get("provider") or ""
).strip().lower()
if provider == "other":
meta = detail.get("meta")
if not isinstance(meta, dict):
raise RuntimeError(
"NPM custom certificate has invalid metadata"
)
intermediate = meta.get(
"intermediate_certificate"
)
return CertificateMaterial(
certificate=self._pem_bytes(
meta.get("certificate"),
"certificate",
),
certificate_key=self._pem_bytes(
meta.get("certificate_key"),
"certificate_key",
),
intermediate_certificate=(
None
if intermediate in (None, "")
else self._pem_bytes(
intermediate,
"intermediate_certificate",
)
),
)
if provider == "letsencrypt":
response = self.request(
"GET",
(
"/nginx/certificates/"
f"{certificate_id}/download"
),
expected_status=200,
)
try:
with ZipFile(BytesIO(response.content)) as archive:
return CertificateMaterial(
certificate=self._zip_pem_member(
archive,
"cert",
),
certificate_key=self._zip_pem_member(
archive,
"privkey",
),
intermediate_certificate=(
self._zip_pem_member(
archive,
"chain",
)
),
)
except BadZipFile as exc:
raise RuntimeError(
"NPM certificate download was not a valid ZIP archive"
) from exc
raise RuntimeError(
f"Unsupported NPM certificate provider: {provider!r}"
)
def create_certificate(
self,
payload: dict[str, Any],
@@ -258,7 +394,7 @@ class NpmClient:
certificate: bytes,
certificate_key: bytes,
intermediate_certificate: bytes | None = None,
) -> dict[str, Any]:
) -> None:
certificate_id = self._local_id(
local_id,
"certificate",
@@ -290,14 +426,9 @@ class NpmClient:
intermediate_certificate,
)
response = self.request(
self.request(
"POST",
f"/nginx/certificates/{certificate_id}/upload",
expected_status=200,
files=files,
)
return self._json_object(
response,
"certificate upload",
)