420dedbc1e
- Add IM service modules: audit, channel roles, custom emojis, forum tags, integrations, invitations, repo links, slash commands, stages, voice, webhooks - Add PR service modules: review requests, templates - Add repo service modules: contributors, release assets, git extras (archive, branch rename, commit extras, diff/merge, tag, tree) - Add user service: social (follow/block) - Add internal auth service - Update existing service modules with expanded functionality - Remove deleted IM modules: articles, delivery trace, drafts, follows, messages, polls, presence, reactions, threads
80 lines
2.5 KiB
Rust
80 lines
2.5 KiB
Rust
use argon2::{
|
|
Argon2, PasswordHasher,
|
|
password_hash::{PasswordHash, PasswordVerifier, SaltString},
|
|
};
|
|
use chrono::Utc;
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::Row;
|
|
|
|
use crate::error::AppError;
|
|
use crate::service::AuthService;
|
|
use crate::session::Session;
|
|
|
|
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
|
|
pub struct ChangePasswordParams {
|
|
pub current_password: String,
|
|
pub new_password: String,
|
|
}
|
|
|
|
impl AuthService {
|
|
pub async fn auth_change_password(
|
|
&self,
|
|
session: &Session,
|
|
params: ChangePasswordParams,
|
|
) -> Result<(), AppError> {
|
|
let user_uid = session.user().ok_or(AppError::Unauthorized)?;
|
|
|
|
let current_password = self
|
|
.auth_rsa_decode(session, params.current_password)
|
|
.await?;
|
|
|
|
let row = sqlx::query("SELECT password_hash FROM user_password WHERE user_id = $1")
|
|
.bind(user_uid)
|
|
.fetch_optional(self.ctx.db.writer())
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
.ok_or(AppError::UserNotFound)?;
|
|
|
|
let hash: String = row.try_get("password_hash").map_err(AppError::Database)?;
|
|
let password_hash = PasswordHash::new(&hash).map_err(|_| AppError::InvalidPassword)?;
|
|
|
|
if Argon2::default()
|
|
.verify_password(current_password.as_bytes(), &password_hash)
|
|
.is_err()
|
|
{
|
|
return Err(AppError::InvalidPassword);
|
|
}
|
|
|
|
let new_password = self.auth_rsa_decode(session, params.new_password).await?;
|
|
crate::service::util::validate_password_strength(&new_password)?;
|
|
|
|
let salt = SaltString::generate(&mut rand::thread_rng());
|
|
let new_hash = Argon2::default()
|
|
.hash_password(new_password.as_bytes(), &salt)
|
|
.map_err(|e| AppError::PasswordHashError(e.to_string()))?
|
|
.to_string();
|
|
|
|
let now = Utc::now();
|
|
let result = sqlx::query(
|
|
"UPDATE user_password SET password_hash = $1, password_updated_at = $2, updated_at = $2 \
|
|
WHERE user_id = $3",
|
|
)
|
|
.bind(&new_hash)
|
|
.bind(now)
|
|
.bind(user_uid)
|
|
.execute(self.ctx.db.writer())
|
|
.await
|
|
.map_err(AppError::Database)?;
|
|
|
|
if result.rows_affected() == 0 {
|
|
return Err(AppError::UserNotFound);
|
|
}
|
|
|
|
session.remove(Self::RSA_PRIVATE_KEY);
|
|
session.remove(Self::RSA_PUBLIC_KEY);
|
|
|
|
tracing::info!(user_uid = %user_uid, "Password changed successfully");
|
|
Ok(())
|
|
}
|
|
}
|