aboutsummaryrefslogtreecommitdiffstats
path: root/atuin-server
diff options
context:
space:
mode:
authorTymanWasTaken <ty@blahaj.land>2024-01-29 06:17:10 -0500
committerGitHub <noreply@github.com>2024-01-29 11:17:10 +0000
commit0faf414cd958137ac60a1f37288994f3a1441780 (patch)
treedf7199c0366893dc393d1cc53230a8f39e88d036 /atuin-server
parentfeat: make history list format configurable (#1638) (diff)
downloadatuin-0faf414cd958137ac60a1f37288994f3a1441780.zip
feat: Add change-password command & support on server (#1615)
* Add change-password command & support on server * Add a test for password change * review: run format --------- Co-authored-by: Ellie Huxtable <ellie@elliehuxtable.com>
Diffstat (limited to '')
-rw-r--r--atuin-server-database/src/lib.rs1
-rw-r--r--atuin-server-postgres/src/lib.rs16
-rw-r--r--atuin-server/src/handlers/user.rs30
-rw-r--r--atuin-server/src/router.rs3
4 files changed, 49 insertions, 1 deletions
diff --git a/atuin-server-database/src/lib.rs b/atuin-server-database/src/lib.rs
index 9b154ea1..dff1204d 100644
--- a/atuin-server-database/src/lib.rs
+++ b/atuin-server-database/src/lib.rs
@@ -54,6 +54,7 @@ pub trait Database: Sized + Clone + Send + Sync + 'static {
async fn get_user_session(&self, u: &User) -> DbResult<Session>;
async fn add_user(&self, user: &NewUser) -> DbResult<i64>;
async fn delete_user(&self, u: &User) -> DbResult<()>;
+ async fn update_user_password(&self, u: &User) -> DbResult<()>;
async fn total_history(&self) -> DbResult<i64>;
async fn count_history(&self, user: &User) -> DbResult<i64>;
diff --git a/atuin-server-postgres/src/lib.rs b/atuin-server-postgres/src/lib.rs
index c1de4d50..1f7cf47a 100644
--- a/atuin-server-postgres/src/lib.rs
+++ b/atuin-server-postgres/src/lib.rs
@@ -290,6 +290,22 @@ impl Database for Postgres {
}
#[instrument(skip_all)]
+ async fn update_user_password(&self, user: &User) -> DbResult<()> {
+ sqlx::query(
+ "update users
+ set password = $1
+ where id = $2",
+ )
+ .bind(&user.password)
+ .bind(user.id)
+ .execute(&self.pool)
+ .await
+ .map_err(fix_error)?;
+
+ Ok(())
+ }
+
+ #[instrument(skip_all)]
async fn add_user(&self, user: &NewUser) -> DbResult<i64> {
let email: &str = &user.email;
let username: &str = &user.username;
diff --git a/atuin-server/src/handlers/user.rs b/atuin-server/src/handlers/user.rs
index fb281ab3..e5651fe2 100644
--- a/atuin-server/src/handlers/user.rs
+++ b/atuin-server/src/handlers/user.rs
@@ -175,6 +175,36 @@ pub async fn delete<DB: Database>(
Ok(Json(DeleteUserResponse {}))
}
+#[instrument(skip_all, fields(user.id = user.id, change_password))]
+pub async fn change_password<DB: Database>(
+ UserAuth(mut user): UserAuth,
+ state: State<AppState<DB>>,
+ Json(change_password): Json<ChangePasswordRequest>,
+) -> Result<Json<ChangePasswordResponse>, ErrorResponseStatus<'static>> {
+ let db = &state.0.database;
+
+ let verified = verify_str(
+ user.password.as_str(),
+ change_password.current_password.borrow(),
+ );
+ if !verified {
+ return Err(
+ ErrorResponse::reply("password is not correct").with_status(StatusCode::UNAUTHORIZED)
+ );
+ }
+
+ let hashed = hash_secret(&change_password.new_password);
+ user.password = hashed;
+
+ if let Err(e) = db.update_user_password(&user).await {
+ error!("failed to change user password: {}", e);
+
+ return Err(ErrorResponse::reply("failed to change user password")
+ .with_status(StatusCode::INTERNAL_SERVER_ERROR));
+ };
+ Ok(Json(ChangePasswordResponse {}))
+}
+
#[instrument(skip_all, fields(user.username = login.username.as_str()))]
pub async fn login<DB: Database>(
state: State<AppState<DB>>,
diff --git a/atuin-server/src/router.rs b/atuin-server/src/router.rs
index 8509058f..74df229a 100644
--- a/atuin-server/src/router.rs
+++ b/atuin-server/src/router.rs
@@ -5,7 +5,7 @@ use axum::{
http::{self, request::Parts},
middleware::Next,
response::{IntoResponse, Response},
- routing::{delete, get, post},
+ routing::{delete, get, patch, post},
Router,
};
use eyre::Result;
@@ -119,6 +119,7 @@ pub fn router<DB: Database>(database: DB, settings: Settings<DB::Settings>) -> R
.route("/history", delete(handlers::history::delete))
.route("/user/:username", get(handlers::user::get))
.route("/account", delete(handlers::user::delete))
+ .route("/account/password", patch(handlers::user::change_password))
.route("/register", post(handlers::user::register))
.route("/login", post(handlers::user::login))
.route("/record", post(handlers::record::post::<DB>))