diff --git a/admin_frontend/src/models.rs b/admin_frontend/src/models.rs index 5c7ae5d4..c9094a1f 100644 --- a/admin_frontend/src/models.rs +++ b/admin_frontend/src/models.rs @@ -10,3 +10,9 @@ pub struct LoginRequest { pub struct LoginResponse { pub access_token: String, } + +#[derive(Deserialize)] +pub struct PutUserRequest { + pub email: String, + pub password: String, +} diff --git a/admin_frontend/src/web_api.rs b/admin_frontend/src/web_api.rs index abaaf3b9..0f35818e 100644 --- a/admin_frontend/src/web_api.rs +++ b/admin_frontend/src/web_api.rs @@ -1,4 +1,5 @@ use crate::error::WebApiError; +use crate::models::PutUserRequest; use crate::response::WebApiResponse; use crate::session::{self, UserSession}; use crate::{models::LoginRequest, AppState}; @@ -19,10 +20,31 @@ pub fn router() -> Router { // TODO .route("/login", post(login_handler)) .route("/logout", post(logout_handler)) - .route("/user/:param", post(post_user_handler).delete(delete_user_handler)) + .route("/user/:param", post(post_user_handler).delete(delete_user_handler).put(put_user_handler)) .route("/user/:email/generate-link", post(post_user_generate_link_handler)) } +pub async fn put_user_handler( + State(state): State, + session: UserSession, + Path(user_uuid): Path, + Json(param): Json, +) -> Result, WebApiError<'static>> { + let res = state + .gotrue_client + .admin_put_user( + &session.access_token, + &user_uuid, + &AdminUserParams { + email: param.email.to_owned(), + password: Some(param.password.to_owned()), + ..Default::default() + }, + ) + .await?; + Ok(res.into()) +} + pub async fn post_user_generate_link_handler( State(state): State, session: UserSession, diff --git a/admin_frontend/templates/user_details.html b/admin_frontend/templates/user_details.html index bd3f8e94..888ad973 100644 --- a/admin_frontend/templates/user_details.html +++ b/admin_frontend/templates/user_details.html @@ -47,14 +47,15 @@ const newPassword = document.getElementById("newPassword").value; let confirmed = confirm("Set new password?"); if (confirmed && newPassword) { - fetch("/web-api/set_user_password", { - method: "POST", + const user_uuid = "{{ user.id|escape }}"; + fetch(`/web-api/user/${user_uuid}`, { + method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ - userId: "{{ user.id }}", - newPassword: newPassword, + email: "{{ user.email|escape }}", + password: newPassword, }), }) .then((response) => { diff --git a/libs/gotrue/src/api.rs b/libs/gotrue/src/api.rs index f39dcefa..7a3c10d7 100644 --- a/libs/gotrue/src/api.rs +++ b/libs/gotrue/src/api.rs @@ -163,6 +163,22 @@ impl Client { check_gotrue_result(resp).await } + pub async fn admin_put_user( + &self, + access_token: &str, + user_uuid: &str, + admin_user_params: &AdminUserParams, + ) -> Result { + let resp = self + .client + .put(format!("{}/admin/users/{}", self.base_url, user_uuid)) + .header("Authorization", format!("Bearer {}", access_token)) + .json(&admin_user_params) + .send() + .await?; + to_gotrue_result(resp).await + } + pub async fn admin_add_user( &self, access_token: &str, diff --git a/libs/infra/src/reqwest.rs b/libs/infra/src/reqwest.rs index 828e7d14..c5895045 100644 --- a/libs/infra/src/reqwest.rs +++ b/libs/infra/src/reqwest.rs @@ -27,10 +27,12 @@ pub async fn from_body(resp: reqwest::Response) -> Result where T: serde::de::DeserializeOwned, { + let status_code = resp.status(); let bytes = resp.bytes().await?; serde_json::from_slice(&bytes).map_err(|e| { anyhow!( - "deserialize error: {}, body: {}", + "deserialize error: {}, status: {}, body: {}", + status_code, e, String::from_utf8_lossy(&bytes) ) diff --git a/tests/gotrue/admin.rs b/tests/gotrue/admin.rs index 79d848ea..cc7612d4 100644 --- a/tests/gotrue/admin.rs +++ b/tests/gotrue/admin.rs @@ -12,7 +12,7 @@ use crate::{ }; #[tokio::test] -async fn admin_user_create_and_list_delete() { +async fn admin_user_create_list_edit_delete() { let http_client = reqwest::Client::new(); let gotrue_client = Client::new(http_client, "http://localhost:9998"); let admin_token = gotrue_client @@ -51,6 +51,7 @@ async fn admin_user_create_and_list_delete() { .unwrap(); assert!(user_token.user.email_confirmed_at.is_some()); + // list users let users = gotrue_client .admin_list_user(&admin_token.access_token) .await @@ -60,6 +61,32 @@ async fn admin_user_create_and_list_delete() { // should be able to find user that was just created let new_user = users.iter().find(|u| u.email == user_email).unwrap(); + // change password for user + let new_password = "Hello456!"; + let _ = gotrue_client + .admin_put_user( + &admin_token.access_token, + new_user.id.as_str(), + &AdminUserParams { + email: user_email.clone(), + password: Some(new_password.to_owned()), + ..Default::default() + }, + ) + .await + .unwrap(); + assert_eq!(user.email, user_email); + assert!(user.email_confirmed_at.is_some()); + + // login user with new password + let _ = gotrue_client + .token(&Grant::Password(PasswordGrant { + email: user_email.clone(), + password: new_password.to_string(), + })) + .await + .unwrap(); + // delete user that was just created gotrue_client .admin_delete_user(