Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / cmake / Description
Author[]Andy Green <andy@warmcat.com> 2026-07-10 15:48 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-07-10 15:59 UTC
Treef8494d06f92578a523e35036e82cef6298be3e64   Raw Patch
 
oauth: provide central enum at front and backend
oauth: provide central enum at front and backend
diff --git a/assets/sai.css b/assets/sai.css index bdbf454..564f857 100644 --- a/assets/sai.css +++ b/assets/sai.css @@ -1700,3 +1700,39 @@ div.ibuil.vm-builder { background-color: var(--term-cursor-bg); color: var(--term-cursor-fg); } + +/* Color the avatar silhouette based on grant level */ +.login-status-container.grant-admin img, +.login-status-container.grant-admin svg, +.login-status-container.grant-admin .avatar { + filter: drop-shadow(0 0 2px rgba(46, 204, 64, 0.4)); + border-color: #2ecc40 !important; +} + +.login-status-container.grant-admin svg path { + fill: #2ecc40 !important; +} + +.login-status-container.grant-user img, +.login-status-container.grant-user svg, +.login-status-container.grant-user .avatar { + filter: drop-shadow(0 0 2px rgba(243, 156, 18, 0.4)); + border-color: #f39c12 !important; +} + +.login-status-container.grant-user svg path { + fill: #f39c12 !important; +} + +.login-status-container.grant-none img, +.login-status-container.grant-none svg, +.login-status-container.grant-none .avatar { + filter: drop-shadow(0 0 2px rgba(231, 76, 60, 0.4)); + border-color: #e74c3c !important; + opacity: 0.6; +} + +.login-status-container.grant-none svg path { + fill: #e74c3c !important; +} + diff --git a/assets/sai.js b/assets/sai.js index 1907408..4e3071c 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -394,7 +394,14 @@ var lang_zhs = "{" + "\"%{pf}前创建, 创作时间: %{ct}ms \"" + "}}"; -var logs = "", redpend = 0, gitohashi_integ = 0, authd = 0, auth_is_admin = 0, auth_grant_level = -1, exptimer, auth_user = "", +const SaiAuthState = { + NOT_LOGGED_IN: 0, + LOGGED_IN_NO_GRANT: 1, + LOGGED_IN_GRANT_USER: 2, // < :2 + LOGGED_IN_GRANT_ADMIN: 3 // >= :2 +}; + +var logs = "", redpend = 0, gitohashi_integ = 0, authd = 0, auth_is_admin = 0, auth_grant_level = -1, auth_state = SaiAuthState.NOT_LOGGED_IN, exptimer, auth_user = "", active_terminals = {}; logAnsiState = {}, logs_pending = "", lines_pending = "", times_pending = "", ongoing_task_activities = {}, last_log_timestamp = 0, spreadsheet_data_cache = {}, loadreport_data_cache = {}, @@ -1079,10 +1086,10 @@ function sai_taskinfo_render(t, now_ut) sai_event_render(t, now_ut, 0) + "</table></td><td class=\"ti\">" + "<span class=\"ti1\">" + sai_plat_icon(t.t.platform, 2) + san(t.t.platform) + "</span>&nbsp;"; - if (auth_is_admin && t.t.state != 0 && t.t.state != 3 && t.t.state != 4 && t.t.state != 5) + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN && t.t.state != 0 && t.t.state != 3 && t.t.state != 4 && t.t.state != 5) s += "<img class=\"rebuild\" alt=\"stop build\" src=\"stop.svg\" " + "id=\"stop-" + san(t.t.uuid) + "\">&nbsp;"; - if (auth_is_admin) + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) s += "<img class=\"rebuild\" alt=\"rebuild\" src=\"rebuild.png\" " + "id=\"rebuild-" + san(t.t.uuid) + "\">&nbsp;"; s += sai_stateful_taskname(t.t.state, t.t.taskname, 1); @@ -1308,7 +1315,7 @@ function sai_event_summary_render(o, now_ut, reset_all_icon) s += "<div class=\"" + cl + "\"><img src=\"/sai/failed.svg\"></div>"; s += "</a>"; - if (reset_all_icon && !gitohashi_integ && auth_is_admin) { + if (reset_all_icon && !gitohashi_integ && auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) { s += "<br><img class=\"rebuild\" alt=\"rebuild all\" src=\"/sai/rebuild.png\" " + "id=\"rebuild-ev-" + san(e.uuid) + "\">&nbsp;"; s += "<img class=\"rebuild\" alt=\"delete event\" src=\"/sai/delete.png\" " + @@ -1625,7 +1632,7 @@ function createBuilderDiv(plat) { platDiv.className += " powering-down"; platDiv.id = "binfo-" + plat.name; - platDiv.title = plat.platform + "@" + plat.name.split('.')[0] + (authd && auth_is_admin && plat.peer_ip ? " / " + plat.peer_ip : ""); + platDiv.title = plat.platform + "@" + plat.name.split('.')[0] + (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN && plat.peer_ip ? " / " + plat.peer_ip : ""); let plat_parts = plat.platform.split('/'); let plat_os = plat_parts[0] || 'generic'; @@ -1646,7 +1653,7 @@ function createBuilderDiv(plat) { `<div class="res-bar"><div class="res-bar-inner res-bar-ram w-0"></div></div>` + `<div class="res-bar"><div class="res-bar-inner res-bar-disk w-0"></div></div>` + `</div>`; - if (authd && auth_is_admin && plat.peer_ip) + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN && plat.peer_ip) innerHTML += `<div class="plat-peer-ip">${hsanitize(plat.peer_ip)}</div>`; innerHTML += `</td></tr></tbody></table>`; @@ -1666,7 +1673,7 @@ function createBuilderDiv(plat) { { label: `<b>LWS:</b> ${plat.lws_hash}` }, ]; - if (authd && auth_is_admin && !plat.online) { + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN && !plat.online) { menuItems.push({ label: "<span class='builder-delete-btn'>Delete Builder</span>", callback: () => { @@ -1681,7 +1688,7 @@ function createBuilderDiv(plat) { }); } - if (authd && auth_is_admin) { + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) { menuItems.push({ label: "<span class='builder-shell-btn'>Open Shell</span>", callback: () => { @@ -1895,7 +1902,7 @@ function createPconDiv(pcon) { { label: `<b>PCON:</b> ${pcon.name}` } ]; - if (authd) { + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) { if (isActuallyOn) { menuItems.push({ label: "Turn Off", @@ -3268,11 +3275,33 @@ window.addEventListener("load", function() { .then(function(res) { return res.json(); }) .then(function(data) { console.log("LOGIN STATUS DEBUG:", data); - if (data.logged_in && data.has_grant) { - authd = 1; - auth_grant_level = data.grant_level !== undefined ? data.grant_level : -1; - if (data.is_admin || auth_grant_level >= 2) - auth_is_admin = 1; + auth_state = SaiAuthState.NOT_LOGGED_IN; + if (data.logged_in) { + if (data.has_grant) { + authd = 1; + auth_grant_level = data.grant_level !== undefined ? data.grant_level : -1; + const isAdmin = data.is_admin === true || data.is_admin === 1 || data.is_admin === "true" || data.is_admin === "1"; + if (auth_grant_level >= 2 || (auth_grant_level === -1 && isAdmin)) { + auth_state = SaiAuthState.LOGGED_IN_GRANT_ADMIN; + auth_is_admin = 1; + } else { + auth_state = SaiAuthState.LOGGED_IN_GRANT_USER; + } + } else { + auth_state = SaiAuthState.LOGGED_IN_NO_GRANT; + } + + const container = document.getElementById('lws-login-status-container'); + if (container) { + container.classList.remove('grant-admin', 'grant-user', 'grant-none'); + if (auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) { + container.classList.add('grant-admin'); + } else if (auth_state === SaiAuthState.LOGGED_IN_GRANT_USER) { + container.classList.add('grant-user'); + } else if (auth_state === SaiAuthState.LOGGED_IN_NO_GRANT) { + container.classList.add('grant-none'); + } + } } }) .catch(function(err) { @@ -3310,7 +3339,7 @@ window.addEventListener("load", function() { target = target.parentElement; } - if (taskDiv && auth_is_admin) { + if (taskDiv && auth_state === SaiAuthState.LOGGED_IN_GRANT_ADMIN) { event.preventDefault(); const taskUuid = taskDiv.id.substring(10); diff --git a/src/web/w-comms.c b/src/web/w-comms.c index 7eb11d2..82fab82 100644 --- a/src/web/w-comms.c +++ b/src/web/w-comms.c @@ -505,16 +505,24 @@ http_resp: } #endif - pss->authorized = 0; + pss->auth_state = SAI_AUTH_STATE_NOT_LOGGED_IN; if (vhd->has_jwk) { #if defined(LWS_WITH_JOSE) const char *reason = "unknown"; struct lws_jwt_auth *ja = lws_jwt_auth_create(wsi, &vhd->jwk, vhd->cookie_name, NULL, NULL, &reason); if (ja) { - if (lws_jwt_auth_query_grant(ja, "*") >= 1 || lws_jwt_auth_query_grant(ja, "com.warmcat.sai") >= 1) { - pss->authorized = 1; + int grant = (int)lws_jwt_auth_query_grant(ja, "com.warmcat.sai"); + int grant_all = (int)lws_jwt_auth_query_grant(ja, "*"); + int max_grant = grant > grant_all ? grant : grant_all; + + if (max_grant >= 2) { + pss->auth_state = SAI_AUTH_STATE_LOGGED_IN_GRANT_ADMIN; lwsl_wsi_notice(wsi, "Authorized WebSocket connection (admin/grant)"); + } else if (max_grant >= 1) { + pss->auth_state = SAI_AUTH_STATE_LOGGED_IN_GRANT_USER; + lwsl_wsi_notice(wsi, "Authorized WebSocket connection (user/grant)"); } else { + pss->auth_state = SAI_AUTH_STATE_LOGGED_IN_NO_GRANT; lwsl_wsi_err(wsi, "JWT validation passed, but no grant found"); } lws_jwt_auth_destroy(&ja); diff --git a/src/web/w-private.h b/src/web/w-private.h index 2e9e057..b9e9ecf 100644 --- a/src/web/w-private.h +++ b/src/web/w-private.h @@ -59,6 +59,14 @@ enum { SAIM_SPECIFIC_TASK, }; +typedef enum { + SAI_AUTH_STATE_NOT_LOGGED_IN, + SAI_AUTH_STATE_LOGGED_IN_NO_GRANT, + SAI_AUTH_STATE_LOGGED_IN_GRANT_USER, /* < :2 */ + SAI_AUTH_STATE_LOGGED_IN_GRANT_ADMIN /* >= :2 */ +} sai_auth_state_t; + + struct pss { struct vhd *vhd; struct lws *wsi; @@ -124,7 +132,7 @@ struct pss { unsigned int announced:1; unsigned int bulk_binary_data:1; unsigned int toggle_favour_sch:1; - unsigned int authorized:1; + sai_auth_state_t auth_state; }; struct vhd { diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 295ed0b..fdc352b 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -645,7 +645,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf, * matched on */ - if (!pss->authorized && ( + if (pss->auth_state != SAI_AUTH_STATE_LOGGED_IN_GRANT_ADMIN && ( a.top_schema_index == SAIM_WS_BROWSER_RX_TASKRESET || a.top_schema_index == SAIM_WS_BROWSER_RX_TASKREMOVEALLTRIES || a.top_schema_index == SAIM_WS_BROWSER_RX_TASKREBUILDLASTSTEP || @@ -654,6 +654,8 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf, a.top_schema_index == SAIM_WS_BROWSER_RX_TASKCANCEL || a.top_schema_index == SAIM_WS_BROWSER_RX_REBUILD || a.top_schema_index == SAIM_WS_BROWSER_RX_PLATRESET || + a.top_schema_index == SAIM_WS_BROWSER_RX_STAY || + a.top_schema_index == SAIM_WS_BROWSER_RX_PCON_CONTROL || a.top_schema_index == SAIM_WS_BROWSER_RX_BUILDERDELETE || a.top_schema_index == SAIM_WS_BROWSER_RX_OPENSHELL || a.top_schema_index == SAIM_WS_BROWSER_RX_CLOSESHELL ||
Page fetched 0s ago, creation time: 6ms (vhost etag hits: 0%, cache hits: 0%)