diff --git a/.gitignore b/.gitignore index 0b745e2..68b3e7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -.env \ No newline at end of file + +.env +config.toml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e529cc9..aa19066 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,11 +408,13 @@ version = "0.0.0" dependencies = [ "diesel", "dotenvy", + "maplit", "password-auth", "regex", "rocket", "serde", "sha", + "toml", ] [[package]] @@ -654,6 +656,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matchers" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4c56fc5..7b492b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" [dependencies] diesel = { version = "=2.1.0", features = ["postgres"] } dotenvy = "0.15.7" +maplit = "1.0.2" password-auth = "0.3.0" regex = "1.9.4" rocket = "=0.5.0-rc.3" serde = { version = "1.0.188", features = ["derive"] } sha = "1.0.3" +toml = "0.7.6" diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..20e2599 --- /dev/null +++ b/config.example.toml @@ -0,0 +1,19 @@ +[general] +# if this path is encountered during path traversal, +# it will be removed. this is useful for instances +# where your absolute domain path is not long enough +# to replace boomlings.com, because you can then point +# it at a different, longer path to fill the gap +# +# example: +# boomlings.com/database/ +# example.com/aaaaaaaaaa/ +# ^^^^^^^^^^^ +# leaving as "/" will disable this +append_path = "/" +# where can your server be accessible? +port = 8000 + +[accounts] +# allow new accounts to be created +allow_registration = true \ No newline at end of file diff --git a/migrations/2023-08-27-090522_users/up.sql b/migrations/2023-08-27-090522_users/up.sql index 026bb93..3230564 100644 --- a/migrations/2023-08-27-090522_users/up.sql +++ b/migrations/2023-08-27-090522_users/up.sql @@ -28,7 +28,6 @@ CREATE TABLE users ( wave INTEGER NOT NULL DEFAULT 0, robot INTEGER NOT NULL DEFAULT 0, spider INTEGER NOT NULL DEFAULT 0, - swing_copter INTEGER NOT NULL DEFAULT 0, explosion INTEGER NOT NULL DEFAULT 0, special INTEGER NOT NULL DEFAULT 0, glow INTEGER NOT NULL DEFAULT 0, diff --git a/readme.md b/readme.md index 767df3b..a6a70e9 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,8 @@ # gdps-server -a [Geometry Dash](https://store.steampowered.com/app/322170/Geometry_Dash/) server reimplementation in [Rust](https://rust-lang.org), focusing on 1:1 recreations of vanilla GD features +a [Geometry Dash](https://store.steampowered.com/app/322170/Geometry_Dash/) server reimplementation in [Rust](https://rust-lang.org) -_this project is in early stages. it is NOT production ready._ - -_ONLY 2.2 is supported._ +this project is based off of (stolen from) the [crystal-gauntlet](https://git.oat.zone/oat/crystal-gauntlet) server ## why? @@ -28,7 +26,7 @@ _these features are implemented_ ### testing -- run `cargo run run` +- run `cargo run` ### building @@ -36,5 +34,6 @@ _these features are implemented_ ## todo -- add login endpoint....... NOW! -- our passwords are a little insecure (`argon2(sha1(password + "mI29fmAnxgTs"))`) and there isnt anything we can do about this because gpj2 is forced like that!! thanks robtop!! (try and find a fix anyway lul) \ No newline at end of file +- ___fix the fucking get users for usernames!!!!___ +- move authorization logic to (./src/helpers/accounts.rs)[./src/helpers/accounts.rs] +- make gjp2 authentication faster (bcrypt?) \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..805c622 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,34 @@ +use serde::Deserialize; +use std::fs; +use std::sync::LazyLock; + +#[derive(Deserialize)] +pub struct Config { + pub general: ConfigGeneral, + pub accounts: ConfigAccounts +} + +#[derive(Deserialize)] +pub struct ConfigGeneral { + pub append_path: String, + pub port: u16 +} + +#[derive(Deserialize)] +pub struct ConfigAccounts { + pub allow_registration: bool +} + +impl Config { + pub fn load_from_file(file_path: &str) -> Self { + let toml_str = fs::read_to_string(file_path).expect("Error finding toml config:"); + let config: Config = toml::from_str(toml_str.as_str()).expect("Error parsing toml config:"); + + return config; + } +} + +pub static CONFIG: LazyLock = LazyLock::new(|| { + let config = Config::load_from_file("config.toml"); + config +}); \ No newline at end of file diff --git a/src/db/models.rs b/src/db/models.rs index 239270a..304e140 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -62,7 +62,6 @@ pub struct User { pub wave: i32, pub robot: i32, pub spider: i32, - pub swing_copter: i32, pub explosion: i32, pub special: i32, pub glow: i32, diff --git a/src/db/schema.rs b/src/db/schema.rs index 232b06a..a56613f 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -47,7 +47,6 @@ diesel::table! { wave -> Int4, robot -> Int4, spider -> Int4, - swing_copter -> Int4, explosion -> Int4, special -> Int4, glow -> Int4, diff --git a/src/endpoints.rs b/src/endpoints.rs index 5184f86..5667352 100644 --- a/src/endpoints.rs +++ b/src/endpoints.rs @@ -1 +1,2 @@ -pub mod accounts; \ No newline at end of file +pub mod accounts; +pub mod users; \ No newline at end of file diff --git a/src/endpoints/accounts.rs b/src/endpoints/accounts.rs index a335f67..fca82a6 100644 --- a/src/endpoints/accounts.rs +++ b/src/endpoints/accounts.rs @@ -1 +1,2 @@ +pub mod login_account; pub mod register_account; \ No newline at end of file diff --git a/src/endpoints/accounts/login_account.rs b/src/endpoints/accounts/login_account.rs new file mode 100644 index 0000000..8c52c0b --- /dev/null +++ b/src/endpoints/accounts/login_account.rs @@ -0,0 +1,55 @@ +use password_auth::verify_password; +use rocket::form::Form; +use rocket::http::Status; +use rocket::response::status; + +use diesel::prelude::*; + +use crate::helpers; +use crate::db; + +#[derive(FromForm)] +pub struct FromLoginAccount { + userName: String, + password: String +} + +#[post("/accounts/loginGJAccount.php", data = "")] +pub fn login_account(input: Form) -> status::Custom<&'static str> { + let connection = &mut db::establish_connection_pg(); + + if input.userName != helpers::clean::clean(input.userName.as_ref()) { + return status::Custom(Status::Ok, "-4") + } + + if input.password.len() < 6 { + return status::Custom(Status::Ok, "-8") + } + + if input.userName.len() < 3 { + return status::Custom(Status::Ok, "-9") + } + + { + use crate::schema::accounts::dsl::*; + + let account_id_gjp2_result = accounts + .select((id, gjp2)) + .filter(username.eq(input.userName.clone())) + .get_result::<(i32, String)>(connection); + + match account_id_gjp2_result { + Ok(account_id_gjp2) => { + let user_id = helpers::accounts::get_user_id_from_account_id(account_id_gjp2.0); + + match verify_password(helpers::gjp2::get_gjp2(input.password.clone()).as_bytes(), account_id_gjp2.1.as_str()) { + Ok(_) => return status::Custom(Status::Ok, + Box::leak(format!("{},{}", account_id_gjp2.0, user_id).into_boxed_str()) + ), + Err(_) => return status::Custom(Status::Ok, "-11") + }; + }, + Err(_) => return status::Custom(Status::Ok, "-1") + } + } +} \ No newline at end of file diff --git a/src/endpoints/accounts/register_account.rs b/src/endpoints/accounts/register_account.rs index e4e5898..583ca2a 100644 --- a/src/endpoints/accounts/register_account.rs +++ b/src/endpoints/accounts/register_account.rs @@ -5,6 +5,7 @@ use rocket::response::status; use diesel::prelude::*; use diesel::result::Error; +use crate::CONFIG; use crate::helpers; use crate::db; @@ -15,9 +16,13 @@ pub struct FormRegisterAccount { email: String } -#[post("/memaddrefix/accounts/registerGJAccount.php", data = "")] +#[post("/accounts/registerGJAccount.php", data = "")] pub fn register_account(input: Form) -> status::Custom<&'static str> { let connection = &mut db::establish_connection_pg(); + + if CONFIG.accounts.allow_registration == false { + return status::Custom(Status::Ok, "-1") + } if input.userName != helpers::clean::clean(input.userName.as_ref()) { return status::Custom(Status::Ok, "-4") diff --git a/src/endpoints/users.rs b/src/endpoints/users.rs new file mode 100644 index 0000000..b244c93 --- /dev/null +++ b/src/endpoints/users.rs @@ -0,0 +1 @@ +pub mod get_users; \ No newline at end of file diff --git a/src/endpoints/users/get_users.rs b/src/endpoints/users/get_users.rs new file mode 100644 index 0000000..fe8530e --- /dev/null +++ b/src/endpoints/users/get_users.rs @@ -0,0 +1,74 @@ +use rocket::form::Form; +use rocket::http::Status; +use rocket::response::status; + +use diesel::prelude::*; +use diesel::result::Error; + +use crate::helpers; +use crate::db; + +#[derive(FromForm)] +pub struct FormGetUsers { + page: i64, + str: String +} + +#[post("/accounts/getGJUsers20.php", data = "")] +pub fn get_users(input: Form) -> status::Custom<&'static str> { + let connection = &mut db::establish_connection_pg(); + + // query users + use crate::schema::users::dsl::*; + use crate::models::User; + + let mut query = users.into_boxed(); + + match input.str.parse::() { + Ok(id_value) => query = query.filter(id.eq(id_value)), + Err(_) => query = query.filter(username.like(input.str.to_owned() + "%")) + }; + + let results = query + .order(stars.desc()) + .limit(10) + .offset(input.page * 10) + .get_result::(connection) + .expect("Fatal error loading users"); + + let response = helpers::format::format(hashmap! { + 1 => results.username, + 2 => results.id.to_string(), + 3 => results.stars.to_string(), + 4 => results.demons.to_string(), + 8 => results.creator_points.to_string(), + 9 => { + vec![ + results.cube, + results.ship, + results.ball, + results.ufo, + results.wave, + results.robot + ][results.icon_type as usize].to_string() + }, + 10 => results.color1.to_string(), + 11 => results.color2.to_string(), + 13 => results.coins.to_string(), + 14 => results.icon_type.to_string(), + 15 => results.special.to_string(), + 16 => { + match results.account_id { + Some(account_id_value) => account_id_value.to_string(), + None => match results.udid { + Some(udid_value) => udid_value.to_string(), + None => panic!("user has no account_id or udid?!?!?") + } + } + } + }); + + println!("{}", response); + + return status::Custom(Status::Ok, "1") +} \ No newline at end of file diff --git a/src/helpers.rs b/src/helpers.rs index 1302a7d..d4d9ee6 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,2 +1,4 @@ -pub mod gjp2; -pub mod clean; \ No newline at end of file +pub mod accounts; +pub mod clean; +pub mod format; +pub mod gjp2; \ No newline at end of file diff --git a/src/helpers/accounts.rs b/src/helpers/accounts.rs new file mode 100644 index 0000000..2cd1b08 --- /dev/null +++ b/src/helpers/accounts.rs @@ -0,0 +1,17 @@ +use diesel::prelude::*; + +use crate::db; + +pub fn get_user_id_from_account_id(ext_id: i32) -> i32 { + use crate::schema::users::dsl::*; + + let connection = &mut db::establish_connection_pg(); + + let user_id = users + .filter(udid.eq(ext_id.to_string()).or(account_id.eq(ext_id))) + .select(id) + .get_result::(connection) + .expect("No user associated with account?!?!?"); + + return user_id +} \ No newline at end of file diff --git a/src/helpers/format.rs b/src/helpers/format.rs new file mode 100644 index 0000000..593fcf7 --- /dev/null +++ b/src/helpers/format.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +pub fn format(map: HashMap) -> String { + let mut result = String::new(); + + for (k, v) in map { + result.push_str(&format!("{}:{}", k, v.to_string())); + result.push(':'); + } + + if !result.is_empty() { + result.pop(); + } + + return result; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fead183..bc0551e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ #![feature(decl_macro)] +#![feature(lazy_cell)] +#[macro_use] extern crate maplit; #[macro_use] extern crate rocket; mod db; @@ -11,16 +13,25 @@ use helpers::*; mod endpoints; use endpoints::*; +mod config; +use config::*; + #[get("/")] fn index() -> String { - return String::from("index | coming soon to a localhost:8000 near u"); + return String::from("gdps-server | https://git.reidlab.online/reidlab/gdps-server"); } #[launch] fn rocket() -> _ { - rocket::build().mount("/", routes![ - index, - - endpoints::accounts::register_account::register_account - ]) + rocket::build() + .configure(rocket::Config::figment().merge(("port", CONFIG.general.port))) + .mount("/", routes![ + index, + ]) + .mount(CONFIG.general.append_path.as_str(), routes![ + endpoints::accounts::login_account::login_account, + endpoints::accounts::register_account::register_account, + + endpoints::users::get_users::get_users + ]) } \ No newline at end of file