From 15155bd8df9c23f57bba0a7968d94f4d429d34ca Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Wed, 24 Dec 2025 14:34:13 -0600 Subject: [PATCH] Move migration related userdata functions to a new file --- src/router/gree.rs | 4 +- src/router/user.rs | 6 +-- src/router/userdata/mod.rs | 73 +++------------------------ src/router/userdata/user.rs | 1 + src/router/userdata/user/migration.rs | 69 +++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 70 deletions(-) create mode 100644 src/router/userdata/user.rs create mode 100644 src/router/userdata/user/migration.rs diff --git a/src/router/gree.rs b/src/router/gree.rs index cf84d1f..fc7e6d5 100644 --- a/src/router/gree.rs +++ b/src/router/gree.rs @@ -233,7 +233,7 @@ pub fn migration_verify(req: HttpRequest, body: String) -> HttpResponse { let uid = code_to_uid(body["migration_code"].to_string()).parse::().unwrap_or(0); - let user = userdata::get_acc_transfer(uid, &body["migration_code"].to_string(), &password); + let user = userdata::user::migration::get_acc_transfer(uid, &body["migration_code"].to_string(), &password); let resp = if !user["success"].as_bool().unwrap() || uid == 0 { object!{ @@ -339,7 +339,7 @@ pub fn migration_password_register(req: HttpRequest, body: String) -> HttpRespon let code = uid_to_code(user["user"]["id"].to_string()); let pass = decrypt_transfer_password(&body["migration_password"].to_string()); - userdata::save_acc_transfer(&code, &pass); + userdata::user::migration::save_acc_transfer(&code, &pass); let resp = object!{ result: "OK" diff --git a/src/router/user.rs b/src/router/user.rs index c4ff4d6..0b1d82e 100644 --- a/src/router/user.rs +++ b/src/router/user.rs @@ -217,7 +217,7 @@ pub fn register_password(req: HttpRequest, body: String) -> Option { let user = userdata::get_acc(&key); let code = uid_to_code(user["user"]["id"].to_string()); - userdata::save_acc_transfer(&code, &body["pass"].to_string()); + userdata::user::migration::save_acc_transfer(&code, &body["pass"].to_string()); Some(array![]) } @@ -227,7 +227,7 @@ pub fn verify_migration_code(_req: HttpRequest, body: String) -> Option().unwrap_or(0); - let user = userdata::get_acc_transfer(uid, &body["migrationCode"].to_string(), &body["pass"].to_string()); + let user = userdata::user::migration::get_acc_transfer(uid, &body["migrationCode"].to_string(), &body["pass"].to_string()); if !user["success"].as_bool().unwrap() || uid == 0 { return None; @@ -247,7 +247,7 @@ pub fn request_migration_code(_req: HttpRequest, body: String) -> Option().unwrap_or(0); - let user = userdata::get_acc_transfer(uid, &body["migrationCode"].to_string(), &body["pass"].to_string()); + let user = userdata::user::migration::get_acc_transfer(uid, &body["migrationCode"].to_string(), &body["pass"].to_string()); if !user["success"].as_bool().unwrap() || uid == 0 { return None; diff --git a/src/router/userdata/mod.rs b/src/router/userdata/mod.rs index 8a2f0a2..920e386 100644 --- a/src/router/userdata/mod.rs +++ b/src/router/userdata/mod.rs @@ -1,9 +1,9 @@ +pub mod user; + use rusqlite::params; use lazy_static::lazy_static; use json::{JsonValue, array, object}; use rand::Rng; -use sha2::{Digest, Sha256}; -use base64::{Engine as _, engine::general_purpose}; use crate::router::global; use crate::router::items; @@ -17,6 +17,10 @@ lazy_static! { }; } +fn get_userdata_database() -> &'static SQLite { + &DATABASE +} + fn setup_tables(conn: &rusqlite::Connection) { conn.execute_batch(" CREATE TABLE IF NOT EXISTS tokens ( @@ -301,67 +305,6 @@ pub fn save_acc_sif(auth_key: &str, data: JsonValue) { save_data(auth_key, "sifcards", data); } -fn generate_salt() -> Vec { - let mut rng = rand::rng(); - let mut bytes = vec![0u8; 16]; - rng.fill(&mut bytes[..]); - bytes -} - -fn hash_password(password: &str) -> String { - let salt = &generate_salt(); - let mut hasher = Sha256::new(); - hasher.update(password.as_bytes()); - hasher.update(salt); - let hashed_password = hasher.finalize(); - - let salt_hash = [&salt[..], &hashed_password[..]].concat(); - general_purpose::STANDARD.encode(salt_hash) -} - -fn verify_password(password: &str, salted_hash: &str) -> bool { - if password.is_empty() || salted_hash.is_empty() { - return false; - } - let bytes = general_purpose::STANDARD.decode(salted_hash); - if bytes.is_err() { - return password == salted_hash; - } - let bytes = bytes.unwrap(); - if bytes.len() < 17 { - return password == salted_hash; - } - let (salt, hashed_password) = bytes.split_at(16); - let hashed_password = &hashed_password[0..32]; - - let mut hasher = Sha256::new(); - hasher.update(password.as_bytes()); - hasher.update(salt); - let input_hash = hasher.finalize(); - - input_hash.as_slice() == hashed_password -} - -pub fn get_acc_transfer(uid: i64, token: &str, password: &str) -> JsonValue { - let data = DATABASE.lock_and_select("SELECT password FROM migration WHERE token=?1", params!(token)); - if data.is_err() { - return object!{success: false}; - } - if verify_password(password, &data.unwrap()) { - let login_token = get_login_token(uid); - if login_token == String::new() { - return object!{success: false}; - } - return object!{success: true, login_token: login_token}; - } - object!{success: false} -} - -pub fn save_acc_transfer(token: &str, password: &str) { - DATABASE.lock_and_exec("DELETE FROM migration WHERE token=?1", params!(token)); - DATABASE.lock_and_exec("INSERT INTO migration (token, password) VALUES (?1, ?2)", params!(token, hash_password(password))); -} - pub fn get_name_and_rank(uid: i64) -> JsonValue { let login_token = get_login_token(uid); if login_token == String::new() { @@ -480,7 +423,7 @@ fn create_webui_token() -> String { pub fn webui_login(uid: i64, password: &str) -> Result { let pass = DATABASE.lock_and_select("SELECT password FROM migration WHERE token=?1", params!(crate::router::user::uid_to_code(uid.to_string()))).unwrap_or_default(); - if !verify_password(password, &pass) { + if !user::migration::verify_password(password, &pass) { if acc_exists(uid) && pass.is_empty() { return Err(String::from("Migration token not set. Set token in game settings.")); } @@ -507,7 +450,7 @@ pub fn webui_import_user(user: JsonValue) -> Result { DATABASE.lock_and_exec("INSERT INTO tokens (user_id, token) VALUES (?1, ?2)", params!(uid, token)); let mig = crate::router::user::uid_to_code(uid.to_string()); - save_acc_transfer(&mig, &user["password"].to_string()); + user::migration::save_acc_transfer(&mig, &user["password"].to_string()); Ok(object!{ uid: uid, diff --git a/src/router/userdata/user.rs b/src/router/userdata/user.rs new file mode 100644 index 0000000..a571eaa --- /dev/null +++ b/src/router/userdata/user.rs @@ -0,0 +1 @@ +pub mod migration; diff --git a/src/router/userdata/user/migration.rs b/src/router/userdata/user/migration.rs new file mode 100644 index 0000000..b704d52 --- /dev/null +++ b/src/router/userdata/user/migration.rs @@ -0,0 +1,69 @@ +use rusqlite::params; +use json::{JsonValue, object}; +use crate::router::userdata; +use rand::Rng; +use sha2::{Digest, Sha256}; +use base64::{Engine as _, engine::general_purpose}; + +pub fn get_acc_transfer(uid: i64, token: &str, password: &str) -> JsonValue { + let database = userdata::get_userdata_database(); + let data = database.lock_and_select("SELECT password FROM migration WHERE token=?1", params!(token)); + if data.is_err() { + return object!{success: false}; + } + if verify_password(password, &data.unwrap()) { + let login_token = userdata::get_login_token(uid); + if login_token == String::new() { + return object!{success: false}; + } + return object!{success: true, login_token: login_token}; + } + object!{success: false} +} + +pub fn save_acc_transfer(token: &str, password: &str) { + let database = userdata::get_userdata_database(); + database.lock_and_exec("DELETE FROM migration WHERE token=?1", params!(token)); + database.lock_and_exec("INSERT INTO migration (token, password) VALUES (?1, ?2)", params!(token, hash_password(password))); +} + +fn generate_salt() -> Vec { + let mut rng = rand::rng(); + let mut bytes = vec![0u8; 16]; + rng.fill(&mut bytes[..]); + bytes +} + +fn hash_password(password: &str) -> String { + let salt = &generate_salt(); + let mut hasher = Sha256::new(); + hasher.update(password.as_bytes()); + hasher.update(salt); + let hashed_password = hasher.finalize(); + + let salt_hash = [&salt[..], &hashed_password[..]].concat(); + general_purpose::STANDARD.encode(salt_hash) +} + +pub fn verify_password(password: &str, salted_hash: &str) -> bool { + if password.is_empty() || salted_hash.is_empty() { + return false; + } + let bytes = general_purpose::STANDARD.decode(salted_hash); + if bytes.is_err() { + return password == salted_hash; + } + let bytes = bytes.unwrap(); + if bytes.len() < 17 { + return password == salted_hash; + } + let (salt, hashed_password) = bytes.split_at(16); + let hashed_password = &hashed_password[0..32]; + + let mut hasher = Sha256::new(); + hasher.update(password.as_bytes()); + hasher.update(salt); + let input_hash = hasher.finalize(); + + input_hash.as_slice() == hashed_password +}