Implement structured iPhone entry viewer
This commit is contained in:
@@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user