Add transient NPM certificate material
This commit is contained in:
+155
-3
@@ -1,9 +1,12 @@
|
||||
import os
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
from zipfile import ZipFile
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from dns_sync.adapters import Endpoint
|
||||
from dns_sync.config import AppConfig
|
||||
from dns_sync.certificates.material import CertificateMaterial
|
||||
from dns_sync.npm_client import NpmClient
|
||||
|
||||
|
||||
@@ -97,6 +100,31 @@ class NpmClientMutationTests(unittest.TestCase):
|
||||
response.json.return_value = data
|
||||
return response
|
||||
|
||||
|
||||
@staticmethod
|
||||
def certificate_zip():
|
||||
buffer = BytesIO()
|
||||
|
||||
with ZipFile(buffer, "w") as archive:
|
||||
archive.writestr(
|
||||
"cert4.pem",
|
||||
b"LEAF-CERTIFICATE",
|
||||
)
|
||||
archive.writestr(
|
||||
"fullchain4.pem",
|
||||
b"FULLCHAIN",
|
||||
)
|
||||
archive.writestr(
|
||||
"chain4.pem",
|
||||
b"INTERMEDIATE-CHAIN",
|
||||
)
|
||||
archive.writestr(
|
||||
"privkey4.pem",
|
||||
b"PRIVATE-KEY",
|
||||
)
|
||||
|
||||
return buffer.getvalue()
|
||||
|
||||
def test_gets_one_proxy_host_by_endpoint_local_id(self):
|
||||
client = self.client()
|
||||
client.get_json = Mock(
|
||||
@@ -174,6 +202,125 @@ class NpmClientMutationTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(result["id"], 67)
|
||||
|
||||
def test_reads_custom_certificate_material_from_metadata(self):
|
||||
client = self.client()
|
||||
client.certificate = Mock(
|
||||
return_value={
|
||||
"id": 67,
|
||||
"provider": "other",
|
||||
"meta": {
|
||||
"certificate": "CUSTOM-CERTIFICATE",
|
||||
"certificate_key": "CUSTOM-PRIVATE-KEY",
|
||||
"intermediate_certificate": "CUSTOM-CHAIN",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
material = client.certificate_material("67")
|
||||
|
||||
self.assertEqual(
|
||||
material,
|
||||
CertificateMaterial(
|
||||
certificate=b"CUSTOM-CERTIFICATE",
|
||||
certificate_key=b"CUSTOM-PRIVATE-KEY",
|
||||
intermediate_certificate=b"CUSTOM-CHAIN",
|
||||
),
|
||||
)
|
||||
|
||||
client.certificate.assert_called_once_with("67")
|
||||
|
||||
def test_reads_letsencrypt_material_from_download_zip(self):
|
||||
client = self.client()
|
||||
client.certificate = Mock(
|
||||
return_value={
|
||||
"id": 10,
|
||||
"provider": "letsencrypt",
|
||||
}
|
||||
)
|
||||
|
||||
response = Mock()
|
||||
response.content = self.certificate_zip()
|
||||
client.request = Mock(return_value=response)
|
||||
|
||||
material = client.certificate_material("10")
|
||||
|
||||
self.assertEqual(
|
||||
material.certificate,
|
||||
b"LEAF-CERTIFICATE",
|
||||
)
|
||||
self.assertEqual(
|
||||
material.certificate_key,
|
||||
b"PRIVATE-KEY",
|
||||
)
|
||||
self.assertEqual(
|
||||
material.intermediate_certificate,
|
||||
b"INTERMEDIATE-CHAIN",
|
||||
)
|
||||
|
||||
client.request.assert_called_once_with(
|
||||
"GET",
|
||||
"/nginx/certificates/10/download",
|
||||
expected_status=200,
|
||||
)
|
||||
|
||||
def test_certificate_material_repr_redacts_secrets(self):
|
||||
material = CertificateMaterial(
|
||||
certificate=b"VISIBLE-CERT-SECRET",
|
||||
certificate_key=b"VISIBLE-KEY-SECRET",
|
||||
intermediate_certificate=b"VISIBLE-CHAIN-SECRET",
|
||||
)
|
||||
|
||||
rendered = repr(material)
|
||||
|
||||
self.assertNotIn(
|
||||
"VISIBLE-CERT-SECRET",
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"VISIBLE-KEY-SECRET",
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"VISIBLE-CHAIN-SECRET",
|
||||
rendered,
|
||||
)
|
||||
self.assertIn("<redacted>", rendered)
|
||||
|
||||
def test_certificate_material_rejects_missing_secret_parts(self):
|
||||
client = self.client()
|
||||
|
||||
client.certificate = Mock(
|
||||
return_value={
|
||||
"id": 67,
|
||||
"provider": "other",
|
||||
"meta": {
|
||||
"certificate": "CERT",
|
||||
"certificate_key": "",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"certificate_key",
|
||||
):
|
||||
client.certificate_material("67")
|
||||
|
||||
def test_certificate_material_rejects_unknown_provider(self):
|
||||
client = self.client()
|
||||
client.certificate = Mock(
|
||||
return_value={
|
||||
"id": 99,
|
||||
"provider": "mystery-provider",
|
||||
}
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"Unsupported NPM certificate provider",
|
||||
):
|
||||
client.certificate_material("99")
|
||||
|
||||
def test_creates_certificate_metadata_with_json_payload(self):
|
||||
client = self.client()
|
||||
payload = {
|
||||
@@ -200,9 +347,13 @@ class NpmClientMutationTests(unittest.TestCase):
|
||||
|
||||
def test_uploads_certificate_material_as_multipart(self):
|
||||
client = self.client()
|
||||
client.request = Mock(
|
||||
return_value=self.response({"id": 84})
|
||||
response = self.response(
|
||||
{
|
||||
"certificate": "SECRET-CERTIFICATE",
|
||||
"certificate_key": "SECRET-PRIVATE-KEY",
|
||||
}
|
||||
)
|
||||
client.request = Mock(return_value=response)
|
||||
|
||||
result = client.upload_certificate(
|
||||
"84",
|
||||
@@ -230,7 +381,8 @@ class NpmClientMutationTests(unittest.TestCase):
|
||||
),
|
||||
},
|
||||
)
|
||||
self.assertEqual(result["id"], 84)
|
||||
self.assertIsNone(result)
|
||||
response.json.assert_not_called()
|
||||
|
||||
def test_certificate_upload_requires_both_required_parts(self):
|
||||
client = self.client()
|
||||
|
||||
Reference in New Issue
Block a user