all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox] section config: support allowing unknown section types
@ 2022-11-25 15:15 Fiona Ebner
  2022-11-25 15:15 ` [pbs-devel] [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections Fiona Ebner
  2022-11-28  8:28 ` [pbs-devel] applied: [PATCH proxmox] section config: support allowing unknown section types Wolfgang Bumiller
  0 siblings, 2 replies; 4+ messages in thread
From: Fiona Ebner @ 2022-11-25 15:15 UTC (permalink / raw)
  To: pbs-devel

Similar to commit c9ede1c ("support unknown types in section config")
in pve-common.

Unknown sections are parsed as String-JSON String key-value pairs
without any additional checks and also written as-is.

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
 proxmox-section-config/src/lib.rs | 202 +++++++++++++++++++++++++-----
 1 file changed, 174 insertions(+), 28 deletions(-)

diff --git a/proxmox-section-config/src/lib.rs b/proxmox-section-config/src/lib.rs
index d9978d1..f5bc315 100644
--- a/proxmox-section-config/src/lib.rs
+++ b/proxmox-section-config/src/lib.rs
@@ -90,11 +90,14 @@ pub struct SectionConfig {
         fn(type_name: &str, section_id: &str, data: &Value) -> Result<String, Error>,
     format_section_content:
         fn(type_name: &str, section_id: &str, key: &str, value: &Value) -> Result<String, Error>,
+
+    allow_unknown_sections: bool,
 }
 
 enum ParseState<'a> {
     BeforeHeader,
     InsideSection(&'a SectionConfigPlugin, String, Value),
+    InsideUnknownSection(String, String, Value),
 }
 
 /// Interface to manipulate configuration data
@@ -238,6 +241,7 @@ impl SectionConfig {
             parse_section_content: Self::default_parse_section_content,
             format_section_header: Self::default_format_section_header,
             format_section_content: Self::default_format_section_content,
+            allow_unknown_sections: false,
         }
     }
 
@@ -250,6 +254,7 @@ impl SectionConfig {
             parse_section_content: Self::systemd_parse_section_content,
             format_section_header: Self::systemd_format_section_header,
             format_section_content: Self::systemd_format_section_content,
+            allow_unknown_sections: false,
         }
     }
 
@@ -277,9 +282,15 @@ impl SectionConfig {
             parse_section_content,
             format_section_header,
             format_section_content,
+            allow_unknown_sections: false,
         }
     }
 
+    pub const fn allow_unknown_sections(mut self, allow_unknown_sections: bool) -> Self {
+        self.allow_unknown_sections = allow_unknown_sections;
+        self
+    }
+
     /// Register a plugin, which defines the `Schema` for a section type.
     pub fn register_plugin(&mut self, plugin: SectionConfigPlugin) {
         self.plugins.insert(plugin.type_name.clone(), plugin);
@@ -324,32 +335,53 @@ impl SectionConfig {
 
         for section_id in list {
             let (type_name, section_config) = config.sections.get(section_id).unwrap();
-            let plugin = self.plugins.get(type_name).unwrap();
 
-            let id_schema = plugin.get_id_schema().unwrap_or(self.id_schema);
-            if let Err(err) = id_schema.parse_simple_value(section_id) {
-                bail!("syntax error in section identifier: {}", err.to_string());
-            }
-            if section_id.chars().any(|c| c.is_control()) {
-                bail!("detected unexpected control character in section ID.");
-            }
-            if let Err(err) = plugin.properties.verify_json(section_config) {
-                bail!("verify section '{}' failed - {}", section_id, err);
-            }
+            match self.plugins.get(type_name) {
+                Some(plugin) => {
+                    let id_schema = plugin.get_id_schema().unwrap_or(self.id_schema);
+                    if let Err(err) = id_schema.parse_simple_value(section_id) {
+                        bail!("syntax error in section identifier: {}", err.to_string());
+                    }
+                    if section_id.chars().any(|c| c.is_control()) {
+                        bail!("detected unexpected control character in section ID.");
+                    }
+                    if let Err(err) = plugin.properties.verify_json(section_config) {
+                        bail!("verify section '{}' failed - {}", section_id, err);
+                    }
 
-            if !raw.is_empty() {
-                raw += "\n"
-            }
+                    if !raw.is_empty() {
+                        raw += "\n"
+                    }
 
-            raw += &(self.format_section_header)(type_name, section_id, section_config)?;
+                    raw += &(self.format_section_header)(type_name, section_id, section_config)?;
 
-            for (key, value) in section_config.as_object().unwrap() {
-                if let Some(id_property) = &plugin.id_property {
-                    if id_property == key {
-                        continue; // skip writing out id properties, they are in the section header
+                    for (key, value) in section_config.as_object().unwrap() {
+                        if let Some(id_property) = &plugin.id_property {
+                            if id_property == key {
+                                continue; // skip writing out id properties, they are in the section header
+                            }
+                        }
+                        raw += &(self.format_section_content)(type_name, section_id, key, value)?;
                     }
                 }
-                raw += &(self.format_section_content)(type_name, section_id, key, value)?;
+                None if self.allow_unknown_sections => {
+                    if section_id.chars().any(|c| c.is_control()) {
+                        bail!("detected unexpected control character in section ID.");
+                    }
+
+                    if !raw.is_empty() {
+                        raw += "\n"
+                    }
+
+                    raw += &(self.format_section_header)(type_name, section_id, section_config)?;
+
+                    for (key, value) in section_config.as_object().unwrap() {
+                        raw += &(self.format_section_content)(type_name, section_id, key, value)?;
+                    }
+                }
+                None => {
+                    bail!("unknown section type '{type_name}'");
+                }
             }
         }
 
@@ -415,6 +447,12 @@ impl SectionConfig {
                                     }
                                     state =
                                         ParseState::InsideSection(plugin, section_id, json!({}));
+                                } else if self.allow_unknown_sections {
+                                    state = ParseState::InsideUnknownSection(
+                                        section_type,
+                                        section_id,
+                                        json!({}),
+                                    );
                                 } else {
                                     bail!("unknown section type '{}'", section_type);
                                 }
@@ -477,18 +515,48 @@ impl SectionConfig {
                                 bail!("syntax error (expected section properties)");
                             }
                         }
+                        ParseState::InsideUnknownSection(
+                            ref section_type,
+                            ref mut section_id,
+                            ref mut config,
+                        ) => {
+                            if line.trim().is_empty() {
+                                // finish section
+                                result.set_data(section_id, section_type, config.take())?;
+                                result.record_order(section_id);
+
+                                state = ParseState::BeforeHeader;
+                                continue;
+                            }
+                            if let Some((key, value)) = (self.parse_section_content)(line) {
+                                config[key] = json!(value);
+                            } else {
+                                bail!("syntax error (expected section properties)");
+                            }
+                        }
                     }
                 }
 
-                if let ParseState::InsideSection(plugin, ref mut section_id, ref mut config) = state
-                {
-                    // finish section
-                    test_required_properties(config, plugin.properties, &plugin.id_property)?;
-                    if let Some(id_property) = &plugin.id_property {
-                        config[id_property] = Value::from(section_id.clone());
+                match state {
+                    ParseState::BeforeHeader => {}
+                    ParseState::InsideSection(plugin, ref mut section_id, ref mut config) => {
+                        // finish section
+                        test_required_properties(config, plugin.properties, &plugin.id_property)?;
+                        if let Some(id_property) = &plugin.id_property {
+                            config[id_property] = Value::from(section_id.clone());
+                        }
+                        result.set_data(section_id, &plugin.type_name, config)?;
+                        result.record_order(section_id);
+                    }
+                    ParseState::InsideUnknownSection(
+                        ref section_type,
+                        ref mut section_id,
+                        ref mut config,
+                    ) => {
+                        // finish section
+                        result.set_data(section_id, section_type, config)?;
+                        result.record_order(section_id);
                     }
-                    result.set_data(section_id, &plugin.type_name, config)?;
-                    result.record_order(section_id);
                 }
 
                 Ok(())
@@ -960,6 +1028,84 @@ user: root@pam
     assert!(config.write(filename, &res.unwrap()).is_err());
 }
 
+#[test]
+fn test_section_config_with_unknown_section_types() {
+    let filename = "user.cfg";
+
+    const ID_SCHEMA: Schema = StringSchema::new("default id schema.")
+        .min_length(3)
+        .schema();
+    let mut config = SectionConfig::new(&ID_SCHEMA).allow_unknown_sections(true);
+
+    const PROPERTIES: [(&str, bool, &proxmox_schema::Schema); 2] = [
+        (
+            "email",
+            false,
+            &StringSchema::new("The e-mail of the user").schema(),
+        ),
+        (
+            "userid",
+            true,
+            &StringSchema::new("The id of the user (name@realm).")
+                .min_length(3)
+                .schema(),
+        ),
+    ];
+
+    const USER_PROPERTIES: ObjectSchema = ObjectSchema {
+        description: "user properties",
+        properties: &PROPERTIES,
+        additional_properties: false,
+        default_key: None,
+    };
+
+    let plugin = SectionConfigPlugin::new(
+        "user".to_string(),
+        Some("userid".to_string()),
+        &USER_PROPERTIES,
+    );
+    config.register_plugin(plugin);
+
+    let raw = r"
+
+user: root@pam
+        email root@example.com
+
+token: asdf@pbs!asdftoken
+        enable true
+        expire 0
+";
+
+    let check = |res: SectionConfigData| {
+        let (_, token_config) = res.sections.get("root@pam").unwrap();
+        assert_eq!(
+            *token_config.get("email").unwrap(),
+            json!("root@example.com")
+        );
+
+        let (token_id, token_config) = res.sections.get("asdf@pbs!asdftoken").unwrap();
+        assert_eq!(token_id, "token");
+        assert_eq!(*token_config.get("enable").unwrap(), json!("true"));
+        assert_eq!(*token_config.get("expire").unwrap(), json!("0"));
+    };
+
+    let res = config.parse(filename, raw).unwrap();
+    println!("RES: {:?}", res);
+    let written = config.write(filename, &res);
+    println!("CONFIG:\n{}", written.as_ref().unwrap());
+
+    check(res);
+
+    let res = config.parse(filename, &written.unwrap()).unwrap();
+    println!("RES second time: {:?}", res);
+
+    check(res);
+
+    let config = config.allow_unknown_sections(false);
+
+    assert!(config.parse(filename, raw).is_err());
+}
+
 /// Generate ReST Documentaion for ``SectionConfig``
 pub fn dump_section_config(config: &SectionConfig) -> String {
     let mut res = String::new();
-- 
2.30.2





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

* [pbs-devel] [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections
  2022-11-25 15:15 [pbs-devel] [PATCH proxmox] section config: support allowing unknown section types Fiona Ebner
@ 2022-11-25 15:15 ` Fiona Ebner
  2022-11-28  8:33   ` [pbs-devel] applied: " Wolfgang Bumiller
  2022-11-28  8:28 ` [pbs-devel] applied: [PATCH proxmox] section config: support allowing unknown section types Wolfgang Bumiller
  1 sibling, 1 reply; 4+ messages in thread
From: Fiona Ebner @ 2022-11-25 15:15 UTC (permalink / raw)
  To: pbs-devel

Previously, configs with entries for tokens could not be parsed
successfully, so extracting the mail for the root user failed.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---

Dependency bump for proxmox-section-config needed.

 src/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index d53a1b4..f3d4193 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -46,7 +46,7 @@ fn normalize_for_return(s: Option<&str>) -> Option<String> {
 
 /// Extract the root user's email address from the PBS user config.
 fn get_pbs_mail_to(content: &str) -> Option<String> {
-    let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA);
+    let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA).allow_unknown_sections(true);
     let user_plugin = SectionConfigPlugin::new(
         "user".to_string(),
         Some("userid".to_string()),
-- 
2.30.2





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

* [pbs-devel] applied: [PATCH proxmox] section config: support allowing unknown section types
  2022-11-25 15:15 [pbs-devel] [PATCH proxmox] section config: support allowing unknown section types Fiona Ebner
  2022-11-25 15:15 ` [pbs-devel] [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections Fiona Ebner
@ 2022-11-28  8:28 ` Wolfgang Bumiller
  1 sibling, 0 replies; 4+ messages in thread
From: Wolfgang Bumiller @ 2022-11-28  8:28 UTC (permalink / raw)
  To: Fiona Ebner; +Cc: pbs-devel

applied, thanks




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

* [pbs-devel] applied: [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections
  2022-11-25 15:15 ` [pbs-devel] [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections Fiona Ebner
@ 2022-11-28  8:33   ` Wolfgang Bumiller
  0 siblings, 0 replies; 4+ messages in thread
From: Wolfgang Bumiller @ 2022-11-28  8:33 UTC (permalink / raw)
  To: Fiona Ebner; +Cc: pbs-devel

applied, thanks

On Fri, Nov 25, 2022 at 04:15:36PM +0100, Fiona Ebner wrote:
> Previously, configs with entries for tokens could not be parsed
> successfully, so extracting the mail for the root user failed.
> 
> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
> ---
> 
> Dependency bump for proxmox-section-config needed.
> 
>  src/main.rs | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/src/main.rs b/src/main.rs
> index d53a1b4..f3d4193 100644
> --- a/src/main.rs
> +++ b/src/main.rs
> @@ -46,7 +46,7 @@ fn normalize_for_return(s: Option<&str>) -> Option<String> {
>  
>  /// Extract the root user's email address from the PBS user config.
>  fn get_pbs_mail_to(content: &str) -> Option<String> {
> -    let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA);
> +    let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA).allow_unknown_sections(true);
>      let user_plugin = SectionConfigPlugin::new(
>          "user".to_string(),
>          Some("userid".to_string()),
> -- 
> 2.30.2




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

end of thread, other threads:[~2022-11-28  8:33 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-25 15:15 [pbs-devel] [PATCH proxmox] section config: support allowing unknown section types Fiona Ebner
2022-11-25 15:15 ` [pbs-devel] [PATCH proxmox-mail-forward] pbs user config: allow parsing unknown sections Fiona Ebner
2022-11-28  8:33   ` [pbs-devel] applied: " Wolfgang Bumiller
2022-11-28  8:28 ` [pbs-devel] applied: [PATCH proxmox] section config: support allowing unknown section types Wolfgang Bumiller

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