]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/access/user.rs
switch from failure to anyhow
[proxmox-backup.git] / src / api2 / access / user.rs
CommitLineData
f7d4e4b5 1use anyhow::{bail, Error};
579728c6
DM
2use serde_json::Value;
3
d4f020f4 4use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
579728c6
DM
5use proxmox::api::schema::{Schema, StringSchema};
6
7use crate::api2::types::*;
8use crate::config::user;
4f66423f 9use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
579728c6
DM
10
11pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
12 .format(&PASSWORD_FORMAT)
13 .min_length(5)
14 .max_length(64)
15 .schema();
16
17#[api(
18 input: {
19 properties: {},
20 },
21 returns: {
22 description: "List users (with config digest).",
23 type: Array,
24 items: {
25 type: Object,
26 description: "User configuration (without password).",
27 properties: {
28 userid: {
29 schema: PROXMOX_USER_ID_SCHEMA,
30 },
31 comment: {
32 schema: SINGLE_LINE_COMMENT_SCHEMA,
33 optional: true,
34 },
35 enable: {
36 schema: user::ENABLE_USER_SCHEMA,
37 optional: true,
38 },
39 expire: {
40 schema: user::EXPIRE_USER_SCHEMA,
41 optional: true,
42 },
43 firstname: {
44 schema: user::FIRST_NAME_SCHEMA,
45 optional: true,
46 },
47 lastname: {
48 schema: user::LAST_NAME_SCHEMA,
49 optional: true,
50 },
51 email: {
52 schema: user::EMAIL_SCHEMA,
53 optional: true,
54 },
55 },
56 },
57 },
d4f020f4
DM
58 access: {
59 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
60 },
579728c6
DM
61)]
62/// List all users
63pub fn list_users(
64 _param: Value,
65 _info: &ApiMethod,
66 _rpcenv: &mut dyn RpcEnvironment,
67) -> Result<Value, Error> {
68
69 let (config, digest) = user::config()?;
70
71 let value = config.convert_to_array("userid", Some(&digest), &[]);
72
73 Ok(value.into())
74}
75
76#[api(
77 protected: true,
78 input: {
79 properties: {
80 userid: {
81 schema: PROXMOX_USER_ID_SCHEMA,
82 },
83 comment: {
84 schema: SINGLE_LINE_COMMENT_SCHEMA,
85 optional: true,
86 },
87 password: {
88 schema: PBS_PASSWORD_SCHEMA,
89 optional: true,
90 },
91 enable: {
92 schema: user::ENABLE_USER_SCHEMA,
93 optional: true,
94 },
95 expire: {
96 schema: user::EXPIRE_USER_SCHEMA,
97 optional: true,
98 },
99 firstname: {
100 schema: user::FIRST_NAME_SCHEMA,
101 optional: true,
102 },
103 lastname: {
104 schema: user::LAST_NAME_SCHEMA,
105 optional: true,
106 },
107 email: {
108 schema: user::EMAIL_SCHEMA,
109 optional: true,
110 },
111 },
112 },
d4f020f4 113 access: {
4f66423f 114 permission: &Permission::Privilege(&[], PRIV_PERMISSIONS_MODIFY, false),
d4f020f4 115 },
579728c6
DM
116)]
117/// Create new user.
7d817b03 118pub fn create_user(userid: String, password: Option<String>, param: Value) -> Result<(), Error> {
579728c6
DM
119
120 let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
121
122 let user: user::User = serde_json::from_value(param.clone())?;
123
124 let (mut config, _digest) = user::config()?;
125
126 if let Some(_) = config.sections.get(&userid) {
127 bail!("user '{}' already exists.", userid);
128 }
129
7d817b03
DM
130 let (username, realm) = crate::auth::parse_userid(&userid)?;
131 let authenticator = crate::auth::lookup_authenticator(&realm)?;
579728c6
DM
132
133 config.set_data(&userid, "user", &user)?;
134
135 user::save_config(&config)?;
136
7d817b03
DM
137 if let Some(password) = password {
138 authenticator.store_password(&username, &password)?;
139 }
140
579728c6
DM
141 Ok(())
142}
143
144#[api(
145 input: {
146 properties: {
147 userid: {
148 schema: PROXMOX_USER_ID_SCHEMA,
149 },
685e1334 150 },
579728c6
DM
151 },
152 returns: {
153 description: "The user configuration (with config digest).",
154 type: user::User,
155 },
d4f020f4
DM
156 access: {
157 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
158 },
579728c6
DM
159)]
160/// Read user configuration data.
161pub fn read_user(userid: String) -> Result<Value, Error> {
162 let (config, digest) = user::config()?;
163 let mut data = config.lookup_json("user", &userid)?;
164 data.as_object_mut().unwrap()
165 .insert("digest".into(), proxmox::tools::digest_to_hex(&digest).into());
166 Ok(data)
167}
168
169#[api(
170 protected: true,
171 input: {
172 properties: {
173 userid: {
174 schema: PROXMOX_USER_ID_SCHEMA,
175 },
176 comment: {
177 optional: true,
178 schema: SINGLE_LINE_COMMENT_SCHEMA,
179 },
180 password: {
181 schema: PBS_PASSWORD_SCHEMA,
182 optional: true,
183 },
184 enable: {
185 schema: user::ENABLE_USER_SCHEMA,
186 optional: true,
187 },
188 expire: {
189 schema: user::EXPIRE_USER_SCHEMA,
190 optional: true,
191 },
192 firstname: {
193 schema: user::FIRST_NAME_SCHEMA,
194 optional: true,
195 },
196 lastname: {
197 schema: user::LAST_NAME_SCHEMA,
198 optional: true,
199 },
200 email: {
201 schema: user::EMAIL_SCHEMA,
202 optional: true,
203 },
204 digest: {
205 optional: true,
206 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
207 },
208 },
209 },
d4f020f4 210 access: {
4f66423f 211 permission: &Permission::Privilege(&[], PRIV_PERMISSIONS_MODIFY, false),
d4f020f4 212 },
579728c6
DM
213)]
214/// Update user configuration.
215pub fn update_user(
216 userid: String,
217 comment: Option<String>,
218 enable: Option<bool>,
219 expire: Option<i64>,
220 password: Option<String>,
221 firstname: Option<String>,
222 lastname: Option<String>,
223 email: Option<String>,
224 digest: Option<String>,
225) -> Result<(), Error> {
226
227 let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
228
229 let (mut config, expected_digest) = user::config()?;
230
231 if let Some(ref digest) = digest {
232 let digest = proxmox::tools::hex_to_digest(digest)?;
233 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
234 }
235
236 let mut data: user::User = config.lookup("user", &userid)?;
237
238 if let Some(comment) = comment {
239 let comment = comment.trim().to_string();
240 if comment.is_empty() {
241 data.comment = None;
242 } else {
243 data.comment = Some(comment);
244 }
245 }
246
247 if let Some(enable) = enable {
248 data.enable = if enable { None } else { Some(false) };
249 }
250
251 if let Some(expire) = expire {
252 data.expire = if expire > 0 { Some(expire) } else { None };
253 }
254
255 if let Some(password) = password {
7d817b03
DM
256 let (username, realm) = crate::auth::parse_userid(&userid)?;
257 let authenticator = crate::auth::lookup_authenticator(&realm)?;
258 authenticator.store_password(&username, &password)?;
579728c6
DM
259 }
260
261 if let Some(firstname) = firstname {
262 data.firstname = if firstname.is_empty() { None } else { Some(firstname) };
263 }
264
265 if let Some(lastname) = lastname {
266 data.lastname = if lastname.is_empty() { None } else { Some(lastname) };
267 }
268 if let Some(email) = email {
269 data.email = if email.is_empty() { None } else { Some(email) };
270 }
271
272 config.set_data(&userid, "user", &data)?;
273
274 user::save_config(&config)?;
275
276 Ok(())
277}
278
279#[api(
280 protected: true,
281 input: {
282 properties: {
283 userid: {
284 schema: PROXMOX_USER_ID_SCHEMA,
285 },
286 digest: {
287 optional: true,
288 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
289 },
290 },
291 },
d4f020f4 292 access: {
4f66423f 293 permission: &Permission::Privilege(&[], PRIV_PERMISSIONS_MODIFY, false),
d4f020f4 294 },
579728c6
DM
295)]
296/// Remove a user from the configuration file.
297pub fn delete_user(userid: String, digest: Option<String>) -> Result<(), Error> {
298
299 let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
300
301 let (mut config, expected_digest) = user::config()?;
302
303 if let Some(ref digest) = digest {
304 let digest = proxmox::tools::hex_to_digest(digest)?;
305 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
306 }
307
308 match config.sections.get(&userid) {
309 Some(_) => { config.sections.remove(&userid); },
310 None => bail!("user '{}' does not exist.", userid),
311 }
312
313 user::save_config(&config)?;
314
315 Ok(())
316}
317
318const ITEM_ROUTER: Router = Router::new()
319 .get(&API_METHOD_READ_USER)
320 .put(&API_METHOD_UPDATE_USER)
321 .delete(&API_METHOD_DELETE_USER);
322
323pub const ROUTER: Router = Router::new()
324 .get(&API_METHOD_LIST_USERS)
325 .post(&API_METHOD_CREATE_USER)
326 .match_all("userid", &ITEM_ROUTER);