Implement iPhone TOTP tab and Watch sharing

This commit is contained in:
2026-08-11 20:54:34 +02:00
parent ae64ce47a3
commit b01cc8bb6d
10 changed files with 1980 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ use url::Url;
use crate::authentication::{AuthenticationTimeout, DEFAULT_AUTHENTICATION_TIMEOUT};
use crate::mobile::MobileTab;
use crate::presentation::{ClipboardTimeout, DEFAULT_CLIPBOARD_TIMEOUT};
use crate::repository::EntryPath;
const APPLICATION_DIRECTORY: &str = "ironstorage";
const CONFIG_FILE: &str = "config.toml";
@@ -38,6 +39,7 @@ pub struct Config {
biometric_unlock_enabled: bool,
mobile_tab: MobileTab,
mobile_home_refreshed_at: Option<i64>,
watch_shared_totp_entries: BTreeSet<EntryPath>,
git_remotes: Vec<GitRemote>,
}
@@ -135,6 +137,10 @@ impl Config {
self.mobile_home_refreshed_at
}
pub fn watch_shared_totp_entries(&self) -> &BTreeSet<EntryPath> {
&self.watch_shared_totp_entries
}
pub fn git_remotes(&self) -> &[GitRemote] {
&self.git_remotes
}
@@ -154,7 +160,7 @@ impl Config {
}
pub fn update_mobile_tab(&self, tab: MobileTab) -> Result<(), ConfigError> {
let mut document = self.document.clone();
let mut document = self.current_document()?;
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
@@ -178,13 +184,46 @@ impl Config {
validate_config(self.source.clone(), document, raw)?.persist()
}
pub fn update_watch_shared_totp_entries(
&self,
entries: &BTreeSet<EntryPath>,
) -> Result<(), ConfigError> {
let mut document = self.current_document()?;
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
path: self.source.clone(),
})?;
let ui = root
.entry("ui")
.or_insert_with(|| toml::Value::Table(toml::Table::new()))
.as_table_mut()
.ok_or(ConfigError::InvalidField { field: "ui" })?;
ui.insert(
"watch_shared_totp_entries".to_owned(),
toml::Value::Array(
entries
.iter()
.map(|entry| toml::Value::String(entry.to_string()))
.collect(),
),
);
let raw = document
.clone()
.try_into::<RawConfig>()
.map_err(|_| ConfigError::Malformed {
path: self.source.clone(),
})?;
validate_config(self.source.clone(), document, raw)?.persist()
}
pub(crate) fn update_mobile_home_refresh(&self, unix_seconds: i64) -> Result<(), ConfigError> {
if unix_seconds <= 0 {
return Err(ConfigError::InvalidField {
field: "ui.home_remote_refreshed_at_unix_seconds",
});
}
let mut document = self.document.clone();
let mut document = self.current_document()?;
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
@@ -209,7 +248,7 @@ impl Config {
}
pub fn update_biometric_unlock(&self, enabled: bool) -> Result<(), ConfigError> {
let mut document = self.document.clone();
let mut document = self.current_document()?;
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
@@ -233,6 +272,10 @@ impl Config {
validate_config(self.source.clone(), document, raw)?.persist()
}
fn current_document(&self) -> Result<toml::Value, ConfigError> {
Self::load(Some(&self.source)).map(|config| config.document)
}
pub(crate) fn create_mobile_clone(
source: PathBuf,
vault: &Path,
@@ -873,6 +916,8 @@ struct RawSecurity {
struct RawUi {
selected_mobile_tab: Option<String>,
home_remote_refreshed_at_unix_seconds: Option<i64>,
#[serde(default)]
watch_shared_totp_entries: Vec<String>,
}
#[derive(Deserialize)]
@@ -964,6 +1009,16 @@ fn validate_config(
}
None => None,
};
let watch_shared_totp_entries = raw
.ui
.watch_shared_totp_entries
.into_iter()
.map(|entry| {
EntryPath::parse(&entry).map_err(|_| ConfigError::InvalidField {
field: "ui.watch_shared_totp_entries",
})
})
.collect::<Result<BTreeSet<_>, _>>()?;
let git_remotes = validate_remotes(raw.git.remotes)?;
Ok(Config {
@@ -978,6 +1033,7 @@ fn validate_config(
biometric_unlock_enabled,
mobile_tab,
mobile_home_refreshed_at,
watch_shared_totp_entries,
git_remotes,
})
}
@@ -1162,6 +1218,7 @@ fn validate_known_fields(value: &toml::Value, source: &Path) -> Result<(), Confi
&[
"selected_mobile_tab",
"home_remote_refreshed_at_unix_seconds",
"watch_shared_totp_entries",
],
)?;
}