Synchronize selected TOTP entries to Apple Watch (#55)
This commit is contained in:
1 parent
affdb55519
commit
a055c93b86
12 files changed
+1888
-98
No files matched your search
@@ -21,6 +21,7 @@ use sha2::{Digest as _, Sha256};
|
||||
use crate::{
|
||||
crypto::{KeyStore, SecretProvider},
|
||||
document::EntryDocument,
|
||||
mobile_watch::{MobileWatchSnapshotState, MobileWatchSnapshotStatus, WatchSnapshotEntry},
|
||||
otp::{OtpError, OtpKind, OtpService},
|
||||
repository::{EncryptedEntry, EntryPath, Repository, RepositoryError, SecretBytes},
|
||||
};
|
||||
@@ -124,28 +125,6 @@ impl MobileTotpOperation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum MobileWatchSnapshotState {
|
||||
Unavailable,
|
||||
Pending,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct MobileWatchSnapshotStatus {
|
||||
state: MobileWatchSnapshotState,
|
||||
detail: String,
|
||||
}
|
||||
|
||||
impl MobileWatchSnapshotStatus {
|
||||
pub fn state(&self) -> MobileWatchSnapshotState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn detail(&self) -> &str {
|
||||
&self.detail
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct MobileTotpRow {
|
||||
path: String,
|
||||
@@ -206,6 +185,10 @@ impl MobileTotpPage {
|
||||
pub fn watch(&self) -> &MobileWatchSnapshotStatus {
|
||||
&self.watch
|
||||
}
|
||||
|
||||
pub(crate) fn set_watch(&mut self, watch: MobileWatchSnapshotStatus) {
|
||||
self.watch = watch;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MobileTotpDetail {
|
||||
@@ -251,6 +234,21 @@ impl MobileTotpDetail {
|
||||
pub fn watch(&self) -> &MobileWatchSnapshotStatus {
|
||||
&self.watch
|
||||
}
|
||||
|
||||
pub(crate) fn set_watch(&mut self, watch: MobileWatchSnapshotStatus) {
|
||||
self.watch = watch;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WatchSnapshotSelection {
|
||||
entries: Vec<WatchSnapshotEntry>,
|
||||
retained: BTreeSet<EntryPath>,
|
||||
}
|
||||
|
||||
impl WatchSnapshotSelection {
|
||||
pub fn into_parts(self) -> (Vec<WatchSnapshotEntry>, BTreeSet<EntryPath>) {
|
||||
(self.entries, self.retained)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MobileTotpDetail {
|
||||
@@ -500,6 +498,40 @@ impl<'a> MobileTotpService<'a> {
|
||||
}
|
||||
detail_for_uri(&path, uri, unix_seconds, shared)
|
||||
}
|
||||
|
||||
pub fn watch_snapshot_selection(
|
||||
&self,
|
||||
selected: &BTreeSet<EntryPath>,
|
||||
provider: &mut impl SecretProvider,
|
||||
) -> Result<WatchSnapshotSelection, MobileTotpError> {
|
||||
let mut entries = Vec::with_capacity(selected.len());
|
||||
let mut retained = BTreeSet::new();
|
||||
for path in selected {
|
||||
let uri = match OtpService::new(self.repository, self.keys)
|
||||
.uri(&path.to_string(), provider)
|
||||
{
|
||||
Ok(uri) if uri.kind() == OtpKind::Totp => uri,
|
||||
Ok(_) => continue,
|
||||
Err(OtpError::Repository(RepositoryError::NotFound { .. }))
|
||||
| Err(OtpError::MissingUri { .. } | OtpError::AmbiguousUri { .. }) => continue,
|
||||
Err(OtpError::Crypto(error)) => return Err(OtpError::Crypto(error).into()),
|
||||
Err(OtpError::Repository(error)) => return Err(error.into()),
|
||||
Err(_) => continue,
|
||||
};
|
||||
let period = uri.period().ok_or(OtpError::NotTotp)?;
|
||||
retained.insert(path.clone());
|
||||
entries.push(WatchSnapshotEntry::new(
|
||||
path.clone(),
|
||||
uri.issuer().map(str::to_owned),
|
||||
uri.account().to_owned(),
|
||||
uri.algorithm(),
|
||||
uri.digits(),
|
||||
period,
|
||||
uri.watch_secret(),
|
||||
));
|
||||
}
|
||||
Ok(WatchSnapshotSelection { entries, retained })
|
||||
}
|
||||
}
|
||||
|
||||
fn detail_for_uri(
|
||||
@@ -755,11 +787,13 @@ fn snapshot_status(selected: usize) -> MobileWatchSnapshotStatus {
|
||||
if selected == 0 {
|
||||
MobileWatchSnapshotStatus {
|
||||
state: MobileWatchSnapshotState::Unavailable,
|
||||
revision: None,
|
||||
detail: "No TOTP codes are selected for Apple Watch.".to_owned(),
|
||||
}
|
||||
} else {
|
||||
MobileWatchSnapshotStatus {
|
||||
state: MobileWatchSnapshotState::Pending,
|
||||
revision: None,
|
||||
detail: format!(
|
||||
"{selected} selected TOTP {} pending Apple Watch synchronization.",
|
||||
if selected == 1 {
|
||||
|
||||
Reference in new issue
Block a user