Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
crates/cli/src/user_cmd.rs +60 −0
| @@ -66,6 +66,14 @@ pub(crate) enum UserCommand { | |||
| 66 | /// The user to verify. | 66 | /// The user to verify. |
| 67 | name: String, | 67 | name: String, |
| 68 | }, | 68 | }, |
| 69 | /// Grant (or, with `--revoke`, remove) a user's site-administrator flag. | ||
| 70 | Admin { | ||
| 71 | /// The user to promote or demote. | ||
| 72 | name: String, | ||
| 73 | /// Revoke admin instead of granting it. | ||
| 74 | #[arg(long)] | ||
| 75 | revoke: bool, | ||
| 76 | }, | ||
| 69 | /// Delete a user. Refuses if the user still owns repositories unless | 77 | /// Delete a user. Refuses if the user still owns repositories unless |
| 70 | /// `--purge` is given. | 78 | /// `--purge` is given. |
| 71 | Del { | 79 | Del { |
| @@ -106,6 +114,7 @@ pub(crate) fn run( | |||
| 106 | UserCommand::Disable { name } => block_on(set_disabled(config_path, name, true)), | 114 | UserCommand::Disable { name } => block_on(set_disabled(config_path, name, true)), |
| 107 | UserCommand::Enable { name } => block_on(set_disabled(config_path, name, false)), | 115 | UserCommand::Enable { name } => block_on(set_disabled(config_path, name, false)), |
| 108 | UserCommand::Verify { name } => block_on(verify(config_path, name)), | 116 | UserCommand::Verify { name } => block_on(verify(config_path, name)), |
| 117 | UserCommand::Admin { name, revoke } => block_on(set_admin(config_path, name, !*revoke)), | ||
| 109 | UserCommand::Del { name, purge, yes } => block_on(del(config_path, name, *purge, *yes)), | 118 | UserCommand::Del { name, purge, yes } => block_on(del(config_path, name, *purge, *yes)), |
| 110 | } | 119 | } |
| 111 | } | 120 | } |
| @@ -304,6 +313,31 @@ async fn verify(config_path: Option<&Path>, name: &str) -> anyhow::Result<ExitCo | |||
| 304 | Ok(ExitCode::SUCCESS) | 313 | Ok(ExitCode::SUCCESS) |
| 305 | } | 314 | } |
| 306 | 315 | ||
| 316 | /// Grant or revoke a user's site-administrator flag. | ||
| 317 | async fn set_admin( | ||
| 318 | config_path: Option<&Path>, | ||
| 319 | name: &str, | ||
| 320 | admin: bool, | ||
| 321 | ) -> anyhow::Result<ExitCode> { | ||
| 322 | let (_config, store) = open_store(config_path).await?; | ||
| 323 | let user = require_user(&store, name).await?; | ||
| 324 | if user.is_admin == admin { | ||
| 325 | println!( | ||
| 326 | "{} is already {}an administrator", | ||
| 327 | user.username, | ||
| 328 | if admin { "" } else { "not " } | ||
| 329 | ); | ||
| 330 | return Ok(ExitCode::SUCCESS); | ||
| 331 | } | ||
| 332 | store.set_admin(&user.id, admin).await?; | ||
| 333 | println!( | ||
| 334 | "{} admin for {}", | ||
| 335 | if admin { "granted" } else { "revoked" }, | ||
| 336 | user.username | ||
| 337 | ); | ||
| 338 | Ok(ExitCode::SUCCESS) | ||
| 339 | } | ||
| 340 | |||
| 307 | async fn del( | 341 | async fn del( |
| 308 | config_path: Option<&Path>, | 342 | config_path: Option<&Path>, |
| 309 | name: &str, | 343 | name: &str, |
| @@ -520,6 +554,32 @@ mod tests { | |||
| 520 | } | 554 | } |
| 521 | 555 | ||
| 522 | #[test] | 556 | #[test] |
| 557 | fn admin_grants_then_revokes_the_flag() { | ||
| 558 | let env = env(); | ||
| 559 | block_on(add( | ||
| 560 | env.path(), | ||
| 561 | "erin", | ||
| 562 | "erin@example.com", | ||
| 563 | Some("pw".to_string()), | ||
| 564 | false, | ||
| 565 | None, | ||
| 566 | )) | ||
| 567 | .unwrap(); | ||
| 568 | |||
| 569 | block_on(set_admin(env.path(), "erin", true)).unwrap(); | ||
| 570 | let promoted = | ||
| 571 | block_on(async { anyhow::Ok(require_user(&env.store().await, "erin").await?) }) | ||
| 572 | .unwrap(); | ||
| 573 | assert!(promoted.is_admin); | ||
| 574 | |||
| 575 | block_on(set_admin(env.path(), "erin", false)).unwrap(); | ||
| 576 | let demoted = | ||
| 577 | block_on(async { anyhow::Ok(require_user(&env.store().await, "erin").await?) }) | ||
| 578 | .unwrap(); | ||
| 579 | assert!(!demoted.is_admin); | ||
| 580 | } | ||
| 581 | |||
| 582 | #[test] | ||
| 523 | fn del_refuses_repo_owner_without_purge_then_purges() { | 583 | fn del_refuses_repo_owner_without_purge_then_purges() { |
| 524 | let env = env(); | 584 | let env = env(); |
| 525 | block_on(add( | 585 | block_on(add( |