]>
Commit | Line | Data |
---|---|---|
a670b99d WB |
1 | //! For now this only has the TFA subdir, which is in this file. |
2 | //! If we add more, it should be moved into a sub module. | |
3 | ||
e6e2927e | 4 | use anyhow::{format_err, Error}; |
25877d05 | 5 | use hex::FromHex; |
e6e2927e | 6 | use serde::{Deserialize, Serialize}; |
a670b99d | 7 | |
6ef1b649 | 8 | use proxmox_router::list_subdirs_api_method; |
e6e2927e WB |
9 | use proxmox_router::{Permission, Router, RpcEnvironment, SubdirMap}; |
10 | use proxmox_schema::api; | |
a670b99d | 11 | |
6227654a DM |
12 | use pbs_api_types::PROXMOX_CONFIG_DIGEST_SCHEMA; |
13 | ||
a670b99d WB |
14 | use crate::config::tfa::{self, WebauthnConfig, WebauthnConfigUpdater}; |
15 | ||
16 | pub const ROUTER: Router = Router::new() | |
17 | .get(&list_subdirs_api_method!(SUBDIRS)) | |
18 | .subdirs(SUBDIRS); | |
19 | ||
20 | const SUBDIRS: SubdirMap = &[("webauthn", &WEBAUTHN_ROUTER)]; | |
21 | ||
22 | const WEBAUTHN_ROUTER: Router = Router::new() | |
23 | .get(&API_METHOD_GET_WEBAUTHN_CONFIG) | |
24 | .put(&API_METHOD_UPDATE_WEBAUTHN_CONFIG); | |
25 | ||
26 | #[api( | |
27 | protected: true, | |
28 | input: { | |
29 | properties: {}, | |
30 | }, | |
31 | returns: { | |
32 | type: WebauthnConfig, | |
33 | optional: true, | |
34 | }, | |
35 | access: { | |
36 | permission: &Permission::Anybody, | |
37 | }, | |
38 | )] | |
39 | /// Get the TFA configuration. | |
40 | pub fn get_webauthn_config( | |
41c1a179 | 41 | rpcenv: &mut dyn RpcEnvironment, |
a670b99d WB |
42 | ) -> Result<Option<WebauthnConfig>, Error> { |
43 | let (config, digest) = match tfa::webauthn_config()? { | |
44 | Some(c) => c, | |
45 | None => return Ok(None), | |
46 | }; | |
16f6766a | 47 | rpcenv["digest"] = hex::encode(digest).into(); |
a670b99d WB |
48 | Ok(Some(config)) |
49 | } | |
50 | ||
e6e2927e WB |
51 | #[api()] |
52 | #[derive(Serialize, Deserialize)] | |
53 | #[serde(rename_all = "kebab-case")] | |
54 | /// Deletable property name | |
55 | pub enum DeletableProperty { | |
8ab1d131 | 56 | /// Delete the `origin` property. |
e6e2927e | 57 | Origin, |
8ab1d131 WB |
58 | |
59 | /// Delete the `allow_subdomains` property. | |
60 | AllowSubdomains, | |
e6e2927e WB |
61 | } |
62 | ||
a670b99d WB |
63 | #[api( |
64 | protected: true, | |
65 | input: { | |
66 | properties: { | |
67 | webauthn: { | |
68 | flatten: true, | |
69 | type: WebauthnConfigUpdater, | |
70 | }, | |
e6e2927e WB |
71 | delete: { |
72 | description: "List of properties to delete.", | |
73 | type: Array, | |
74 | optional: true, | |
75 | items: { | |
76 | type: DeletableProperty, | |
77 | } | |
78 | }, | |
a670b99d WB |
79 | digest: { |
80 | optional: true, | |
81 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
82 | }, | |
83 | }, | |
84 | }, | |
85 | )] | |
86 | /// Update the TFA configuration. | |
87 | pub fn update_webauthn_config( | |
88 | webauthn: WebauthnConfigUpdater, | |
e6e2927e | 89 | delete: Option<Vec<DeletableProperty>>, |
a670b99d WB |
90 | digest: Option<String>, |
91 | ) -> Result<(), Error> { | |
92 | let _lock = tfa::write_lock(); | |
93 | ||
94 | let mut tfa = tfa::read()?; | |
95 | ||
96 | if let Some(wa) = &mut tfa.webauthn { | |
97 | if let Some(ref digest) = digest { | |
25877d05 | 98 | let digest = <[u8; 32]>::from_hex(digest)?; |
9407810f WB |
99 | crate::tools::detect_modified_configuration_file( |
100 | &digest, | |
9a37bd6c | 101 | &crate::config::tfa::webauthn_config_digest(wa)?, |
9407810f | 102 | )?; |
a670b99d | 103 | } |
e6e2927e WB |
104 | |
105 | if let Some(delete) = delete { | |
106 | for delete in delete { | |
107 | match delete { | |
108 | DeletableProperty::Origin => { | |
109 | wa.origin = None; | |
110 | } | |
8ab1d131 WB |
111 | DeletableProperty::AllowSubdomains => { |
112 | wa.allow_subdomains = None; | |
113 | } | |
e6e2927e WB |
114 | } |
115 | } | |
116 | } | |
117 | ||
118 | if let Some(rp) = webauthn.rp { | |
119 | wa.rp = rp; | |
120 | } | |
121 | if webauthn.origin.is_some() { | |
122 | wa.origin = webauthn.origin; | |
123 | } | |
8ab1d131 WB |
124 | if webauthn.allow_subdomains.is_some() { |
125 | wa.allow_subdomains = webauthn.allow_subdomains; | |
126 | } | |
e6e2927e WB |
127 | if let Some(id) = webauthn.id { |
128 | wa.id = id; | |
129 | } | |
a670b99d | 130 | } else { |
e6e2927e WB |
131 | let rp = webauthn |
132 | .rp | |
d20137e5 | 133 | .ok_or_else(|| format_err!("missing property: 'rp'"))?; |
e6e2927e WB |
134 | let origin = webauthn.origin; |
135 | let id = webauthn | |
136 | .id | |
137 | .ok_or_else(|| format_err!("missing property: 'id'"))?; | |
8ab1d131 WB |
138 | let allow_subdomains = webauthn.allow_subdomains; |
139 | tfa.webauthn = Some(WebauthnConfig { | |
140 | rp, | |
141 | origin, | |
142 | id, | |
143 | allow_subdomains, | |
144 | }); | |
a670b99d WB |
145 | } |
146 | ||
147 | tfa::write(&tfa)?; | |
148 | ||
149 | Ok(()) | |
150 | } |