- Remove all use slog::* imports and slog::Logger fields/parameters - Replace slog::info!/warn!/error! with tracing::info!/warn!/error! - AppService: remove pub logs: slog::Logger field, update callers of AppEmail::init(), MessageProducer::new(), RoomService::new(), start_email_worker(), start_room_workers() - auth/: captcha, email, login, logout, password, register, rsa, totp - git/: archive, blame, blob, branch, commit, contributors, diff, refs, star, tag, tree, watch - agent/: billing (ai_usage_recorded), code_review, pr_summary, sync - project/activity.rs, workspace/alert.rs
124 lines
4.8 KiB
Rust
124 lines
4.8 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use argon2::{Argon2, PasswordHash, PasswordVerifier};
|
|
use models::users::{user_activity_log, user_password};
|
|
// use rand::RngExt;
|
|
// use redis::AsyncCommands;
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
// use sha1::Digest;
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
|
|
pub struct LoginParams {
|
|
pub username: String,
|
|
pub password: String,
|
|
pub captcha: String,
|
|
// pub totp_code: Option<String>, // 2FA disabled
|
|
}
|
|
|
|
impl AppService {
|
|
// pub const TOTP_KEY: &'static str = "totp_key"; // 2FA disabled
|
|
pub async fn auth_login(&self, params: LoginParams, context: Session) -> Result<(), AppError> {
|
|
self.auth_check_captcha(&context, params.captcha).await?;
|
|
let password = self.auth_rsa_decode(&context, params.password).await?;
|
|
let user = match self
|
|
.utils_find_user_by_username(params.username.clone())
|
|
.await
|
|
{
|
|
Ok(user) => user,
|
|
Err(_) => {
|
|
self.utils_find_user_by_email(params.username.clone())
|
|
.await?
|
|
}
|
|
};
|
|
let user_password = user_password::Entity::find()
|
|
.filter(user_password::Column::User.eq(user.uid))
|
|
.one(&self.db)
|
|
.await
|
|
.ok()
|
|
.flatten()
|
|
.ok_or(AppError::UserNotFound)?;
|
|
let password_hash =
|
|
PasswordHash::new(&user_password.password_hash).map_err(|_| AppError::UserNotFound)?;
|
|
|
|
if let Err(_e) = Argon2::default().verify_password(password.as_bytes(), &password_hash) {
|
|
tracing::warn!(username = %params.username, ip = ?context.ip_address(), "Login failed: invalid password");
|
|
return Err(AppError::UserNotFound);
|
|
}
|
|
|
|
// 2FA disabled {
|
|
// let needs_totp_verification = context
|
|
// .get::<String>(Self::TOTP_KEY)
|
|
// .ok()
|
|
// .flatten()
|
|
// .is_some();
|
|
//
|
|
// if needs_totp_verification {
|
|
// if let Some(ref totp_code) = params.totp_code {
|
|
// if !self.auth_2fa_verify_login(&context, totp_code).await? {
|
|
// tracing::warn!(username = %params.username, ip = %context.ip_address(), "Login failed: invalid 2FA code");
|
|
// return Err(AppError::InvalidTwoFactorCode);
|
|
// }
|
|
// }
|
|
// } else if !self.auth_2fa_status_by_uid(user.uid).await?.is_enabled {
|
|
// let user_uid = user.uid;
|
|
// let mut rng = rand::rng();
|
|
// let mut sha = sha1::Sha1::default();
|
|
// for _ in 0..5 {
|
|
// sha.update(
|
|
// (0..1024)
|
|
// .map(|_| {
|
|
// format!(
|
|
// "{:04}-{:04}-{:04}",
|
|
// rng.random_range(0..10000),
|
|
// rng.random_range(0..10000),
|
|
// rng.random_range(0..10000)
|
|
// )
|
|
// })
|
|
// .collect::<String>()
|
|
// .as_bytes(),
|
|
// )
|
|
// }
|
|
// let key = format!("{:?}", sha.finalize());
|
|
// context.insert(Self::TOTP_KEY, key.clone()).ok();
|
|
// if let Ok(mut conn) = self.cache.conn().await {
|
|
// conn.set_ex::<String, String, ()>(key, user_uid.to_string(), 60 * 5)
|
|
// .await
|
|
// .ok();
|
|
// }
|
|
// tracing::info!(username = %params.username, ip = %context.ip_address(), "Login 2FA triggered for new 2FA user");
|
|
// return Err(AppError::TwoFactorRequired);
|
|
// }
|
|
// }
|
|
|
|
let mut arch = user.clone().into_active_model();
|
|
arch.last_sign_in_at = Set(Some(chrono::Utc::now()));
|
|
arch.update(&self.db)
|
|
.await
|
|
.map_err(|_| AppError::UserNotFound)?;
|
|
|
|
let _ = user_activity_log::ActiveModel {
|
|
user_uid: Set(Some(user.uid)),
|
|
action: Set("login".to_string()),
|
|
ip_address: Set(context.ip_address()),
|
|
user_agent: Set(context.user_agent()),
|
|
details: Set(Some(serde_json::json!({
|
|
"method": "password",
|
|
"username": user.username,
|
|
}))
|
|
.into()),
|
|
created_at: Set(chrono::Utc::now()),
|
|
..Default::default()
|
|
}
|
|
.insert(&self.db)
|
|
.await;
|
|
|
|
context.set_user(user.uid);
|
|
context.remove(Self::RSA_PRIVATE_KEY);
|
|
context.remove(Self::RSA_PUBLIC_KEY);
|
|
tracing::info!(user_uid = %user.uid, username = %user.username, ip = ?context.ip_address(), "User logged in successfully");
|
|
Ok(())
|
|
}
|
|
}
|