Fix pass Git config and lazy OTP commits

This commit is contained in:
Hermes Agent
2026-08-10 09:42:53 +00:00
parent 6ea184cbc1
commit c86ea9eb0e
8 changed files with 231 additions and 22 deletions

View File

@@ -11,6 +11,7 @@ use zeroize::Zeroize as _;
use crate::{
command::{OtpAppendRequest, OtpInputSource, OtpInsertRequest},
crypto::{CryptoError, KeyStore, SecretProvider},
git::{AutomaticEntryCommitter, GitError, GitIdentity},
recipient::{RecipientPolicyError, RecipientPolicyManager, SigningPolicy},
repository::{EncryptedEntry, EntryPath, Repository, RepositoryError, SecretBytes},
write::{EntryAction, EntryCommit, EntryCommitError, EntryCommitter, OverwriteDecision},
@@ -613,12 +614,80 @@ impl<'a> OtpService<'a> {
provider: &mut impl SecretProvider,
committer: &mut impl EntryCommitter,
) -> Result<OtpCodeOutcome, OtpError> {
let (path, original, plaintext, range, uri) = self.load_code_entry(entry, provider)?;
self.finish_code(
path,
original,
plaintext,
range,
uri,
unix_seconds,
signing,
committer,
)
}
/// Generate a code with storage-owned lazy Git selection. TOTP is
/// read-only and never opens Git; HOTP opens the innermost repository only
/// after the token has been decrypted and identified as counter based.
pub fn code_automatic(
&self,
entry: &str,
unix_seconds: u64,
signing: Option<&SigningPolicy>,
provider: &mut impl SecretProvider,
) -> Result<OtpCodeOutcome, OtpError> {
let (path, original, plaintext, range, uri) = self.load_code_entry(entry, provider)?;
if uri.kind() == OtpKind::Totp {
return Ok(OtpCodeOutcome {
code: uri.code_at(unix_seconds)?,
counter: None,
});
}
let entry = path.to_string();
let mut committer = AutomaticEntryCommitter::for_entry(
self.repository,
&entry,
GitIdentity::ironstorage(),
)?;
self.finish_code(
path,
original,
plaintext,
range,
uri,
unix_seconds,
signing,
&mut committer,
)
}
fn load_code_entry(
&self,
entry: &str,
provider: &mut impl SecretProvider,
) -> Result<(EntryPath, EncryptedEntry, SecretBytes, Range<usize>, OtpUri), OtpError> {
let path = parse_entry(entry)?;
let original = self.repository.read_entry(&path)?;
let plaintext = self.keys.decrypt(&original, provider)?;
let (range, uri) = find_uri(&plaintext, &path)?.ok_or_else(|| OtpError::MissingUri {
entry: path.clone(),
})?;
Ok((path, original, plaintext, range, uri))
}
#[allow(clippy::too_many_arguments)]
fn finish_code(
&self,
path: EntryPath,
original: EncryptedEntry,
plaintext: SecretBytes,
range: Range<usize>,
uri: OtpUri,
unix_seconds: u64,
signing: Option<&SigningPolicy>,
committer: &mut impl EntryCommitter,
) -> Result<OtpCodeOutcome, OtpError> {
match uri.kind() {
OtpKind::Totp => Ok(OtpCodeOutcome {
code: uri.code_at(unix_seconds)?,
@@ -722,6 +791,7 @@ pub enum OtpError {
Repository(RepositoryError),
Crypto(CryptoError),
RecipientPolicy(RecipientPolicyError),
Git(GitError),
InvalidUri,
InvalidScheme,
UnsupportedType,
@@ -771,6 +841,7 @@ impl fmt::Display for OtpError {
Self::Repository(error) => error.fmt(formatter),
Self::Crypto(error) => error.fmt(formatter),
Self::RecipientPolicy(error) => error.fmt(formatter),
Self::Git(error) => error.fmt(formatter),
Self::InvalidUri => formatter.write_str("OTP key URI is not valid UTF-8 URI text"),
Self::InvalidScheme => formatter.write_str("OTP key URI must use the otpauth scheme"),
Self::UnsupportedType => formatter.write_str("OTP key URI type must be totp or hotp"),
@@ -848,6 +919,12 @@ impl From<RecipientPolicyError> for OtpError {
}
}
impl From<GitError> for OtpError {
fn from(error: GitError) -> Self {
Self::Git(error)
}
}
fn validate_input(input: &[u8]) -> Result<(), OtpError> {
if input.is_empty() {
Err(OtpError::EmptyInput)