feat(M2): Authentication, Authorization & Frontend Shell
- pm-auth::password: Argon2id (m=65536,t=3,p=1) hashing + verification - pm-auth::jwt: EdDSA/Ed25519 JWT issuance + validation (15-min TTL) - pm-auth::refresh: Opaque 256-bit refresh tokens, SHA-256 hashed, 1-hour sliding inactivity timeout, rotation on use, revocable - pm-auth::mfa_totp: TOTP setup/verify (HMAC-SHA1, 6-digit, 30s) with otpauth:// URI generation (Google Authenticator compatible) - pm-auth::mfa_webauthn: Stub (full implementation deferred) - pm-auth::rbac: Axum middleware for JWT auth + IP whitelist + admin/operator role enforcement + FromRequestParts extractor - pm-auth::session: Full login flow (password → MFA → tokens), token refresh, logout, force-logout - pm-web auth routes: POST /api/v1/auth/login|refresh|logout, GET /api/v1/auth/mfa/setup, POST /api/v1/auth/mfa/verify - IP whitelist middleware on all protected connection points - migrations/002_seed_admin.sql: Default admin account seed - Frontend: Auth store (Zustand with persistence), login page with MFA prompt, MFA setup page (stepper), JWT auto-refresh interceptor, route guards (RequireAuth), updated App.tsx routing - cargo check --workspace: zero errors, 1 minor warning Closes M2.
This commit is contained in:
@ -1,8 +1,11 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { CssBaseline, ThemeProvider } from '@mui/material'
|
||||
import { lightTheme } from './theme/theme'
|
||||
import { useAuthStore } from './store/authStore'
|
||||
import LoginPage from './pages/LoginPage'
|
||||
import MfaSetupPage from './pages/MfaSetupPage'
|
||||
|
||||
// Placeholder pages — implemented in M2+
|
||||
// Placeholder pages — implemented in M3+
|
||||
const PlaceholderPage = ({ title }: { title: string }) => (
|
||||
<div style={{ padding: 32 }}>
|
||||
<h2>{title}</h2>
|
||||
@ -10,24 +13,36 @@ 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 />
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||||
<Route path="/dashboard" element={<PlaceholderPage title="Dashboard" />} />
|
||||
<Route path="/hosts" element={<PlaceholderPage title="Hosts" />} />
|
||||
<Route path="/hosts/:id" element={<PlaceholderPage title="Host Detail" />} />
|
||||
<Route path="/jobs" element={<PlaceholderPage title="Jobs" />} />
|
||||
<Route path="/deployment" element={<PlaceholderPage title="Patch Deployment" />} />
|
||||
<Route path="/maintenance" element={<PlaceholderPage title="Maintenance Windows" />} />
|
||||
<Route path="/groups" element={<PlaceholderPage title="Groups" />} />
|
||||
<Route path="/reports" element={<PlaceholderPage title="Reports" />} />
|
||||
<Route path="/users" element={<PlaceholderPage title="Users" />} />
|
||||
<Route path="/certificates" element={<PlaceholderPage title="Certificates" />} />
|
||||
<Route path="/settings" element={<PlaceholderPage title="Settings" />} />
|
||||
<Route path="/login" element={<PlaceholderPage title="Login" />} />
|
||||
{/* Public routes */}
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
|
||||
{/* Protected routes */}
|
||||
<Route path="/" element={<RequireAuth><Navigate to="/dashboard" replace /></RequireAuth>} />
|
||||
<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="/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>
|
||||
|
||||
Reference in New Issue
Block a user