feat: M15a — certificate revocation API, CRL endpoint, and revocation notifications

Implements core revocation infrastructure: POST /api/v1/certificates/{id}/revoke
with all 8 RFC 5280 reason codes, JSON-formatted CRL at GET /api/v1/crl, webhook
and email revocation notifications, best-effort issuer notification, and immutable
revocation audit trail. Includes 48 new tests across service, handler, integration,
and domain layers (600+ total). Fixes 3 pre-existing test bugs (team_test error
matching, agent_group delete status code, team handler per_page validation).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-03-22 10:59:18 -04:00
parent d881403d11
commit 5d98e373e3
27 changed files with 1710 additions and 37 deletions
+33
View File
@@ -0,0 +1,33 @@
-- Migration 000005: Revocation Infrastructure
-- Adds revocation tracking to managed_certificates and a dedicated revocations table for CRL generation.
-- Add revocation columns to managed_certificates
ALTER TABLE managed_certificates ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ;
ALTER TABLE managed_certificates ADD COLUMN IF NOT EXISTS revocation_reason VARCHAR(50);
-- Certificate revocations table for CRL generation
-- Each row represents a revoked certificate version (by serial number).
-- This is the authoritative source for CRL content.
CREATE TABLE IF NOT EXISTS certificate_revocations (
id TEXT PRIMARY KEY,
certificate_id TEXT NOT NULL REFERENCES managed_certificates(id),
serial_number TEXT NOT NULL,
reason VARCHAR(50) NOT NULL DEFAULT 'unspecified',
revoked_by TEXT NOT NULL, -- actor who initiated revocation
revoked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
issuer_id TEXT REFERENCES issuers(id),
issuer_notified BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Index for CRL generation (all revoked certs, ordered by revocation time)
CREATE INDEX IF NOT EXISTS idx_certificate_revocations_revoked_at ON certificate_revocations(revoked_at);
-- Index for looking up revocations by certificate
CREATE INDEX IF NOT EXISTS idx_certificate_revocations_cert_id ON certificate_revocations(certificate_id);
-- Index for looking up revocations by serial (OCSP lookup, future M15b)
CREATE UNIQUE INDEX IF NOT EXISTS idx_certificate_revocations_serial ON certificate_revocations(serial_number);
-- Add revocation notification type
-- (NotificationType is enforced in Go code, not DB constraints, so no ALTER needed)