diff --git a/src/api2/config/access/tfa/mod.rs b/src/api2/config/access/tfa/mod.rs index 63c34815..6ac9b5de 100644 --- a/src/api2/config/access/tfa/mod.rs +++ b/src/api2/config/access/tfa/mod.rs @@ -5,6 +5,7 @@ use anyhow::Error; use crate::api2::types::PROXMOX_CONFIG_DIGEST_SCHEMA; use proxmox::api::{api, Permission, Router, RpcEnvironment, SubdirMap}; +use proxmox::api::schema::Updatable; use proxmox::list_subdirs_api_method; use crate::config::tfa::{self, WebauthnConfig, WebauthnConfigUpdater}; @@ -73,9 +74,9 @@ pub fn update_webauthn_config( let digest = proxmox::tools::hex_to_digest(digest)?; crate::tools::detect_modified_configuration_file(&digest, &wa.digest()?)?; } - webauthn.apply_to(wa); + wa.update_from::<&str>(webauthn, &[])?; } else { - tfa.webauthn = Some(webauthn.build()?); + tfa.webauthn = Some(WebauthnConfig::try_build_from(webauthn)?); } tfa::write(&tfa)?; diff --git a/src/config/tfa.rs b/src/config/tfa.rs index 380df7e8..5afb5827 100644 --- a/src/config/tfa.rs +++ b/src/config/tfa.rs @@ -18,6 +18,7 @@ use webauthn_rs::Webauthn; use webauthn_rs::proto::Credential as WebauthnCredential; use proxmox::api::api; +use proxmox::api::schema::{Updatable, Updater}; use proxmox::sys::error::SysError; use proxmox::tools::fs::CreateOptions; use proxmox::tools::tfa::totp::Totp; @@ -87,7 +88,7 @@ pub struct U2fConfig { } #[api] -#[derive(Clone, Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize, Updater)] #[serde(deny_unknown_fields)] /// Server side webauthn server configuration. pub struct WebauthnConfig { @@ -115,53 +116,6 @@ impl WebauthnConfig { } } -// TODO: api macro should be able to generate this struct & impl automatically: -#[api] -#[derive(Default, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -/// Server side webauthn server configuration. -pub struct WebauthnConfigUpdater { - /// Relying party name. Any text identifier. - /// - /// Changing this *may* break existing credentials. - rp: Option, - - /// Site origin. Must be a `https://` URL (or `http://localhost`). Should contain the address - /// users type in their browsers to access the web interface. - /// - /// Changing this *may* break existing credentials. - origin: Option, - - /// Relying part ID. Must be the domain name without protocol, port or location. - /// - /// Changing this *will* break existing credentials. - id: Option, -} - -impl WebauthnConfigUpdater { - pub fn apply_to(self, target: &mut WebauthnConfig) { - if let Some(val) = self.rp { - target.rp = val; - } - - if let Some(val) = self.origin { - target.origin = val; - } - - if let Some(val) = self.id { - target.id = val; - } - } - - pub fn build(self) -> Result { - Ok(WebauthnConfig { - rp: self.rp.ok_or_else(|| format_err!("missing required field: `rp`"))?, - origin: self.origin.ok_or_else(|| format_err!("missing required field: `origin`"))?, - id: self.id.ok_or_else(|| format_err!("missing required field: `origin`"))?, - }) - } -} - /// For now we just implement this on the configuration this way. /// /// Note that we may consider changing this so `get_origin` returns the `Host:` header provided by