Implement structured iPhone entry viewer

This commit is contained in:
2026-08-11 19:59:18 +02:00
parent 873db91204
commit 900e62e523
9 changed files with 1728 additions and 92 deletions

View File

@@ -8,6 +8,9 @@ use crate::{
},
config::{Config, ConfigError},
crypto::{CryptoError, KeyInfo, KeyStore, SecretProvider, SecretProviderError},
document::{DocumentError, EntryDocument, EntryDocumentService, EntryFieldId},
git::{AutomaticEntryCommitter, GitIdentity},
mobile_entry::{MobileEntryPage, MobileEntryValueError, field_value},
repository::{EntryPath, Repository, RepositoryError, SecretBytes},
secret_store::{SecretProtectionPolicy, SecretStoreError},
};
@@ -109,6 +112,22 @@ pub struct MobileAuthenticationState {
remaining_seconds: u64,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MobileEntryCopy {
value: String,
timeout_seconds: u64,
}
impl MobileEntryCopy {
pub fn value(&self) -> &str {
&self.value
}
pub fn timeout_seconds(&self) -> u64 {
self.timeout_seconds
}
}
impl MobileAuthenticationState {
pub fn unlocked(self) -> bool {
self.unlocked
@@ -328,6 +347,50 @@ impl MobileAuthentication {
.map_err(MobileAuthenticationError::authentication)
}
pub fn entry_page(&self, path: &str) -> Result<MobileEntryPage, MobileAuthenticationError> {
let document = self.open_active_document(path)?;
Ok(MobileEntryPage::from_document(&document))
}
pub fn reveal_entry_field(
&self,
path: &str,
field: u64,
) -> Result<String, MobileAuthenticationError> {
let document = self.open_active_document(path)?;
field_value(&document, field).map_err(value_error)
}
pub fn copy_entry_field(
&self,
path: &str,
field: u64,
) -> Result<MobileEntryCopy, MobileAuthenticationError> {
Ok(MobileEntryCopy {
value: self.reveal_entry_field(path, field)?,
timeout_seconds: self.config.clipboard_timeout().duration().as_secs(),
})
}
pub fn replace_entry_field(
&self,
path: &str,
field: u64,
value: String,
) -> Result<MobileEntryPage, MobileAuthenticationError> {
let mut document = self.open_active_document(path)?;
document
.replace_field_value(EntryFieldId::from_value(field), value.into_bytes())
.map_err(document_error)?;
let mut committer =
AutomaticEntryCommitter::for_entry(&self.repository, path, GitIdentity::ironstorage())
.map_err(|error| entry_detail("Password Entry Could Not Be Saved", error))?;
EntryDocumentService::new(&self.repository, &self.keys)
.save_recoverable(&document, None, &mut committer)
.map_err(document_error)?;
Ok(MobileEntryPage::from_document(&document))
}
pub fn manual_lock(&self) -> Result<(), MobileAuthenticationError> {
self.session
.manual_lock()
@@ -383,6 +446,21 @@ impl MobileAuthentication {
self.status()?.active = Some(ActiveMobileLease { handle, key });
Ok(())
}
fn open_active_document(&self, path: &str) -> Result<EntryDocument, MobileAuthenticationError> {
let (handle, key) = {
let status = self.status()?;
let active = status.active.as_ref().ok_or_else(locked_error)?;
(active.handle.clone(), active.key.clone())
};
handle
.ensure_active()
.map_err(MobileAuthenticationError::authentication)?;
let mut provider = KeyOnlyProvider::new(handle, &key);
EntryDocumentService::new(&self.repository, &self.keys)
.open(path, &mut provider)
.map_err(document_error)
}
}
struct KeyOnlyProvider<'a> {
@@ -431,3 +509,27 @@ fn key_error(error: CryptoError) -> MobileAuthenticationError {
error.to_string(),
)
}
fn locked_error() -> MobileAuthenticationError {
MobileAuthenticationError::new(
MobileAuthenticationErrorKind::Expired,
"IronStorage Locked",
"Authenticate before using protected content.",
)
}
fn document_error(error: DocumentError) -> MobileAuthenticationError {
entry_detail("Password Entry Is Unavailable", error)
}
fn value_error(error: MobileEntryValueError) -> MobileAuthenticationError {
entry_detail("Field Value Is Unavailable", error)
}
fn entry_detail(title: &str, error: impl fmt::Display) -> MobileAuthenticationError {
MobileAuthenticationError::new(
MobileAuthenticationErrorKind::Entry,
title,
error.to_string(),
)
}