ask the user to accept unauthorized devices

This commit is contained in:
Ferdinand Schober
2025-03-22 20:03:43 +01:00
parent 15296263b2
commit 6e66f47887
12 changed files with 421 additions and 99 deletions

View File

@@ -0,0 +1,19 @@
mod imp;
use glib::Object;
use gtk::{gio, glib, subclass::prelude::ObjectSubclassIsExt};
glib::wrapper! {
pub struct AuthorizationWindow(ObjectSubclass<imp::AuthorizationWindow>)
@extends adw::Window, gtk::Window, gtk::Widget,
@implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
}
impl AuthorizationWindow {
pub(crate) fn new(fingerprint: &str) -> Self {
let window: Self = Object::builder().build();
window.imp().set_fingerprint(fingerprint);
window
}
}

View File

@@ -0,0 +1,75 @@
use std::sync::OnceLock;
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::subclass::InitializingObject;
use gtk::{
glib::{self, subclass::Signal},
template_callbacks, Button, CompositeTemplate, Label,
};
#[derive(CompositeTemplate, Default)]
#[template(resource = "/de/feschber/LanMouse/authorization_window.ui")]
pub struct AuthorizationWindow {
#[template_child]
pub fingerprint: TemplateChild<Label>,
#[template_child]
pub cancel_button: TemplateChild<Button>,
#[template_child]
pub confirm_button: TemplateChild<Button>,
}
#[glib::object_subclass]
impl ObjectSubclass for AuthorizationWindow {
const NAME: &'static str = "AuthorizationWindow";
const ABSTRACT: bool = false;
type Type = super::AuthorizationWindow;
type ParentType = adw::Window;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
klass.bind_template_callbacks();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[template_callbacks]
impl AuthorizationWindow {
#[template_callback]
fn handle_confirm(&self, _button: Button) {
let fp = self.fingerprint.text().as_str().trim().to_owned();
self.obj().emit_by_name("confirm-clicked", &[&fp])
}
#[template_callback]
fn handle_cancel(&self, _: Button) {
self.obj().emit_by_name("cancel-clicked", &[])
}
pub(super) fn set_fingerprint(&self, fingerprint: &str) {
self.fingerprint.set_text(fingerprint);
}
}
impl ObjectImpl for AuthorizationWindow {
fn signals() -> &'static [Signal] {
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
SIGNALS.get_or_init(|| {
vec![
Signal::builder("confirm-clicked")
.param_types([String::static_type()])
.build(),
Signal::builder("cancel-clicked").build(),
]
})
}
}
impl WidgetImpl for AuthorizationWindow {}
impl WindowImpl for AuthorizationWindow {}
impl ApplicationWindowImpl for AuthorizationWindow {}
impl AdwWindowImpl for AuthorizationWindow {}

View File

@@ -1,7 +1,7 @@
mod imp;
use glib::Object;
use gtk::{gio, glib};
use gtk::{gio, glib, prelude::ObjectExt, subclass::prelude::ObjectSubclassIsExt};
glib::wrapper! {
pub struct FingerprintWindow(ObjectSubclass<imp::FingerprintWindow>)
@@ -11,8 +11,12 @@ glib::wrapper! {
}
impl FingerprintWindow {
pub(crate) fn new() -> Self {
pub(crate) fn new(fingerprint: Option<String>) -> Self {
let window: Self = Object::builder().build();
if let Some(fp) = fingerprint {
window.imp().fingerprint.set_property("text", fp);
window.imp().fingerprint.set_property("editable", false);
}
window
}
}

View File

@@ -1,3 +1,4 @@
mod authorization_window;
mod client_object;
mod client_row;
mod fingerprint_window;
@@ -146,8 +147,21 @@ fn build_ui(app: &Application) {
FrontendEvent::EmulationStatus(s) => window.set_emulation(s.into()),
FrontendEvent::AuthorizedUpdated(keys) => window.set_authorized_keys(keys),
FrontendEvent::PublicKeyFingerprint(fp) => window.set_pk_fp(&fp),
FrontendEvent::IncomingConnected(_fingerprint, addr, pos) => {
window.show_toast(format!("device connected: {addr} ({pos})").as_str());
FrontendEvent::ConnectionAttempt { fingerprint } => {
window.request_authorization(&fingerprint);
}
FrontendEvent::DeviceConnected {
fingerprint: _,
addr,
} => {
window.show_toast(format!("device connected: {addr}").as_str());
}
FrontendEvent::DeviceEntered {
fingerprint: _,
addr,
pos,
} => {
window.show_toast(format!("device entered: {addr} ({pos})").as_str());
}
FrontendEvent::IncomingDisconnected(addr) => {
window.show_toast(format!("{addr} disconnected").as_str());

View File

@@ -16,7 +16,10 @@ use lan_mouse_ipc::{
DEFAULT_PORT,
};
use crate::{fingerprint_window::FingerprintWindow, key_object::KeyObject, key_row::KeyRow};
use crate::{
authorization_window::AuthorizationWindow, fingerprint_window::FingerprintWindow,
key_object::KeyObject, key_row::KeyRow,
};
use super::{client_object::ClientObject, client_row::ClientRow};
@@ -394,8 +397,8 @@ impl Window {
self.request(FrontendRequest::Create);
}
fn open_fingerprint_dialog(&self) {
let window = FingerprintWindow::new();
fn open_fingerprint_dialog(&self, fp: Option<String>) {
let window = FingerprintWindow::new(fp);
window.set_transient_for(Some(self));
window.connect_closure(
"confirm-clicked",
@@ -469,4 +472,29 @@ impl Window {
pub(super) fn set_pk_fp(&self, fingerprint: &str) {
self.imp().fingerprint_row.set_subtitle(fingerprint);
}
pub(super) fn request_authorization(&self, fingerprint: &str) {
let window = AuthorizationWindow::new(fingerprint);
window.set_transient_for(Some(self));
window.connect_closure(
"confirm-clicked",
false,
closure_local!(
#[strong(rename_to = parent)]
self,
move |w: AuthorizationWindow, fp: String| {
w.close();
parent.open_fingerprint_dialog(Some(fp));
}
),
);
window.connect_closure(
"cancel-clicked",
false,
closure_local!(move |w: AuthorizationWindow| {
w.close();
}),
);
window.present();
}
}

View File

@@ -149,7 +149,7 @@ impl Window {
#[template_callback]
fn handle_add_cert_fingerprint(&self, _button: &Button) {
self.obj().open_fingerprint_dialog();
self.obj().open_fingerprint_dialog(None);
}
pub fn set_port(&self, port: u16) {