feat(wifi): add WiFi management and fix portal auth on set-password#105
Open
JanZachmann wants to merge 16 commits intoomnect:mainfrom
Open
feat(wifi): add WiFi management and fix portal auth on set-password#105JanZachmann wants to merge 16 commits intoomnect:mainfrom
JanZachmann wants to merge 16 commits intoomnect:mainfrom
Conversation
531374d to
1f082af
Compare
1f082af to
0fd7d6e
Compare
- **WiFi Management:** Implemented WiFi scanning, connection, and PSK calculation (WPA-PSK/WPA2-PSK) in the Crux Core and updated the UI to support wireless network configuration via wifi-commissioning-service.
- **Model & Synchronization:** Moved factoryResetResultAcked and updateValidationAcked from the persistent Healthcheck state to ephemeral sessionStorage. This ensures that these one-time notification flags do not persist across device reboots or accidental state synchronization cycles, preventing redundant modal popups.
- **Core Logic:** Implemented FetchInitialHealthcheck in the Crux Core to decouple the initial application hydration from subsequent polling cycles. This allows for a deterministic startup sequence where the UI can react to the device state immediately after WASM initialization.
- **Test Infrastructure:**
- Refactored setupAndLogin and NetworkTestHarness to utilize sessionStorage for modal suppression, replacing the fragile healthcheck injection pattern.
- Introduced setupWithLogin and mockNewIpProxy in the harness to reduce boilerplate and improve reliability of complex network redirection tests.
- Standardized healthcheck mocking across all E2E tests to ensure consistent behavior during version-mismatch and rollback scenarios.
- **Backend & Utilities:** Added parse_json_response_any_status to handle cases where the backend returns valid JSON alongside non-2xx status codes (e.g., 503 during version mismatch).
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
- **Session Restore:** Implemented session persistence across page refreshes using a server-side cookie. During WASM initialization, the UI now attempts to refresh the session via `GET /token/refresh`.
- **Backend Persistence:** Introduced `SessionKeyService` to persist the session signing key in `/data/session.key`, ensuring sessions remain valid across backend restarts.
- **Route Guards & Navigation:**
- Improved the `/set-password` guard to avoid redundant OIDC round-trips by checking authentication and password status.
- Added a catch-all route that redirects unknown paths to the root, which is then handled by existing authentication guards.
- **API Reliability:** Ensured the token refresh endpoint returns the correct `text/plain` content type to satisfy frontend security checks.
- **Test Infrastructure:** Expanded E2E coverage for session restore, route guards, and cookie handling, including tighter validation of session cookies in mocks.
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
0fd7d6e to
f326f29
Compare
Adds a Settings page that allows users to view and modify the timeout values for device operations (reboot, factory reset, firmware update, network rollback). Settings are persisted on the backend via a JSON file and loaded on startup. - Backend: new settings service with get/save endpoints (`/settings`) - Core: new `TimeoutSettings` type, `SaveSettings` event and handler; model carries `timeout_settings` loaded from backend on init - Update default firmware update timeout to 900s Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Drastically reduces the Docker image size (~71MB reduction, from 106MB to 34.9MB) by removing the external Centrifugo binary and its dependencies. - **Backend:** Integrated `actix-ws` into the Rust backend. Added a dual-port listener: HTTPS (1977) for user traffic and local HTTP (8000) for internal device status publishing from `omnect-device-service`. - **Core:** Replaced magic strings with a type-safe `WebSocketChannel` enum. Refactored all Centrifugo references to generic WebSocket terminology. - **Frontend:** Replaced the `centrifuge` npm package with a native WebSocket client (`useWebSocket.ts`). - **Reliability:** Implemented a `republish` sync mechanism to ensure the UI state is perfectly updated upon connection, compensating for the lack of Centrifugo's message history. - **Cleanup:** Removed obsolete scripts (`setup-centrifugo.sh`), configurations, and documentation references to Centrifugo. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
- Standardized all page and section headers to text-h4 format. - Improved metric alignment on Device page using a nested grid layout. - Compacted network adapter sidebar by reducing tab widths and padding. - Added navigation icons to sidebar routes and Documentation link. - Implemented reliable autofocus for password fields via template refs. - Restored semantic <h1> tags on password pages to fix failing E2E tests. - Fixed layout padding on Device Overview to prevent header truncation. - Updated UI dependencies (Vuetify 4, UnoCSS 66.6.5) and workspace crates. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
- Split dual-bind server into isolated HTTPS (UI) and HTTP (ODS publish) servers to prevent exposing UI routes on unencrypted HTTP - Add AuthMw to /ws, /network, /republish, /ack-rollback, /ack-factory-reset-result, /ack-update-validation, POST /api/settings - Clear WebSocket subscriptions on disconnect to prevent memory leaks - Remove dead history() method (no-op after Centrifugo removal) - Delete accidentally committed .bak file - Add backend integration tests for route auth enforcement - Add E2E tests for route guards and subscription lifecycle Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
550bda0 to
1eb4829
Compare
Implement WiFi commissioning integration: version-aware availability check (requires >= 0.1.0), fix VERSION_ENDPOINT to /api/v1/version, move WiFi DTOs to shared core types, add UI display of service version with compatibility hint, and extend e2e tests for WiFi availability states. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Prior to this, every unit test in src/app/src/update/ discarded the returned Command, verifying only model state. A handler could send requests to the wrong URL or with wrong headers without any test catching it. Using the new Command testing API introduced in crux_core 0.17.0-rc3 (expect_one_effect, expect_effect, expect_http, expect_web_socket, split), add 7 new tests that verify the produced effects: - auth: Login POSTs to /token/login with Basic auth header - auth: Logout POSTs to /logout with Bearer auth header - auth: CheckRequiresPasswordSet GETs /require-set-password - websocket: SubscribeToChannels emits WebSocketOperation::SubscribeAll - websocket: UnsubscribeFromChannels emits WebSocketOperation::UnsubscribeAll - reconnection: ReconnectionCheckTick GETs /healthcheck - wifi: CheckAvailability GETs /wifi/available Single-effect commands use the generated expect_http()/expect_web_socket() helpers directly (matching the upstream counter example pattern). Multi- effect commands (Command::all([render, http])) collect both effects and use find_map to locate the Http effect, since no single-variant helper applies when the effect type is unknown upfront. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…ust 2024 edition Move all Shell-owned setInterval polling loops into Core-owned self-rescheduling crux_time timers. The four pollers (WiFi scan 500ms, WiFi connect 1s, reconnection 5s, new IP 5s) are now scheduled by Core using TimeCmd::notify_after; each tick handler re-schedules the next tick or returns Command::done() when polling should stop. Page-refresh resilience is handled via #[serde(skip)] bool flags in Model that gate the first poll on WebSocket delivery of an active operation state. Shell changes: Remove four setInterval/clearInterval blocks and checkPendingNetworkChange from timers.ts; add time.ts Time-capability handler with VITE_RECONNECTION_POLL_INTERVAL_MS build-time cap so E2E test builds fire polls at 500ms instead of 5s; wire EffectVariantTime in effects.ts. Also adopts Rust 2024 edition for the Core crate (edition.workspace = true) which enables let-chains used throughout the update handlers. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Adopts std::sync::LazyLock instead of lazy_static, simplifies Future prelude usage, and documents future-facing edition improvements in rust-2024.md. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Add countdown tick event variants (ReconnectionCountdownTick, NewIpCountdownTick) and OverlaySpinnerState::decrement_countdown(), completing the move of all polling and countdown scheduling from the Vue Shell to Core via crux_time. Remove the now-redundant timers.ts shell module; its sole remaining responsibility (navigation on newIpReachable) is inlined into sync.ts where it fits naturally alongside other post-sync side-effects. Replace the per-operation timeout env vars in run-e2e-tests.sh with a unified VITE_RECONNECTION_TIMEOUT_MS cap for Core-driven one-shot timers. Add effect-level assertions to Core unit tests covering all TimeRequest NotifyAfter schedules: poll intervals, countdown ticks, and configurable operation timeouts across reconnection.rs, operations.rs, and verification.rs. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
… change When the device IP changes, the UI polls /healthcheck on the new IP from the old IP's origin — a cross-origin request. Two issues blocked this: 1. `credentials: 'include'` was hardcoded globally in http.ts. Browsers reject `Access-Control-Allow-Origin: *` when credentials mode is 'include', so the response was blocked despite returning HTTP 200. Changed to `credentials: 'same-origin'`: same-origin requests (all authenticated API calls) still send cookies; cross-origin ones do not, which is correct since the healthcheck is unauthenticated. 2. /healthcheck was missing the Access-Control-Allow-Origin: * header, so browsers blocked the cross-origin read entirely. Added the header to all response branches. Without this fix, the poll always fails → no redirect to the new IP → no login → POST /token/login never fires → cancel_rollback() is never called → ODS rolls back the network config after the timeout. Also improve two e2e tests: - "rollback cancellation" test: use waitForURL instead of manual harness state assertions to verify the redirect actually occurs - "DHCP -> Static (New IP)": exercise the full flow including redirect - "DHCP -> Static (Same IP)": assert overlay clears after healthcheck succeeds at the same host Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
When the Network page mounts, no adapter was pre-selected, leaving the content area empty until the user manually clicked a tab. This occurred on every page load since networkStatus arrives async via WebSocket, and was especially visible after an IP change caused a browser redirect and re-login. Adds a watch on networkStatus that selects the current connection adapter (matched by browser hostname) or falls back to the first adapter in the list, once, when the tab is still unset. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Update default socket paths for device service and wifi commissioning to match the actual paths used on the device. Remove the centrifugo config file that is no longer part of this repository. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Documents all connections, transport security, authentication flows, session/token properties, authorization model, credential storage, public vs. protected endpoints, and security considerations. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
wifi-commissioning-gattservice — scan for networks, connect, forget, and view current connection status from the Network pagerequiresPortalAuthrouter guard — prevents "portal authentication required" when submitting the set-password form after a factory reset (regular browser tab; incognito was unaffected)statefield to scan results responseReason
WiFi management: Devices with a
wifi-commissioning-gattservice need a UI surface to configure WiFi without physical access to the device.Portal auth bug: After a factory reset, the OIDC user persists in localStorage. The
requiresPortalAuthguard on/set-passwordsaw a valid (non-expired) OIDC user and passed — butCallback.vuewas bypassed, soportal_validatedwas never set on the fresh backend session. Theset_passwordendpoint returned 401. Fix: the guard now callstoken/validateto establish the backend session flag before allowing access. If validation fails (stale session), the stale OIDC user is removed and a fresh Keycloak flow is triggered.Verification
./scripts/run-e2e-tests.sh)src/ui/tests/wifi.spec.ts