Make public key extraction idempotent
This commit is contained in:
@@ -36,7 +36,7 @@ from ..service import (
|
||||
renew_certificate,
|
||||
resolve_signature_hash,
|
||||
revoke_certificate,
|
||||
save_key,
|
||||
save_key_once,
|
||||
search_text,
|
||||
serialize_certificate_detail,
|
||||
serialize_certificate_summary,
|
||||
@@ -592,14 +592,14 @@ def register(app: KayaApp, kca: "Kca") -> None:
|
||||
await send_error(ctx, 404, "certificate not found")
|
||||
return
|
||||
parsed = cert_core.parse_certificate(bytes(cert.cert_der))
|
||||
key = await save_key(
|
||||
key, created = await save_key_once(
|
||||
vault,
|
||||
cert_core.certificate_public_key(parsed),
|
||||
name=f"{cert.name} public key",
|
||||
comment=f"Extracted from certificate {cert.name}",
|
||||
source="transformed",
|
||||
)
|
||||
await send_json(ctx, 201, {"id": str(key.id), "name": key.name})
|
||||
await send_json(ctx, 201 if created else 200, {"id": str(key.id), "name": key.name})
|
||||
|
||||
@app.POST("/api/vaults/${vault_id}/certificates/${cert_id}/transform/request")
|
||||
@operation(summary="Create a request from a certificate", tags=["certificates"])
|
||||
|
||||
@@ -27,6 +27,7 @@ from ..service import (
|
||||
parse_uuid,
|
||||
resolve_signature_hash,
|
||||
save_key,
|
||||
save_key_once,
|
||||
search_text,
|
||||
serialize_request_detail,
|
||||
serialize_request_summary,
|
||||
@@ -258,14 +259,14 @@ def register(app: KayaApp, kca: "Kca") -> None:
|
||||
await send_error(ctx, 404, "request not found")
|
||||
return
|
||||
public_key = req_core.csr_public_key(req_core.parse_csr(bytes(row.req_der)))
|
||||
key = await save_key(
|
||||
key, created = await save_key_once(
|
||||
vault,
|
||||
public_key,
|
||||
name=f"{row.name} public key",
|
||||
comment=f"Extracted from request {row.name}",
|
||||
source="transformed",
|
||||
)
|
||||
await send_json(ctx, 201, {"id": str(key.id), "name": key.name})
|
||||
await send_json(ctx, 201 if created else 200, {"id": str(key.id), "name": key.name})
|
||||
|
||||
@app.POST("/api/vaults/${vault_id}/requests/${request_id}/transform/template")
|
||||
@operation(summary="Create a template from a request", tags=["requests"])
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple
|
||||
from uuid import UUID
|
||||
|
||||
from cryptography import x509
|
||||
from tortoise.exceptions import IntegrityError
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from kca.core.algorithms import (
|
||||
@@ -577,6 +578,35 @@ async def save_key(
|
||||
return key
|
||||
|
||||
|
||||
async def save_key_once(
|
||||
vault: Vault,
|
||||
key_obj: Any,
|
||||
*,
|
||||
name: str,
|
||||
comment: str = "",
|
||||
source: str = "transformed",
|
||||
) -> Tuple[Key, bool]:
|
||||
"""Return an existing key with the same public material, or save a new one.
|
||||
|
||||
Public-key extraction from a certificate or request is idempotent: when
|
||||
the key already exists in the vault, return it instead of violating the
|
||||
``(vault, public_key_sha256)`` unique constraint.
|
||||
"""
|
||||
info = key_core.key_info(key_obj)
|
||||
existing = await Key.get_or_none(vault_id=vault.id, public_key_sha256=info.public_sha256)
|
||||
if existing is not None:
|
||||
return existing, False
|
||||
try:
|
||||
created = await save_key(vault, key_obj, name=name, comment=comment, source=source)
|
||||
except IntegrityError:
|
||||
# A concurrent request may have inserted the same key first.
|
||||
existing = await Key.get_or_none(vault_id=vault.id, public_key_sha256=info.public_sha256)
|
||||
if existing is None:
|
||||
raise
|
||||
return existing, False
|
||||
return created, True
|
||||
|
||||
|
||||
async def load_private_key(key: Key) -> Any:
|
||||
if not key.has_private or key.private_der is None or key.private_nonce is None:
|
||||
raise ValidationError("this key has no private part")
|
||||
@@ -1544,6 +1574,7 @@ __all__ = [
|
||||
"renew_certificate",
|
||||
"revoke_certificate",
|
||||
"save_key",
|
||||
"save_key_once",
|
||||
"serialize_certificate_detail",
|
||||
"serialize_certificate_summary",
|
||||
"serialize_crl_detail",
|
||||
|
||||
Reference in New Issue
Block a user