Commit Graph

6 Commits

Author SHA1 Message Date
c41165c294 Remove client-side SHA-256 pre-hashing from setup and login
The sha256Hex helper used window.crypto.subtle.digest(), which is only
available in a secure context (HTTPS / localhost). In the HTTP Docker
environment crypto.subtle is undefined, causing a TypeError before any
request is sent — the setup and login forms both silently failed with
'An unexpected error occurred'.

Fix: pass raw passwords directly to the API. The backend already applies
bcrypt, which is sufficient. No stored hashes need migration because
setup never completed successfully in the HTTP environment.

* frontend/src/pages/SetupPage.tsx  — remove sha256Hex call
* frontend/src/api/auth.ts          — remove sha256Hex call
* frontend/src/pages/__tests__/SetupPage.test.tsx — drop crypto mock
* frontend/src/utils/crypto.ts      — deleted (no remaining callers)
2026-03-15 21:29:23 +01:00
21753c4f06 Fix Stage 0 bootstrap and startup regression
Task 0.1: Create database parent directory before connecting
- main.py _lifespan now calls Path(database_path).parent.mkdir(parents=True,
  exist_ok=True) before aiosqlite.connect() so the app starts cleanly on
  a fresh Docker volume with a nested database path.

Task 0.2: SetupRedirectMiddleware redirects when db is None
- Guard now reads: if db is None or not is_setup_complete(db)
  A missing database (startup still in progress) is treated as setup not
  complete instead of silently allowing all API routes through.

Task 0.3: SetupGuard redirects to /setup on API failure
- .catch() handler now sets status to 'pending' instead of 'done'.
  A crashed backend cannot serve protected routes; conservative fallback
  is to redirect to /setup.

Task 0.4: SetupPage shows spinner while checking setup status
- Added 'checking' boolean state; full-screen Spinner is rendered until
  getSetupStatus() resolves, preventing form flash before redirect.
- Added console.warn in catch block; cleanup return added to useEffect.

Also: remove unused type: ignore[call-arg] from config.py.

Tests: 18 backend tests pass; 117 frontend tests pass.
2026-03-15 18:05:53 +01:00
36e1e12a8a Fix console errors: React Router v7 flags, form a11y, bans 500
- Add v7_startTransition and v7_relativeSplatPath future flags to
  BrowserRouter to silence React Router deprecation warnings
- Add hidden autocomplete='username' inputs to LoginPage and SetupPage
  so password managers and browsers stop warning about missing username
  fields in password forms
- Mount fail2ban-dev-config volume into backend container at /config:ro
  so ban_service can open the fail2ban SQLite database returned by
  'get dbfile'; this fixes the 500 on GET /api/dashboard/bans
- Track compose.debug.yml in git (was previously untracked)
2026-03-01 20:15:23 +01:00
c097e55222 fix: setup routing, async bcrypt, password hashing, clean command
- Add SetupGuard component: redirects to /setup if setup not complete,
  shown as spinner while loading. All routes except /setup now wrapped.
- SetupPage redirects to /login on mount when setup already done.
- Fix async blocking: offload bcrypt.hashpw and bcrypt.checkpw to
  run_in_executor so they never stall the asyncio event loop.
- Hash password with SHA-256 (SubtleCrypto) before transmission; added
  src/utils/crypto.ts with sha256Hex(). Backend stores bcrypt(sha256).
- Add Makefile with make up/down/restart/logs/clean targets.
- Add tests: _check_password async, concurrent bcrypt, expired session,
  login-without-setup, run_setup event-loop interleaving.
- Update Architekture.md and Features.md to reflect all changes.
2026-03-01 19:16:49 +01:00
7f81f0614b Stage 7: configuration view — backend service, routers, tests, and frontend
- config_service.py: read/write jail config via asyncio.gather, global
  settings, in-process regex validation, log preview via _read_tail_lines
- server_service.py: read/write server settings, flush logs
- config router: 9 endpoints for jail/global config, regex-test,
  logpath management, log preview
- server router: GET/PUT settings, POST flush-logs
- models/config.py expanded with JailConfig, GlobalConfigUpdate,
  LogPreview* models
- 285 tests pass (68 new), ruff clean, mypy clean (44 files)
- Frontend: types/config.ts, api/config.ts, hooks/useConfig.ts,
  ConfigPage.tsx full implementation (Jails accordion editor,
  Global config, Server settings, Regex Tester with preview)
- Fixed pre-existing frontend lint: JSX.Element → React.JSX.Element
  (10 files), void/promise patterns in useServerStatus + useJails,
  no-misused-spread in client.ts, eslint.config.ts self-excluded
2026-03-01 14:37:55 +01:00
750785680b feat: Stage 2 — authentication and setup flow
Backend (tasks 2.1–2.6, 2.10):
- settings_repo: get/set/delete/get_all CRUD for the key-value settings table
- session_repo: create/get/delete/delete_expired for session rows
- setup_service: bcrypt password hashing, one-time-only enforcement,
  run_setup() / is_setup_complete() / get_password_hash()
- auth_service: login() with bcrypt verify + token creation,
  validate_session() with expiry check, logout()
- setup router: GET /api/setup (status), POST /api/setup (201 / 409)
- auth router: POST /api/auth/login (token + HttpOnly cookie),
               POST /api/auth/logout (clears cookie, idempotent)
- SetupRedirectMiddleware: 307 → /api/setup for all API paths until setup done
- require_auth dependency: cookie or Bearer token → Session or 401
- conftest.py: manually bootstraps app.state.db for router tests
  (ASGITransport does not trigger ASGI lifespan)
- 85 tests pass; ruff 0 errors; mypy --strict 0 errors

Frontend (tasks 2.7–2.9):
- types/auth.ts, types/setup.ts, api/auth.ts, api/setup.ts
- AuthProvider: sessionStorage-backed context (isAuthenticated, login, logout)
- RequireAuth: guard component → /login?next=<path> when unauthenticated
- SetupPage: Fluent UI form, client-side validation, inline errors
- LoginPage: single password input, ?next= redirect after success
- DashboardPage: placeholder (full impl Stage 5)
- App.tsx: full route tree (/setup, /login, /, *)
2026-02-28 21:33:30 +01:00