mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:21:30 +00:00
gui/cert-detail: revocation endpoints panel (CRL/OCSP) — Phase 5
CertificateDetailPage now surfaces a Revocation Endpoints card showing
the standards-compliant /.well-known/pki/crl/{issuer_id} CRL distribution
point (RFC 5280 §4.2.1.13) and /.well-known/pki/ocsp/{issuer_id} OCSP
responder URL (RFC 6960 §A.1) for relying parties that don't already know
certctl's well-known scheme.
Two action buttons exercise the same network path the issued leaves'
AIA/CDP extensions advertise, so an operator can confirm 'did the
backend Phases 1-4 actually wire end-to-end?' without curl:
* 'Test CRL fetch' — fetchCRL(issuer_id) helper, surfaces byte count
* 'Check OCSP status' — getOCSPStatus(issuer_id, serial_hex) helper
Admin-only cache-age badge: when useAuth().admin is true the panel pulls
GET /api/v1/admin/crl/cache (M-008 admin-gated handler) and shows
'Cache fresh · 2m ago' / 'Cache stale' / 'Not yet generated' next to
the heading. Non-admin callers don't trigger the fetch (gated client-side
on enabled flag, server-side on middleware.IsAdmin) so the badge cannot
leak generation cadence.
Test coverage in CertificateDetailPage.test.tsx pins:
1. CRL + OCSP URLs render with issuer_id substituted
2. Test CRL fetch button calls fetchCRL with the issuer_id and renders
the byte-count success message
3. Check OCSP status button calls getOCSPStatus with (issuer_id, serial)
and renders the DER byte-count
4. Admin badge stays HIDDEN (and getAdminCRLCache is NEVER called) when
useAuth().admin is false — pins the no-info-leak invariant
P-1 closure docblock + CI guardrail (.github/workflows/ci.yml) updated
to remove getOCSPStatus from the documented-orphan list since it now
has a real consumer.
types.ts: CRLCacheRow / CRLCacheEvent / CRLCacheResponse mirrors of the
backend admin handler payload (admin_crl_cache.go).
client.ts: fetchCRL + getAdminCRLCache helpers; getOCSPStatus already
existed and is now an active consumer.
Tests: 6/6 in CertificateDetailPage.test.tsx, 150/150 across api+page
suite. tsc --noEmit clean.
This commit is contained in:
+30
-2
@@ -1,4 +1,4 @@
|
||||
import type { Certificate, CertificateVersion, Agent, Job, Notification, AuditEvent, PolicyRule, PolicyViolation, RenewalPolicy, Issuer, Target, CertificateProfile, Owner, Team, AgentGroup, PaginatedResponse, DashboardSummary, CertificateStatusCount, ExpirationBucket, JobTrendDataPoint, IssuanceRateDataPoint, MetricsResponse, DiscoveredCertificate, DiscoveryScan, DiscoverySummary, NetworkScanTarget, EndpointHealthCheck, HealthHistoryEntry, HealthCheckSummary, AgentDependencyCounts, RetireAgentResponse, BlockedByDependenciesResponse } from './types';
|
||||
import type { Certificate, CertificateVersion, Agent, Job, Notification, AuditEvent, PolicyRule, PolicyViolation, RenewalPolicy, Issuer, Target, CertificateProfile, Owner, Team, AgentGroup, PaginatedResponse, DashboardSummary, CertificateStatusCount, ExpirationBucket, JobTrendDataPoint, IssuanceRateDataPoint, MetricsResponse, DiscoveredCertificate, DiscoveryScan, DiscoverySummary, NetworkScanTarget, EndpointHealthCheck, HealthHistoryEntry, HealthCheckSummary, AgentDependencyCounts, RetireAgentResponse, BlockedByDependenciesResponse, CRLCacheResponse } from './types';
|
||||
|
||||
const BASE = '/api/v1';
|
||||
|
||||
@@ -16,11 +16,16 @@ const BASE = '/api/v1';
|
||||
// getAgentGroup, getAgentGroupMembers, getAuditEvent,
|
||||
// getCertificateDeployments, getDiscoveredCertificate,
|
||||
// getHealthCheck, getHealthCheckHistory, getNetworkScanTarget,
|
||||
// getNotification, getOCSPStatus, getOwner, getPolicy,
|
||||
// getNotification, getOwner, getPolicy,
|
||||
// getPolicyViolations, getRenewalPolicy, getTeam, registerAgent
|
||||
// (by-design pull-only; see C-1 closure docblock above its export),
|
||||
// updateHealthCheck.
|
||||
//
|
||||
// CRL/OCSP-Responder Phase 5 closed the getOCSPStatus orphan: the
|
||||
// CertificateDetailPage Revocation Endpoints panel now exercises it
|
||||
// via the "Check OCSP status" button, so it's removed from the list
|
||||
// above (and from the CI guardrail's DOCUMENTED list).
|
||||
//
|
||||
// CI guardrail at .github/workflows/ci.yml::"Documented orphan
|
||||
// client fns sync guard (P-1)" enforces the docblock list ↔
|
||||
// export list relationship: every name above must still be
|
||||
@@ -268,6 +273,29 @@ export const getOCSPStatus = (issuerId: string, serial: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
// CRL/OCSP-Responder Phase 5: GUI-side helper for the "Test CRL fetch" button
|
||||
// on CertificateDetailPage. Fetches the DER-encoded CRL from the well-known
|
||||
// endpoint and returns the byte length so the panel can show "OK — N bytes".
|
||||
// The Authorization header is intentionally omitted: /.well-known/pki/crl/ is
|
||||
// the standards-compliant relying-party surface and runs unauthenticated.
|
||||
export const fetchCRL = (issuerId: string) => {
|
||||
return fetch(`/.well-known/pki/crl/${issuerId}`)
|
||||
.then(async r => {
|
||||
if (!r.ok) throw new Error(`CRL fetch failed: ${r.status}`);
|
||||
const buf = await r.arrayBuffer();
|
||||
return { byteLength: buf.byteLength, contentType: r.headers.get('content-type') ?? '' };
|
||||
});
|
||||
};
|
||||
|
||||
// CRL/OCSP-Responder Phase 5 admin endpoint mirror.
|
||||
//
|
||||
// Backend handler: internal/api/handler/admin_crl_cache.go::ListCache.
|
||||
// M-008 admin-gated; non-admin Bearer callers get HTTP 403 — the GUI hides
|
||||
// the badge entirely (rather than letting it 403 noisily) by gating the
|
||||
// React-Query enabled flag on useAuth().admin at the call site.
|
||||
export const getAdminCRLCache = () =>
|
||||
fetchJSON<CRLCacheResponse>(`${BASE}/admin/crl/cache`);
|
||||
|
||||
// Agents
|
||||
export const getAgents = (params: Record<string, string> = {}) => {
|
||||
const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString();
|
||||
|
||||
Reference in New Issue
Block a user