From 344433e6977ee37a9f711f5e6b6494896850c591 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Mon, 5 May 2025 21:18:48 +0200 Subject: [PATCH] improve vaultwarden --- Cargo.lock | 285 ------------------------------- examples/worf-warden/Cargo.toml | 1 - examples/worf-warden/src/main.rs | 90 +++++++--- worf/src/lib/gui.rs | 71 ++++---- 4 files changed, 107 insertions(+), 340 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24e3082..b7d67f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,15 +96,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2", -] - [[package]] name = "cairo-rs" version = "0.20.7" @@ -230,46 +221,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.9.0", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.9.0", - "core-foundation", - "libc", -] - [[package]] name = "crossbeam" version = "0.8.4" @@ -371,25 +322,6 @@ dependencies = [ "phf", ] -[[package]] -name = "enigo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types-shared", - "libc", - "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "windows", - "xkbcommon", - "xkeysym", -] - [[package]] name = "env_filter" version = "0.1.3" @@ -463,33 +395,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "freedesktop-file-parser" version = "0.1.3" @@ -1053,15 +958,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1115,105 +1011,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - -[[package]] -name = "objc2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys", - "objc2-encode", -] - -[[package]] -name = "objc2-app-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" -dependencies = [ - "bitflags 2.9.0", - "block2", - "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.9.0", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.9.0", - "block2", - "libc", - "objc2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.9.0", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.9.0", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1969,70 +1766,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core", - "windows-targets", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2184,7 +1917,6 @@ name = "worf-warden" version = "0.1.0" dependencies = [ "anyhow", - "enigo", "worf", ] @@ -2200,23 +1932,6 @@ dependencies = [ "tini", ] -[[package]] -name = "xkbcommon" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" -dependencies = [ - "libc", - "memmap2", - "xkeysym", -] - -[[package]] -name = "xkeysym" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" - [[package]] name = "xml-rs" version = "0.8.26" diff --git a/examples/worf-warden/Cargo.toml b/examples/worf-warden/Cargo.toml index 0f7af7b..b51c50e 100644 --- a/examples/worf-warden/Cargo.toml +++ b/examples/worf-warden/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" [dependencies] worf = {path = "../../worf"} -enigo = "0.3.0" anyhow = "1.0.98" # todo re-add this diff --git a/examples/worf-warden/src/main.rs b/examples/worf-warden/src/main.rs index 3232291..0243399 100644 --- a/examples/worf-warden/src/main.rs +++ b/examples/worf-warden/src/main.rs @@ -1,11 +1,11 @@ -use enigo::{Enigo, Keyboard}; use std::process::Command; use std::thread::sleep; use std::time::Duration; use worf_lib::config::Config; +use worf_lib::desktop::spawn_fork; use worf_lib::gui::{ItemProvider, Key, KeyBinding, MenuItem, Modifier}; -use worf_lib::{Error, config, gui}; +use worf_lib::{config, gui}; #[derive(Clone)] struct PasswordProvider { @@ -52,6 +52,34 @@ impl ItemProvider for PasswordProvider { } } +fn groups() -> String { + let output = Command::new("groups") + .output() + .expect("Failed to get groups"); + String::from_utf8_lossy(&output.stdout) + .trim_end() + .to_string() +} + +fn keyboard_type(text: &str) { + Command::new("ydotool") + .arg("type") + .arg(text) + .output() + .expect("Failed to execute ydotool"); +} + +fn keyboard_tab() { + Command::new("ydotool") + .arg("key") + .arg("-d") + .arg("10") + .arg("15:1") + .arg("15:0") + .output() + .expect("Failed to execute ydotool"); +} + fn rbw_get(name: &str, field: &str) -> String { let output = Command::new("rbw") .arg("get") @@ -74,9 +102,22 @@ fn rbw_get_password(name: &str) -> String { rbw_get(name, "password") } +fn rbw_get_totp(name: &str) -> String { + rbw_get(name, "totp") +} + fn main() -> anyhow::Result<()> { + let args = config::parse_args(); let config = config::load_config(Some(&args)).unwrap_or(args); + + if !groups().contains("input") { + eprintln!("User must be in input group. 'sudo usermod -aG input $USER', then login again"); + std::process::exit(1) + } + + // will exit if there is a daemon running already, so it's fine to call this everytime. + spawn_fork("ydotoold", None).expect("failed to spawn ydotoold"); // todo eventually use a propper rust client for this, for now rbw is good enough let provider = PasswordProvider::new(&config); @@ -90,13 +131,19 @@ fn main() -> anyhow::Result<()> { let type_user = KeyBinding { key: Key::Num2, modifiers: Modifier::Alt, - label: "Alt+2 Type All".to_string(), + label: "Alt+2 Type User".to_string(), + }; + + let type_password = KeyBinding { + key: Key::Num3, + modifiers: Modifier::Alt, + label: "Alt+3 Type Password".to_string(), }; let type_totp = KeyBinding { - key: Key::Num3, + key: Key::Num4, modifiers: Modifier::Alt, - label: "Alt+3 Sync".to_string(), + label: "Alt+3 Type Totp".to_string(), }; let reload = KeyBinding { @@ -142,26 +189,31 @@ fn main() -> anyhow::Result<()> { None, Some(vec![ type_all.clone(), - type_user, - type_totp, - reload, - urls, - names, - folders, - totp, - lock, + type_user.clone(), + type_password.clone(), + type_totp.clone(), + reload.clone(), + urls.clone(), + names.clone(), + folders.clone(), + totp.clone(), + lock.clone(), ]), ) { Ok(selection) => { - let mut enigo = Enigo::new(&enigo::Settings::default())?; let id = selection.menu.label.replace("\n", ""); sleep(Duration::from_millis(250)); if let Some(key) = selection.custom_key { - if key.label == type_all.label { - enigo.text(&rbw_get_user(&id))?; - enigo.key(enigo::Key::Tab, enigo::Direction::Press)?; - enigo.key(enigo::Key::Tab, enigo::Direction::Release)?; - enigo.text(&rbw_get_password(&id))?; + if key == type_all { + keyboard_type(&rbw_get_user(&id)); + keyboard_tab(); + keyboard_type(&rbw_get_password(&id)); + } else if key == type_user { + keyboard_type(&rbw_get_user(&id)); + } else if key == type_password { + keyboard_type(&rbw_get_password(&id)); + } else if key == type_totp { + keyboard_type(&rbw_get_totp(&id)); } } } diff --git a/worf/src/lib/gui.rs b/worf/src/lib/gui.rs index 0d59e7e..82e83eb 100644 --- a/worf/src/lib/gui.rs +++ b/worf/src/lib/gui.rs @@ -6,10 +6,10 @@ use std::time::{Duration, Instant}; use crossbeam::channel; use crossbeam::channel::Sender; +use gdk4::Display; use gdk4::gio::File; use gdk4::glib::{Propagation, timeout_add_local}; use gdk4::prelude::{Cast, DisplayExt, MonitorExt, SurfaceExt}; -use gdk4::{Display, ModifierType}; use gtk4::glib::ControlFlow; use gtk4::prelude::{ ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt, @@ -217,9 +217,9 @@ pub enum Key { Tilde, // ~ } -impl Into for gdk::Key { - fn into(self) -> Key { - match self { +impl From for Key { + fn from(value: gdk4::Key) -> Self { + match value { // Letters gdk4::Key::A => Key::A, gdk4::Key::B => Key::B, @@ -338,15 +338,15 @@ pub enum Modifier { None, } -impl Into for gdk::ModifierType { - fn into(self) -> Modifier { - match self { - ModifierType::SHIFT_MASK => Modifier::Shift, - ModifierType::CONTROL_MASK => Modifier::Control, - ModifierType::ALT_MASK => Modifier::Alt, - ModifierType::SUPER_MASK => Modifier::Super, - ModifierType::META_MASK => Modifier::Meta, - ModifierType::LOCK_MASK => Modifier::CapsLock, +impl From for Modifier { + fn from(value: gdk4::ModifierType) -> Self { + match value { + gdk4::ModifierType::SHIFT_MASK => Modifier::Shift, + gdk4::ModifierType::CONTROL_MASK => Modifier::Control, + gdk4::ModifierType::ALT_MASK => Modifier::Alt, + gdk4::ModifierType::SUPER_MASK => Modifier::Super, + gdk4::ModifierType::META_MASK => Modifier::Meta, + gdk4::ModifierType::LOCK_MASK => Modifier::CapsLock, _ => Modifier::None, } } @@ -448,7 +448,7 @@ where app.clone(), new_on_empty, search_ignored_words.clone(), - custom_keys.clone(), + custom_keys.as_ref(), ); }); @@ -464,7 +464,7 @@ fn build_ui( app: Application, new_on_empty: bool, search_ignored_words: Option>, - custom_keys: Option>, + custom_keys: Option<&Vec>, ) where T: Clone + 'static + Send, P: ItemProvider + 'static + Send, @@ -503,7 +503,7 @@ fn build_ui( }); // handle keys as soon as possible - setup_key_event_handler(&ui_elements, &meta, custom_keys.as_ref()); + setup_key_event_handler(&ui_elements, &meta, custom_keys); log::debug!("keyboard ready after {:?}", start.elapsed()); @@ -530,7 +530,7 @@ fn build_ui( let outer_box = gtk4::Box::new(config.orientation().into(), 0); outer_box.set_widget_name("outer-box"); outer_box.append(&ui_elements.search); - build_custom_key_view(config, &ui_elements, &custom_keys, &outer_box); + build_custom_key_view(custom_keys, &outer_box); ui_elements.window.set_child(Some(&outer_box)); @@ -626,29 +626,29 @@ fn build_search_entry( } } -fn build_custom_key_view( - config: &Config, - ui: &Rc>, - custom_keys: &Option>, - outer_box: >k4::Box, -) where - T: 'static + Clone + Send, -{ - let inner_box = gtk4::Box::new(Orientation::Horizontal, 0); - inner_box.set_halign(Align::Start); +fn build_custom_key_view(custom_keys: Option<&Vec>, outer_box: >k4::Box) { + let inner_box = FlowBox::new(); + inner_box.set_halign(Align::Fill); inner_box.set_widget_name("custom-key-box"); if let Some(custom_keys) = custom_keys { for key in custom_keys { - let label_box = gtk4::Box::new(Orientation::Horizontal, 0); - label_box.set_halign(Align::Start); + let label_box = FlowBoxChild::new(); + label_box.set_halign(Align::Fill); + inner_box.set_valign(Align::Start); label_box.set_widget_name("custom-key-label-box"); inner_box.append(&label_box); + inner_box.set_vexpand(false); + inner_box.set_hexpand(false); let label = Label::new(Some(&key.label)); + label.set_halign(Align::Fill); + label.set_valign(Align::Start); label.set_use_markup(true); label.set_hexpand(true); + label.set_vexpand(false); label.set_widget_name("custom-key-label-text"); - label.set_wrap(true); - label_box.append(&label); + label.set_wrap(false); + label.set_xalign(0.0); + label_box.set_child(Some(&label)); } } outer_box.append(&inner_box); @@ -740,7 +740,7 @@ fn setup_key_event_handler( let ui_clone = Rc::clone(ui); let meta_clone = Rc::clone(meta); - let keys_clone = custom_keys.map(|s| s.clone()); + let keys_clone = custom_keys.cloned(); key_controller.connect_key_pressed(move |_, key_value, _, modifier| { handle_key_press( &ui_clone, @@ -759,7 +759,7 @@ fn handle_key_press( ui: &Rc>, meta: &Rc>, keyboard_key: gdk4::Key, - modifier_type: ModifierType, + modifier_type: gdk4::ModifierType, custom_keys: Option<&Vec>, ) -> Propagation { let update_view = |query: &String| { @@ -979,7 +979,7 @@ where visible: true, }; - send_selected_item(&ui, meta, custom_key, &item); + send_selected_item(ui, meta, custom_key, &item); Ok(()) } else { Err("selected item cannot be resolved".to_owned()) @@ -994,13 +994,14 @@ fn send_selected_item( ) where T: Clone + Send, { - close_gui(&ui.app); + ui.window.close(); if let Err(e) = meta.selected_sender.send(Ok(Selection { menu: selected_item.clone(), custom_key: custom_key.cloned(), })) { log::error!("failed to send message {e}"); } + close_gui(&ui.app); } fn add_menu_item(