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