mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 22:01:36 +00:00
api, handler: 4 admin-gated CA hierarchy endpoints + OpenAPI (Rank 8 commit 4)
Rank 8 commit 4 of 5. The API + RBAC layer that operators drive
the new hierarchy management surface from.
Endpoints (all admin-gated via middleware.IsAdmin; non-admin Bearer
callers get 403):
POST /api/v1/issuers/{id}/intermediates
Discriminator on body shape:
empty parent_ca_id + root_cert_pem + key_driver_id
→ CreateRoot (registers operator-supplied root CA).
parent_ca_id non-empty
→ CreateChild (signs new sub-CA cert under parent).
Service-layer error → HTTP code mapping:
ErrCANotSelfSigned → 400
ErrCAKeyMismatch → 400
ErrPathLenExceeded → 400
ErrNameConstraintExceeded → 400
ErrInvalidCertPEM → 400
ErrParentCANotActive → 409
ErrIntermediateCANotFound → 404
(other) → 500
GET /api/v1/issuers/{id}/intermediates
Returns flat list ordered by created_at; caller renders the
tree from each row's parent_ca_id (nil = root).
GET /api/v1/intermediates/{id}
Single-row detail.
POST /api/v1/intermediates/{id}/retire
Two-phase: confirm=false → active→retiring; confirm=true →
retiring→retired with active-children check (drain-first
semantics; ErrCAStillHasActiveChildren → 409).
Files changed:
internal/api/handler/intermediate_ca.go — 4 handlers
+ handler-defined
service interface
(dependency
inversion).
internal/api/handler/intermediate_ca_test.go — 8 test variants
(M-008 admin-
gate triplet
complete).
internal/api/handler/m008_admin_gate_test.go — register the
new admin-gated
handler in
AdminGatedHandlers
so the M-008
coherence
scanner stays
green.
internal/api/router/router.go — 4 r.Register
calls + new
IntermediateCAs
field on
HandlerRegistry.
cmd/server/main.go — wire the
postgres repo +
service +
handler. Reuses
the same
signer.FileDriver
instance the
OCSP responder
bootstrap path
feeds.
api/openapi.yaml — 4 new
operationIds,
full body
schema + status-
code dispatch.
Tests (8 in this commit):
TestIntermediateCA_Handler_NonAdmin_Returns403 (admin gate
— table-driven across all 4 endpoints)
TestIntermediateCA_Handler_AdminExplicitFalse_Returns403
(defensive: AdminKey present but false ≠ AdminKey absent)
TestIntermediateCA_Handler_AdminPermitted_ForwardsActor
(admin actor forwarded to service for audit attribution)
TestIntermediateCA_HandlerCreate_RootDispatch
(body discriminator: empty parent_ca_id → CreateRoot)
TestIntermediateCA_HandlerCreate_ChildDispatch
(body discriminator: parent_ca_id present → CreateChild)
TestIntermediateCA_HandlerCreate_BadRequestOnMissingRootBundle
(validation: no parent + no root bundle → 400)
TestIntermediateCA_HandlerCreate_ServiceErrorMappings
(table-driven: 7 service errors → expected HTTP codes)
TestIntermediateCA_HandlerRetire_TwoPhaseConfirm
(confirm=false then confirm=true forwarded correctly)
TestIntermediateCA_HandlerRetire_StillHasActiveChildren_Returns409
(drain-first contract — 409 not 500)
Verified locally:
gofmt: clean.
go vet ./...: exit 0.
go test -short -count=1 ./internal/api/handler/...: ok 4.498s.
bash scripts/ci-guards/openapi-handler-parity.sh: clean
(router routes: 182, openapi operations: 148; the +4 new routes
have +4 new operationIds — parity preserved).
bash scripts/ci-guards/* (all 24 guards): clean.
Out of scope of THIS commit (commit 5):
- web/src/pages/IssuerHierarchyPage.tsx (recursive tree render).
- docs/intermediate-ca-hierarchy.md sysadmin runbook (FedRAMP /
financial-services / internal-PKI patterns).
- docs/connectors.md hierarchy_mode row.
- WORKSPACE-ROADMAP entries (HSM-backed roots, automated
rotation, CRL chaining, NameConstraints templates, D3
dendrogram).
Reference: cowork/rank-8-intermediate-ca-hierarchy-prompt.md, commit 4.
This commit is contained in:
@@ -286,6 +286,24 @@ func main() {
|
||||
certificateService.SetProfileRepo(profileRepo)
|
||||
approvalHandler := handler.NewApprovalHandler(approvalService)
|
||||
|
||||
// Rank 8 of the 2026-05-03 deep-research deliverable — first-class
|
||||
// CA hierarchy management (intermediate_cas table + admin-gated
|
||||
// hierarchy endpoints). The service receives the issuerRepo so
|
||||
// future surface area (issuer-row hierarchy_mode validation) can
|
||||
// query the issuer config; for the commit-4 wiring it carries
|
||||
// only the fields used today. The signer.FileDriver shared with
|
||||
// the OCSP responder bootstrap path is reused here — operators
|
||||
// can plug in PKCS#11 / cloud-KMS drivers via the same Driver
|
||||
// interface without touching the service. See
|
||||
// docs/intermediate-ca-hierarchy.md.
|
||||
intermediateCARepo := postgres.NewIntermediateCARepository(db)
|
||||
intermediateCAMetrics := service.NewIntermediateCAMetrics()
|
||||
// Defer wiring the service + handler — signerDriver is constructed
|
||||
// further down in this function alongside the OCSP responder
|
||||
// bootstrap path. The service holds a reference to issuerRepo for
|
||||
// future hierarchy_mode validation surface area.
|
||||
_ = intermediateCAMetrics // service constructed below alongside signerDriver
|
||||
|
||||
notifierRegistry := make(map[string]service.Notifier)
|
||||
|
||||
// Wire notifier connectors from config
|
||||
@@ -390,6 +408,15 @@ func main() {
|
||||
RotationGrace: cfg.OCSPResponder.RotationGrace,
|
||||
Validity: cfg.OCSPResponder.Validity,
|
||||
})
|
||||
|
||||
// Rank 8 service + handler — wired here so signerDriver is in
|
||||
// scope. The same FileDriver instance feeds both the OCSP
|
||||
// responder bootstrap path and the intermediate-CA hierarchy.
|
||||
// Operators that swap to PKCS#11 / cloud-KMS drivers reuse the
|
||||
// single Driver instance across both surfaces.
|
||||
intermediateCAService := service.NewIntermediateCAService(
|
||||
intermediateCARepo, issuerRepo, signerDriver, auditService, intermediateCAMetrics)
|
||||
intermediateCAHandler := handler.NewIntermediateCAHandler(intermediateCAService)
|
||||
crlCacheService := service.NewCRLCacheService(crlCacheRepo, caOperationsSvc, issuerRegistry, logger)
|
||||
|
||||
// Production hardening II Phase 2: OCSP response cache. Mirrors the
|
||||
@@ -930,6 +957,10 @@ func main() {
|
||||
// the 2026-05-03 Infisical deep-research deliverable. See
|
||||
// docs/approval-workflow.md.
|
||||
Approvals: approvalHandler,
|
||||
// IntermediateCAs — first-class CA hierarchy management.
|
||||
// Rank 8 of the 2026-05-03 deep-research deliverable. See
|
||||
// docs/intermediate-ca-hierarchy.md.
|
||||
IntermediateCAs: intermediateCAHandler,
|
||||
})
|
||||
// Register EST (RFC 7030) handlers if enabled.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user