tfa: entry access/iteration cleanup

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-12-14 16:35:41 +01:00
parent d831846706
commit f58e5132aa

View File

@ -107,6 +107,31 @@ fn to_data(data: TfaUserData) -> Vec<TypedTfaInfo> {
out out
} }
/// Iterate through tuples of `(type, index, id)`.
fn tfa_id_iter(data: &TfaUserData) -> impl Iterator<Item = (TfaType, usize, &str)> {
data.totp
.iter()
.enumerate()
.map(|(i, entry)| (TfaType::Totp, i, entry.info.id.as_str()))
.chain(
data.webauthn
.iter()
.enumerate()
.map(|(i, entry)| (TfaType::Webauthn, i, entry.info.id.as_str())),
)
.chain(
data.u2f
.iter()
.enumerate()
.map(|(i, entry)| (TfaType::U2f, i, entry.info.id.as_str())),
)
.chain(
data.recovery
.iter()
.map(|_| (TfaType::Recovery, 0, "recovery")),
)
}
#[api( #[api(
protected: true, protected: true,
input: { input: {
@ -149,40 +174,37 @@ pub fn get_tfa_entry(userid: Userid, id: String) -> Result<TypedTfaInfo, Error>
let _lock = crate::config::tfa::read_lock()?; let _lock = crate::config::tfa::read_lock()?;
if let Some(user_data) = crate::config::tfa::read()?.users.remove(&userid) { if let Some(user_data) = crate::config::tfa::read()?.users.remove(&userid) {
if id == "recovery" { match {
if user_data.has_recovery() { // scope to prevent the temprary iter from borrowing across the whole match
let entry = tfa_id_iter(&user_data).find(|(_ty, _index, entry_id)| id == *entry_id);
entry.map(|(ty, index, _)| (ty, index))
} {
Some((TfaType::Recovery, _)) => {
return Ok(TypedTfaInfo { return Ok(TypedTfaInfo {
ty: TfaType::Recovery, ty: TfaType::Recovery,
info: TfaInfo::recovery(), info: TfaInfo::recovery(),
}); })
} }
} else { Some((TfaType::Totp, index)) => {
for tfa in user_data.totp {
if tfa.info.id == id {
return Ok(TypedTfaInfo { return Ok(TypedTfaInfo {
ty: TfaType::Totp, ty: TfaType::Totp,
info: tfa.info, // `into_iter().nth()` to *move* out of it
info: user_data.totp.into_iter().nth(index).unwrap().info,
}); });
} }
} Some((TfaType::Webauthn, index)) => {
for tfa in user_data.webauthn {
if tfa.info.id == id {
return Ok(TypedTfaInfo { return Ok(TypedTfaInfo {
ty: TfaType::Webauthn, ty: TfaType::Webauthn,
info: tfa.info, info: user_data.webauthn.into_iter().nth(index).unwrap().info,
}); });
} }
} Some((TfaType::U2f, index)) => {
for tfa in user_data.u2f {
if tfa.info.id == id {
return Ok(TypedTfaInfo { return Ok(TypedTfaInfo {
ty: TfaType::U2f, ty: TfaType::U2f,
info: tfa.info, info: user_data.u2f.into_iter().nth(index).unwrap().info,
}); });
} }
} None => (),
} }
} }
@ -228,29 +250,16 @@ pub fn delete_tfa(
.get_mut(&userid) .get_mut(&userid)
.ok_or_else(|| http_err!(NOT_FOUND, "no such entry: {}/{}", userid, id))?; .ok_or_else(|| http_err!(NOT_FOUND, "no such entry: {}/{}", userid, id))?;
let found = if id == "recovery" { match {
let found = user_data.has_recovery(); // scope to prevent the temprary iter from borrowing across the whole match
user_data.recovery = None; let entry = tfa_id_iter(&user_data).find(|(_, _, entry_id)| id == *entry_id);
found entry.map(|(ty, index, _)| (ty, index))
} else if let Some(i) = user_data.totp.iter().position(|entry| entry.info.id == id) { } {
user_data.totp.remove(i); Some((TfaType::Recovery, _)) => user_data.recovery = None,
true Some((TfaType::Totp, index)) => drop(user_data.totp.remove(index)),
} else if let Some(i) = user_data Some((TfaType::Webauthn, index)) => drop(user_data.webauthn.remove(index)),
.webauthn Some((TfaType::U2f, index)) => drop(user_data.u2f.remove(index)),
.iter() None => http_bail!(NOT_FOUND, "no such tfa entry: {}/{}", userid, id),
.position(|entry| entry.info.id == id)
{
user_data.webauthn.remove(i);
true
} else if let Some(i) = user_data.u2f.iter().position(|entry| entry.info.id == id) {
user_data.u2f.remove(i);
true
} else {
false
};
if !found {
http_bail!(NOT_FOUND, "no such tfa entry: {}/{}", userid, id);
} }
if user_data.is_empty() { if user_data.is_empty() {