all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH backup 1/2] tfa: remember recovery indices
@ 2021-01-18 11:46 Wolfgang Bumiller
  2021-01-18 11:46 ` [pbs-devel] [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window Wolfgang Bumiller
  2021-01-18 12:52 ` [pbs-devel] applied: [PATCH backup 1/2] tfa: remember recovery indices Thomas Lamprecht
  0 siblings, 2 replies; 4+ messages in thread
From: Wolfgang Bumiller @ 2021-01-18 11:46 UTC (permalink / raw)
  To: pbs-devel

and tell the client which keys are still available rather
than just yes/no/low

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 src/config/tfa.rs | 76 ++++++++++++++++++++++-------------------------
 1 file changed, 35 insertions(+), 41 deletions(-)

diff --git a/src/config/tfa.rs b/src/config/tfa.rs
index e0f2fcfe..5d01ea82 100644
--- a/src/config/tfa.rs
+++ b/src/config/tfa.rs
@@ -1088,7 +1088,7 @@ impl TfaUserData {
 #[derive(Deserialize, Serialize)]
 pub struct Recovery {
     secret: String,
-    entries: Vec<String>,
+    entries: Vec<Option<String>>,
 }
 
 impl Recovery {
@@ -1116,7 +1116,7 @@ impl Recovery {
                 AsHex(&b[6..8]),
             );
 
-            this.entries.push(this.hash(entry.as_bytes())?);
+            this.entries.push(Some(this.hash(entry.as_bytes())?));
             original.push(entry);
         }
 
@@ -1138,31 +1138,32 @@ impl Recovery {
         Ok(AsHex(&hmac).to_string())
     }
 
-    /// Shortcut to get the count.
-    fn len(&self) -> usize {
-        self.entries.len()
+    /// Iterator over available keys.
+    fn available(&self) -> impl Iterator<Item = &str> {
+        self.entries.iter().filter_map(Option::as_deref)
     }
 
-    /// Check if this entry is empty.
-    fn is_empty(&self) -> bool {
-        self.entries.is_empty()
+    /// Count the available keys.
+    fn count_available(&self) -> usize {
+        self.available().count()
     }
 
     /// Convenience serde method to check if either the option is `None` or the content `is_empty`.
     fn option_is_empty(this: &Option<Self>) -> bool {
-        this.as_ref().map_or(true, Self::is_empty)
+        this.as_ref()
+            .map_or(true, |this| this.count_available() == 0)
     }
 
     /// Verify a key and remove it. Returns whether the key was valid. Errors on openssl errors.
     fn verify(&mut self, key: &str) -> Result<bool, Error> {
         let hash = self.hash(key.as_bytes())?;
-        Ok(match self.entries.iter().position(|entry| *entry == hash) {
-            Some(index) => {
-                self.entries.remove(index);
-                true
+        for entry in &mut self.entries {
+            if entry.as_ref() == Some(&hash) {
+                *entry = None;
+                return Ok(true);
             }
-            None => false,
-        })
+        }
+        Ok(false)
     }
 }
 
@@ -1283,45 +1284,38 @@ pub fn verify_challenge(
 }
 
 /// Used to inform the user about the recovery code status.
-#[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
-#[serde(rename_all = "kebab-case")]
-pub enum RecoveryState {
-    Unavailable,
-    Low,
-    Available,
-}
+///
+/// This contains the available key indices.
+#[derive(Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
+pub struct RecoveryState(Vec<usize>);
 
 impl RecoveryState {
-    fn from_count(count: usize) -> Self {
-        match count {
-            0 => RecoveryState::Unavailable,
-            1..=3 => RecoveryState::Low,
-            _ => RecoveryState::Available,
-        }
-    }
-
-    // serde needs `&self` but this is a tiny Copy type, so we mark this as inline
-    #[inline]
     fn is_unavailable(&self) -> bool {
-        *self == RecoveryState::Unavailable
-    }
-}
-
-impl Default for RecoveryState {
-    fn default() -> Self {
-        RecoveryState::Unavailable
+        self.0.is_empty()
     }
 }
 
 impl From<&Option<Recovery>> for RecoveryState {
     fn from(r: &Option<Recovery>) -> Self {
         match r {
-            Some(r) => Self::from_count(r.len()),
-            None => RecoveryState::Unavailable,
+            Some(r) => Self::from(r),
+            None => Self::default(),
         }
     }
 }
 
+impl From<&Recovery> for RecoveryState {
+    fn from(r: &Recovery) -> Self {
+        Self(
+            r.entries
+                .iter()
+                .enumerate()
+                .filter_map(|(idx, key)| if key.is_some() { Some(idx) } else { None })
+                .collect(),
+        )
+    }
+}
+
 /// When sending a TFA challenge to the user, we include information about what kind of challenge
 /// the user may perform. If webauthn credentials are available, a webauthn challenge will be
 /// included.
-- 
2.20.1





^ permalink raw reply	[flat|nested] 4+ messages in thread

* [pbs-devel] [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window
  2021-01-18 11:46 [pbs-devel] [PATCH backup 1/2] tfa: remember recovery indices Wolfgang Bumiller
@ 2021-01-18 11:46 ` Wolfgang Bumiller
  2021-01-18 12:52   ` [pbs-devel] applied: " Thomas Lamprecht
  2021-01-18 12:52 ` [pbs-devel] applied: [PATCH backup 1/2] tfa: remember recovery indices Thomas Lamprecht
  1 sibling, 1 reply; 4+ messages in thread
From: Wolfgang Bumiller @ 2021-01-18 11:46 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 www/LoginView.js             | 20 +++++++++++++++++---
 www/window/AddTfaRecovery.js |  7 ++++++-
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/www/LoginView.js b/www/LoginView.js
index fa8772b5..2dd84de9 100644
--- a/www/LoginView.js
+++ b/www/LoginView.js
@@ -281,9 +281,9 @@ Ext.define('PBS.login.TfaWindow', {
 		me.lookup('totpButton').setVisible(false);
 	    }
 
-	    if (!view.challenge.recovery) {
+	    if (!view.challenge.recovery || !view.challenge.recovery.length) {
 		me.lookup('recoveryButton').setVisible(false);
-	    } else if (view.challenge.recovery === "low") {
+	    } else if (view.challenge.recovery.length <= 3) {
 		me.lookup('recoveryButton')
 		    .setIconCls('fa fa-fw fa-exclamation-triangle');
 	    }
@@ -384,7 +384,12 @@ Ext.define('PBS.login.TfaWindow', {
 		me.lookup('webauthnButton').setVisible(false);
 		me.lookup('recoveryButton').setText(gettext("Confirm"));
 		me.lookup('recoveryInfo').setVisible(true);
-		if (view.challenge.recovery === "low") {
+		console.log("RECOVERY:", view.challenge.recovery);
+		me.lookup('availableRecovery').update(Ext.String.htmlEncode(
+		    gettext('Available recovery keys: ') + view.challenge.recovery.join(', ')
+		));
+		me.lookup('availableRecovery').setVisible(true);
+		if (view.challenge.recovery.length <= 3) {
 		    me.lookup('recoveryLow').setVisible(true);
 		}
 	    }
@@ -456,6 +461,15 @@ Ext.define('PBS.login.TfaWindow', {
 		textAlign: "center",
 	    },
 	},
+	{
+	    xtype: 'box',
+	    padding: '0 5',
+	    reference: 'availableRecovery',
+	    hidden: true,
+	    style: {
+		textAlign: "center",
+	    },
+	},
 	{
 	    xtype: 'box',
 	    padding: '0 5',
diff --git a/www/window/AddTfaRecovery.js b/www/window/AddTfaRecovery.js
index b63e8576..1718f026 100644
--- a/www/window/AddTfaRecovery.js
+++ b/www/window/AddTfaRecovery.js
@@ -24,7 +24,12 @@ Ext.define('PBS.window.AddTfaRecovery', {
 	    return;
 	}
 
-	let values = response.result.data.recovery.join("\n");
+	let values = response
+	    .result
+	    .data
+	    .recovery
+	    .map((v, i) => `${i}: ${v}`)
+	    .join("\n");
 	Ext.create('PBS.window.TfaRecoveryShow', {
 	    autoShow: true,
 	    userid: this.getViewModel().get('userid'),
-- 
2.20.1





^ permalink raw reply	[flat|nested] 4+ messages in thread

* [pbs-devel] applied: [PATCH backup 1/2] tfa: remember recovery indices
  2021-01-18 11:46 [pbs-devel] [PATCH backup 1/2] tfa: remember recovery indices Wolfgang Bumiller
  2021-01-18 11:46 ` [pbs-devel] [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window Wolfgang Bumiller
@ 2021-01-18 12:52 ` Thomas Lamprecht
  1 sibling, 0 replies; 4+ messages in thread
From: Thomas Lamprecht @ 2021-01-18 12:52 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Wolfgang Bumiller

On 18.01.21 12:46, Wolfgang Bumiller wrote:
> and tell the client which keys are still available rather
> than just yes/no/low
> 
> Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
> ---
>  src/config/tfa.rs | 76 ++++++++++++++++++++++-------------------------
>  1 file changed, 35 insertions(+), 41 deletions(-)
> 
>

applied, thanks!




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [pbs-devel] applied: [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window
  2021-01-18 11:46 ` [pbs-devel] [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window Wolfgang Bumiller
@ 2021-01-18 12:52   ` Thomas Lamprecht
  0 siblings, 0 replies; 4+ messages in thread
From: Thomas Lamprecht @ 2021-01-18 12:52 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Wolfgang Bumiller

On 18.01.21 12:46, Wolfgang Bumiller wrote:
> Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
> ---
>  www/LoginView.js             | 20 +++++++++++++++++---
>  www/window/AddTfaRecovery.js |  7 ++++++-
>  2 files changed, 23 insertions(+), 4 deletions(-)
> 
>

applied, thanks!




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-01-18 12:52 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-18 11:46 [pbs-devel] [PATCH backup 1/2] tfa: remember recovery indices Wolfgang Bumiller
2021-01-18 11:46 ` [pbs-devel] [PATCH backup 2/2] gui: enumerate recovery keys and list in 2nd factor window Wolfgang Bumiller
2021-01-18 12:52   ` [pbs-devel] applied: " Thomas Lamprecht
2021-01-18 12:52 ` [pbs-devel] applied: [PATCH backup 1/2] tfa: remember recovery indices Thomas Lamprecht

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal