tfa: add 'created' timestamp to entries
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
bad6e32075
commit
ad5cee1d22
@ -82,12 +82,12 @@ fn to_data(data: TfaUserData) -> Vec<TypedTfaInfo> {
|
|||||||
data.totp.len()
|
data.totp.len()
|
||||||
+ data.u2f.len()
|
+ data.u2f.len()
|
||||||
+ data.webauthn.len()
|
+ data.webauthn.len()
|
||||||
+ if data.has_recovery() { 1 } else { 0 },
|
+ if data.recovery().is_some() { 1 } else { 0 },
|
||||||
);
|
);
|
||||||
if data.has_recovery() {
|
if let Some(recovery) = data.recovery() {
|
||||||
out.push(TypedTfaInfo {
|
out.push(TypedTfaInfo {
|
||||||
ty: TfaType::Recovery,
|
ty: TfaType::Recovery,
|
||||||
info: TfaInfo::recovery(),
|
info: TfaInfo::recovery(recovery.created),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for entry in data.totp {
|
for entry in data.totp {
|
||||||
@ -184,10 +184,12 @@ fn get_tfa_entry(userid: Userid, id: String) -> Result<TypedTfaInfo, Error> {
|
|||||||
entry.map(|(ty, index, _)| (ty, index))
|
entry.map(|(ty, index, _)| (ty, index))
|
||||||
} {
|
} {
|
||||||
Some((TfaType::Recovery, _)) => {
|
Some((TfaType::Recovery, _)) => {
|
||||||
return Ok(TypedTfaInfo {
|
if let Some(recovery) = user_data.recovery() {
|
||||||
ty: TfaType::Recovery,
|
return Ok(TypedTfaInfo {
|
||||||
info: TfaInfo::recovery(),
|
ty: TfaType::Recovery,
|
||||||
})
|
info: TfaInfo::recovery(recovery.created),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some((TfaType::Totp, index)) => {
|
Some((TfaType::Totp, index)) => {
|
||||||
return Ok(TypedTfaInfo {
|
return Ok(TypedTfaInfo {
|
||||||
|
@ -345,6 +345,9 @@ pub struct TfaInfo {
|
|||||||
/// User chosen description for this entry.
|
/// User chosen description for this entry.
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
|
||||||
|
/// Creation time of this entry as unix epoch.
|
||||||
|
pub created: i64,
|
||||||
|
|
||||||
/// Whether this TFA entry is currently enabled.
|
/// Whether this TFA entry is currently enabled.
|
||||||
#[serde(skip_serializing_if = "is_default_tfa_enable")]
|
#[serde(skip_serializing_if = "is_default_tfa_enable")]
|
||||||
#[serde(default = "default_tfa_enable")]
|
#[serde(default = "default_tfa_enable")]
|
||||||
@ -353,11 +356,12 @@ pub struct TfaInfo {
|
|||||||
|
|
||||||
impl TfaInfo {
|
impl TfaInfo {
|
||||||
/// For recovery keys we have a fixed entry.
|
/// For recovery keys we have a fixed entry.
|
||||||
pub(crate) fn recovery() -> Self {
|
pub(crate) fn recovery(created: i64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: "recovery".to_string(),
|
id: "recovery".to_string(),
|
||||||
description: "recovery keys".to_string(),
|
description: "recovery keys".to_string(),
|
||||||
enable: true,
|
enable: true,
|
||||||
|
created,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,6 +387,7 @@ impl<T> TfaEntry<T> {
|
|||||||
id: Uuid::generate().to_string(),
|
id: Uuid::generate().to_string(),
|
||||||
enable: true,
|
enable: true,
|
||||||
description,
|
description,
|
||||||
|
created: proxmox::tools::time::epoch_i64(),
|
||||||
},
|
},
|
||||||
entry,
|
entry,
|
||||||
}
|
}
|
||||||
@ -748,9 +753,13 @@ pub struct TfaUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TfaUserData {
|
impl TfaUserData {
|
||||||
/// Shortcut for the option type.
|
/// Shortcut to get the recovery entry only if it is not empty!
|
||||||
pub fn has_recovery(&self) -> bool {
|
pub fn recovery(&self) -> Option<&Recovery> {
|
||||||
!Recovery::option_is_empty(&self.recovery)
|
if Recovery::option_is_empty(&self.recovery) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.recovery.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `true` if no second factors exist
|
/// `true` if no second factors exist
|
||||||
@ -758,7 +767,7 @@ impl TfaUserData {
|
|||||||
self.totp.is_empty()
|
self.totp.is_empty()
|
||||||
&& self.u2f.is_empty()
|
&& self.u2f.is_empty()
|
||||||
&& self.webauthn.is_empty()
|
&& self.webauthn.is_empty()
|
||||||
&& !self.has_recovery()
|
&& self.recovery().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find an entry by id, except for the "recovery" entry which we're currently treating
|
/// Find an entry by id, except for the "recovery" entry which we're currently treating
|
||||||
@ -1087,8 +1096,16 @@ impl TfaUserData {
|
|||||||
/// Recovery entries. We use HMAC-SHA256 with a random secret as a salted hash replacement.
|
/// Recovery entries. We use HMAC-SHA256 with a random secret as a salted hash replacement.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Recovery {
|
pub struct Recovery {
|
||||||
|
/// "Salt" used for the key HMAC.
|
||||||
secret: String,
|
secret: String,
|
||||||
|
|
||||||
|
/// Recovery key entries are HMACs of the original data. When used up they will become `None`
|
||||||
|
/// since the user is presented an enumerated list of codes, so we know the indices of used and
|
||||||
|
/// unused codes.
|
||||||
entries: Vec<Option<String>>,
|
entries: Vec<Option<String>>,
|
||||||
|
|
||||||
|
/// Creation timestamp as a unix epoch.
|
||||||
|
pub created: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Recovery {
|
impl Recovery {
|
||||||
@ -1101,6 +1118,7 @@ impl Recovery {
|
|||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
secret: AsHex(&secret).to_string(),
|
secret: AsHex(&secret).to_string(),
|
||||||
entries: Vec::with_capacity(10),
|
entries: Vec::with_capacity(10),
|
||||||
|
created: proxmox::tools::time::epoch_i64(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut original = Vec::new();
|
let mut original = Vec::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user