From 2c53b0f7652529d47038bf5981b875b42b79e551 Mon Sep 17 00:00:00 2001 From: kenken714 Date: Mon, 28 Oct 2024 19:43:59 +0900 Subject: [PATCH 1/3] feat:add put /me/password --- src/handler.rs | 1 + src/handler/users.rs | 60 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index 63dfee3..2a8b7c9 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -16,6 +16,7 @@ pub fn make_router(app_state: Repository) -> Router { let users_router = Router::new() .route("/me", get(users::get_me).put(users::put_me)) .route("/me/email", put(users::put_me_email)) + .route("/me/password", put(users::put_me_password)) .route("/:userId", get(users::get_user)); Router::new() diff --git a/src/handler/users.rs b/src/handler/users.rs index 102b5c3..c9bb9a6 100644 --- a/src/handler/users.rs +++ b/src/handler/users.rs @@ -8,11 +8,6 @@ use super::Repository; use crate::repository::users::UpdateUser; use crate::utils::validator::{RuleType, Validator}; -#[derive(Deserialize)] -pub struct EmailUpdate { - email: String, -} - pub async fn get_me( State(state): State, TypedHeader(cookie): TypedHeader, @@ -34,6 +29,11 @@ pub async fn get_me( Ok(Json(user)) } +#[derive(Deserialize)] +pub struct EmailUpdate { + email: String, +} + pub async fn put_me_email( State(state): State, TypedHeader(cookie): TypedHeader, @@ -73,6 +73,56 @@ https://link/{jwt}" Ok(StatusCode::NO_CONTENT) } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PasswordUpdate { + old_password: String, + new_password: String, +} + +impl Validator for PasswordUpdate { + fn validate(&self) -> anyhow::Result<()> { + RuleType::Password.validate(&self.old_password)?; + RuleType::Password.validate(&self.new_password)?; + Ok(()) + } +} + +pub async fn put_me_password( + State(state): State, + TypedHeader(cookie): TypedHeader, + Json(body): Json, +) -> anyhow::Result { + body.validate().map_err(|_| StatusCode::BAD_REQUEST)?; + let session_id = cookie.get("session_id").ok_or(StatusCode::UNAUTHORIZED)?; + + let display_id = state + .get_display_id_by_session_id(session_id) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .ok_or(StatusCode::UNAUTHORIZED)?; + + let user = state + .get_user_by_display_id(display_id) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?; + + match state + .verify_user_password(user.id, &body.old_password) + .await + { + Ok(true) => { + state + .update_user_password(user.id, &body.new_password) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + Ok(StatusCode::NO_CONTENT) + } + _ => Err(StatusCode::UNAUTHORIZED), + } +} + #[derive(serde::Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct PutMeRequest { From 21f929e893f2ebca76f48994fe6fbb8c88b40fb4 Mon Sep 17 00:00:00 2001 From: kenken714 Date: Mon, 28 Oct 2024 19:44:29 +0900 Subject: [PATCH 2/3] fix: rename save_password --- src/handler/authentication.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/authentication.rs b/src/handler/authentication.rs index d549cb7..ba827f5 100644 --- a/src/handler/authentication.rs +++ b/src/handler/authentication.rs @@ -86,7 +86,7 @@ pub async fn sign_up( .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; state - .save_raw_password(id, &body.password) + .save_user_password(id, &body.password) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(StatusCode::CREATED) From 227577dce9ec65f16ce207142076765772c497fa Mon Sep 17 00:00:00 2001 From: kenken714 Date: Mon, 28 Oct 2024 19:44:53 +0900 Subject: [PATCH 3/3] feat impl update and verify password --- src/repository/user_password.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/repository/user_password.rs b/src/repository/user_password.rs index d50d5d6..4b8ad6b 100644 --- a/src/repository/user_password.rs +++ b/src/repository/user_password.rs @@ -1,7 +1,7 @@ use super::{users::UserId, Repository}; impl Repository { - pub async fn save_raw_password(&self, id: UserId, password: &str) -> anyhow::Result<()> { + pub async fn save_user_password(&self, id: UserId, password: &str) -> anyhow::Result<()> { let hash = bcrypt::hash(password, self.bcrypt_cost)?; sqlx::query("INSERT INTO users_passwords (user_id, password) VALUES (?, ?)") @@ -12,4 +12,27 @@ impl Repository { Ok(()) } + + pub async fn update_user_password(&self, id: UserId, password: &str) -> anyhow::Result<()> { + let hash = bcrypt::hash(password, self.bcrypt_cost)?; + + sqlx::query("UPDATE users_passwords SET password = ? WHERE user_id = ?") + .bind(&hash) + .bind(id) + .execute(&self.pool) + .await?; + + Ok(()) + } + + pub async fn verify_user_password(&self, id: UserId, password: &str) -> anyhow::Result { + let hash = sqlx::query_scalar::<_, String>( + "SELECT password FROM users_passwords WHERE user_id = ?", + ) + .bind(id) + .fetch_one(&self.pool) + .await?; + + Ok(bcrypt::verify(password, &hash)?) + } }