]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/access/user.rs
depend on proxmox 0.4.2
[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 use proxmox::tools::fs::open_file_locked;
7
8 use crate::api2::types::*;
9 use crate::config::user;
10 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
11 use crate::config::cached_user_info::CachedUserInfo;
12
13 pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
14 .format(&PASSWORD_FORMAT)
15 .min_length(5)
16 .max_length(64)
17 .schema();
18
19 #[api(
20 input: {
21 properties: {},
22 },
23 returns: {
24 description: "List users (with config digest).",
25 type: Array,
26 items: { type: user::User },
27 },
28 access: {
29 permission: &Permission::Anybody,
30 description: "Returns all or just the logged-in user, depending on privileges.",
31 },
32 )]
33 /// List users
34 pub fn list_users(
35 _param: Value,
36 _info: &ApiMethod,
37 mut rpcenv: &mut dyn RpcEnvironment,
38 ) -> Result<Vec<user::User>, Error> {
39
40 let (config, digest) = user::config()?;
41
42 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
43 let user_info = CachedUserInfo::new()?;
44
45 let top_level_privs = user_info.lookup_privs(&userid, &["access", "users"]);
46 let top_level_allowed = (top_level_privs & PRIV_SYS_AUDIT) != 0;
47
48 let filter_by_privs = |user: &user::User| {
49 top_level_allowed || user.userid == userid
50 };
51
52 let list:Vec<user::User> = config.convert_to_typed_array("user")?;
53
54 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
55
56 Ok(list.into_iter().filter(filter_by_privs).collect())
57 }
58
59 #[api(
60 protected: true,
61 input: {
62 properties: {
63 userid: {
64 type: Userid,
65 },
66 comment: {
67 schema: SINGLE_LINE_COMMENT_SCHEMA,
68 optional: true,
69 },
70 password: {
71 schema: PBS_PASSWORD_SCHEMA,
72 optional: true,
73 },
74 enable: {
75 schema: user::ENABLE_USER_SCHEMA,
76 optional: true,
77 },
78 expire: {
79 schema: user::EXPIRE_USER_SCHEMA,
80 optional: true,
81 },
82 firstname: {
83 schema: user::FIRST_NAME_SCHEMA,
84 optional: true,
85 },
86 lastname: {
87 schema: user::LAST_NAME_SCHEMA,
88 optional: true,
89 },
90 email: {
91 schema: user::EMAIL_SCHEMA,
92 optional: true,
93 },
94 },
95 },
96 access: {
97 permission: &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
98 },
99 )]
100 /// Create new user.
101 pub fn create_user(password: Option<String>, param: Value) -> Result<(), Error> {
102
103 let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
104
105 let user: user::User = serde_json::from_value(param)?;
106
107 let (mut config, _digest) = user::config()?;
108
109 if let Some(_) = config.sections.get(user.userid.as_str()) {
110 bail!("user '{}' already exists.", user.userid);
111 }
112
113 let authenticator = crate::auth::lookup_authenticator(&user.userid.realm())?;
114
115 config.set_data(user.userid.as_str(), "user", &user)?;
116
117 user::save_config(&config)?;
118
119 if let Some(password) = password {
120 authenticator.store_password(user.userid.name(), &password)?;
121 }
122
123 Ok(())
124 }
125
126 #[api(
127 input: {
128 properties: {
129 userid: {
130 type: Userid,
131 },
132 },
133 },
134 returns: {
135 description: "The user configuration (with config digest).",
136 type: user::User,
137 },
138 access: {
139 permission: &Permission::Or(&[
140 &Permission::Privilege(&["access", "users"], PRIV_SYS_AUDIT, false),
141 &Permission::UserParam("userid"),
142 ]),
143 },
144 )]
145 /// Read user configuration data.
146 pub fn read_user(userid: Userid, mut rpcenv: &mut dyn RpcEnvironment) -> Result<user::User, Error> {
147 let (config, digest) = user::config()?;
148 let user = config.lookup("user", userid.as_str())?;
149 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
150 Ok(user)
151 }
152
153 #[api(
154 protected: true,
155 input: {
156 properties: {
157 userid: {
158 type: Userid,
159 },
160 comment: {
161 optional: true,
162 schema: SINGLE_LINE_COMMENT_SCHEMA,
163 },
164 password: {
165 schema: PBS_PASSWORD_SCHEMA,
166 optional: true,
167 },
168 enable: {
169 schema: user::ENABLE_USER_SCHEMA,
170 optional: true,
171 },
172 expire: {
173 schema: user::EXPIRE_USER_SCHEMA,
174 optional: true,
175 },
176 firstname: {
177 schema: user::FIRST_NAME_SCHEMA,
178 optional: true,
179 },
180 lastname: {
181 schema: user::LAST_NAME_SCHEMA,
182 optional: true,
183 },
184 email: {
185 schema: user::EMAIL_SCHEMA,
186 optional: true,
187 },
188 digest: {
189 optional: true,
190 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
191 },
192 },
193 },
194 access: {
195 permission: &Permission::Or(&[
196 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
197 &Permission::UserParam("userid"),
198 ]),
199 },
200 )]
201 /// Update user configuration.
202 pub fn update_user(
203 userid: Userid,
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 = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
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.as_str())?;
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 authenticator = crate::auth::lookup_authenticator(userid.realm())?;
244 authenticator.store_password(userid.name(), &password)?;
245 }
246
247 if let Some(firstname) = firstname {
248 data.firstname = if firstname.is_empty() { None } else { Some(firstname) };
249 }
250
251 if let Some(lastname) = lastname {
252 data.lastname = if lastname.is_empty() { None } else { Some(lastname) };
253 }
254 if let Some(email) = email {
255 data.email = if email.is_empty() { None } else { Some(email) };
256 }
257
258 config.set_data(userid.as_str(), "user", &data)?;
259
260 user::save_config(&config)?;
261
262 Ok(())
263 }
264
265 #[api(
266 protected: true,
267 input: {
268 properties: {
269 userid: {
270 type: Userid,
271 },
272 digest: {
273 optional: true,
274 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
275 },
276 },
277 },
278 access: {
279 permission: &Permission::Or(&[
280 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
281 &Permission::UserParam("userid"),
282 ]),
283 },
284 )]
285 /// Remove a user from the configuration file.
286 pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error> {
287
288 let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
289
290 let (mut config, expected_digest) = user::config()?;
291
292 if let Some(ref digest) = digest {
293 let digest = proxmox::tools::hex_to_digest(digest)?;
294 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
295 }
296
297 match config.sections.get(userid.as_str()) {
298 Some(_) => { config.sections.remove(userid.as_str()); },
299 None => bail!("user '{}' does not exist.", userid),
300 }
301
302 user::save_config(&config)?;
303
304 Ok(())
305 }
306
307 const ITEM_ROUTER: Router = Router::new()
308 .get(&API_METHOD_READ_USER)
309 .put(&API_METHOD_UPDATE_USER)
310 .delete(&API_METHOD_DELETE_USER);
311
312 pub const ROUTER: Router = Router::new()
313 .get(&API_METHOD_LIST_USERS)
314 .post(&API_METHOD_CREATE_USER)
315 .match_all("userid", &ITEM_ROUTER);