From 1b40b16fd8ff883bf71ee1fa5c3d2b53704e1f95 Mon Sep 17 00:00:00 2001 From: reidlab Date: Sat, 2 Sep 2023 21:05:08 -0700 Subject: [PATCH 1/3] gjp -> encryption, xor, level db boilerplate --- migrations/2023-09-03-032651_levels/down.sql | 1 + migrations/2023-09-03-032651_levels/up.sql | 42 ++++++++++++++++++++ readme.md | 4 +- src/db/models.rs | 40 +++++++++++++++++++ src/db/schema.rs | 41 +++++++++++++++++++ src/endpoints.rs | 1 + src/endpoints/accounts/login_account.rs | 2 +- src/endpoints/accounts/register_account.rs | 2 +- src/endpoints/levels.rs | 0 src/helpers.rs | 4 +- src/helpers/encryption.rs | 28 +++++++++++++ src/helpers/gjp.rs | 12 ------ 12 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 migrations/2023-09-03-032651_levels/down.sql create mode 100644 migrations/2023-09-03-032651_levels/up.sql create mode 100644 src/endpoints/levels.rs create mode 100644 src/helpers/encryption.rs delete mode 100644 src/helpers/gjp.rs diff --git a/migrations/2023-09-03-032651_levels/down.sql b/migrations/2023-09-03-032651_levels/down.sql new file mode 100644 index 0000000..954cb80 --- /dev/null +++ b/migrations/2023-09-03-032651_levels/down.sql @@ -0,0 +1 @@ +DROP TABLE levels; \ No newline at end of file diff --git a/migrations/2023-09-03-032651_levels/up.sql b/migrations/2023-09-03-032651_levels/up.sql new file mode 100644 index 0000000..531b2b3 --- /dev/null +++ b/migrations/2023-09-03-032651_levels/up.sql @@ -0,0 +1,42 @@ +CREATE TABLE levels ( + id SERIAL PRIMARY KEY, + created_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')), + modified_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')), + + name VARCHAR(20) NOT NULL, + user_id INTEGER NOT NULL references users(id), + description VARCHAR(140) NOT NULL DEFAULT '', + original INTEGER, + + game_version INTEGER NOT NULL, + binary_version INTEGER NOT NULL, + + password TEXT, + requested_stars INTEGER, + unlisted INTEGER NOT NULL DEFAULT 0, + + version INTEGER NOT NULL DEFAULT 0, + extra_data BYTEA NOT NULL, + level_info BYTEA NOT NULL, + + editor_time INTEGER NOT NULL, + editor_time_copies INTEGER NOT NULL, + + song_id INTEGER NOT NULL, + + length INTEGER NOT NULL, + objects INTEGER NOT NULL, + coins INTEGER NOT NULL DEFAULT 0, + has_ldm INTEGER NOT NULL DEFAULT 0, + two_player INTEGER NOT NULL DEFAULT 0, + + downloads INTEGER NOT NULL DEFAULT 0, + likes INTEGER NOT NULL DEFAULT 0, + difficulty INTEGER, + community_difficulty INTEGER, + demon_difficulty INTEGER, + stars INTEGER, + featured INTEGER NOT NULL DEFAULT 0, + epic INTEGER NOT NULL DEFAULT 0, + rated_coins INTEGER NOT NULL DEFAULT 0 +); diff --git a/readme.md b/readme.md index 35c1143..4a42832 100644 --- a/readme.md +++ b/readme.md @@ -34,4 +34,6 @@ _these features are implemented_ ## todo -- move authorization logic to (./src/helpers/accounts.rs)[./src/helpers/accounts.rs] \ No newline at end of file +- green name users (fuck green names!!) +- move authorization logic to (./src/helpers/accounts.rs)[./src/helpers/accounts.rs] +- maybe swap to timestamp type instead of `(TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS'))` (thats REALLY ugly!!) \ No newline at end of file diff --git a/src/db/models.rs b/src/db/models.rs index 9a390d8..f7452c1 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -79,4 +79,44 @@ pub struct NewUser { pub account_id: i32, pub username: String, pub registered: i32 +} + +#[derive(Queryable, Serialize)] +pub struct Level { + pub id: i32, + + pub created_at: String, + pub modified_at: String, + + pub name: String, + + pub user_id: i32, + + pub description: String, + pub original: Option, + pub game_version: i32, + pub binary_version: i32, + pub password: Option, + pub requested_stars: Option, + pub unlisted: i32, + pub version: i32, + pub extra_data: Vec, + pub level_info: Vec, + pub editor_time: i32, + pub editor_time_copies: i32, + pub song_id: i32, + pub length: i32, + pub objects: i32, + pub coins: i32, + pub has_ldm: i32, + pub two_player: i32, + pub downloads: i32, + pub likes: i32, + pub difficulty: Option, + pub community_difficulty: Option, + pub demon_difficulty: Option, + pub stars: Option, + pub featured: i32, + pub epic: i32, + pub rated_coins: i32 } \ No newline at end of file diff --git a/src/db/schema.rs b/src/db/schema.rs index a56613f..ab0d3dc 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -22,6 +22,45 @@ diesel::table! { } } +diesel::table! { + levels (id) { + id -> Int4, + created_at -> Text, + modified_at -> Text, + #[max_length = 20] + name -> Varchar, + user_id -> Int4, + #[max_length = 140] + description -> Varchar, + original -> Nullable, + game_version -> Int4, + binary_version -> Int4, + password -> Nullable, + requested_stars -> Nullable, + unlisted -> Int4, + version -> Int4, + extra_data -> Bytea, + level_info -> Bytea, + editor_time -> Int4, + editor_time_copies -> Int4, + song_id -> Int4, + length -> Int4, + objects -> Int4, + coins -> Int4, + has_ldm -> Int4, + two_player -> Int4, + downloads -> Int4, + likes -> Int4, + difficulty -> Nullable, + community_difficulty -> Nullable, + demon_difficulty -> Nullable, + stars -> Nullable, + featured -> Int4, + epic -> Int4, + rated_coins -> Int4, + } +} + diesel::table! { users (id) { id -> Int4, @@ -57,9 +96,11 @@ diesel::table! { } } +diesel::joinable!(levels -> users (user_id)); diesel::joinable!(users -> accounts (account_id)); diesel::allow_tables_to_appear_in_same_query!( accounts, + levels, users, ); diff --git a/src/endpoints.rs b/src/endpoints.rs index 5667352..94ebf5d 100644 --- a/src/endpoints.rs +++ b/src/endpoints.rs @@ -1,2 +1,3 @@ pub mod accounts; +pub mod levels; pub mod users; \ No newline at end of file diff --git a/src/endpoints/accounts/login_account.rs b/src/endpoints/accounts/login_account.rs index f21cbd3..affb5c5 100644 --- a/src/endpoints/accounts/login_account.rs +++ b/src/endpoints/accounts/login_account.rs @@ -43,7 +43,7 @@ pub fn login_account(input: Form) -> status::Custom<&'static s Ok(account_id_gjp2) => { let user_id = helpers::accounts::get_user_id_from_account_id(account_id_gjp2.0); - match verify_password(helpers::gjp::get_gjp2(input.password.clone()).as_bytes(), account_id_gjp2.1.as_str()) { + match verify_password(helpers::encryption::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()) ), diff --git a/src/endpoints/accounts/register_account.rs b/src/endpoints/accounts/register_account.rs index 1dc7b04..c00b70a 100644 --- a/src/endpoints/accounts/register_account.rs +++ b/src/endpoints/accounts/register_account.rs @@ -60,7 +60,7 @@ pub fn register_account(input: Form) -> status::Custom<&'st let new_account = NewAccount { username: input.userName.clone(), - gjp2: helpers::gjp::get_gjp2_hashed(input.password.clone()), + gjp2: helpers::encryption::get_gjp2_hashed(input.password.clone()), email: input.email.clone() }; inserted_account = diesel::insert_into(accounts) diff --git a/src/endpoints/levels.rs b/src/endpoints/levels.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/helpers.rs b/src/helpers.rs index 4802350..49728fc 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,4 +1,4 @@ pub mod accounts; pub mod clean; -pub mod format; -pub mod gjp; \ No newline at end of file +pub mod encryption; +pub mod format; \ No newline at end of file diff --git a/src/helpers/encryption.rs b/src/helpers/encryption.rs new file mode 100644 index 0000000..018b0e9 --- /dev/null +++ b/src/helpers/encryption.rs @@ -0,0 +1,28 @@ +use sha::sha1::Sha1; +use sha::utils::{Digest, DigestExt}; + +use password_auth::generate_hash; + +pub fn cyclic_xor(data: &[u8], key: &[u8]) -> Vec { + data.iter() + .zip(key.iter().cycle()) + .map(|(&byte, &key_byte)| byte ^ key_byte) + .collect() +} + +pub fn cyclic_xor_string(string: &str, key: &str) -> String { + let data = string.as_bytes(); + let key_bytes = key.as_bytes(); + let result_bytes = cyclic_xor(data, key_bytes); + let result_str = String::from_utf8(result_bytes).expect("invalid UTF-8 sequence (L)"); + + return String::from(result_str); +} + +pub fn get_gjp2(password: String) -> String { + return Sha1::default().digest(String::from(password + "mI29fmAnxgTs").as_bytes()).to_hex(); +} + +pub fn get_gjp2_hashed(password: String) -> String { + return generate_hash(get_gjp2(password)) +} \ No newline at end of file diff --git a/src/helpers/gjp.rs b/src/helpers/gjp.rs deleted file mode 100644 index fc2525e..0000000 --- a/src/helpers/gjp.rs +++ /dev/null @@ -1,12 +0,0 @@ -use sha::sha1::Sha1; -use sha::utils::{Digest, DigestExt}; - -use password_auth::generate_hash; - -pub fn get_gjp2(password: String) -> String { - return Sha1::default().digest(String::from(password + "mI29fmAnxgTs").as_bytes()).to_hex(); -} - -pub fn get_gjp2_hashed(password: String) -> String { - return generate_hash(get_gjp2(password)) -} \ No newline at end of file From ccafbfe860fb8f6eeabd9cea3652811cf3818a3b Mon Sep 17 00:00:00 2001 From: reidlab Date: Sun, 3 Sep 2023 15:49:49 -0700 Subject: [PATCH 2/3] add keep to level endpoints --- src/endpoints/levels/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/endpoints/levels/.keep diff --git a/src/endpoints/levels/.keep b/src/endpoints/levels/.keep new file mode 100644 index 0000000..e69de29 From e237cd9b8da56878ddf6d55358d14c69b7cb0bbb Mon Sep 17 00:00:00 2001 From: reidlab Date: Sun, 3 Sep 2023 16:12:41 -0700 Subject: [PATCH 3/3] add passwords too!! not just gjp2 --- migrations/2023-08-26-071607_accounts/up.sql | 1 + src/db/models.rs | 4 +++- src/db/schema.rs | 1 + src/endpoints/accounts/login_account.rs | 14 +++++++------- src/endpoints/accounts/register_account.rs | 5 ++++- src/helpers/encryption.rs | 6 +----- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/migrations/2023-08-26-071607_accounts/up.sql b/migrations/2023-08-26-071607_accounts/up.sql index f17d4e9..a0be4b2 100644 --- a/migrations/2023-08-26-071607_accounts/up.sql +++ b/migrations/2023-08-26-071607_accounts/up.sql @@ -9,6 +9,7 @@ CREATE TABLE accounts ( username VARCHAR(20) NOT NULL COLLATE case_insensitive UNIQUE, gjp2 TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well) + password TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well) email VARCHAR(254) NOT NULL, -- todo: swap to proper rank system diff --git a/src/db/models.rs b/src/db/models.rs index f7452c1..0dd7f17 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -7,6 +7,7 @@ pub struct Account { pub id: i32, pub username: String, + pub password: String, pub gjp2: String, pub email: String, @@ -29,7 +30,8 @@ pub struct Account { pub struct NewAccount { pub username: String, pub gjp2: String, - pub email: String + pub password: String, + pub email: String, } #[derive(Queryable, Serialize)] diff --git a/src/db/schema.rs b/src/db/schema.rs index ab0d3dc..1a25502 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -6,6 +6,7 @@ diesel::table! { #[max_length = 20] username -> Varchar, gjp2 -> Text, + password -> Text, #[max_length = 254] email -> Varchar, is_admin -> Int4, diff --git a/src/endpoints/accounts/login_account.rs b/src/endpoints/accounts/login_account.rs index affb5c5..f84fa50 100644 --- a/src/endpoints/accounts/login_account.rs +++ b/src/endpoints/accounts/login_account.rs @@ -34,18 +34,18 @@ pub fn login_account(input: Form) -> status::Custom<&'static s { use crate::schema::accounts::dsl::*; - let account_id_gjp2_result = accounts - .select((id, gjp2)) + let account_id_password_result = accounts + .select((id, password)) .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 account_id_password_result { + Ok(account_id_password) => { + let user_id = helpers::accounts::get_user_id_from_account_id(account_id_password.0); - match verify_password(helpers::encryption::get_gjp2(input.password.clone()).as_bytes(), account_id_gjp2.1.as_str()) { + match verify_password(input.password.clone().as_bytes(), account_id_password.1.as_str()) { Ok(_) => return status::Custom(Status::Ok, - Box::leak(format!("{},{}", account_id_gjp2.0, user_id).into_boxed_str()) + Box::leak(format!("{},{}", account_id_password.0, user_id).into_boxed_str()) ), Err(_) => return status::Custom(Status::Ok, "-11") }; diff --git a/src/endpoints/accounts/register_account.rs b/src/endpoints/accounts/register_account.rs index c00b70a..c754a52 100644 --- a/src/endpoints/accounts/register_account.rs +++ b/src/endpoints/accounts/register_account.rs @@ -5,6 +5,8 @@ use rocket::response::status; use diesel::prelude::*; use diesel::result::Error; +use password_auth::generate_hash; + use crate::CONFIG; use crate::helpers; use crate::db; @@ -60,7 +62,8 @@ pub fn register_account(input: Form) -> status::Custom<&'st let new_account = NewAccount { username: input.userName.clone(), - gjp2: helpers::encryption::get_gjp2_hashed(input.password.clone()), + password: generate_hash(input.password.clone()), + gjp2: generate_hash(helpers::encryption::get_gjp2(input.password.clone())), email: input.email.clone() }; inserted_account = diesel::insert_into(accounts) diff --git a/src/helpers/encryption.rs b/src/helpers/encryption.rs index 018b0e9..fb3f088 100644 --- a/src/helpers/encryption.rs +++ b/src/helpers/encryption.rs @@ -14,15 +14,11 @@ pub fn cyclic_xor_string(string: &str, key: &str) -> String { let data = string.as_bytes(); let key_bytes = key.as_bytes(); let result_bytes = cyclic_xor(data, key_bytes); - let result_str = String::from_utf8(result_bytes).expect("invalid UTF-8 sequence (L)"); + let result_str = String::from_utf8(result_bytes).expect("invalid UTF-8 sequence (how did this happen?)"); return String::from(result_str); } pub fn get_gjp2(password: String) -> String { return Sha1::default().digest(String::from(password + "mI29fmAnxgTs").as_bytes()).to_hex(); -} - -pub fn get_gjp2_hashed(password: String) -> String { - return generate_hash(get_gjp2(password)) } \ No newline at end of file