diff --git a/Cargo.lock b/Cargo.lock index 48022a7..58ccc92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,18 +131,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "argon2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" -dependencies = [ - "base64ct", - "blake2", - "cpufeatures", - "password-hash", -] - [[package]] name = "atoi" version = "2.0.0" @@ -191,19 +179,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bcrypt" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" -dependencies = [ - "base64 0.22.1", - "blowfish", - "getrandom 0.3.1", - "subtle", - "zeroize", -] - [[package]] name = "bitflags" version = "2.6.0" @@ -213,15 +188,6 @@ dependencies = [ "serde", ] -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -231,16 +197,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -294,9 +250,7 @@ name = "chatserver" version = "0.1.0" dependencies = [ "aes-gcm", - "argon2", "base64 0.21.7", - "bcrypt", "chrono", "colog", "crossterm", @@ -307,7 +261,6 @@ dependencies = [ "ratatui", "serde", "sqlx", - "thiserror", "tokio", "toml", "x25519-dalek", @@ -1426,17 +1379,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "paste" version = "1.0.15" @@ -2180,18 +2122,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f4e8af5..8a8fe4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,4 @@ toml = "0.8.19" ratatui = "0.29.0" crossterm = "0.28.1" chrono = "0.4.39" -sqlx = { version = "0.8", features = [ "sqlite", "runtime-tokio", "tls-native-tls" ] } -bcrypt = "0.17.0" -argon2 = "0.5.3" -thiserror = "2.0.12" +sqlx = { version = "0.8", features = [ "sqlite", "runtime-tokio", "tls-native-tls" ] } \ No newline at end of file diff --git a/db.sqlite b/db.sqlite index 5fd4abf..54f9924 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/migrations/002_create_admin_flag.sql b/migrations/002_create_admin_flag.sql deleted file mode 100644 index ec70f0e..0000000 --- a/migrations/002_create_admin_flag.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users -ADD COLUMN is_admin BOOLEAN DEFAULT FALSE; diff --git a/migrations/003_create_ban_flag.sql b/migrations/003_create_ban_flag.sql deleted file mode 100644 index 2459b6a..0000000 --- a/migrations/003_create_ban_flag.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE users -ADD COLUMN is_banned BOOLEAN DEFAULT FALSE; - -ALTER TABLE users -ADD COLUMN ban_reason VARCHAR(255); diff --git a/src/client/mod.rs b/src/client/mod.rs index dd6c3de..283d75c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,9 +4,6 @@ pub(crate) mod handlers { Aes256Gcm, Key, Nonce, }; - use crate::db::users::{ - check_ban, check_for_account, create_user, get_ban_reason, hash_password, verify_password, - }; use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; use log::{debug, error, info}; use serde::Deserialize; @@ -15,6 +12,8 @@ pub(crate) mod handlers { use tokio::net::TcpStream; use tokio::sync::broadcast; use x25519_dalek::{EphemeralSecret, PublicKey}; + + use crate::db::users::create_user; /* Specifications of the packet 32 bytes - Command name @@ -87,122 +86,8 @@ pub(crate) mod handlers { let username = Arc::new(String::from_utf8(decrypted)?); let username_read = Arc::clone(&username); // Clone the Arc for read task let username_write = Arc::clone(&username); // Clone the Arc for write task - info!("Username received: {}", username); - // Check if the user already exists in the database - if check_for_account(&username).await? { - // Check if the user is banned - if check_ban(&username).await? == true { - let ban_reason_result = get_ban_reason(&username).await; - - let message: String = match ban_reason_result { - Ok(Some(reason)) => { - info!("User {} is banned, Reason: {}", username, reason); - format!("User {} is banned, Reason: {}", username, reason).to_string() - } - Ok(None) => { - info!("User {} is banned, but no reason provided", username); - format!("User {} is banned, but no reason provided", username).to_string() - } - Err(e) => { - error!("Error fetching ban reason: {}", e); - format!("You are banned").to_string(); - return Ok(()); - } - }; - - let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - let message = format!("{}\n", BASE64.encode(&encrypted)); - writer.write_all(message.as_bytes()).await?; - return Ok(()); - } - - info!("User {} already exists", username); - // Send a message to the client - let message = format!("User {} is registered, input your password", username); - let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - let message = format!("{}\n", BASE64.encode(&encrypted)); - writer.write_all(message.as_bytes()).await?; - - // Read the password from the client - line.clear(); - reader.read_line(&mut line).await?; - let decoded = BASE64.decode(line.trim().as_bytes())?; - let decrypted = cipher_reader - .decrypt(&nonce_reader, decoded.as_ref()) - .unwrap(); - // verifiy password - let password = String::from_utf8(decrypted)?; - if verify_password(&password, &username).await.is_ok() { - info!("Password verified successfully"); - // Send a success message to the client - let message = format!("Welcome back, {}!", username); - let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - let message = format!("{}\n", BASE64.encode(&encrypted)); - writer.write_all(message.as_bytes()).await?; - } else { - info!("Password verification failed"); - // Send an error message to the client - let message = format!("Invalid password for user {}", username); - let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - let message = format!("{}\n", BASE64.encode(&encrypted)); - writer.write_all(message.as_bytes()).await?; - return Ok(()); - } - } else { - // User does not exist, create a new account - // Send a message to the client - let message = format!("User {} is not registered, input your password", username); - let encrypted = match cipher_writer.encrypt(&nonce_writer, message.as_bytes()) { - Ok(encrypted) => encrypted, - Err(e) => { - error!("Encryption error: {}", e); - return Ok(()); - } - }; - let message = format!("{}\n", BASE64.encode(&encrypted)); - writer.write_all(message.as_bytes()).await?; - // Read the password from the client - line.clear(); - reader.read_line(&mut line).await?; - let decoded = BASE64.decode(line.trim().as_bytes())?; - let decrypted = cipher_reader - .decrypt(&nonce_reader, decoded.as_ref()) - .unwrap(); - let password = String::from_utf8(decrypted)?; - info!("Password received"); - // Hash the password - let password_hash = hash_password(&password).await; - let password_hash = password_hash.as_str(); - info!("Password hashed successfully"); - debug!("Hash: {}", password_hash); - // Create the user in the database - create_user(&username, password_hash).await?; - } + create_user(&username, "1234").await?; // Read task for receiving messages from the client let read_task = tokio::spawn(async move { diff --git a/src/db/mod.rs b/src/db/mod.rs index bb6b225..5e99f89 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,21 +1,5 @@ pub(crate) mod users { - use argon2::{ - password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, - Argon2, - }; - use log::info; - use sqlx::{sqlite::SqlitePool, Row}; - use thiserror::Error; - - #[derive(Error, Debug)] - pub enum DbError { - #[error("Database error: {0}")] - Database(#[from] sqlx::Error), - #[error("Password hashing error: {0}")] - Hashing(argon2::password_hash::Error), - #[error("User not found")] - UserNotFound, - } + use sqlx::sqlite::SqlitePool; pub async fn connect_to_db() -> Result { let pool = SqlitePool::connect("sqlite:./db.sqlite").await?; @@ -28,46 +12,6 @@ pub(crate) mod users { Ok(pool) } - pub async fn get_user_by_username( - username: &str, - ) -> Result, sqlx::Error> { - let pool = create_db_pool().await?; - - let user = sqlx::query( - r#" - SELECT id, username - FROM users - WHERE username = ? - "#, - ) - .bind(username) - .fetch_optional(&pool) - .await?; - - Ok(user.map(|row| (row.get(0), row.get(1)))) - } - - pub async fn check_for_account(username: &str) -> Result { - // Fixed error type - let pool = create_db_pool().await?; - - let exists = sqlx::query( - r#" - SELECT EXISTS( - SELECT 1 - FROM users - WHERE username = ? - ) - "#, - ) - .bind(username) - .fetch_one(&pool) - .await? - .get::(0); - - Ok(exists == 1) - } - pub async fn create_user(username: &str, password_hash: &str) -> Result<(), sqlx::Error> { let pool = create_db_pool().await?; @@ -83,135 +27,4 @@ pub(crate) mod users { .await?; Ok(()) } - - pub async fn hash_password(password: &str) -> String { - let salt = SaltString::generate(&mut rand::thread_rng()); - let argon2 = Argon2::default(); - let password_hash = argon2 - .hash_password(password.as_bytes(), &salt) - .expect("Failed to hash password"); - password_hash.to_string() - } - - pub async fn check_ban(username: &str) -> Result { - let pool = create_db_pool().await?; - - let is_banned = sqlx::query( - r#" - SELECT EXISTS( - SELECT 1 - FROM users - WHERE username = ? - ) - "#, - ) - .bind(username) - .fetch_one(&pool) - .await? - .get::(0); - - // Check if the user is banned - if is_banned == 1 { - info!("User {} is banned", username); - } else { - info!("User {} is not banned", username); - } - - Ok(is_banned == 1) - } - - pub async fn get_ban_reason(username: &str) -> Result, sqlx::Error> { - let pool = create_db_pool().await?; - info!("Attempting to fetch ban reason for user: {}", username); - - let row_option = sqlx::query( - r#" - SELECT ban_reason - FROM users - WHERE username = ? - "#, - ) - .bind(username) - .fetch_optional(&pool) - .await?; - - // Process the result - match row_option { - Some(row) => { - // Row found, now get the ban_reason (which might be NULL) - let reason: Option = row.get(0); // Type annotation clarifies intent - if let Some(ref r) = reason { - info!("User {} found. Ban reason: {}", username, r); - } else { - // User exists, but ban_reason is NULL in the database - info!( - "User {} found, but ban_reason is NULL (not banned)", - username - ); - } - Ok(reason) - } - None => { - // No row found for the username - info!("User {} not found in the database", username); - // Return Ok(None) as per the function signature, indicating no ban reason found - // because the user doesn't exist. - Ok(None) - } - } - } - - pub async fn verify_password( - // Use clearer argument names - username: &str, - provided_password: &str, - ) -> Result { - let pool = create_db_pool().await?; // Propagates sqlx::Error - - // Fetch the stored hash for the user - let user_row = sqlx::query( - r#" - SELECT password_hash - FROM users - WHERE username = ? - "#, - ) - .bind(username) - .fetch_optional(&pool) // Use fetch_optional to handle not found case - .await?; - - // Get the stored hash string or return error if user not found - let stored_hash_str = match user_row { - Some(row) => row.get::(0), - None => return Err(DbError::UserNotFound), - }; - - // Parse the stored hash - let parsed_hash = PasswordHash::new(&stored_hash_str).map_err(DbError::Hashing)?; // Manually map the error - - let argon2 = Argon2::default(); - - let verification_result = - argon2.verify_password(provided_password.as_bytes(), &parsed_hash); - - // Check the result and return true/false accordingly - match verification_result { - Ok(()) => { - info!("Password check successful for user: {}", username); - Ok(true) - } - Err(argon2::password_hash::Error::Password) => { - info!("Password check failed (mismatch) for user: {}", username); - Ok(false) - } - Err(e) => { - // Handle other potential argon2 errors (e.g., invalid hash format) - info!( - "Password check failed for user {} with error: {}", - username, e - ); - Err(DbError::Hashing(e)) - } - } - } } diff --git a/src/main.rs b/src/main.rs index 698357a..6202b4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ mod client; +mod tui; // Add this new module mod db; -mod tui; use client::handlers::handle_client; use db::users::create_db_pool;