diff --git a/src/controllers/users_controller.rs b/src/controllers/users_controller.rs index c4b1187..e9886c0 100644 --- a/src/controllers/users_controller.rs +++ b/src/controllers/users_controller.rs @@ -303,6 +303,44 @@ pub async fn set_approved<'r>( }) } +#[post("/users//reject")] +pub async fn reject<'r>( + username: String, + _session: AdminSession, + mailer: &'r State, + conf: &'r State, + db: DbConn, +) -> Result> { + let user = User::find_by_username(username, &db).await?; + + if user.state != UserState::PendingApproval { + return Err(ZauthError::Unprocessable(String::from( + "user is not in the pending approval state", + ))); + } + + mailer + .create( + &user, + String::from("[Zauth] Your account has been rejected"), + template!( + "mails/user_rejected.txt"; + name: String = user.full_name.to_string(), + admin_email: String = conf.admin_email.clone() + ) + .render() + .map_err(InternalError::from)?, + ) + .await?; + + user.delete(&db).await?; + + Ok(Accepter { + html: Redirect::to(uri!(list_users())), + json: Custom(Status::NoContent, ()), + }) +} + #[get("/users/forgot_password")] pub fn forgot_password_get<'r>() -> impl Responder<'r, 'static> { template! { "users/forgot_password.html" } diff --git a/src/lib.rs b/src/lib.rs index 02cd6aa..a6f1140 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,7 @@ fn assemble(rocket: Rocket) -> Rocket { users_controller::change_state, users_controller::set_admin, users_controller::set_approved, + users_controller::reject, users_controller::forgot_password_get, users_controller::forgot_password_post, users_controller::reset_password_get, diff --git a/templates/mails/user_rejected.txt b/templates/mails/user_rejected.txt new file mode 100644 index 0000000..70ee83e --- /dev/null +++ b/templates/mails/user_rejected.txt @@ -0,0 +1,7 @@ +Hi {{name}} + +Your account has been rejected. +If you think this is a mistake you can contact us at {{admin_email}}. + +Kind regards +The Zeus Authentication Server diff --git a/templates/users/index.html b/templates/users/index.html index d977ab2..45c8b21 100644 --- a/templates/users/index.html +++ b/templates/users/index.html @@ -33,6 +33,7 @@ Email Created at Approve + Reject @@ -59,6 +60,13 @@ + + + +
+ +
+ {% endfor %} diff --git a/tests/users.rs b/tests/users.rs index a01e2ad..c88eaec 100644 --- a/tests/users.rs +++ b/tests/users.rs @@ -788,6 +788,79 @@ async fn user_approval_flow() { .await; } +#[rocket::async_test] +async fn user_rejectal_flow() { + common::as_admin(async move |http_client: HttpClient, db, _admin| { + let email = String::from("test@example.com"); + let user = User::create_pending( + NewUser { + username: String::from("user"), + password: String::from("password"), + full_name: String::from("name"), + email: email.clone(), + ssh_key: None, + not_a_robot: true, + }, + &common::config(), + &db, + ) + .await + .unwrap(); + + let token = user + .pending_email_token + .as_ref() + .expect("email token") + .clone(); + + let response = http_client + .get(format!("/users/confirm/{}", token)) + .header(Accept::HTML) + .header(ContentType::Form) + .dispatch() + .await; + + assert_eq!(response.status(), Status::Ok); + + let response = + common::expect_mail_to(vec!["admin@localhost"], async || { + http_client + .post("/users/confirm") + .header(Accept::HTML) + .header(ContentType::Form) + .body(format!("token={}", token)) + .dispatch() + .await + }) + .await; + + assert_eq!(response.status(), Status::Ok); + + let user = user.reload(&db).await.expect("reload user"); + + assert_eq!( + user.state, + UserState::PendingApproval, + "after email is confirmed, user should be pending for approval" + ); + + common::expect_mail_to(vec![&email], async || { + let response = http_client + .post(format!("/users/{}/reject/", user.username)) + .header(Accept::HTML) + .header(ContentType::Form) + .dispatch() + .await; + + assert_eq!(response.status(), Status::SeeOther); + }) + .await; + + user.reload(&db).await.expect_err("user should be removed"); + }) + .await; +} + #[rocket::async_test] async fn refuse_robots() { common::as_visitor(async move |http_client: HttpClient, db| {