age: Extract RecipientsAccumulator from cli_common::read_recipients

This commit is contained in:
Jack Grigg 2024-08-24 01:39:14 +00:00
parent d31fb568b7
commit 2f9cf3f86f
2 changed files with 110 additions and 83 deletions

View file

@ -2,7 +2,8 @@ use std::io::{self, BufReader};
use super::StdinGuard;
use super::{identities::parse_identity_files, ReadError};
use crate::{x25519, IdentityFileEntry, Recipient};
use crate::identity::RecipientsAccumulator;
use crate::{x25519, Recipient};
#[cfg(feature = "plugin")]
use crate::{cli_common::UiCallbacks, plugin};
@ -52,8 +53,7 @@ where
fn parse_recipient(
_filename: &str,
s: String,
recipients: &mut Vec<Box<dyn Recipient + Send>>,
#[cfg(feature = "plugin")] plugin_recipients: &mut Vec<plugin::Recipient>,
recipients: &mut RecipientsAccumulator,
) -> Result<(), ReadError> {
if let Ok(pk) = s.parse::<x25519::Recipient>() {
recipients.push(Box::new(pk));
@ -78,7 +78,7 @@ fn parse_recipient(
None::<Infallible>
} {
#[cfg(feature = "plugin")]
plugin_recipients.push(_recipient);
recipients.push_plugin(_recipient);
} else {
return Err(ReadError::InvalidRecipient(s));
}
@ -90,8 +90,7 @@ fn parse_recipient(
fn read_recipients_list<R: io::BufRead>(
filename: &str,
buf: R,
recipients: &mut Vec<Box<dyn Recipient + Send>>,
#[cfg(feature = "plugin")] plugin_recipients: &mut Vec<plugin::Recipient>,
recipients: &mut RecipientsAccumulator,
) -> Result<(), ReadError> {
for (line_number, line) in buf.lines().enumerate() {
let line = line?;
@ -99,13 +98,7 @@ fn read_recipients_list<R: io::BufRead>(
// Skip empty lines and comments
if line.is_empty() || line.find('#') == Some(0) {
continue;
} else if let Err(_e) = parse_recipient(
filename,
line,
recipients,
#[cfg(feature = "plugin")]
plugin_recipients,
) {
} else if let Err(_e) = parse_recipient(filename, line, recipients) {
#[cfg(feature = "ssh")]
match _e {
ReadError::RsaModulusTooLarge
@ -140,20 +133,10 @@ pub fn read_recipients(
max_work_factor: Option<u8>,
stdin_guard: &mut StdinGuard,
) -> Result<Vec<Box<dyn Recipient + Send>>, ReadError> {
let mut recipients: Vec<Box<dyn Recipient + Send>> = vec![];
#[cfg(feature = "plugin")]
let mut plugin_recipients: Vec<plugin::Recipient> = vec![];
#[cfg(feature = "plugin")]
let mut plugin_identities: Vec<plugin::Identity> = vec![];
let mut recipients = RecipientsAccumulator::new();
for arg in recipient_strings {
parse_recipient(
"",
arg,
&mut recipients,
#[cfg(feature = "plugin")]
&mut plugin_recipients,
)?;
parse_recipient("", arg, &mut recipients)?;
}
for arg in recipients_file_strings {
@ -164,29 +147,16 @@ pub fn read_recipients(
_ => e,
})?;
let buf = BufReader::new(f);
read_recipients_list(
&arg,
buf,
&mut recipients,
#[cfg(feature = "plugin")]
&mut plugin_recipients,
)?;
read_recipients_list(&arg, buf, &mut recipients)?;
}
#[cfg(feature = "plugin")]
let ctx = &mut (&mut recipients, &mut plugin_identities);
#[cfg(not(feature = "plugin"))]
let ctx = &mut recipients;
parse_identity_files::<_, ReadError>(
identity_strings,
max_work_factor,
stdin_guard,
ctx,
&mut recipients,
#[cfg(feature = "armor")]
|recipients, identity| {
#[cfg(feature = "plugin")]
let (recipients, _) = recipients;
recipients.extend(identity.recipients().map_err(|e| {
// Only one error can occur here.
if let EncryptError::EncryptedIdentities(e) = e {
@ -199,8 +169,6 @@ pub fn read_recipients(
},
#[cfg(feature = "ssh")]
|recipients, filename, identity| {
#[cfg(feature = "plugin")]
let (recipients, _) = recipients;
let recipient = parse_ssh_recipient(
|| ssh::Recipient::try_from(identity),
|| Err(ReadError::InvalidRecipient(filename.to_owned())),
@ -211,50 +179,28 @@ pub fn read_recipients(
Ok(())
},
|recipients, identity_file| {
for entry in identity_file.into_identities() {
#[cfg(feature = "plugin")]
let (recipients, plugin_identities) = recipients;
match entry {
IdentityFileEntry::Native(i) => recipients.push(Box::new(i.to_public())),
#[cfg(feature = "plugin")]
IdentityFileEntry::Plugin(i) => plugin_identities.push(i),
}
}
recipients.with_identities(identity_file);
Ok(())
},
)?;
#[cfg(feature = "plugin")]
{
// Collect the names of the required plugins.
let mut plugin_names = plugin_recipients
.iter()
.map(|r| r.plugin())
.chain(plugin_identities.iter().map(|i| i.plugin()))
.collect::<Vec<_>>();
plugin_names.sort_unstable();
plugin_names.dedup();
recipients
.build(
#[cfg(feature = "plugin")]
UiCallbacks,
)
.map_err(|_e| {
// Only one error can occur here.
#[cfg(feature = "plugin")]
{
if let EncryptError::MissingPlugin { binary_name } = _e {
ReadError::MissingPlugin { binary_name }
} else {
unreachable!()
}
}
// Find the required plugins.
for plugin_name in plugin_names {
recipients.push(Box::new(
plugin::RecipientPluginV1::new(
plugin_name,
&plugin_recipients,
&plugin_identities,
UiCallbacks,
)
.map_err(|e| {
// Only one error can occur here.
if let EncryptError::MissingPlugin { binary_name } = e {
ReadError::MissingPlugin { binary_name }
} else {
unreachable!()
}
})?,
))
}
}
Ok(recipients)
#[cfg(not(feature = "plugin"))]
unreachable!()
})
}

View file

@ -140,6 +140,87 @@ impl IdentityFile {
}
}
pub(crate) struct RecipientsAccumulator {
recipients: Vec<Box<dyn crate::Recipient + Send>>,
#[cfg(feature = "plugin")]
plugin_recipients: Vec<plugin::Recipient>,
#[cfg(feature = "plugin")]
plugin_identities: Vec<plugin::Identity>,
}
impl RecipientsAccumulator {
pub(crate) fn new() -> Self {
Self {
recipients: vec![],
#[cfg(feature = "plugin")]
plugin_recipients: vec![],
#[cfg(feature = "plugin")]
plugin_identities: vec![],
}
}
#[cfg(feature = "cli-common")]
pub(crate) fn push(&mut self, recipient: Box<dyn crate::Recipient + Send>) {
self.recipients.push(recipient);
}
#[cfg(feature = "plugin")]
pub(crate) fn push_plugin(&mut self, recipient: plugin::Recipient) {
self.plugin_recipients.push(recipient);
}
#[cfg(feature = "armor")]
pub(crate) fn extend(
&mut self,
iter: impl IntoIterator<Item = Box<dyn crate::Recipient + Send>>,
) {
self.recipients.extend(iter);
}
#[cfg(feature = "cli-common")]
pub(crate) fn with_identities(&mut self, identity_file: IdentityFile) {
for entry in identity_file.identities {
match entry {
IdentityFileEntry::Native(i) => self.recipients.push(Box::new(i.to_public())),
#[cfg(feature = "plugin")]
IdentityFileEntry::Plugin(i) => self.plugin_identities.push(i),
}
}
}
#[cfg_attr(not(feature = "plugin"), allow(unused_mut))]
pub(crate) fn build(
mut self,
#[cfg(feature = "plugin")] callbacks: impl Callbacks,
) -> Result<Vec<Box<dyn crate::Recipient + Send>>, EncryptError> {
#[cfg(feature = "plugin")]
{
// Collect the names of the required plugins.
let mut plugin_names = self
.plugin_recipients
.iter()
.map(|r| r.plugin())
.chain(self.plugin_identities.iter().map(|i| i.plugin()))
.collect::<Vec<_>>();
plugin_names.sort_unstable();
plugin_names.dedup();
// Find the required plugins.
for plugin_name in plugin_names {
self.recipients
.push(Box::new(plugin::RecipientPluginV1::new(
plugin_name,
&self.plugin_recipients,
&self.plugin_identities,
callbacks.clone(),
)?))
}
}
Ok(self.recipients)
}
}
#[cfg(test)]
pub(crate) mod tests {
use age_core::secrecy::ExposeSecret;