Private
Public Access
1
0

feat(M3): Host Management, Groups, Users, CIDR Discovery

- pm-core::models: Host, HostSummary, Group, User, DiscoveryResult
  types + request payloads for all CRUD operations
- pm-core::audit: Tamper-evident hash-chained audit log writer
  (SHA-256 chain, non-fatal, covers all M3 events)
- pm-web/routes/hosts: Full host CRUD with RBAC scoping;
  FQDN DNS resolution on registration; host↔group membership;
  operator group-scoped access enforcement; audit on register/remove
- pm-web/routes/groups: Full group CRUD; host↔group and user↔group
  membership management; admin-only create/delete/update
- pm-web/routes/users: Full user CRUD (admin); current user profile;
  password hashing (Argon2id); role management; session revocation
- pm-web/routes/discovery: CIDR scan with bounded concurrency
  (128 workers), TCP probe with 2s timeout, reverse DNS lookup,
  scan results table, register-from-discovery flow with audit log
- Frontend: HostsPage (filterable table with health chips),
  HostDetailPage, GroupsPage (create/delete dialog),
  UsersPage (create/revoke sessions)
- App.tsx updated with all M3 routes wired to real pages
- cargo check --workspace: zero errors

Closes M3.
This commit is contained in:
2026-04-23 16:25:08 +00:00
parent 6811f84a7c
commit a6eb762962
17 changed files with 1887 additions and 51 deletions

View File

@ -4,8 +4,12 @@ import { lightTheme } from './theme/theme'
import { useAuthStore } from './store/authStore'
import LoginPage from './pages/LoginPage'
import MfaSetupPage from './pages/MfaSetupPage'
import HostsPage from './pages/HostsPage'
import HostDetailPage from './pages/HostDetailPage'
import GroupsPage from './pages/GroupsPage'
import UsersPage from './pages/UsersPage'
// Placeholder pages — implemented in M3+
// Placeholder pages — implemented in later milestones
const PlaceholderPage = ({ title }: { title: string }) => (
<div style={{ padding: 32 }}>
<h2>{title}</h2>
@ -13,7 +17,6 @@ const PlaceholderPage = ({ title }: { title: string }) => (
</div>
)
// Guard component: redirects to /login if not authenticated
function RequireAuth({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />
@ -24,25 +27,28 @@ function App() {
<ThemeProvider theme={lightTheme}>
<CssBaseline />
<Routes>
{/* Public routes */}
{/* Public */}
<Route path="/login" element={<LoginPage />} />
{/* Protected routes */}
{/* Protected — M2 */}
<Route path="/" element={<RequireAuth><Navigate to="/dashboard" replace /></RequireAuth>} />
<Route path="/mfa/setup" element={<RequireAuth><MfaSetupPage /></RequireAuth>} />
{/* Protected — M3 */}
<Route path="/dashboard" element={<RequireAuth><PlaceholderPage title="Dashboard" /></RequireAuth>} />
<Route path="/hosts" element={<RequireAuth><PlaceholderPage title="Hosts" /></RequireAuth>} />
<Route path="/hosts/:id" element={<RequireAuth><PlaceholderPage title="Host Detail" /></RequireAuth>} />
<Route path="/hosts" element={<RequireAuth><HostsPage /></RequireAuth>} />
<Route path="/hosts/:id" element={<RequireAuth><HostDetailPage /></RequireAuth>} />
<Route path="/groups" element={<RequireAuth><GroupsPage /></RequireAuth>} />
<Route path="/users" element={<RequireAuth><UsersPage /></RequireAuth>} />
{/* Protected — later milestones */}
<Route path="/jobs" element={<RequireAuth><PlaceholderPage title="Jobs" /></RequireAuth>} />
<Route path="/deployment" element={<RequireAuth><PlaceholderPage title="Patch Deployment" /></RequireAuth>} />
<Route path="/maintenance" element={<RequireAuth><PlaceholderPage title="Maintenance Windows" /></RequireAuth>} />
<Route path="/groups" element={<RequireAuth><PlaceholderPage title="Groups" /></RequireAuth>} />
<Route path="/reports" element={<RequireAuth><PlaceholderPage title="Reports" /></RequireAuth>} />
<Route path="/users" element={<RequireAuth><PlaceholderPage title="Users" /></RequireAuth>} />
<Route path="/certificates" element={<RequireAuth><PlaceholderPage title="Certificates" /></RequireAuth>} />
<Route path="/settings" element={<RequireAuth><PlaceholderPage title="Settings" /></RequireAuth>} />
<Route path="/mfa/setup" element={<RequireAuth><MfaSetupPage /></RequireAuth>} />
{/* 404 */}
<Route path="*" element={<PlaceholderPage title="404 Not Found" />} />
</Routes>
</ThemeProvider>