Cache TOTP discovery and report progress (#74)

This commit is contained in:
hugo committed 2026-08-11 23:15:23 +02:00
1 parent 6edcb5fc86
commit 9759162eff
12 files changed
+1577 -198

No files matched your search

+37 -4
View File
@@ -19,7 +19,10 @@ use crate::{
MobileMutationAction, MobileMutationError, MobileMutationOutcome, MobileMutationPlan,
MobileMutationRequest, MobileMutationService,
},
mobile_totp::{MobileTotpDetail, MobileTotpError, MobileTotpPage, MobileTotpService},
mobile_totp::{
MobileTotpDetail, MobileTotpError, MobileTotpOperation, MobileTotpPage, MobileTotpService,
},
otp::OtpError,
recipient::RecipientPolicyManager,
repository::{
DirectoryPath, EncryptedEntry, EntryPath, Repository, RepositoryError, SecretBytes,
@@ -177,6 +180,7 @@ pub struct MobileAuthentication {
keys: KeyStore,
session: NativeAuthenticationSession,
status: Mutex<MobileAuthenticationStatus>,
totp_discovery: Mutex<()>,
}
impl MobileAuthentication {
@@ -208,6 +212,7 @@ impl MobileAuthentication {
repository,
keys,
session,
totp_discovery: Mutex::new(()),
})
}
@@ -481,8 +486,17 @@ impl MobileAuthentication {
self.unlock_ciphertext(&ciphertext, passphrase)
}
pub fn totp_page(&self) -> Result<MobileTotpPage, MobileAuthenticationError> {
pub fn totp_page(
&self,
operation: &MobileTotpOperation,
) -> Result<MobileTotpPage, MobileAuthenticationError> {
self.ensure_active()?;
let _discovery = self.totp_discovery.lock().map_err(|_| {
entry_detail(
"TOTP Discovery Is Unavailable",
"the TOTP discovery lock is unavailable",
)
})?;
let (handle, key, shared) = {
let status = self.status()?;
let active = status.active.as_ref().ok_or_else(locked_error)?;
@@ -492,12 +506,19 @@ impl MobileAuthentication {
status.watch_shared_totp_entries.clone(),
)
};
let cache_path = self.config.source().with_file_name("totp-catalog.toml");
let mut provider = KeyOnlyProvider::new(handle, &key);
MobileTotpService::new(&self.repository, &self.keys)
.page(&shared, &mut provider)
.discover(&shared, &mut provider, &cache_path, operation)
.map_err(totp_error)
}
pub fn cached_totp_page(&self) -> Result<Option<MobileTotpPage>, MobileAuthenticationError> {
let shared = self.status()?.watch_shared_totp_entries.clone();
let cache_path = self.config.source().with_file_name("totp-catalog.toml");
Ok(MobileTotpService::new(&self.repository, &self.keys).cached_page(&shared, &cache_path))
}
pub fn totp_detail(
&self,
path: &str,
@@ -1043,7 +1064,19 @@ fn editor_error(error: MobileEntryEditorError) -> MobileAuthenticationError {
}
fn totp_error(error: MobileTotpError) -> MobileAuthenticationError {
entry_detail("TOTP Is Unavailable", error)
match error {
MobileTotpError::Otp(OtpError::Cancelled) => MobileAuthenticationError::new(
MobileAuthenticationErrorKind::Cancelled,
"TOTP Discovery Cancelled",
"TOTP discovery was cancelled",
),
MobileTotpError::ConcurrentModification => MobileAuthenticationError::new(
MobileAuthenticationErrorKind::Conflict,
"TOTP Discovery Needs Refreshing",
"the password store changed while TOTP discovery was in progress",
),
error => entry_detail("TOTP Is Unavailable", error),
}
}
fn editor_missing() -> MobileAuthenticationError {