Add kick and file management features
- Implement kick functionality with add, remove, and check operations - Add file management with create, request, and verify operations - Update database schema with new tables and columns - Enhance command handling in client module for new features - Fix argument order in verify_password function
This commit is contained in:
parent
91b6942348
commit
b8f2a91411
6 changed files with 436 additions and 10 deletions
BIN
db.sqlite
BIN
db.sqlite
Binary file not shown.
7
migrations/004_create_kick_table.sql
Normal file
7
migrations/004_create_kick_table.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE IF NOT EXISTS kick (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, -- Unique ID for each kick
|
||||
user_name VARCHAR(255) NOT NULL -- ID of the user who made the kick
|
||||
);
|
||||
|
||||
-- -- Create an index on the user_name column for faster lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_kick_user_name ON kick (user_name);
|
8
migrations/005_create_files_table.sql
Normal file
8
migrations/005_create_files_table.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE IF NOT EXISTS files (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
path VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_files_name ON files (name);
|
2
migrations/006_add_admin_verified_to_files.sql
Normal file
2
migrations/006_add_admin_verified_to_files.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE files
|
||||
ADD COLUMN admin_verified BOOLEAN DEFAULT FALSE;
|
|
@ -5,8 +5,9 @@ pub(crate) mod handlers {
|
|||
};
|
||||
|
||||
use crate::db::users::{
|
||||
ban_user, check_ban, check_for_account, create_user, get_ban_reason, hash_password,
|
||||
unban_user, verify_admin, verify_password,
|
||||
add_kick, add_new_file, add_verified_flag_to_file, ban_user, change_password, check_ban,
|
||||
check_file_verified, check_for_account, check_kick, create_user, get_ban_reason,
|
||||
hash_password, remove_kick, request_file, unban_user, verify_admin, verify_password,
|
||||
};
|
||||
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
||||
use log::{debug, error, info};
|
||||
|
@ -147,7 +148,7 @@ pub(crate) mod handlers {
|
|||
.unwrap();
|
||||
// verifiy password
|
||||
let password = String::from_utf8(decrypted)?;
|
||||
if verify_password(&password, &username).await.is_ok() {
|
||||
if verify_password(&username, &password).await? == true {
|
||||
info!("Password verified successfully");
|
||||
} else {
|
||||
info!("Password verification failed");
|
||||
|
@ -245,6 +246,22 @@ pub(crate) mod handlers {
|
|||
info!("Parsing message");
|
||||
|
||||
let parsed_message = parse_message(message.as_str());
|
||||
|
||||
if check_kick(&username).await.unwrap() == true {
|
||||
info!("User {} is kicked", username);
|
||||
let message = format!("User {} is kicked", username);
|
||||
let _ = tx.send(message);
|
||||
remove_kick(&username).await.unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
if check_ban(&username).await.unwrap() == true {
|
||||
info!("User {} is banned", username);
|
||||
let message = format!("User {} is banned", username);
|
||||
let _ = tx.send(message);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle commands
|
||||
if !parsed_message.command.is_empty() {
|
||||
match parsed_message.command[0].as_str() {
|
||||
|
@ -284,6 +301,189 @@ pub(crate) mod handlers {
|
|||
break;
|
||||
}
|
||||
|
||||
"/kick" => {
|
||||
if parsed_message.argument.is_empty() {
|
||||
error!("Invalid /kick format. Usage: /kick username");
|
||||
match tx.send(
|
||||
format!("Error! Invalid /kick format").to_string(),
|
||||
) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
match verify_admin(&username_read).await {
|
||||
Ok(true) => {
|
||||
info!("User {} is admin", username);
|
||||
let target_user = &parsed_message.argument[0];
|
||||
info!("Kicking user: {}", target_user);
|
||||
add_kick(&target_user).await.unwrap();
|
||||
match tx.send(format!(
|
||||
"User {} has been kicked",
|
||||
target_user
|
||||
)) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
error!("User {} is not admin", username);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error verifying admin: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"/addfile" => {
|
||||
if parsed_message.argument.is_empty() {
|
||||
error!("Invalid /addfile format. Usage: /addfile filename link");
|
||||
match tx.send(
|
||||
format!("Invalid /addfile format. Usage: /addfile filename link").to_string(),
|
||||
) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = &parsed_message.argument[0];
|
||||
let file_link = &parsed_message.argument[1];
|
||||
info!("Adding file: {}", file_name);
|
||||
info!("File link: {}", file_link);
|
||||
|
||||
add_new_file(&file_name, &file_link).await.unwrap();
|
||||
|
||||
match tx.send(format!("File {} has been added", file_name)) {
|
||||
Ok(_) => {
|
||||
info!("Error message sent to client {}", username_write)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"/verifylink" => {
|
||||
if parsed_message.argument.is_empty() {
|
||||
error!("Invalid /verifylink format. Usage: /verifylink filename");
|
||||
match tx.send(
|
||||
format!("Invalid /verifylink format. Usage: /verifylink filename").to_string(),
|
||||
) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = &parsed_message.argument[0];
|
||||
info!("Verifying link for file: {}", file_name);
|
||||
|
||||
match verify_admin(&username).await {
|
||||
Ok(true) => {
|
||||
info!("User {} is admin", username);
|
||||
add_verified_flag_to_file(file_name).await.unwrap();
|
||||
match tx.send(format!(
|
||||
"File {} has been verified",
|
||||
file_name
|
||||
)) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
error!("User {} is not admin", username);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error verifying admin: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"/requestfile" => {
|
||||
if parsed_message.argument.is_empty() {
|
||||
error!("Invalid /requestfile format. Usage: /requestfile filename");
|
||||
match tx.send(
|
||||
format!("Invalid /requestfile format. Usage: /requestfile filename").to_string(),
|
||||
) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = &parsed_message.argument[0];
|
||||
info!("Requesting file: {}", file_name);
|
||||
|
||||
let file_link = request_file(file_name).await.unwrap();
|
||||
|
||||
match tx.send(format!("Link for {}: {}", file_name, file_link))
|
||||
{
|
||||
Ok(_) => {
|
||||
info!("message sent to client {}", username_write)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if check_file_verified(file_name).await.unwrap() == true {
|
||||
match tx.send(format!("dl! {}", file_link)) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"/ban" => {
|
||||
if parsed_message.argument.is_empty() {
|
||||
error!("Invalid /ban format. Usage: /ban username");
|
||||
|
@ -430,6 +630,78 @@ pub(crate) mod handlers {
|
|||
}
|
||||
}
|
||||
|
||||
"/changepassword" => {
|
||||
if parsed_message.argument.len() < 2 {
|
||||
error!("Invalid /changepassword format. Usage: /changepassword old_password new_password");
|
||||
match tx
|
||||
.send(format!("Invalid /changepassword format. Usage: /changepassword old_password new_password"))
|
||||
{
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let old_password = &parsed_message.argument[0];
|
||||
let new_password = &parsed_message.argument[1];
|
||||
|
||||
info!("Changing password for user {}", username);
|
||||
info!("new password: {}", new_password);
|
||||
info!("old password: {}", old_password);
|
||||
|
||||
if verify_password(old_password, &username).await.is_ok() {
|
||||
match change_password(&username, new_password).await {
|
||||
Ok(_) => {
|
||||
info!("Password changed successfully");
|
||||
let _ = tx.send(
|
||||
"Password changed successfully".to_string(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error changing password: {:?}", e);
|
||||
match tx.send(format!(
|
||||
"Error changing password: {:?}",
|
||||
e
|
||||
)) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to send error message: {:?}",
|
||||
e
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("Old password verification failed");
|
||||
match tx.send(format!(
|
||||
"Invalid old password for user {}",
|
||||
username
|
||||
)) {
|
||||
Ok(_) => info!(
|
||||
"Error message sent to client {}",
|
||||
username_write
|
||||
),
|
||||
Err(e) => {
|
||||
error!("Failed to send error message: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
error!("Unknown command: {}", parsed_message.command[0]);
|
||||
match tx.send("Error! Unknown command".to_string()) {
|
||||
|
|
151
src/db/mod.rs
151
src/db/mod.rs
|
@ -179,11 +179,29 @@ pub(crate) mod users {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn verify_password(
|
||||
// Use clearer argument names
|
||||
username: &str,
|
||||
provided_password: &str,
|
||||
) -> Result<bool, DbError> {
|
||||
pub async fn change_password(username: &str, new_password: &str) -> Result<(), sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
// Hash the new password
|
||||
let new_password_hash = hash_password(new_password).await;
|
||||
|
||||
// Update the password in the database
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE users
|
||||
SET password_hash = ?
|
||||
WHERE username = ?
|
||||
"#,
|
||||
)
|
||||
.bind(new_password_hash)
|
||||
.bind(username)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn verify_password(username: &str, provided_password: &str) -> Result<bool, DbError> {
|
||||
let pool = create_db_pool().await?; // Propagates sqlx::Error
|
||||
|
||||
// Fetch the stored hash for the user
|
||||
|
@ -205,8 +223,7 @@ pub(crate) mod users {
|
|||
};
|
||||
|
||||
// Parse the stored hash
|
||||
let parsed_hash = PasswordHash::new(&stored_hash_str).map_err(DbError::Hashing)?; // Manually map the error
|
||||
|
||||
let parsed_hash = PasswordHash::new(&stored_hash_str).map_err(DbError::Hashing)?;
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
let verification_result =
|
||||
|
@ -252,4 +269,124 @@ pub(crate) mod users {
|
|||
|
||||
Ok(is_admin == 1)
|
||||
}
|
||||
|
||||
pub async fn add_kick(username: &str) -> Result<(), sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO kick (user_name)
|
||||
VALUES (?)
|
||||
"#,
|
||||
)
|
||||
.bind(username)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_kick(username: &str) -> Result<(), sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
DELETE FROM kick
|
||||
WHERE user_name = ?
|
||||
"#,
|
||||
)
|
||||
.bind(username)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check_kick(username: &str) -> Result<bool, sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
let exists = sqlx::query(
|
||||
r#"
|
||||
SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM kick
|
||||
WHERE user_name = ?
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.bind(username)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.get::<i64, _>(0);
|
||||
|
||||
Ok(exists == 1)
|
||||
}
|
||||
|
||||
pub async fn add_new_file(name: &str, link: &str) -> Result<(), sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO files (name, path)
|
||||
VALUES (?, ?)
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.bind(link)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn request_file(name: &str) -> Result<String, sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
let file_path = sqlx::query(
|
||||
r#"
|
||||
SELECT path
|
||||
FROM files
|
||||
WHERE name = ?
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.get::<String, _>(0);
|
||||
|
||||
Ok(file_path)
|
||||
}
|
||||
|
||||
pub async fn add_verified_flag_to_file(name: &str) -> Result<(), sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE files
|
||||
SET admin_verified = 1
|
||||
WHERE name = ?
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check_file_verified(name: &str) -> Result<bool, sqlx::Error> {
|
||||
let pool = create_db_pool().await?;
|
||||
|
||||
let is_verified = sqlx::query(
|
||||
r#"
|
||||
SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM files
|
||||
WHERE name = ? AND admin_verified = 1
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.get::<i64, _>(0);
|
||||
|
||||
Ok(is_verified == 1)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue