mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-08 04:28:53 +00:00
9a50d9a2dc
(Profiles + Recent Activity + Trust Bundle tabs) + CLI subcommand
family `certctl-cli est {cacerts,csrattrs,enroll,reenroll,
serverkeygen,test}` + 6 MCP tools.
Phase 8 — ESTAdminPage tabbed GUI:
- web/src/pages/ESTAdminPage.tsx mirrors SCEPAdminPage's three-tab
surface. Profiles tab renders per-profile cards with auth-mode
badges (mTLS / Basic / ServerKeygen), mTLS trust-anchor expiry
countdown (good ≥30d / warn 7-30d / bad <7d / EXPIRED), 12-cell
counter grid (success_simpleenroll/.../internal_error), and the
admin-gated "Reload trust anchor" action. Recent Activity tab
merges the four EST audit actions (est_simple_enroll +
est_simple_reenroll + est_server_keygen + est_auth_failed) across
four parallel useQuery calls with chip filters for All/Enrollment/
Re-enrollment/ServerKeygen/AuthFailure. Trust Bundle tab renders
per-mTLS-profile cert subjects + expiries.
- M-009 useTrackedMutation guard: every mutation routes through
the tracked hook so audit/progress hooks fire.
- Page-level admin gate renders "Admin access required" banner for
non-admin callers + skips underlying API requests so the server
never sees a 403-prone request. Server-side enforcement is the
M-008 admin gate; this is a UX hint.
- Wired into web/src/main.tsx at /est; nav link added to Layout.tsx.
- New web/src/api/types.ts types ESTStatsSnapshot +
ESTTrustAnchorInfo + ESTProfilesResponse + ESTReloadTrustResponse
mirror service.ESTStatsSnapshot 1:1.
- New web/src/api/client.ts helpers getAdminESTProfiles +
reloadAdminESTTrust.
- 14 Vitest cases (admin gate non-admin / non-auth-required deploy /
default tab / tab switch / deep-link tab / per-profile card render
+ counter cells / reload-button mTLS-only / trust-expiry badge
band / reload modal Confirm-Cancel-Error paths / Trust Bundle
empty-state / Activity filter chip toggle).
Phase 9.1 — CLI subcommands:
- internal/cli/est.go adds 6 subcommands: cacerts / csrattrs /
enroll / reenroll / serverkeygen / test. CSR input via --csr
with file-path or '-' for stdin; multipart serverkeygen response
is parsed by stdlib mime/multipart and split into <prefix>.cert.pem
+ <prefix>.key.enveloped so the operator can decrypt the key with
openssl smime. EST `test` smoke-tests cacerts + csrattrs + emits
one-line OK/FAIL diagnostics.
- cmd/cli/main.go grows the `est` dispatch + Usage entries.
Phase 9.2 — MCP tools:
- internal/mcp/tools_est.go adds 6 tools mapped to the EST endpoints
+ admin observability: est_list_profiles + est_admin_stats (alias)
+ est_get_cacerts + est_get_csrattrs + est_enroll + est_reenroll.
Tool count grew from 87 → 93 (verified via the registered-vs-
covered guard in tools_per_tool_test.go); the per-tool happy/error-
path table grew with 6 matching entries so the future-tool-no-test
CI guard stays green.
- internal/mcp/client.go grows PostRaw — non-JSON POST helper that
the EST enroll/reenroll tools use to ship raw application/pkcs10
CSR bytes through the MCP fence-wrapped response.
- estRawResultJSON wraps the raw response body in a JSON envelope
the MCP consumer can structurally consume (content_type +
body_base64 + body_size_bytes). Mirrors the CRL/OCSP MCP tools'
binary-DER envelope.
Phase 9.3 — Tests:
- internal/cli/est_test.go: 8 cases pinning the wire-shape contract
on the CLI side without dragging the full ESTHandler into the
test build.
- internal/mcp/tools_est_test.go: path-builder + JSON-envelope unit
tests + end-to-end tool exercise that pins all 5 captured request
paths through a fake API.
Pre-commit verification (sandbox): gofmt clean, go vet clean
(excluding repository/postgres which the sandbox can't build —
pre-existing testcontainers limit), staticcheck clean across
cli/mcp/cmd/cli, go test -short -count=1 green for every non-
postgres Go package, Vitest green for ESTAdminPage (14) +
SCEPAdminPage (20) — 34 page tests total. G-3 docs-drift guard
reproduced locally clean (Phases 8-9 added zero new env vars).
Spec preserved at cowork/est-rfc7030-hardening-prompt.md. Phases
10-13 (libest sidecar e2e / bulk revocation + audit codes /
docs/est.md / release prep + tag) remain — post-2.1.0 work.
111 lines
5.7 KiB
TypeScript
111 lines
5.7 KiB
TypeScript
import { StrictMode } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import ErrorBoundary from './components/ErrorBoundary';
|
|
import AuthProvider from './components/AuthProvider';
|
|
import AuthGate from './components/AuthGate';
|
|
import Layout from './components/Layout';
|
|
import DashboardPage from './pages/DashboardPage';
|
|
import CertificatesPage from './pages/CertificatesPage';
|
|
import CertificateDetailPage from './pages/CertificateDetailPage';
|
|
import AgentsPage from './pages/AgentsPage';
|
|
import AgentDetailPage from './pages/AgentDetailPage';
|
|
import JobsPage from './pages/JobsPage';
|
|
import NotificationsPage from './pages/NotificationsPage';
|
|
import PoliciesPage from './pages/PoliciesPage';
|
|
import RenewalPoliciesPage from './pages/RenewalPoliciesPage';
|
|
import IssuersPage from './pages/IssuersPage';
|
|
import TargetsPage from './pages/TargetsPage';
|
|
import ProfilesPage from './pages/ProfilesPage';
|
|
import OwnersPage from './pages/OwnersPage';
|
|
import TeamsPage from './pages/TeamsPage';
|
|
import AgentGroupsPage from './pages/AgentGroupsPage';
|
|
import AuditPage from './pages/AuditPage';
|
|
import ShortLivedPage from './pages/ShortLivedPage';
|
|
import AgentFleetPage from './pages/AgentFleetPage';
|
|
import DiscoveryPage from './pages/DiscoveryPage';
|
|
import NetworkScanPage from './pages/NetworkScanPage';
|
|
import HealthMonitorPage from './pages/HealthMonitorPage';
|
|
import DigestPage from './pages/DigestPage';
|
|
import ObservabilityPage from './pages/ObservabilityPage';
|
|
import JobDetailPage from './pages/JobDetailPage';
|
|
import IssuerDetailPage from './pages/IssuerDetailPage';
|
|
import TargetDetailPage from './pages/TargetDetailPage';
|
|
import SCEPAdminPage from './pages/SCEPAdminPage';
|
|
import ESTAdminPage from './pages/ESTAdminPage';
|
|
import './index.css';
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
staleTime: 10_000,
|
|
retry: 1,
|
|
refetchOnWindowFocus: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
createRoot(document.getElementById('root')!).render(
|
|
<StrictMode>
|
|
<ErrorBoundary>
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthProvider>
|
|
<AuthGate>
|
|
<BrowserRouter>
|
|
<Routes>
|
|
<Route element={<Layout />}>
|
|
<Route index element={<DashboardPage />} />
|
|
<Route path="certificates" element={<CertificatesPage />} />
|
|
<Route path="certificates/:id" element={<CertificateDetailPage />} />
|
|
<Route path="agents" element={<AgentsPage />} />
|
|
<Route path="agents/:id" element={<AgentDetailPage />} />
|
|
<Route path="fleet" element={<AgentFleetPage />} />
|
|
<Route path="jobs" element={<JobsPage />} />
|
|
<Route path="jobs/:id" element={<JobDetailPage />} />
|
|
<Route path="notifications" element={<NotificationsPage />} />
|
|
<Route path="policies" element={<PoliciesPage />} />
|
|
<Route path="renewal-policies" element={<RenewalPoliciesPage />} />
|
|
<Route path="profiles" element={<ProfilesPage />} />
|
|
<Route path="issuers" element={<IssuersPage />} />
|
|
<Route path="issuers/:id" element={<IssuerDetailPage />} />
|
|
<Route path="targets" element={<TargetsPage />} />
|
|
<Route path="targets/:id" element={<TargetDetailPage />} />
|
|
<Route path="owners" element={<OwnersPage />} />
|
|
<Route path="teams" element={<TeamsPage />} />
|
|
<Route path="agent-groups" element={<AgentGroupsPage />} />
|
|
<Route path="audit" element={<AuditPage />} />
|
|
<Route path="short-lived" element={<ShortLivedPage />} />
|
|
<Route path="discovery" element={<DiscoveryPage />} />
|
|
<Route path="network-scans" element={<NetworkScanPage />} />
|
|
<Route path="health-monitor" element={<HealthMonitorPage />} />
|
|
<Route path="digest" element={<DigestPage />} />
|
|
<Route path="observability" element={<ObservabilityPage />} />
|
|
{/* SCEP RFC 8894 + Intune master bundle Phase 9.4 (initial)
|
|
+ Phase 9 follow-up (rebrand): per-profile SCEP
|
|
Administration page with Profiles / Intune Monitoring /
|
|
Recent Activity tabs. Route is unconditional; the page
|
|
itself renders an "Admin access required" banner for
|
|
non-admin callers and skips the underlying API calls so
|
|
the server never sees a 403-prone request. */}
|
|
<Route path="scep" element={<SCEPAdminPage />} />
|
|
{/* Backward-compat alias for external bookmarks the Phase 9
|
|
release advertised. Lands on the Intune Monitoring tab. */}
|
|
<Route path="scep/intune" element={<SCEPAdminPage />} />
|
|
{/* EST RFC 7030 hardening master bundle Phase 8: per-profile
|
|
EST Administration page with Profiles / Recent Activity /
|
|
Trust Bundle tabs. Same admin-gate pattern as SCEP — the
|
|
route is unconditional; the page renders an "Admin access
|
|
required" banner for non-admin callers and skips the
|
|
underlying API calls so the server never sees a 403. */}
|
|
<Route path="est" element={<ESTAdminPage />} />
|
|
</Route>
|
|
</Routes>
|
|
</BrowserRouter>
|
|
</AuthGate>
|
|
</AuthProvider>
|
|
</QueryClientProvider>
|
|
</ErrorBoundary>
|
|
</StrictMode>
|
|
);
|