mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-08 23:08:53 +00:00
feat: M15b — OCSP responder, DER CRL, short-lived exemption, revocation GUI
Backend:
- Embedded OCSP responder: GET /api/v1/ocsp/{issuer_id}/{serial} returns
signed OCSP responses (good/revoked/unknown) using CA key
- DER-encoded X.509 CRL: GET /api/v1/crl/{issuer_id} returns proper DER CRL
signed by issuing CA with 24h validity window
- Short-lived cert exemption: certs with profile TTL < 1 hour skip CRL/OCSP
(expiry is sufficient revocation for ephemeral workloads)
- Extended issuer connector interface with GenerateCRL and SignOCSPResponse
- Local CA implements full CRL/OCSP signing; ACME and step-ca return
appropriate "use native endpoint" errors
- IssuerConnectorAdapter bridges new methods between layers
Frontend:
- Revoke button on certificate detail page with RFC 5280 reason modal
- Revocation banner with reason display and timestamp
- Revocation status indicators in lifecycle section
- "Revoked" filter option in certificates list
- API client: revokeCertificate() function and Certificate type extensions
Tests: ~31 new tests across connector, service, handler, and adapter layers
Docs: milestones renumbered (M13-M14, M16-M18), M15b marked complete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,12 @@ export const triggerDeployment = (id: string, targetId: string) =>
|
||||
body: JSON.stringify({ target_id: targetId }),
|
||||
});
|
||||
|
||||
export const revokeCertificate = (id: string, reason: string) =>
|
||||
fetchJSON<{ status: string }>(`${BASE}/certificates/${id}/revoke`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ reason }),
|
||||
});
|
||||
|
||||
// Agents
|
||||
export const getAgents = (params: Record<string, string> = {}) => {
|
||||
const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString();
|
||||
|
||||
@@ -9,17 +9,31 @@ export interface Certificate {
|
||||
owner_id: string;
|
||||
team_id: string;
|
||||
renewal_policy_id: string;
|
||||
certificate_profile_id: string;
|
||||
serial_number: string;
|
||||
fingerprint: string;
|
||||
key_algorithm: string;
|
||||
key_size: number;
|
||||
issued_at: string;
|
||||
expires_at: string;
|
||||
revoked_at?: string;
|
||||
revocation_reason?: string;
|
||||
tags: Record<string, string>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export const REVOCATION_REASONS = [
|
||||
{ value: 'unspecified', label: 'Unspecified' },
|
||||
{ value: 'keyCompromise', label: 'Key Compromise' },
|
||||
{ value: 'caCompromise', label: 'CA Compromise' },
|
||||
{ value: 'affiliationChanged', label: 'Affiliation Changed' },
|
||||
{ value: 'superseded', label: 'Superseded' },
|
||||
{ value: 'cessationOfOperation', label: 'Cessation of Operation' },
|
||||
{ value: 'certificateHold', label: 'Certificate Hold' },
|
||||
{ value: 'privilegeWithdrawn', label: 'Privilege Withdrawn' },
|
||||
] as const;
|
||||
|
||||
export interface CertificateVersion {
|
||||
id: string;
|
||||
certificate_id: string;
|
||||
|
||||
Reference in New Issue
Block a user