feat: support keyfiles
I don't personally use them, so this is untested, but I imagine it works. in addition, I removed an unecessary bool "db_loaded", changed the style of the picker buttons based on whether the files exist, and improved ordering of the State struct
This commit is contained in:
parent
f7340ffcb9
commit
6e7d50a79d
1 changed files with 76 additions and 11 deletions
|
|
@ -17,6 +17,10 @@ enum Message {
|
|||
PickDb,
|
||||
PickedDb(Option<FileHandle>),
|
||||
|
||||
ChangeKeyfilePath(String),
|
||||
PickKeyfile,
|
||||
PickedKeyfile(Option<FileHandle>),
|
||||
|
||||
ChangePassword(String),
|
||||
ToggleSecurity,
|
||||
OpenDb,
|
||||
|
|
@ -48,18 +52,19 @@ struct State {
|
|||
db_path: String,
|
||||
db_path_exists: bool,
|
||||
|
||||
keyfile_path: String,
|
||||
keyfile_path_exists: bool,
|
||||
|
||||
is_secure: bool,
|
||||
password: String,
|
||||
|
||||
db: Option<keepass::Database>,
|
||||
|
||||
db_loaded: bool,
|
||||
db_view: Vec<keepass::db::Entry>,
|
||||
selected_entry: usize,
|
||||
selected_field: db::BasicField,
|
||||
query: String,
|
||||
|
||||
password: String,
|
||||
|
||||
is_secure: bool,
|
||||
|
||||
status: Cow<'static, str>,
|
||||
error: Cow<'static, str>,
|
||||
}
|
||||
|
|
@ -103,6 +108,22 @@ impl State {
|
|||
}
|
||||
Message::PickedDb(None) => eprintln!("no path received from picker"),
|
||||
|
||||
Message::ChangeKeyfilePath(keyfile_path) => {
|
||||
self.keyfile_path_exists = fs::exists(&keyfile_path)
|
||||
.inspect_err(|e| eprintln!("failed to check '{keyfile_path}': {e}"))
|
||||
.is_ok_and(|exists| exists);
|
||||
self.keyfile_path = keyfile_path
|
||||
}
|
||||
Message::PickKeyfile => {
|
||||
return Task::perform(AsyncFileDialog::new().pick_file(), Message::PickedKeyfile);
|
||||
}
|
||||
Message::PickedKeyfile(Some(file)) => {
|
||||
return Task::done(Message::ChangeKeyfilePath(
|
||||
file.path().to_string_lossy().to_string(),
|
||||
));
|
||||
}
|
||||
Message::PickedKeyfile(None) => eprintln!("no path received from picker"),
|
||||
|
||||
Message::ChangePassword(password) => self.password = password,
|
||||
Message::ToggleSecurity => self.is_secure = !self.is_secure,
|
||||
Message::OpenDb => return self.open_db(),
|
||||
|
|
@ -113,7 +134,6 @@ impl State {
|
|||
self.error = "".into();
|
||||
self.db = Some(db);
|
||||
self.populate_view();
|
||||
self.db_loaded = true;
|
||||
}
|
||||
Err(e) => self.error = e.into(),
|
||||
}
|
||||
|
|
@ -172,7 +192,7 @@ impl State {
|
|||
Task::none()
|
||||
}
|
||||
fn view(&self) -> Element<'_> {
|
||||
let main = if self.db_loaded {
|
||||
let main = if self.db.is_some() {
|
||||
widget::column([
|
||||
widget::text_input("search query", &self.query)
|
||||
.on_input(Message::ChangeQuery)
|
||||
|
|
@ -263,7 +283,30 @@ impl State {
|
|||
widget::text_input("database path", &self.db_path)
|
||||
.on_input(Message::ChangeDbPath)
|
||||
.into(),
|
||||
widget::button("pick file").on_press(Message::PickDb).into(),
|
||||
widget::button("pick db")
|
||||
.style(if self.db_path_exists {
|
||||
widget::button::primary
|
||||
} else {
|
||||
widget::button::danger
|
||||
})
|
||||
.on_press(Message::PickDb)
|
||||
.into(),
|
||||
])
|
||||
.into(),
|
||||
widget::row([
|
||||
widget::text_input("keyfile path", &self.keyfile_path)
|
||||
.on_input(Message::ChangeKeyfilePath)
|
||||
.into(),
|
||||
widget::button("pick keyfile")
|
||||
.style(
|
||||
if self.keyfile_path.is_empty() || self.keyfile_path_exists {
|
||||
widget::button::primary
|
||||
} else {
|
||||
widget::button::danger
|
||||
},
|
||||
)
|
||||
.on_press(Message::PickKeyfile)
|
||||
.into(),
|
||||
])
|
||||
.into(),
|
||||
widget::row([
|
||||
|
|
@ -279,7 +322,9 @@ impl State {
|
|||
.spacing(10)
|
||||
.into(),
|
||||
widget::container(widget::button("open database").on_press_maybe(
|
||||
if self.db_path_exists {
|
||||
if self.db_path_exists
|
||||
&& (self.keyfile_path.is_empty() || self.keyfile_path_exists)
|
||||
{
|
||||
Some(Message::OpenDb)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -376,12 +421,32 @@ impl State {
|
|||
|
||||
self.status = "loading...".into();
|
||||
|
||||
let (db_path, password) = (self.db_path.clone(), self.password.clone());
|
||||
let (db_path, keyfile_path, password) = (
|
||||
self.db_path.clone(),
|
||||
self.keyfile_path.clone(),
|
||||
self.password.clone(),
|
||||
);
|
||||
Task::perform(
|
||||
async move {
|
||||
let key = {
|
||||
let mut key = keepass::DatabaseKey::new();
|
||||
if !password.is_empty() {
|
||||
key = key.with_password(&password);
|
||||
}
|
||||
if !keyfile_path.is_empty() {
|
||||
key = key
|
||||
.with_keyfile(
|
||||
&mut fs::File::open(&keyfile_path).map_err(|e| e.to_string())?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
key
|
||||
};
|
||||
|
||||
let db = keepass::Database::open(
|
||||
&mut fs::File::open(&db_path).map_err(|e| e.to_string())?,
|
||||
keepass::DatabaseKey::new().with_password(&password),
|
||||
key,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue