feat: add bounded admin WebSocket backend (Phase 8D.5)

- Require current admin cookie sessions, Origin checks and single-use
  tickets
- Reuse the shared console with session-aware authorization and slot
  allocation
- Add HTTPD-owned I/O, bounded buffering and revocation cleanup
- Prevent LRU eviction of serial clients and stale admin socket closure
- Reject unsupported web-shell mutations before side effects
- Add host regressions, a smoke client and resource accounting

Validated by user sign-off after a 15-minute full-client soak at 230400
baud, with a few broker drops under heavy output. Browser UI remains
for Phase 8D.6; numeric memory reserves remain open.
This commit is contained in:
2026-09-06 14:41:41 +02:00
parent e5dce12ed4
commit aeb2043396
37 changed files with 3651 additions and 91 deletions
+15 -1
View File
@@ -5,6 +5,9 @@
#include "web_cookie_auth.h"
#include "web_httpd_adapter.h"
#include "esp_httpd_priv.h"
#ifdef HOST_ADMIN
#include "web_admin_transport.h"
#endif
static struct httpd_data server = {.config.max_resp_headers = 8};
static struct sock_db socket_state;
@@ -49,7 +52,11 @@ int httpd_req_recv(httpd_req_t *r, char *out, size_t size) {
}
esp_err_t web_login_ui_send_response(httpd_req_t *r) { return httpd_resp_sendstr(r, "login document"); }
esp_err_t web_serial_transport_revoke_web_session(web_session_id_t id) {
web_session_store_invalidate(id); return ESP_OK;
web_session_store_invalidate(id);
#ifdef HOST_ADMIN
web_admin_transport_revoke(id, NULL, 0);
#endif
return ESP_OK;
}
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *r, const char *protocol) {
(void)r; (void)protocol; ++upgrades; return ESP_OK;
@@ -109,6 +116,10 @@ static void auth_reset(void) {
password_calls = 0; password_hook = NULL;
}
#ifdef HOST_ADMIN
#include "admin_test.c"
#endif
int main(void) {
assert(store_tests() == 0); auth_reset();
char token[65], csrf[65], session[65], cookies[200];
@@ -253,5 +264,8 @@ int main(void) {
server.config.max_resp_headers = 6; expect("200 OK"); assert(cookie_count == 2);
server.config.max_resp_headers = 8;
puts("PASS: exact six-header successful login budget; all smaller header capacities invalidate unpublished login");
#ifdef HOST_ADMIN
admin_tests();
#endif
return 0;
}