diff --git a/visions/server/src/handlers/mod.rs b/visions/server/src/handlers/mod.rs index 0900855..be48e68 100644 --- a/visions/server/src/handlers/mod.rs +++ b/visions/server/src/handlers/mod.rs @@ -141,7 +141,7 @@ pub async fn get_user( ResultExt::Ok(None) => (StatusCode::NOT_FOUND, Json(None)), ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)), ResultExt::Fatal(err) => panic!("{}", err), - } + }, None => ( StatusCode::OK, Json(Some(UserProfile { @@ -169,13 +169,39 @@ pub async fn create_user( admin_required(core.clone(), headers, |_admin| async { match core.create_user(&req.username).await { ResultExt::Ok(_) => (StatusCode::OK, Json(None)), - ResultExt::Err(err) => (StatusCode::BAD_REQUEST, Json(None)), + ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)), ResultExt::Fatal(fatal) => panic!("{}", fatal), } }) .await } +#[derive(Deserialize, Serialize)] +#[typeshare] +pub struct SetPasswordRequest { + pub password_1: String, + pub password_2: String, +} + +pub async fn set_password( + core: Core, + headers: HeaderMap, + req: SetPasswordRequest, +) -> (StatusCode, Json>) { + auth_required(core.clone(), headers, |user| async { + if req.password_1 == req.password_2 { + match core.set_password(user.id, req.password_1).await { + ResultExt::Ok(_) => (StatusCode::OK, Json(None)), + ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)), + ResultExt::Fatal(fatal) => panic!("{}", fatal), + } + } else { + (StatusCode::BAD_REQUEST, Json(None)) + } + }) + .await +} + /* pub async fn handle_auth( auth_ctx: &AuthDB, diff --git a/visions/server/src/routes.rs b/visions/server/src/routes.rs index 86f6474..81db116 100644 --- a/visions/server/src/routes.rs +++ b/visions/server/src/routes.rs @@ -3,7 +3,7 @@ use std::fmt; use axum::{ extract::Path, http::{HeaderMap, StatusCode}, - routing::{get, post}, + routing::{get, post, put}, Json, Router, }; @@ -11,7 +11,8 @@ use crate::{ core::Core, database::UserId, handlers::{ - check_password, create_user, get_user, healthcheck, AuthRequest, CreateUserRequest, + check_password, create_user, get_user, healthcheck, set_password, AuthRequest, + CreateUserRequest, SetPasswordRequest, }, }; @@ -46,6 +47,16 @@ pub fn routes(core: Core) -> Router { } }), ) + .route( + "/api/v1/user/password", + put({ + let core = core.clone(); + move |headers: HeaderMap, req: Json| { + let Json(req) = req; + set_password(core, headers, req) + } + }), + ) .route( "/api/v1/user/:user_id", get({ @@ -253,9 +264,60 @@ mod test { assert_eq!(profile.username, "admin"); } + #[tokio::test] + async fn a_user_can_change_their_password() { + let (_core, server) = setup_with_user().await; + + let response = server + .post("/api/v1/auth") + .json(&AuthRequest { + username: "savanni".to_owned(), + password: "".to_owned(), + }) + .await; + let session_id: Option = response.json(); + let session_id = session_id.unwrap(); + + let response = server + .get("/api/v1/user") + .add_header("Authorization", format!("Bearer {}", session_id)) + .await; + let profile = response.json::>().unwrap(); + assert_eq!(profile.username, "savanni"); + + let response = server + .put("/api/v1/user/password") + .json(&SetPasswordRequest { + password_1: "abcdefg".to_owned(), + password_2: "abcd".to_owned(), + }) + .await; + response.assert_status(StatusCode::UNAUTHORIZED); + + let response = server + .put("/api/v1/user/password") + .add_header("Authorization", format!("Bearer {}", session_id)) + .json(&SetPasswordRequest { + password_1: "abcdefg".to_owned(), + password_2: "abcd".to_owned(), + }) + .await; + response.assert_status(StatusCode::BAD_REQUEST); + + let response = server + .put(&format!("/api/v1/user/password")) + .add_header("Authorization", format!("Bearer {}", session_id)) + .json(&SetPasswordRequest { + password_1: "abcdefg".to_owned(), + password_2: "abcdefg".to_owned(), + }) + .await; + response.assert_status(StatusCode::OK); + } + #[ignore] #[tokio::test] - async fn a_user_can_get_change_their_password() { + async fn a_user_cannot_change_another_users_password() { unimplemented!(); }