improve vaultwarden

This commit is contained in:
Alexander Mohr 2025-05-05 21:18:48 +02:00
parent c040a28539
commit 344433e697
4 changed files with 107 additions and 340 deletions

285
Cargo.lock generated
View file

@ -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"

View file

@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
worf = {path = "../../worf"}
enigo = "0.3.0"
anyhow = "1.0.98"
# todo re-add this

View file

@ -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<String> 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,10 +102,23 @@ 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: "<b>Alt+2</b> Type All".to_string(),
label: "<b>Alt+2</b> Type User".to_string(),
};
let type_password = KeyBinding {
key: Key::Num3,
modifiers: Modifier::Alt,
label: "<b>Alt+3</b> Type Password".to_string(),
};
let type_totp = KeyBinding {
key: Key::Num3,
key: Key::Num4,
modifiers: Modifier::Alt,
label: "<b>Alt+3</b> Sync".to_string(),
label: "<b>Alt+3</b> 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));
}
}
}

View file

@ -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<Key> for gdk::Key {
fn into(self) -> Key {
match self {
impl From<gdk::Key> 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<Modifier> 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<gdk4::ModifierType> 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<T, P>(
app: Application,
new_on_empty: bool,
search_ignored_words: Option<Vec<Regex>>,
custom_keys: Option<Vec<KeyBinding>>,
custom_keys: Option<&Vec<KeyBinding>>,
) where
T: Clone + 'static + Send,
P: ItemProvider<T> + 'static + Send,
@ -503,7 +503,7 @@ fn build_ui<T, P>(
});
// 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<T, P>(
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<T: Clone + Send>(
}
}
fn build_custom_key_view<T>(
config: &Config,
ui: &Rc<UiElements<T>>,
custom_keys: &Option<Vec<KeyBinding>>,
outer_box: &gtk4::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<KeyBinding>>, outer_box: &gtk4::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<T: Clone + 'static + Send>(
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<T: Clone + 'static + Send>(
ui: &Rc<UiElements<T>>,
meta: &Rc<MetaData<T>>,
keyboard_key: gdk4::Key,
modifier_type: ModifierType,
modifier_type: gdk4::ModifierType,
custom_keys: Option<&Vec<KeyBinding>>,
) -> 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<T>(
) 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<T: Clone + 'static + Send>(