]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/remote.rs
verify jobs: add permissions
[proxmox-backup.git] / src / api2 / config / remote.rs
CommitLineData
f7d4e4b5 1use anyhow::{bail, Error};
141304d6 2use serde_json::Value;
5211705f 3use ::serde::{Deserialize, Serialize};
141304d6 4
70e5f246 5use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
98c259b4 6use proxmox::tools::fs::open_file_locked;
141304d6 7
167971ed 8use crate::api2::types::*;
f357390c 9use crate::config::remote;
8247db5b 10use 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
29pub 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 85pub 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
123pub 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
139pub 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.
199pub 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 272pub 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
293const 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
298pub const ROUTER: Router = Router::new()
299 .get(&API_METHOD_LIST_REMOTES)
300 .post(&API_METHOD_CREATE_REMOTE)
08195ac8 301 .match_all("name", &ITEM_ROUTER);