Add S3 OCSP status publishing and standalone Rust Axum responder
This commit is contained in:
@@ -401,3 +401,103 @@ class BulkRevocationTests(unittest.TestCase):
|
||||
json={"certificate_ids": ["00000000-0000-0000-0000-000000000000"], "reason": "superseded"},
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
class OcspSnapshotTests(unittest.TestCase):
|
||||
@async_test
|
||||
async def test_snapshot_contains_status_and_reason(self) -> None:
|
||||
async with api_client() as client:
|
||||
with oidc_user("alice"):
|
||||
vault_id = await _vault(client)
|
||||
ca_key = await _key(client, vault_id, "ocsp ca")
|
||||
ca = await _cert(client, vault_id, ca_key["id"], "OCSP CA", ca=True)
|
||||
leaf_key = await _key(client, vault_id, "ocsp leaf")
|
||||
leaf = await _cert(client, vault_id, leaf_key["id"], "OCSP Leaf", signer_id=ca["id"])
|
||||
await client.post(
|
||||
f"/api/vaults/{vault_id}/certificates/{leaf['id']}/revoke",
|
||||
json={"reason": "keyCompromise"},
|
||||
)
|
||||
response = await client.get(
|
||||
f"/api/vaults/{vault_id}/certificates/{ca['id']}/ocsp-status"
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
payload = response.json()
|
||||
assert payload["enabled"] is False
|
||||
snapshot = payload["snapshot"]
|
||||
assert snapshot["schema"] == "kca-ocsp-status/v1"
|
||||
assert snapshot["issuer"]["sha256"] == ca["fingerprint_sha256"]
|
||||
entries = {entry["serial_hex"]: entry for entry in snapshot["entries"]}
|
||||
assert entries[leaf["serial_hex"]]["status"] == "revoked"
|
||||
assert entries[leaf["serial_hex"]]["reason"] == "keyCompromise"
|
||||
assert entries[leaf["serial_hex"]]["revoked_at"]
|
||||
|
||||
@async_test
|
||||
async def test_revocation_marks_snapshot_dirty(self) -> None:
|
||||
from dataclasses import replace
|
||||
from unittest.mock import patch
|
||||
|
||||
from kca import ocsp
|
||||
from kca.models import OcspSnapshot
|
||||
|
||||
async with api_client() as client:
|
||||
with oidc_user("alice"):
|
||||
vault_id = await _vault(client)
|
||||
ca_key = await _key(client, vault_id, "dirty ca")
|
||||
ca = await _cert(client, vault_id, ca_key["id"], "Dirty CA", ca=True)
|
||||
leaf_key = await _key(client, vault_id, "dirty leaf")
|
||||
leaf = await _cert(client, vault_id, leaf_key["id"], "Dirty Leaf", signer_id=ca["id"])
|
||||
configured = replace(ocsp.settings, ocsp_s3_bucket="test-bucket")
|
||||
with patch.object(ocsp, "settings", configured):
|
||||
response = await client.post(
|
||||
f"/api/vaults/{vault_id}/certificates/{leaf['id']}/revoke",
|
||||
json={"reason": "superseded"},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
row = await OcspSnapshot.get(ca_id=ca["id"])
|
||||
assert row.dirty is True
|
||||
|
||||
@async_test
|
||||
async def test_publisher_marks_dirty_and_uploads(self) -> None:
|
||||
from dataclasses import replace
|
||||
from unittest.mock import patch
|
||||
|
||||
from kca import ocsp
|
||||
from kca.models import OcspSnapshot
|
||||
|
||||
class FakeS3:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def put_object(self, **kwargs):
|
||||
self.calls.append(kwargs)
|
||||
|
||||
fake = FakeS3()
|
||||
async with api_client() as client:
|
||||
with oidc_user("alice"):
|
||||
vault_id = await _vault(client)
|
||||
ca_key = await _key(client, vault_id, "ocsp publish ca")
|
||||
ca = await _cert(client, vault_id, ca_key["id"], "OCSP Publish CA", ca=True)
|
||||
configured = replace(
|
||||
ocsp.settings,
|
||||
ocsp_s3_bucket="test-bucket",
|
||||
ocsp_s3_prefix="ocsp",
|
||||
)
|
||||
with patch.object(ocsp, "settings", configured), patch.object(
|
||||
ocsp, "_s3_client", lambda: fake
|
||||
):
|
||||
await ocsp.mark_ocsp_dirty(ca["id"])
|
||||
row = await OcspSnapshot.get(ca_id=ca["id"])
|
||||
assert row.dirty is True
|
||||
async def ensure_context():
|
||||
return None
|
||||
|
||||
mixin = ocsp.OcspPublisherMixin(1, ensure_context)
|
||||
await mixin.run_once()
|
||||
assert len(fake.calls) == 1
|
||||
call = fake.calls[0]
|
||||
assert call["Bucket"] == "test-bucket"
|
||||
assert call["Key"] == f"ocsp/{ca['fingerprint_sha256']}.json"
|
||||
assert b"kca-ocsp-status/v1" in call["Body"]
|
||||
row = await OcspSnapshot.get(ca_id=ca["id"])
|
||||
assert row.dirty is False
|
||||
assert row.published_at is not None
|
||||
|
||||
Reference in New Issue
Block a user