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