]>
Commit | Line | Data |
---|---|---|
f7d4e4b5 | 1 | use anyhow::{bail, Error}; |
141304d6 | 2 | use serde_json::Value; |
5211705f | 3 | use ::serde::{Deserialize, Serialize}; |
141304d6 | 4 | |
70e5f246 | 5 | use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; |
98c259b4 | 6 | use proxmox::tools::fs::open_file_locked; |
141304d6 | 7 | |
167971ed | 8 | use crate::api2::types::*; |
f357390c | 9 | use crate::config::remote; |
8247db5b | 10 | use crate::config::acl::{PRIV_REMOTE_AUDIT, PRIV_REMOTE_MODIFY}; |
141304d6 DM |
11 | |
12 | #[api( | |
13 | input: { | |
14 | properties: {}, | |
15 | }, | |
16 | returns: { | |
f3ec5dae | 17 | description: "The list of configured remotes (with config digest).", |
141304d6 DM |
18 | type: Array, |
19 | items: { | |
83fd4b3b | 20 | type: remote::Remote, |
5eeea607 | 21 | description: "Remote configuration (without password).", |
141304d6 DM |
22 | }, |
23 | }, | |
70e5f246 | 24 | access: { |
8247db5b | 25 | permission: &Permission::Privilege(&["remote"], PRIV_REMOTE_AUDIT, false), |
70e5f246 | 26 | }, |
141304d6 DM |
27 | )] |
28 | /// List all remotes | |
29 | pub fn list_remotes( | |
30 | _param: Value, | |
31 | _info: &ApiMethod, | |
83fd4b3b DC |
32 | mut rpcenv: &mut dyn RpcEnvironment, |
33 | ) -> Result<Vec<remote::Remote>, Error> { | |
141304d6 | 34 | |
f357390c | 35 | let (config, digest) = remote::config()?; |
141304d6 | 36 | |
83fd4b3b | 37 | let mut list: Vec<remote::Remote> = config.convert_to_typed_array("remote")?; |
5211705f | 38 | |
83fd4b3b DC |
39 | // don't return password in api |
40 | for remote in &mut list { | |
41 | remote.password = "".to_string(); | |
42 | } | |
43 | ||
44 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | |
45 | Ok(list) | |
141304d6 DM |
46 | } |
47 | ||
48 | #[api( | |
688fbe07 | 49 | protected: true, |
141304d6 DM |
50 | input: { |
51 | properties: { | |
52 | name: { | |
167971ed | 53 | schema: REMOTE_ID_SCHEMA, |
141304d6 DM |
54 | }, |
55 | comment: { | |
56 | optional: true, | |
454c13ed | 57 | schema: SINGLE_LINE_COMMENT_SCHEMA, |
141304d6 DM |
58 | }, |
59 | host: { | |
163dc16c | 60 | schema: DNS_NAME_OR_IP_SCHEMA, |
141304d6 | 61 | }, |
ba20987a DC |
62 | port: { |
63 | description: "The (optional) port.", | |
64 | type: u16, | |
65 | optional: true, | |
66 | default: 8007, | |
67 | }, | |
141304d6 | 68 | userid: { |
e6dc35ac | 69 | type: Authid, |
141304d6 DM |
70 | }, |
71 | password: { | |
f357390c | 72 | schema: remote::REMOTE_PASSWORD_SCHEMA, |
141304d6 | 73 | }, |
6afbe1d8 DM |
74 | fingerprint: { |
75 | optional: true, | |
76 | schema: CERT_FINGERPRINT_SHA256_SCHEMA, | |
77 | }, | |
141304d6 DM |
78 | }, |
79 | }, | |
70e5f246 | 80 | access: { |
8247db5b | 81 | permission: &Permission::Privilege(&["remote"], PRIV_REMOTE_MODIFY, false), |
70e5f246 | 82 | }, |
141304d6 DM |
83 | )] |
84 | /// Create new remote. | |
de4db62c | 85 | pub fn create_remote(password: String, param: Value) -> Result<(), Error> { |
141304d6 | 86 | |
b56c111e | 87 | let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; |
141304d6 | 88 | |
de4db62c DC |
89 | let mut data = param.clone(); |
90 | data["password"] = Value::from(base64::encode(password.as_bytes())); | |
91 | let remote: remote::Remote = serde_json::from_value(data)?; | |
141304d6 | 92 | |
f357390c | 93 | let (mut config, _digest) = remote::config()?; |
141304d6 | 94 | |
db0c2287 DC |
95 | if let Some(_) = config.sections.get(&remote.name) { |
96 | bail!("remote '{}' already exists.", remote.name); | |
141304d6 DM |
97 | } |
98 | ||
db0c2287 | 99 | config.set_data(&remote.name, "remote", &remote)?; |
141304d6 | 100 | |
f357390c | 101 | remote::save_config(&config)?; |
141304d6 DM |
102 | |
103 | Ok(()) | |
104 | } | |
105 | ||
08195ac8 DM |
106 | #[api( |
107 | input: { | |
108 | properties: { | |
109 | name: { | |
110 | schema: REMOTE_ID_SCHEMA, | |
111 | }, | |
112 | }, | |
113 | }, | |
f3ec5dae DM |
114 | returns: { |
115 | description: "The remote configuration (with config digest).", | |
f357390c | 116 | type: remote::Remote, |
f3ec5dae | 117 | }, |
70e5f246 | 118 | access: { |
8247db5b | 119 | permission: &Permission::Privilege(&["remote", "{name}"], PRIV_REMOTE_AUDIT, false), |
70e5f246 | 120 | } |
08195ac8 DM |
121 | )] |
122 | /// Read remote configuration data. | |
4f966d05 DC |
123 | pub fn read_remote( |
124 | name: String, | |
125 | _info: &ApiMethod, | |
126 | mut rpcenv: &mut dyn RpcEnvironment, | |
83fd4b3b | 127 | ) -> Result<remote::Remote, Error> { |
f357390c | 128 | let (config, digest) = remote::config()?; |
83fd4b3b DC |
129 | let mut data: remote::Remote = config.lookup("remote", &name)?; |
130 | data.password = "".to_string(); // do not return password in api | |
4f966d05 | 131 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); |
08195ac8 DM |
132 | Ok(data) |
133 | } | |
30003baa | 134 | |
5211705f DM |
135 | #[api()] |
136 | #[derive(Serialize, Deserialize)] | |
137 | #[allow(non_camel_case_types)] | |
138 | /// Deletable property name | |
139 | pub enum DeletableProperty { | |
140 | /// Delete the comment property. | |
141 | comment, | |
142 | /// Delete the fingerprint property. | |
143 | fingerprint, | |
ba20987a DC |
144 | /// Delete the port property. |
145 | port, | |
5211705f | 146 | } |
08195ac8 DM |
147 | |
148 | #[api( | |
149 | protected: true, | |
150 | input: { | |
151 | properties: { | |
152 | name: { | |
153 | schema: REMOTE_ID_SCHEMA, | |
154 | }, | |
155 | comment: { | |
156 | optional: true, | |
157 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
158 | }, | |
159 | host: { | |
160 | optional: true, | |
161 | schema: DNS_NAME_OR_IP_SCHEMA, | |
162 | }, | |
ba20987a DC |
163 | port: { |
164 | description: "The (optional) port.", | |
165 | type: u16, | |
166 | optional: true, | |
167 | }, | |
08195ac8 DM |
168 | userid: { |
169 | optional: true, | |
e6dc35ac | 170 | type: Authid, |
08195ac8 DM |
171 | }, |
172 | password: { | |
173 | optional: true, | |
f357390c | 174 | schema: remote::REMOTE_PASSWORD_SCHEMA, |
08195ac8 | 175 | }, |
6afbe1d8 DM |
176 | fingerprint: { |
177 | optional: true, | |
178 | schema: CERT_FINGERPRINT_SHA256_SCHEMA, | |
179 | }, | |
5211705f DM |
180 | delete: { |
181 | description: "List of properties to delete.", | |
182 | type: Array, | |
183 | optional: true, | |
184 | items: { | |
185 | type: DeletableProperty, | |
186 | } | |
187 | }, | |
002a191a DM |
188 | digest: { |
189 | optional: true, | |
190 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
191 | }, | |
08195ac8 DM |
192 | }, |
193 | }, | |
70e5f246 | 194 | access: { |
8247db5b | 195 | permission: &Permission::Privilege(&["remote", "{name}"], PRIV_REMOTE_MODIFY, false), |
70e5f246 | 196 | }, |
08195ac8 DM |
197 | )] |
198 | /// Update remote configuration. | |
199 | pub fn update_remote( | |
200 | name: String, | |
201 | comment: Option<String>, | |
202 | host: Option<String>, | |
ba20987a | 203 | port: Option<u16>, |
34aa8e13 | 204 | userid: Option<Authid>, |
08195ac8 | 205 | password: Option<String>, |
6afbe1d8 | 206 | fingerprint: Option<String>, |
5211705f | 207 | delete: Option<Vec<DeletableProperty>>, |
002a191a | 208 | digest: Option<String>, |
08195ac8 DM |
209 | ) -> Result<(), Error> { |
210 | ||
b56c111e | 211 | let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; |
347834df | 212 | |
f357390c | 213 | let (mut config, expected_digest) = remote::config()?; |
002a191a DM |
214 | |
215 | if let Some(ref digest) = digest { | |
216 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
217 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
218 | } | |
08195ac8 | 219 | |
f357390c | 220 | let mut data: remote::Remote = config.lookup("remote", &name)?; |
08195ac8 | 221 | |
5211705f DM |
222 | if let Some(delete) = delete { |
223 | for delete_prop in delete { | |
224 | match delete_prop { | |
225 | DeletableProperty::comment => { data.comment = None; }, | |
226 | DeletableProperty::fingerprint => { data.fingerprint = None; }, | |
ba20987a | 227 | DeletableProperty::port => { data.port = None; }, |
5211705f DM |
228 | } |
229 | } | |
230 | } | |
231 | ||
08195ac8 DM |
232 | if let Some(comment) = comment { |
233 | let comment = comment.trim().to_string(); | |
234 | if comment.is_empty() { | |
235 | data.comment = None; | |
236 | } else { | |
237 | data.comment = Some(comment); | |
238 | } | |
239 | } | |
240 | if let Some(host) = host { data.host = host; } | |
ba20987a | 241 | if port.is_some() { data.port = port; } |
08195ac8 DM |
242 | if let Some(userid) = userid { data.userid = userid; } |
243 | if let Some(password) = password { data.password = password; } | |
244 | ||
6afbe1d8 DM |
245 | if let Some(fingerprint) = fingerprint { data.fingerprint = Some(fingerprint); } |
246 | ||
08195ac8 DM |
247 | config.set_data(&name, "remote", &data)?; |
248 | ||
f357390c | 249 | remote::save_config(&config)?; |
08195ac8 DM |
250 | |
251 | Ok(()) | |
252 | } | |
253 | ||
141304d6 | 254 | #[api( |
688fbe07 | 255 | protected: true, |
141304d6 DM |
256 | input: { |
257 | properties: { | |
258 | name: { | |
167971ed | 259 | schema: REMOTE_ID_SCHEMA, |
141304d6 | 260 | }, |
99f443c6 DC |
261 | digest: { |
262 | optional: true, | |
263 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
264 | }, | |
141304d6 DM |
265 | }, |
266 | }, | |
70e5f246 | 267 | access: { |
8247db5b | 268 | permission: &Permission::Privilege(&["remote", "{name}"], PRIV_REMOTE_MODIFY, false), |
70e5f246 | 269 | }, |
141304d6 DM |
270 | )] |
271 | /// Remove a remote from the configuration file. | |
99f443c6 | 272 | pub fn delete_remote(name: String, digest: Option<String>) -> Result<(), Error> { |
141304d6 | 273 | |
b56c111e | 274 | let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; |
141304d6 | 275 | |
99f443c6 DC |
276 | let (mut config, expected_digest) = remote::config()?; |
277 | ||
278 | if let Some(ref digest) = digest { | |
279 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
280 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
281 | } | |
141304d6 DM |
282 | |
283 | match config.sections.get(&name) { | |
284 | Some(_) => { config.sections.remove(&name); }, | |
285 | None => bail!("remote '{}' does not exist.", name), | |
286 | } | |
287 | ||
98574722 DC |
288 | remote::save_config(&config)?; |
289 | ||
141304d6 DM |
290 | Ok(()) |
291 | } | |
292 | ||
08195ac8 DM |
293 | const ITEM_ROUTER: Router = Router::new() |
294 | .get(&API_METHOD_READ_REMOTE) | |
295 | .put(&API_METHOD_UPDATE_REMOTE) | |
296 | .delete(&API_METHOD_DELETE_REMOTE); | |
297 | ||
141304d6 DM |
298 | pub const ROUTER: Router = Router::new() |
299 | .get(&API_METHOD_LIST_REMOTES) | |
300 | .post(&API_METHOD_CREATE_REMOTE) | |
08195ac8 | 301 | .match_all("name", &ITEM_ROUTER); |