From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [RFC backup 1/6] add tools::serde_filter submodule
Date: Thu, 19 Nov 2020 15:56:03 +0100 [thread overview]
Message-ID: <20201119145608.16866-2-w.bumiller@proxmox.com> (raw)
In-Reply-To: <20201119145608.16866-1-w.bumiller@proxmox.com>
can be used to perform filtering at parse time
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
src/tools.rs | 1 +
src/tools/serde_filter.rs | 97 +++++++++++++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 src/tools/serde_filter.rs
diff --git a/src/tools.rs b/src/tools.rs
index 08f9d22f..8cc446dd 100644
--- a/src/tools.rs
+++ b/src/tools.rs
@@ -32,6 +32,7 @@ pub mod loopdev;
pub mod lru_cache;
pub mod nom;
pub mod runtime;
+pub mod serde_filter;
pub mod socket;
pub mod statistics;
pub mod subscription;
diff --git a/src/tools/serde_filter.rs b/src/tools/serde_filter.rs
new file mode 100644
index 00000000..b8402696
--- /dev/null
+++ b/src/tools/serde_filter.rs
@@ -0,0 +1,97 @@
+use std::marker::PhantomData;
+
+use serde::Deserialize;
+
+/// Helper to filter data while deserializing it.
+///
+/// An example use case is filtering out expired registration challenges at load time of our TFA
+/// config:
+///
+/// ```
+/// # use proxmox_backup::tools::serde_filter::FilteredVecVisitor;
+/// # use serde::{Deserialize, Deserializer, Serialize};
+/// # const CHALLENGE_TIMEOUT: i64 = 2 * 60;
+/// #[derive(Deserialize)]
+/// struct Challenge {
+/// /// Expiration time as unix epoch.
+/// expires: i64,
+///
+/// // ...other entries...
+/// }
+///
+/// #[derive(Default, Deserialize)]
+/// #[serde(deny_unknown_fields)]
+/// #[serde(rename_all = "kebab-case")]
+/// pub struct TfaUserData {
+/// // ...other entries...
+///
+/// #[serde(skip_serializing_if = "Vec::is_empty", default)]
+/// #[serde(deserialize_with = "filter_expired_registrations")]
+/// registrations: Vec<Challenge>,
+/// }
+///
+/// fn filter_expired_registrations<'de, D>(deserializer: D) -> Result<Vec<Challenge>, D::Error>
+/// where
+/// D: Deserializer<'de>,
+/// {
+/// let expire_before = proxmox::tools::time::epoch_i64() - CHALLENGE_TIMEOUT;
+///
+/// Ok(deserializer.deserialize_seq(
+/// FilteredVecVisitor::new(
+/// "a u2f registration challenge entry",
+/// move |c: &Challenge| c.expires < expire_before,
+/// )
+/// )?)
+/// }
+/// ```
+pub struct FilteredVecVisitor<F, T>
+where
+ F: Fn(&T) -> bool
+{
+ filter: F,
+ expecting: &'static str,
+ _ty: PhantomData<T>,
+}
+
+impl<F, T> FilteredVecVisitor<F, T>
+where
+ F: Fn(&T) -> bool,
+{
+ pub fn new(expecting: &'static str, filter: F) -> Self {
+ Self {
+ filter,
+ expecting,
+ _ty: PhantomData,
+ }
+ }
+}
+
+impl<'de, F, T> serde::de::Visitor<'de> for FilteredVecVisitor<F, T>
+where
+ F: Fn(&T) -> bool,
+ T: Deserialize<'de>,
+{
+ type Value = Vec<T>;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str(self.expecting)
+ }
+
+ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let mut out = match seq.size_hint() {
+ Some(hint) => Vec::with_capacity(hint),
+ None => Vec::new(),
+ };
+
+ while let Some(entry) = seq.next_element::<T>()? {
+ if (self.filter)(&entry) {
+ out.push(entry);
+ }
+ }
+
+ Ok(out)
+ }
+}
--
2.20.1
next prev parent reply other threads:[~2020-11-19 14:56 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-11-19 14:56 [pbs-devel] [RFC backup 0/6] Two factor authentication Wolfgang Bumiller
2020-11-19 14:56 ` Wolfgang Bumiller [this message]
2020-11-19 14:56 ` [pbs-devel] [RFC backup 2/6] config: add tfa configuration Wolfgang Bumiller
2020-11-19 14:56 ` [pbs-devel] [RFC backup 3/6] api: tfa management and login Wolfgang Bumiller
2020-11-19 14:56 ` [pbs-devel] [RFC backup 4/6] depend on libjs-qrcodejs Wolfgang Bumiller
2020-11-19 14:56 ` [pbs-devel] [RFC backup 5/6] proxy: expose qrcodejs Wolfgang Bumiller
2020-11-19 14:56 ` [pbs-devel] [RFC backup 6/6] gui: tfa support Wolfgang Bumiller
2020-11-24 9:42 ` Wolfgang Bumiller
2020-11-24 9:51 ` Thomas Lamprecht
2020-12-02 10:56 ` [pbs-devel] [RFC backup 0/6] Two factor authentication Oguz Bektas
2020-12-02 12:27 ` Thomas Lamprecht
2020-12-02 12:34 ` Thomas Lamprecht
2020-12-02 12:48 ` Oguz Bektas
2020-12-02 12:59 ` Wolfgang Bumiller
2020-12-02 13:08 ` Oguz Bektas
2020-12-02 12:35 ` Oguz Bektas
2020-12-02 12:51 ` Wolfgang Bumiller
2020-12-02 13:15 ` Thomas Lamprecht
2020-12-02 13:07 ` Thomas Lamprecht
2020-12-02 13:35 ` Oguz Bektas
2020-12-02 14:05 ` Thomas Lamprecht
2020-12-02 14:21 ` Oguz Bektas
2020-12-02 14:29 ` Wolfgang Bumiller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201119145608.16866-2-w.bumiller@proxmox.com \
--to=w.bumiller@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox