pvsecret: Support retrieving a secret by index

Allow users to retrieve a secret by its index. This allows users to
extract a specific secret in case of multiple ids.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Tested-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Steffen Eiden
2025-04-23 11:24:28 +02:00
committed by Jan Höppner
parent e62cdf9a6e
commit 1fefdecbe7
2 changed files with 60 additions and 28 deletions

View File

@@ -351,6 +351,8 @@ pub enum RetrInpFmt {
Hex,
/// Use a name-string. Will hash it if no secret with the name found.
Name,
/// Use the secret-index (base 10) instead of the secret-ID
Idx,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]

View File

@@ -2,7 +2,7 @@
//
// Copyright IBM Corp. 2024
use std::collections::VecDeque;
use std::{collections::VecDeque, fmt::Display};
use anyhow::{anyhow, bail, Context, Result};
use log::{debug, info, warn};
@@ -17,6 +17,42 @@ use utils::get_writer_from_cli_file_arg;
use super::list::list_uvc;
use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions};
enum Value {
Id(SecretId),
Idx(u16),
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Id(id) => write!(f, "ID {id}"),
Value::Idx(idx) => write!(f, "index {idx}"),
}
}
}
impl TryFrom<&RetrSecretOptions> for Value {
type Error = anyhow::Error;
fn try_from(opt: &RetrSecretOptions) -> Result<Self> {
match opt.inform {
RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
GuestSecret::Retrievable { id, .. } => Ok(Self::Id(id)),
gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
},
RetrInpFmt::Hex => serde_yaml::from_str(&opt.input)
.context("Cannot parse SecretId information")
.map(Self::Id),
RetrInpFmt::Name => Ok(Self::Id(SecretId::from_string(&opt.input))),
RetrInpFmt::Idx => opt
.input
.parse()
.context("Invalid index value")
.map(Self::Idx),
}
}
}
fn find_secret_by_id(secrets: &SecretList, id: &SecretId) -> Option<SecretEntry> {
let mut secrets: VecDeque<_> = secrets
.into_iter()
@@ -37,26 +73,31 @@ fn find_secret_by_id(secrets: &SecretList, id: &SecretId) -> Option<SecretEntry>
secret.cloned()
}
fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
fn retrieve(value: Value) -> Result<RetrievedSecret> {
let uv = UvDevice::open()?;
let secrets = list_uvc(&uv)?;
let secret = match find_secret_by_id(&secrets, id) {
Some(s) => s,
// hash it + try again if it is ASCII-representable
None => match id.as_ascii() {
Some(s) => find_secret_by_id(&secrets, &GuestSecret::name_to_id(s)?),
None => None,
let entry = match &value {
Value::Id(id) => {
match find_secret_by_id(&secrets, id) {
Some(s) => Some(s),
// hash it + try again if it is ASCII-representable
None => match id.as_ascii() {
Some(s) => find_secret_by_id(&secrets, &GuestSecret::name_to_id(s)?),
None => None,
},
}
}
.ok_or(anyhow!(
"The UV secret-store has no secret with the ID {id}"
))?,
};
Value::Idx(idx) => secrets.into_iter().find(|e| &e.index() == idx),
}
.ok_or(anyhow!(
"The UV secret-store has no secret with the {value}"
))?;
info!("Try to retrieve secret at index: {}", secret.index());
debug!("Try to retrieve: {secret:?}");
info!("Try to retrieve secret at index: {}", entry.index());
debug!("Try to retrieve: {entry:?}");
let mut uv_cmd = RetrieveCmd::from_entry(secret)?;
let mut uv_cmd = RetrieveCmd::from_entry(entry)?;
uv.send_cmd(&mut uv_cmd)?;
Ok(RetrievedSecret::from_cmd(uv_cmd))
@@ -64,19 +105,8 @@ fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
let mut output = get_writer_from_cli_file_arg(&opt.output)?;
let id = match &opt.inform {
RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
GuestSecret::Retrievable { id, .. } => id,
gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
},
RetrInpFmt::Hex => {
serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
}
RetrInpFmt::Name => SecretId::from_string(&opt.input),
};
let retr_secret =
retrieve(&id).context("Could not retrieve the secret from the UV secret store.")?;
let retr_secret = retrieve(opt.try_into()?)
.context("Could not retrieve the secret from the UV secret store.")?;
let out_data = match opt.outform {
RetrOutFmt::Bin => retr_secret.into_bytes(),