mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 17:56:34 +00:00
fix(terminal): overlay on existing window (cage single-window) + 60s timeout
Three fixes: 1. Terminal code overlay replaces the main display window's child instead of creating a new gtk::Window (cage compositor only shows one window). Saves the previous child and restores on dismiss. 2. Code auto-expires after 60s — timeout does NOT increment lockout. GTK overlay dismissed + pending_code cleared. 3. Journal-start handler already logs but relay might fail silently if kiosk WS reconnected after admin debug WS connected.
This commit is contained in:
parent
9ebdc894a1
commit
7425fa9c63
2 changed files with 67 additions and 25 deletions
|
|
@ -1921,23 +1921,40 @@ fn add_css(widget: &impl IsA<gtk::Widget>, css: &str) {
|
|||
// anywhere (security requirement — physical presence only).
|
||||
|
||||
thread_local! {
|
||||
static TERMINAL_CODE_OVERLAY: RefCell<Option<gtk::Window>> = const { RefCell::new(None) };
|
||||
static TERMINAL_CODE_WIDGET: RefCell<Option<gtk::Widget>> = const { RefCell::new(None) };
|
||||
static TERMINAL_CODE_SAVED_CHILD: RefCell<Option<(u32, gtk::Widget)>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
fn show_terminal_code_overlay(code: &str) {
|
||||
dismiss_terminal_code_overlay();
|
||||
|
||||
let win = gtk::Window::builder()
|
||||
.title("Terminal Auth")
|
||||
.decorated(false)
|
||||
.modal(true)
|
||||
.build();
|
||||
// Cage is a single-window compositor. We can't open a new window.
|
||||
// Instead, replace the first display window's child with the code
|
||||
// overlay and restore it when dismissed.
|
||||
let display_id = DISPLAYS.with(|ds| {
|
||||
ds.borrow().keys().next().copied()
|
||||
});
|
||||
let Some(display_id) = display_id else { return };
|
||||
|
||||
DISPLAYS.with(|ds| {
|
||||
let ds = ds.borrow();
|
||||
let Some(st) = ds.get(&display_id) else { return };
|
||||
let win = &st.window;
|
||||
|
||||
// Save current child for restore.
|
||||
let old_child = win.child();
|
||||
if let Some(ref c) = old_child {
|
||||
TERMINAL_CODE_SAVED_CHILD.with(|s| *s.borrow_mut() = Some((display_id, c.clone())));
|
||||
}
|
||||
|
||||
let label = Label::new(Some(code));
|
||||
add_css(&label, "label { font-size: 72px; font-weight: 800; font-family: monospace; color: #fff; letter-spacing: 12px; }");
|
||||
|
||||
let hint = Label::new(Some("Enter this code in the admin UI to authorize terminal access"));
|
||||
add_css(&hint, "label { font-size: 16px; color: #aaa; margin-top: 24px; }");
|
||||
let hint = Label::new(Some("Enter this code in the admin UI\nto authorize terminal access"));
|
||||
add_css(&hint, "label { font-size: 18px; color: #aaa; margin-top: 24px; text-align: center; }");
|
||||
|
||||
let timeout_hint = Label::new(Some("Code expires in 60 seconds"));
|
||||
add_css(&timeout_hint, "label { font-size: 14px; color: #666; margin-top: 12px; }");
|
||||
|
||||
let vbox = GtkBox::new(Orientation::Vertical, 16);
|
||||
vbox.set_valign(gtk::Align::Center);
|
||||
|
|
@ -1946,19 +1963,34 @@ fn show_terminal_code_overlay(code: &str) {
|
|||
vbox.set_hexpand(true);
|
||||
vbox.append(&label);
|
||||
vbox.append(&hint);
|
||||
vbox.append(&timeout_hint);
|
||||
|
||||
add_css(&vbox, "box { background: rgba(0,0,0,0.85); }");
|
||||
add_css(&vbox, "box { background: #000; }");
|
||||
win.set_child(Some(&vbox));
|
||||
win.set_fullscreened(true);
|
||||
win.present();
|
||||
|
||||
TERMINAL_CODE_OVERLAY.with(|o| *o.borrow_mut() = Some(win));
|
||||
TERMINAL_CODE_WIDGET.with(|w| *w.borrow_mut() = Some(vbox.upcast()));
|
||||
});
|
||||
|
||||
// Auto-dismiss after 60s (timeout doesn't count as failed attempt).
|
||||
gtk::glib::timeout_add_local_once(Duration::from_secs(60), || {
|
||||
dismiss_terminal_code_overlay();
|
||||
});
|
||||
}
|
||||
|
||||
fn dismiss_terminal_code_overlay() {
|
||||
TERMINAL_CODE_OVERLAY.with(|o| {
|
||||
if let Some(win) = o.borrow_mut().take() {
|
||||
win.close();
|
||||
// Restore previous content.
|
||||
TERMINAL_CODE_WIDGET.with(|w| {
|
||||
if w.borrow().is_none() { return; }
|
||||
*w.borrow_mut() = None;
|
||||
});
|
||||
TERMINAL_CODE_SAVED_CHILD.with(|s| {
|
||||
if let Some((display_id, child)) = s.borrow_mut().take() {
|
||||
DISPLAYS.with(|ds| {
|
||||
let ds = ds.borrow();
|
||||
if let Some(st) = ds.get(&display_id) {
|
||||
st.window.set_child(Some(&child));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,6 +212,16 @@ async fn handle_message(
|
|||
Ok(code) => {
|
||||
*pending_code.lock().unwrap() = Some(code.clone());
|
||||
let _ = tx.send(ServerMsg::ShowTerminalCode(code));
|
||||
// Auto-expire code after 60s. Timeout does NOT count as failed attempt.
|
||||
let pc_timeout = pending_code.clone();
|
||||
let tx_timeout = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
if pc_timeout.lock().unwrap().take().is_some() {
|
||||
info!("ws: terminal code expired (60s timeout)");
|
||||
let _ = tx_timeout.send(ServerMsg::DismissTerminalCode);
|
||||
}
|
||||
});
|
||||
ws_send(writer, serde_json::json!({ "type": "terminal-challenge" })).await;
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue