tfa: derive WebauthnConfigUpdater via api macro
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
51c80c5a52
commit
7f9d8438ab
|
@ -5,6 +5,7 @@ use anyhow::Error;
|
||||||
|
|
||||||
use crate::api2::types::PROXMOX_CONFIG_DIGEST_SCHEMA;
|
use crate::api2::types::PROXMOX_CONFIG_DIGEST_SCHEMA;
|
||||||
use proxmox::api::{api, Permission, Router, RpcEnvironment, SubdirMap};
|
use proxmox::api::{api, Permission, Router, RpcEnvironment, SubdirMap};
|
||||||
|
use proxmox::api::schema::Updatable;
|
||||||
use proxmox::list_subdirs_api_method;
|
use proxmox::list_subdirs_api_method;
|
||||||
|
|
||||||
use crate::config::tfa::{self, WebauthnConfig, WebauthnConfigUpdater};
|
use crate::config::tfa::{self, WebauthnConfig, WebauthnConfigUpdater};
|
||||||
|
@ -73,9 +74,9 @@ pub fn update_webauthn_config(
|
||||||
let digest = proxmox::tools::hex_to_digest(digest)?;
|
let digest = proxmox::tools::hex_to_digest(digest)?;
|
||||||
crate::tools::detect_modified_configuration_file(&digest, &wa.digest()?)?;
|
crate::tools::detect_modified_configuration_file(&digest, &wa.digest()?)?;
|
||||||
}
|
}
|
||||||
webauthn.apply_to(wa);
|
wa.update_from::<&str>(webauthn, &[])?;
|
||||||
} else {
|
} else {
|
||||||
tfa.webauthn = Some(webauthn.build()?);
|
tfa.webauthn = Some(WebauthnConfig::try_build_from(webauthn)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
tfa::write(&tfa)?;
|
tfa::write(&tfa)?;
|
||||||
|
|
|
@ -18,6 +18,7 @@ use webauthn_rs::Webauthn;
|
||||||
use webauthn_rs::proto::Credential as WebauthnCredential;
|
use webauthn_rs::proto::Credential as WebauthnCredential;
|
||||||
|
|
||||||
use proxmox::api::api;
|
use proxmox::api::api;
|
||||||
|
use proxmox::api::schema::{Updatable, Updater};
|
||||||
use proxmox::sys::error::SysError;
|
use proxmox::sys::error::SysError;
|
||||||
use proxmox::tools::fs::CreateOptions;
|
use proxmox::tools::fs::CreateOptions;
|
||||||
use proxmox::tools::tfa::totp::Totp;
|
use proxmox::tools::tfa::totp::Totp;
|
||||||
|
@ -87,7 +88,7 @@ pub struct U2fConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api]
|
#[api]
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize, Updater)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
/// Server side webauthn server configuration.
|
/// Server side webauthn server configuration.
|
||||||
pub struct WebauthnConfig {
|
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<String>,
|
|
||||||
|
|
||||||
/// 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<String>,
|
|
||||||
|
|
||||||
/// Relying part ID. Must be the domain name without protocol, port or location.
|
|
||||||
///
|
|
||||||
/// Changing this *will* break existing credentials.
|
|
||||||
id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<WebauthnConfig, Error> {
|
|
||||||
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.
|
/// 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
|
/// Note that we may consider changing this so `get_origin` returns the `Host:` header provided by
|
||||||
|
|
Loading…
Reference in New Issue