]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/access/user.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / src / api2 / access / user.rs
CommitLineData
bf78f708
DM
1//! User Management
2
08ac90f9 3use anyhow::{bail, format_err, Error};
6746bbb1 4use serde::{Serialize, Deserialize};
942078c4 5use serde_json::{json, Value};
6746bbb1 6use std::collections::HashMap;
25877d05 7use hex::FromHex;
579728c6 8
6ef1b649
WB
9use proxmox_router::{ApiMethod, Router, RpcEnvironment, SubdirMap, Permission};
10use proxmox_schema::api;
579728c6 11
2b7f8dd5 12use pbs_api_types::{
b65dfff5
DM
13 PROXMOX_CONFIG_DIGEST_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, Authid,
14 Tokenname, UserWithTokens, Userid, User, UserUpdater, ApiToken,
15 ENABLE_USER_SCHEMA, EXPIRE_USER_SCHEMA, PBS_PASSWORD_SCHEMA,
16 PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY,
2b7f8dd5 17};
1cb08a0a 18use pbs_config::token_shadow;
2b7f8dd5 19
ba3d7e19 20use pbs_config::CachedUserInfo;
579728c6 21
b65dfff5 22fn new_user_with_tokens(user: User) -> UserWithTokens {
2b7f8dd5
WB
23 UserWithTokens {
24 userid: user.userid,
25 comment: user.comment,
26 enable: user.enable,
27 expire: user.expire,
28 firstname: user.firstname,
29 lastname: user.lastname,
30 email: user.email,
31 tokens: Vec::new(),
6746bbb1
FG
32 }
33}
34
579728c6
DM
35#[api(
36 input: {
6746bbb1
FG
37 properties: {
38 include_tokens: {
39 type: bool,
40 description: "Include user's API tokens in returned list.",
41 optional: true,
42 default: false,
43 },
44 },
579728c6
DM
45 },
46 returns: {
47 description: "List users (with config digest).",
48 type: Array,
906ef6c5 49 items: { type: UserWithTokens },
579728c6 50 },
d4f020f4 51 access: {
be3bd0f9 52 permission: &Permission::Anybody,
08ac90f9 53 description: "Returns all or just the logged-in user (/API token owner), depending on privileges.",
d4f020f4 54 },
579728c6 55)]
be3bd0f9 56/// List users
579728c6 57pub fn list_users(
6746bbb1 58 include_tokens: bool,
579728c6 59 _info: &ApiMethod,
522c0da0 60 mut rpcenv: &mut dyn RpcEnvironment,
6746bbb1 61) -> Result<Vec<UserWithTokens>, Error> {
579728c6 62
ba3d7e19 63 let (config, digest) = pbs_config::user::config()?;
579728c6 64
08ac90f9
FG
65 let auth_id: Authid = rpcenv
66 .get_auth_id()
67 .ok_or_else(|| format_err!("no authid available"))?
68 .parse()?;
69
70 let userid = auth_id.user();
e6dc35ac 71
be3bd0f9
FG
72 let user_info = CachedUserInfo::new()?;
73
e6dc35ac 74 let top_level_privs = user_info.lookup_privs(&auth_id, &["access", "users"]);
be3bd0f9
FG
75 let top_level_allowed = (top_level_privs & PRIV_SYS_AUDIT) != 0;
76
b65dfff5 77 let filter_by_privs = |user: &User| {
08ac90f9 78 top_level_allowed || user.userid == *userid
be3bd0f9
FG
79 };
80
6746bbb1 81
b65dfff5 82 let list:Vec<User> = config.convert_to_typed_array("user")?;
522c0da0 83
25877d05 84 rpcenv["digest"] = hex::encode(&digest).into();
579728c6 85
6746bbb1
FG
86 let iter = list.into_iter().filter(filter_by_privs);
87 let list = if include_tokens {
b65dfff5 88 let tokens: Vec<ApiToken> = config.convert_to_typed_array("token")?;
6746bbb1
FG
89 let mut user_to_tokens = tokens
90 .into_iter()
91 .fold(
92 HashMap::new(),
b65dfff5 93 |mut map: HashMap<Userid, Vec<ApiToken>>, token: ApiToken| {
6746bbb1
FG
94 if token.tokenid.is_token() {
95 map
96 .entry(token.tokenid.user().clone())
97 .or_default()
98 .push(token);
99 }
100 map
101 });
102 iter
b65dfff5 103 .map(|user: User| {
2b7f8dd5 104 let mut user = new_user_with_tokens(user);
ea1853a1 105 user.tokens = user_to_tokens.remove(&user.userid).unwrap_or_default();
6746bbb1
FG
106 user
107 })
108 .collect()
109 } else {
2b7f8dd5 110 iter.map(new_user_with_tokens)
6746bbb1
FG
111 .collect()
112 };
113
114 Ok(list)
579728c6
DM
115}
116
117#[api(
118 protected: true,
119 input: {
120 properties: {
b65dfff5
DM
121 config: {
122 type: User,
123 flatten: true,
579728c6
DM
124 },
125 password: {
126 schema: PBS_PASSWORD_SCHEMA,
127 optional: true,
128 },
579728c6
DM
129 },
130 },
d4f020f4 131 access: {
74c08a57 132 permission: &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
d4f020f4 133 },
579728c6
DM
134)]
135/// Create new user.
5aa10190
OB
136pub fn create_user(
137 password: Option<String>,
b65dfff5 138 config: User,
5aa10190
OB
139 rpcenv: &mut dyn RpcEnvironment
140) -> Result<(), Error> {
579728c6 141
ba3d7e19 142 let _lock = pbs_config::user::lock_config()?;
579728c6 143
ba3d7e19 144 let (mut section_config, _digest) = pbs_config::user::config()?;
579728c6 145
b65dfff5
DM
146 if section_config.sections.get(config.userid.as_str()).is_some() {
147 bail!("user '{}' already exists.", config.userid);
579728c6
DM
148 }
149
b65dfff5 150 section_config.set_data(config.userid.as_str(), "user", &config)?;
579728c6 151
b65dfff5 152 let realm = config.userid.realm();
5aa10190
OB
153
154 // Fails if realm does not exist!
155 let authenticator = crate::auth::lookup_authenticator(realm)?;
156
ba3d7e19 157 pbs_config::user::save_config(&section_config)?;
579728c6 158
7d817b03 159 if let Some(password) = password {
5aa10190
OB
160 let user_info = CachedUserInfo::new()?;
161 let current_auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
162 if realm == "pam" && !user_info.is_superuser(&current_auth_id) {
163 bail!("only superuser can edit pam credentials!");
164 }
b65dfff5 165 authenticator.store_password(config.userid.name(), &password)?;
7d817b03
DM
166 }
167
579728c6
DM
168 Ok(())
169}
170
171#[api(
172 input: {
173 properties: {
174 userid: {
e7cb4dc5 175 type: Userid,
579728c6 176 },
685e1334 177 },
579728c6 178 },
b65dfff5 179 returns: { type: User },
d4f020f4 180 access: {
be3bd0f9
FG
181 permission: &Permission::Or(&[
182 &Permission::Privilege(&["access", "users"], PRIV_SYS_AUDIT, false),
183 &Permission::UserParam("userid"),
184 ]),
d4f020f4 185 },
579728c6
DM
186)]
187/// Read user configuration data.
b65dfff5 188pub fn read_user(userid: Userid, mut rpcenv: &mut dyn RpcEnvironment) -> Result<User, Error> {
ba3d7e19 189 let (config, digest) = pbs_config::user::config()?;
e7cb4dc5 190 let user = config.lookup("user", userid.as_str())?;
25877d05 191 rpcenv["digest"] = hex::encode(&digest).into();
522c0da0 192 Ok(user)
579728c6
DM
193}
194
c0026563
DC
195#[api()]
196#[derive(Serialize, Deserialize)]
197#[serde(rename_all="kebab-case")]
198#[allow(non_camel_case_types)]
199pub enum DeletableProperty {
200 /// Delete the comment property.
201 comment,
202 /// Delete the firstname property.
203 firstname,
204 /// Delete the lastname property.
205 lastname,
206 /// Delete the email property.
207 email,
208}
209
579728c6
DM
210#[api(
211 protected: true,
212 input: {
213 properties: {
214 userid: {
e7cb4dc5 215 type: Userid,
579728c6 216 },
b65dfff5
DM
217 update: {
218 type: UserUpdater,
219 flatten: true,
579728c6
DM
220 },
221 password: {
222 schema: PBS_PASSWORD_SCHEMA,
223 optional: true,
224 },
c0026563
DC
225 delete: {
226 description: "List of properties to delete.",
227 type: Array,
228 optional: true,
229 items: {
230 type: DeletableProperty,
231 }
232 },
579728c6
DM
233 digest: {
234 optional: true,
235 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
236 },
237 },
238 },
d4f020f4 239 access: {
be3bd0f9
FG
240 permission: &Permission::Or(&[
241 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
242 &Permission::UserParam("userid"),
243 ]),
d4f020f4 244 },
579728c6
DM
245)]
246/// Update user configuration.
367c0ff7 247#[allow(clippy::too_many_arguments)]
579728c6 248pub fn update_user(
e7cb4dc5 249 userid: Userid,
b65dfff5 250 update: UserUpdater,
579728c6 251 password: Option<String>,
c0026563 252 delete: Option<Vec<DeletableProperty>>,
579728c6 253 digest: Option<String>,
5aa10190 254 rpcenv: &mut dyn RpcEnvironment,
579728c6
DM
255) -> Result<(), Error> {
256
ba3d7e19 257 let _lock = pbs_config::user::lock_config()?;
579728c6 258
ba3d7e19 259 let (mut config, expected_digest) = pbs_config::user::config()?;
579728c6
DM
260
261 if let Some(ref digest) = digest {
25877d05 262 let digest = <[u8; 32]>::from_hex(digest)?;
579728c6
DM
263 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
264 }
265
b65dfff5 266 let mut data: User = config.lookup("user", userid.as_str())?;
579728c6 267
c0026563
DC
268 if let Some(delete) = delete {
269 for delete_prop in delete {
270 match delete_prop {
271 DeletableProperty::comment => data.comment = None,
272 DeletableProperty::firstname => data.firstname = None,
273 DeletableProperty::lastname => data.lastname = None,
274 DeletableProperty::email => data.email = None,
275 }
276 }
277 }
278
b65dfff5 279 if let Some(comment) = update.comment {
579728c6
DM
280 let comment = comment.trim().to_string();
281 if comment.is_empty() {
282 data.comment = None;
283 } else {
284 data.comment = Some(comment);
285 }
286 }
287
b65dfff5 288 if let Some(enable) = update.enable {
579728c6
DM
289 data.enable = if enable { None } else { Some(false) };
290 }
291
b65dfff5 292 if let Some(expire) = update.expire {
579728c6
DM
293 data.expire = if expire > 0 { Some(expire) } else { None };
294 }
295
296 if let Some(password) = password {
5aa10190
OB
297 let user_info = CachedUserInfo::new()?;
298 let current_auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
299 let self_service = current_auth_id.user() == &userid;
300 let target_realm = userid.realm();
301 if !self_service && target_realm == "pam" && !user_info.is_superuser(&current_auth_id) {
302 bail!("only superuser can edit pam credentials!");
303 }
e7cb4dc5
WB
304 let authenticator = crate::auth::lookup_authenticator(userid.realm())?;
305 authenticator.store_password(userid.name(), &password)?;
579728c6
DM
306 }
307
b65dfff5 308 if let Some(firstname) = update.firstname {
579728c6
DM
309 data.firstname = if firstname.is_empty() { None } else { Some(firstname) };
310 }
311
b65dfff5 312 if let Some(lastname) = update.lastname {
579728c6
DM
313 data.lastname = if lastname.is_empty() { None } else { Some(lastname) };
314 }
b65dfff5 315 if let Some(email) = update.email {
579728c6
DM
316 data.email = if email.is_empty() { None } else { Some(email) };
317 }
318
e7cb4dc5 319 config.set_data(userid.as_str(), "user", &data)?;
579728c6 320
ba3d7e19 321 pbs_config::user::save_config(&config)?;
579728c6
DM
322
323 Ok(())
324}
325
326#[api(
327 protected: true,
328 input: {
329 properties: {
330 userid: {
e7cb4dc5 331 type: Userid,
579728c6
DM
332 },
333 digest: {
334 optional: true,
335 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
336 },
337 },
338 },
d4f020f4 339 access: {
be3bd0f9
FG
340 permission: &Permission::Or(&[
341 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
342 &Permission::UserParam("userid"),
343 ]),
d4f020f4 344 },
579728c6
DM
345)]
346/// Remove a user from the configuration file.
e7cb4dc5 347pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error> {
579728c6 348
ba3d7e19 349 let _lock = pbs_config::user::lock_config()?;
f22dfb5e 350 let _tfa_lock = crate::config::tfa::write_lock()?;
b65dfff5 351
ba3d7e19 352 let (mut config, expected_digest) = pbs_config::user::config()?;
579728c6
DM
353
354 if let Some(ref digest) = digest {
25877d05 355 let digest = <[u8; 32]>::from_hex(digest)?;
579728c6
DM
356 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
357 }
358
e7cb4dc5
WB
359 match config.sections.get(userid.as_str()) {
360 Some(_) => { config.sections.remove(userid.as_str()); },
579728c6
DM
361 None => bail!("user '{}' does not exist.", userid),
362 }
363
ba3d7e19 364 pbs_config::user::save_config(&config)?;
579728c6 365
a4e871f5
DC
366 let authenticator = crate::auth::lookup_authenticator(userid.realm())?;
367 match authenticator.remove_password(userid.name()) {
368 Ok(()) => {},
369 Err(err) => {
370 eprintln!(
371 "error removing password after deleting user {:?}: {}",
372 userid, err
373 );
374 }
375 }
376
f22dfb5e 377 match crate::config::tfa::read().and_then(|mut cfg| {
9407810f
WB
378 let _: proxmox_tfa::api::NeedsSaving =
379 cfg.remove_user(crate::config::tfa::UserAccess, userid.as_str())?;
f22dfb5e
WB
380 crate::config::tfa::write(&cfg)
381 }) {
382 Ok(()) => (),
383 Err(err) => {
384 eprintln!(
385 "error updating TFA config after deleting user {:?}: {}",
386 userid, err
387 );
388 }
389 }
390
579728c6
DM
391 Ok(())
392}
393
942078c4
FG
394#[api(
395 input: {
396 properties: {
397 userid: {
398 type: Userid,
399 },
f54634a8 400 "token-name": {
942078c4
FG
401 type: Tokenname,
402 },
403 },
404 },
b65dfff5 405 returns: { type: ApiToken },
942078c4
FG
406 access: {
407 permission: &Permission::Or(&[
408 &Permission::Privilege(&["access", "users"], PRIV_SYS_AUDIT, false),
409 &Permission::UserParam("userid"),
410 ]),
411 },
412)]
413/// Read user's API token metadata
414pub fn read_token(
415 userid: Userid,
f54634a8 416 token_name: Tokenname,
942078c4
FG
417 _info: &ApiMethod,
418 mut rpcenv: &mut dyn RpcEnvironment,
b65dfff5 419) -> Result<ApiToken, Error> {
942078c4 420
ba3d7e19 421 let (config, digest) = pbs_config::user::config()?;
942078c4 422
f54634a8 423 let tokenid = Authid::from((userid, Some(token_name)));
942078c4 424
25877d05 425 rpcenv["digest"] = hex::encode(&digest).into();
942078c4
FG
426 config.lookup("token", &tokenid.to_string())
427}
428
429#[api(
430 protected: true,
431 input: {
432 properties: {
433 userid: {
434 type: Userid,
435 },
f54634a8 436 "token-name": {
942078c4
FG
437 type: Tokenname,
438 },
439 comment: {
440 optional: true,
441 schema: SINGLE_LINE_COMMENT_SCHEMA,
442 },
443 enable: {
b65dfff5 444 schema: ENABLE_USER_SCHEMA,
942078c4
FG
445 optional: true,
446 },
447 expire: {
b65dfff5 448 schema: EXPIRE_USER_SCHEMA,
942078c4
FG
449 optional: true,
450 },
451 digest: {
452 optional: true,
453 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
454 },
455 },
456 },
457 access: {
458 permission: &Permission::Or(&[
459 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
460 &Permission::UserParam("userid"),
461 ]),
462 },
463 returns: {
464 description: "API token identifier + generated secret.",
465 properties: {
466 value: {
467 type: String,
468 description: "The API token secret",
469 },
470 tokenid: {
471 type: String,
472 description: "The API token identifier",
473 },
474 },
475 },
476)]
477/// Generate a new API token with given metadata
478pub fn generate_token(
479 userid: Userid,
f54634a8 480 token_name: Tokenname,
942078c4
FG
481 comment: Option<String>,
482 enable: Option<bool>,
483 expire: Option<i64>,
484 digest: Option<String>,
485) -> Result<Value, Error> {
486
ba3d7e19 487 let _lock = pbs_config::user::lock_config()?;
942078c4 488
ba3d7e19 489 let (mut config, expected_digest) = pbs_config::user::config()?;
942078c4
FG
490
491 if let Some(ref digest) = digest {
25877d05 492 let digest = <[u8; 32]>::from_hex(digest)?;
942078c4
FG
493 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
494 }
495
f54634a8 496 let tokenid = Authid::from((userid.clone(), Some(token_name.clone())));
942078c4
FG
497 let tokenid_string = tokenid.to_string();
498
3984a5fd 499 if config.sections.get(&tokenid_string).is_some() {
f54634a8 500 bail!("token '{}' for user '{}' already exists.", token_name.as_str(), userid);
942078c4
FG
501 }
502
6ef1b649 503 let secret = format!("{:x}", proxmox_uuid::Uuid::generate());
942078c4
FG
504 token_shadow::set_secret(&tokenid, &secret)?;
505
b65dfff5 506 let token = ApiToken {
44288184 507 tokenid,
942078c4
FG
508 comment,
509 enable,
510 expire,
511 };
512
513 config.set_data(&tokenid_string, "token", &token)?;
514
ba3d7e19 515 pbs_config::user::save_config(&config)?;
942078c4
FG
516
517 Ok(json!({
518 "tokenid": tokenid_string,
519 "value": secret
520 }))
521}
522
523#[api(
524 protected: true,
525 input: {
526 properties: {
527 userid: {
528 type: Userid,
529 },
f54634a8 530 "token-name": {
942078c4
FG
531 type: Tokenname,
532 },
533 comment: {
534 optional: true,
535 schema: SINGLE_LINE_COMMENT_SCHEMA,
536 },
537 enable: {
b65dfff5 538 schema: ENABLE_USER_SCHEMA,
942078c4
FG
539 optional: true,
540 },
541 expire: {
b65dfff5 542 schema: EXPIRE_USER_SCHEMA,
942078c4
FG
543 optional: true,
544 },
545 digest: {
546 optional: true,
547 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
548 },
549 },
550 },
551 access: {
552 permission: &Permission::Or(&[
553 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
554 &Permission::UserParam("userid"),
555 ]),
556 },
557)]
558/// Update user's API token metadata
559pub fn update_token(
560 userid: Userid,
f54634a8 561 token_name: Tokenname,
942078c4
FG
562 comment: Option<String>,
563 enable: Option<bool>,
564 expire: Option<i64>,
565 digest: Option<String>,
566) -> Result<(), Error> {
567
ba3d7e19 568 let _lock = pbs_config::user::lock_config()?;
942078c4 569
ba3d7e19 570 let (mut config, expected_digest) = pbs_config::user::config()?;
942078c4
FG
571
572 if let Some(ref digest) = digest {
25877d05 573 let digest = <[u8; 32]>::from_hex(digest)?;
942078c4
FG
574 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
575 }
576
f54634a8 577 let tokenid = Authid::from((userid, Some(token_name)));
942078c4
FG
578 let tokenid_string = tokenid.to_string();
579
b65dfff5 580 let mut data: ApiToken = config.lookup("token", &tokenid_string)?;
942078c4
FG
581
582 if let Some(comment) = comment {
583 let comment = comment.trim().to_string();
584 if comment.is_empty() {
585 data.comment = None;
586 } else {
587 data.comment = Some(comment);
588 }
589 }
590
591 if let Some(enable) = enable {
592 data.enable = if enable { None } else { Some(false) };
593 }
594
595 if let Some(expire) = expire {
596 data.expire = if expire > 0 { Some(expire) } else { None };
597 }
598
599 config.set_data(&tokenid_string, "token", &data)?;
600
ba3d7e19 601 pbs_config::user::save_config(&config)?;
942078c4
FG
602
603 Ok(())
604}
605
606#[api(
607 protected: true,
608 input: {
609 properties: {
610 userid: {
611 type: Userid,
612 },
f54634a8 613 "token-name": {
942078c4
FG
614 type: Tokenname,
615 },
616 digest: {
617 optional: true,
618 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
619 },
620 },
621 },
622 access: {
623 permission: &Permission::Or(&[
624 &Permission::Privilege(&["access", "users"], PRIV_PERMISSIONS_MODIFY, false),
625 &Permission::UserParam("userid"),
626 ]),
627 },
628)]
629/// Delete a user's API token
630pub fn delete_token(
631 userid: Userid,
f54634a8 632 token_name: Tokenname,
942078c4
FG
633 digest: Option<String>,
634) -> Result<(), Error> {
635
ba3d7e19 636 let _lock = pbs_config::user::lock_config()?;
942078c4 637
ba3d7e19 638 let (mut config, expected_digest) = pbs_config::user::config()?;
942078c4
FG
639
640 if let Some(ref digest) = digest {
25877d05 641 let digest = <[u8; 32]>::from_hex(digest)?;
942078c4
FG
642 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
643 }
644
f54634a8 645 let tokenid = Authid::from((userid.clone(), Some(token_name.clone())));
942078c4
FG
646 let tokenid_string = tokenid.to_string();
647
648 match config.sections.get(&tokenid_string) {
649 Some(_) => { config.sections.remove(&tokenid_string); },
f54634a8 650 None => bail!("token '{}' of user '{}' does not exist.", token_name.as_str(), userid),
942078c4
FG
651 }
652
653 token_shadow::delete_secret(&tokenid)?;
654
ba3d7e19 655 pbs_config::user::save_config(&config)?;
942078c4
FG
656
657 Ok(())
658}
659
f54634a8
DC
660#[api(
661 properties: {
662 "token-name": { type: Tokenname },
663 token: { type: ApiToken },
664 }
665)]
666#[derive(Serialize, Deserialize)]
667#[serde(rename_all="kebab-case")]
668/// A Token Entry that contains the token-name
669pub struct TokenApiEntry {
670 /// The Token name
671 pub token_name: Tokenname,
672 #[serde(flatten)]
673 pub token: ApiToken,
674}
675
942078c4
FG
676#[api(
677 input: {
678 properties: {
679 userid: {
680 type: Userid,
681 },
682 },
683 },
684 returns: {
685 description: "List user's API tokens (with config digest).",
686 type: Array,
f54634a8 687 items: { type: TokenApiEntry },
942078c4
FG
688 },
689 access: {
690 permission: &Permission::Or(&[
691 &Permission::Privilege(&["access", "users"], PRIV_SYS_AUDIT, false),
692 &Permission::UserParam("userid"),
693 ]),
694 },
695)]
696/// List user's API tokens
697pub fn list_tokens(
698 userid: Userid,
699 _info: &ApiMethod,
700 mut rpcenv: &mut dyn RpcEnvironment,
f54634a8 701) -> Result<Vec<TokenApiEntry>, Error> {
942078c4 702
ba3d7e19 703 let (config, digest) = pbs_config::user::config()?;
942078c4 704
b65dfff5 705 let list:Vec<ApiToken> = config.convert_to_typed_array("token")?;
942078c4 706
25877d05 707 rpcenv["digest"] = hex::encode(&digest).into();
942078c4 708
f54634a8
DC
709 let filter_by_owner = |token: ApiToken| {
710 if token.tokenid.is_token() && token.tokenid.user() == &userid {
711 let token_name = token.tokenid.tokenname().unwrap().to_owned();
712 Some(TokenApiEntry {
713 token_name,
714 token,
715 })
942078c4 716 } else {
f54634a8 717 None
942078c4
FG
718 }
719 };
720
f54634a8
DC
721 let res = list.into_iter().filter_map(filter_by_owner).collect();
722
723 Ok(res)
942078c4
FG
724}
725
726const TOKEN_ITEM_ROUTER: Router = Router::new()
727 .get(&API_METHOD_READ_TOKEN)
728 .put(&API_METHOD_UPDATE_TOKEN)
729 .post(&API_METHOD_GENERATE_TOKEN)
730 .delete(&API_METHOD_DELETE_TOKEN);
731
732const TOKEN_ROUTER: Router = Router::new()
733 .get(&API_METHOD_LIST_TOKENS)
f54634a8 734 .match_all("token-name", &TOKEN_ITEM_ROUTER);
942078c4
FG
735
736const USER_SUBDIRS: SubdirMap = &[
737 ("token", &TOKEN_ROUTER),
738];
739
740const USER_ROUTER: Router = Router::new()
579728c6
DM
741 .get(&API_METHOD_READ_USER)
742 .put(&API_METHOD_UPDATE_USER)
942078c4
FG
743 .delete(&API_METHOD_DELETE_USER)
744 .subdirs(USER_SUBDIRS);
579728c6
DM
745
746pub const ROUTER: Router = Router::new()
747 .get(&API_METHOD_LIST_USERS)
748 .post(&API_METHOD_CREATE_USER)
942078c4 749 .match_all("userid", &USER_ROUTER);