Implement iPhone and Apple Watch preferences (#53)

This commit is contained in:
hugo committed 2026-08-15 22:43:51 +02:00
1 parent 81886756a2
commit affdb55519
15 files changed
+1409 -60

No files matched your search

+35 -5
View File
@@ -134,7 +134,7 @@ struct LeaseState {
struct Shared<B: SecretStoreBackend, C: AuthenticationClock> {
store: SecretStore<B>,
timeout: AuthenticationTimeout,
timeout: Mutex<AuthenticationTimeout>,
clock: C,
/// Serializes access checks with relock, eliminating check-then-use races.
operation: Mutex<()>,
@@ -168,9 +168,14 @@ impl<B: SecretStoreBackend, C: AuthenticationClock> Clone for AuthenticationSess
impl<B: SecretStoreBackend, C: AuthenticationClock> fmt::Debug for AuthenticationSession<B, C> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let timeout = self
.shared
.timeout
.lock()
.map_or_else(|poisoned| *poisoned.into_inner(), |timeout| *timeout);
formatter
.debug_struct("AuthenticationSession")
.field("timeout", &self.shared.timeout)
.field("timeout", &timeout)
.field("unlock_material", &"[REDACTED]")
.finish()
}
@@ -203,7 +208,7 @@ impl<B: SecretStoreBackend, C: AuthenticationClock> AuthenticationSession<B, C>
// The lease is the only cache. This prevents a second lifetime
// policy in SecretStore from retaining duplicate records.
store: SecretStore::new(backend, SecretCachePolicy::Disabled, protections),
timeout,
timeout: Mutex::new(timeout),
clock,
operation: Mutex::new(()),
state: Mutex::new(LeaseState {
@@ -308,6 +313,23 @@ impl<B: SecretStoreBackend, C: AuthenticationClock> AuthenticationSession<B, C>
let _operation = self.shared.operation()?;
self.shared.revoke(RevocationReason::Cancelled)
}
/// Apply a new shared inactivity timeout to this process and any active lease.
pub fn set_timeout(&self, timeout: AuthenticationTimeout) -> Result<(), AuthenticationError> {
let _operation = self.shared.operation()?;
self.shared.expire_if_needed()?;
let deadline = self
.shared
.clock
.now()
.checked_add(timeout.duration())
.ok_or(AuthenticationError::ClockOverflow)?;
*self.shared.timeout()? = timeout;
if let Some(active) = self.shared.state()?.active.as_mut() {
active.deadline = deadline;
}
Ok(())
}
}
/// Generation-bound secret access. Clones are revoked together.
@@ -351,7 +373,7 @@ impl<B: SecretStoreBackend, C: AuthenticationClock> AuthenticationHandle<B, C> {
self.shared.expire_if_needed()?;
let now = self.shared.clock.now();
let deadline = now
.checked_add(self.shared.timeout.duration())
.checked_add(self.shared.timeout()?.duration())
.ok_or(AuthenticationError::ClockOverflow)?;
self.shared
.with_active(self.generation, |active| active.deadline = deadline)
@@ -432,10 +454,18 @@ impl<B: SecretStoreBackend, C: AuthenticationClock> Shared<B, C> {
.map_err(|_| AuthenticationError::SecretStore(SecretStoreError::Unavailable))
}
fn timeout(
&self,
) -> Result<std::sync::MutexGuard<'_, AuthenticationTimeout>, AuthenticationError> {
self.timeout
.lock()
.map_err(|_| AuthenticationError::SecretStore(SecretStoreError::Unavailable))
}
fn begin_lease(&self) -> Result<u64, AuthenticationError> {
let now = self.clock.now();
let deadline = now
.checked_add(self.timeout.duration())
.checked_add(self.timeout()?.duration())
.ok_or(AuthenticationError::ClockOverflow)?;
let (generation, was_active) = {
let mut state = self.state()?;