From a6ce34b7de8dabf1fda3c625ff47112e16f52e33 Mon Sep 17 00:00:00 2001 From: Lennart Kloock <lennart.kloock@protonmail.com> Date: Sun, 22 Oct 2023 00:05:19 +0200 Subject: [PATCH] feat(api): change password endpoint --- platform/api/src/api/v1/gql/mutations/auth.rs | 4 +- platform/api/src/api/v1/gql/mutations/user.rs | 54 +++++++++++++++++++ schema.graphql | 10 ++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/platform/api/src/api/v1/gql/mutations/auth.rs b/platform/api/src/api/v1/gql/mutations/auth.rs index e5e4c5e3c..50c8fc159 100644 --- a/platform/api/src/api/v1/gql/mutations/auth.rs +++ b/platform/api/src/api/v1/gql/mutations/auth.rs @@ -83,12 +83,12 @@ impl AuthMutation { .bind(user.id) .bind(!user.totp_enabled) .bind(expires_at) - .fetch_one(&mut *tx) + .fetch_one(tx.as_mut()) .await?; sqlx::query("UPDATE users SET last_login_at = NOW() WHERE id = $1") .bind(user.id) - .execute(&mut *tx) + .execute(tx.as_mut()) .await?; tx.commit().await?; diff --git a/platform/api/src/api/v1/gql/mutations/user.rs b/platform/api/src/api/v1/gql/mutations/user.rs index 3c030f157..5a64c88b9 100644 --- a/platform/api/src/api/v1/gql/mutations/user.rs +++ b/platform/api/src/api/v1/gql/mutations/user.rs @@ -3,6 +3,7 @@ use bytes::Bytes; use prost::Message; use crate::api::middleware::auth::AuthError; +use crate::api::v1::gql::validators::PasswordValidator; use crate::{ api::v1::gql::{ error::{GqlError, Result, ResultExt}, @@ -148,6 +149,59 @@ impl UserMutation { Ok(user.into()) } + async fn password<'ctx>( + &self, + ctx: &Context<'_>, + #[graphql(desc = "Current password")] current_password: String, + #[graphql(desc = "New password", validator(custom = "PasswordValidator"))] + new_password: String, + ) -> Result<User> { + let global = ctx.get_global(); + let request_context = ctx.get_req_context(); + + let auth = request_context + .auth() + .await? + .ok_or(GqlError::Auth(AuthError::NotLoggedIn))?; + + let user = global + .user_by_id_loader + .load(auth.session.user_id.0) + .await + .map_err_gql("failed to fetch user")? + .map_err_gql(GqlError::NotFound("user"))?; + + if !user.verify_password(¤t_password) { + return Err(GqlError::InvalidInput { + fields: vec!["password"], + message: "wrong password", + } + .into()); + } + + let mut tx = global.db.begin().await?; + + let user: database::User = + sqlx::query_as("UPDATE users SET password_hash = $1 WHERE id = $2 RETURNING *") + .bind(database::User::hash_password(&new_password)) + .bind(user.id) + .fetch_one(tx.as_mut()) + .await?; + + // Delete all sessions except current + sqlx::query("DELETE FROM user_sessions WHERE user_id = $1 AND id != $2") + .bind(user.id) + .bind(auth.session.id) + .execute(tx.as_mut()) + .await?; + + // TODO: Logout active connections + + tx.commit().await?; + + Ok(user.into()) + } + /// Follow or unfollow a user. async fn follow<'ctx>( &self, diff --git a/schema.graphql b/schema.graphql index 5842b2e6c..97aba37fa 100644 --- a/schema.graphql +++ b/schema.graphql @@ -351,6 +351,16 @@ type UserMutation { """ follow: Boolean! ): Boolean! + password( + """ + Current password + """ + currentPassword: String! + """ + New password + """ + newPassword: String! + ): User! twoFa: TwoFaMutation! }