diff --git a/Cargo.lock b/Cargo.lock index 7706670..38a2bbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,20 +8,20 @@ version = "0.1.0" dependencies = [ "argon2", "base64", + "bcrypt", "bytemuck", "chrono", "dotenvy", "hex", "log", "once_cell", - "quick-xml 0.37.2", + "quick-xml", "rocket", - "rust-s3", "serde", "serde_json", "sha2", "sqlx", - "thiserror 2.0.11", + "thiserror", "tokio", ] @@ -139,52 +139,12 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "attohttpc" -version = "0.28.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07a9b245ba0739fc90935094c29adbaee3f977218b5fb95e822e261cda7f56a3" -dependencies = [ - "http 1.2.0", - "log", - "native-tls", - "serde", - "serde_json", - "url", -] - [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "aws-creds" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f84143206b9c72b3c5cb65415de60c7539c79cd1559290fddec657939131be0" -dependencies = [ - "attohttpc", - "home", - "log", - "quick-xml 0.32.0", - "rust-ini", - "serde", - "thiserror 1.0.69", - "time", - "url", -] - -[[package]] -name = "aws-region" -version = "0.25.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aed3f9c7eac9be28662fdb3b0f4d1951e812f7c64fed4f0327ba702f459b3b" -dependencies = [ - "thiserror 1.0.69", -] - [[package]] name = "backtrace" version = "0.3.74" @@ -212,6 +172,19 @@ 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", + "blowfish", + "getrandom 0.3.1", + "subtle", + "zeroize", +] + [[package]] name = "binascii" version = "0.1.4" @@ -245,6 +218,16 @@ 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.17.0" @@ -299,6 +282,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -314,26 +307,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "tiny-keccak", -] - [[package]] name = "cookie" version = "0.18.1" @@ -400,12 +373,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crunchy" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" - [[package]] name = "crypto-common" version = "0.1.6" @@ -434,7 +401,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", - "serde", ] [[package]] @@ -493,15 +459,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "dotenvy" version = "0.15.7" @@ -639,7 +596,6 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", - "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -690,17 +646,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "futures-sink" version = "0.3.31" @@ -722,7 +667,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-macro", "futures-sink", "futures-task", "memchr", @@ -808,12 +752,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.2" @@ -831,7 +769,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -954,19 +892,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1136,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", "serde", ] @@ -1146,6 +1071,15 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "is-terminal" version = "0.4.15" @@ -1256,17 +1190,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "maybe-async" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "md-5" version = "0.10.6" @@ -1277,12 +1200,6 @@ dependencies = [ "digest", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "memchr" version = "2.7.4" @@ -1295,15 +1212,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minidom" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" -dependencies = [ - "rxml", -] - [[package]] name = "miniz_oxide" version = "0.8.5" @@ -1492,16 +1400,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "overload" version = "0.1.1" @@ -1662,16 +1560,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quick-xml" version = "0.37.2" @@ -1895,54 +1783,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-ini" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" -dependencies = [ - "cfg-if", - "ordered-multimap", - "trim-in-place", -] - -[[package]] -name = "rust-s3" -version = "0.35.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3df3f353b1f4209dcf437d777cda90279c397ab15a0cd6fd06bd32c88591533" -dependencies = [ - "async-trait", - "aws-creds", - "aws-region", - "base64", - "bytes", - "cfg-if", - "futures", - "hex", - "hmac", - "http 0.2.12", - "hyper", - "hyper-tls", - "log", - "maybe-async", - "md5", - "minidom", - "native-tls", - "percent-encoding", - "quick-xml 0.32.0", - "serde", - "serde_derive", - "serde_json", - "sha2", - "thiserror 1.0.69", - "time", - "tokio", - "tokio-native-tls", - "tokio-stream", - "url", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1968,23 +1808,6 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" -[[package]] -name = "rxml" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" -dependencies = [ - "bytes", - "rxml_validation", - "smartstring", -] - -[[package]] -name = "rxml_validation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" - [[package]] name = "ryu" version = "1.0.19" @@ -2162,17 +1985,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - [[package]] name = "socket2" version = "0.5.8" @@ -2222,6 +2034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" dependencies = [ "bytes", + "chrono", "crc", "crossbeam-queue", "either", @@ -2230,7 +2043,7 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", + "hashbrown", "hashlink", "indexmap", "log", @@ -2242,7 +2055,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.11", + "thiserror", "tokio", "tokio-stream", "tracing", @@ -2299,6 +2112,7 @@ dependencies = [ "bitflags", "byteorder", "bytes", + "chrono", "crc", "digest", "dotenvy", @@ -2325,7 +2139,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror", "tracing", "whoami", ] @@ -2340,6 +2154,7 @@ dependencies = [ "base64", "bitflags", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -2362,7 +2177,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror", "tracing", "whoami", ] @@ -2374,6 +2189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -2414,12 +2230,6 @@ dependencies = [ "loom", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stringprep" version = "0.1.5" @@ -2473,33 +2283,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.11", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -2554,15 +2344,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -2616,16 +2397,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.17" @@ -2752,12 +2523,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 06a9234..196d860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,12 @@ dotenvy = "0.15.7" once_cell = "1.20.3" serde_json = "1.0.139" chrono = { version = "0.4.39", features = ["serde"] } -rust-s3 = "0.35.1" argon2 = "0.5.3" sha2 = "0.10.8" bytemuck = "1.21.0" base64 = "0.22.1" hex = "0.4.3" thiserror = "2.0.11" -sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls", "postgres" ] } +bcrypt = "0.17.0" +sqlx = { version = "0.8.3", features = [ "runtime-tokio", "tls-native-tls", "postgres", "chrono" ] } diff --git a/src/account/account.rs b/src/account/account.rs index df3dc88..cd8d5f4 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -5,6 +5,7 @@ use argon2::password_hash::SaltString; use base64::Engine; use base64::prelude::BASE64_STANDARD; use bytemuck::bytes_of; +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; use log::{error, warn}; use rocket::http::Status; use rocket::{async_trait, Request}; @@ -34,18 +35,21 @@ const INVALID_TOKEN_ERRORS: Errors<'static> = Errors{ // optimization note: add token caching pub struct User { - pub id: i32, - pub name: String, + pub pid: i32, + pub username: String, pub password: String, - pub birthdate: chrono::NaiveDate, + pub birthdate: NaiveDate, pub timezone: String, pub email: String, + pub account_level: i32, + pub email_verified_since: Option, pub gender: String, pub country: String, pub language: String, pub marketing_allowed: bool, + pub off_device_allowed: bool, pub region: i32, - pub mii_identifier: String + pub mii_data: String } fn generate_nintendo_hash(pid: i32, text_password: &str) -> String{ @@ -59,40 +63,39 @@ fn generate_nintendo_hash(pid: i32, text_password: &str) -> String{ } impl User{ - fn generate_nintendo_hash(&self, text_password: &str) -> String{ - generate_nintendo_hash(self.id, text_password) + pub async fn get_by_username(name: &str, pool: &Pool) -> Option{ + sqlx::query_as!( + Self, + "SELECT * FROM users WHERE username = $1", + name + ).fetch_one(pool) + .await + .ok() } - pub fn verify_password(&self, cleartext_password: &str) -> (bool, bool){ - let Ok(hash) = PasswordHash::new(&self.password) else { - error!("invalid password in database for user with pid: {}", self.id); + fn generate_nintendo_hash(&self, text_password: &str) -> String{ + generate_nintendo_hash(self.pid, text_password) + } - if self.password == self.generate_nintendo_hash(cleartext_password){ - return (true, true) - } + pub fn verify_cleartext_password(&self, cleartext_password: &str) -> Option{ + let nintendo_hash = self.generate_nintendo_hash(cleartext_password); - return (false, false) - }; + self.verify_hashed_password(cleartext_password) + } - let argon = Argon2::default(); - - (argon.verify_password(cleartext_password.as_bytes(), &hash).is_ok(), false) + pub fn verify_hashed_password(&self, hashed_password: &str) -> Option{ + bcrypt::verify(hashed_password, &self.password).ok() } } pub fn generate_password(pid: i32, cleartext_password: &str) -> Option{ let password = generate_nintendo_hash(pid, cleartext_password); - let salt = SaltString::generate(&mut OsRng); - let argon = Argon2::default(); - - let pw = argon.hash_password(password.as_bytes(), &salt).ok()?; - - Some(pw.to_string()) + bcrypt::hash(password, 10).ok() } -pub async fn read_basic_auth_token(connection: &mut AsyncMysqlConnection, token: &str) -> Option { +pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option { let data = BASE64_STANDARD.decode(&token).ok()?; let decoded_basic_token = String::from_utf8(data).ok()?; @@ -101,28 +104,22 @@ pub async fn read_basic_auth_token(connection: &mut AsyncMysqlConnection, token: - let mut user: User = users - .filter(name.eq(login_username)) - .select(User::as_select()) - .first(connection) - .await.ok()?; + let mut user = sqlx::query_as!( + User, + "SELECT * FROM users WHERE username = $1", + login_username + ).fetch_one(connection).await.ok()?; - let (password_valid, upgrade_password) = user.verify_password(&login_password); - - if password_valid{ - if upgrade_password{ - user.password = generate_password(&login_password).unwrap(); - - user = connection.update_and_fetch(&user).await.ok()?; - } + let password_valid = user.verify_cleartext_password(&login_password); + if password_valid == Some(true){ Some(user) } else { None } } -async fn read_bearer_auth_token(connection: &mut AsyncMysqlConnection, token: &str) -> Option { +async fn read_bearer_auth_token(connection: &Pool, token: &str) -> Option { let data = BASE64_STANDARD.decode(&token).ok()?; warn!("bearer token login currently unsupported"); @@ -137,15 +134,13 @@ impl<'r> FromRequest<'r> for User{ async fn from_request(request: &'r Request<'_>) -> Outcome { let pool: &Pool = request.rocket().state().unwrap(); - let mut connection = pool.get().await.unwrap(); - let auth = request_try!(request.headers().get("Authorization").next().ok_or(INVALID_TOKEN_ERRORS)); let (auth_type, token) = request_try!(auth.split_once(' ').ok_or(INVALID_TOKEN_ERRORS)); let user = match auth_type{ - "Basic" => read_basic_auth_token(&mut connection, token).await, - "Bearer" => read_bearer_auth_token(&mut connection, token).await, + "Basic" => read_basic_auth_token(pool, token).await, + "Bearer" => read_bearer_auth_token(pool, token).await, _ => return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)), }; diff --git a/src/data_wrapper.rs b/src/data_wrapper.rs new file mode 100644 index 0000000..af308a2 --- /dev/null +++ b/src/data_wrapper.rs @@ -0,0 +1,5 @@ +use serde::{Deserialize, Serialize}; + +trait DataWrapper{ + +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e3f89eb..4656e8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod nnid; mod account; mod error; mod dsresponse; +mod data_wrapper; type Pool = sqlx::Pool; @@ -42,7 +43,7 @@ async fn launch() -> _ { .to_string() )); - response.adjoin_header(Header::new("Content-Type", "text/xml; charset=utf-8")); + //response.adjoin_header(Header::new("Content-Type", "text/xml; charset=utf-8")); response.remove_header("x-content-type-options"); @@ -55,6 +56,8 @@ async fn launch() -> _ { nnid::agreements::get_agreement, nnid::timezones::get_timezone, nnid::person_exists::person_exists, - nnid::email::validate + nnid::email::validate, + nnid::create_account::create_account, + nnid::oauth::generate_token::generate_token ]) } diff --git a/src/nnid/create_account.rs b/src/nnid/create_account.rs index cf97482..6e3bc79 100644 --- a/src/nnid/create_account.rs +++ b/src/nnid/create_account.rs @@ -1,18 +1,21 @@ -use chrono::NaiveDate; +use chrono::{Datelike, NaiveDate}; use rocket::{post, State}; use serde::{Deserialize, Serialize}; use crate::account::account::{generate_password, User}; use crate::error::Errors; +use crate::nnid::pid_distribution::next_pid; use crate::Pool; use crate::xml::{Xml, YesNoVal}; + + #[derive(Deserialize)] -struct Email{ +pub struct Email{ address: Box } #[derive(Deserialize, Serialize)] -struct Mii{ +pub struct Mii{ name: Box, primary: YesNoVal, data: Box, @@ -20,7 +23,7 @@ struct Mii{ #[derive(Deserialize)] #[serde(rename(serialize = "person"))] -struct AccountCreationData{ +pub struct AccountCreationData{ birth_date: NaiveDate, user_id: Box, password: Box, @@ -28,21 +31,28 @@ struct AccountCreationData{ language: Box, tz_name: Box, email: Email, + mii: Mii, gender: Box, marketing_flag: YesNoVal, + off_device_flag: YesNoVal, region: i32 } #[derive(Serialize)] #[serde(rename(serialize = "person"))] -struct AccountCreationResponseData{ +pub struct AccountCreationResponseData{ pid: i32 } #[post("/v1/api/people", data="")] -async fn create_account(database: &State, data: Xml) -> Result, Errors>{ +pub async fn create_account(database: &State, data: Xml) -> Result, Option>{ let database = database.inner(); + // its fine to crash here if we cant get the next pid as that is in my opinion a dead state + // anyways as noone can register anymore, EVER + + let pid = next_pid(database).await; + let AccountCreationData { user_id, password, @@ -52,22 +62,27 @@ async fn create_account(database: &State, data: Xml) email: Email{ address }, + mii: Mii{ + data, + .. + }, marketing_flag, gender, region, country, + off_device_flag, .. - } = *data; + } = data.0; + let password = generate_password(pid, &password).ok_or(None)?; - let new_account = sqlx::query(" - INSERT INTO users.users ( + sqlx::query!(" + INSERT INTO users ( pid, username, password, birthdate, - birthdate, timezone, email, country, @@ -75,26 +90,34 @@ async fn create_account(database: &State, data: Xml) marketing_allowed, off_device_allowed, region, + gender, mii_data ) VALUES ( - ?,?,?,?,?,?,?,?,?,? + $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13 ) - "); + ", + pid, + user_id.as_ref(), + password, + birth_date, + tz_name.as_ref(), + address.as_ref(), + country.as_ref(), + language.as_ref(), + marketing_flag.0, + off_device_flag.0, + region, + gender.as_ref(), + data.as_ref() + ).execute(database).await.unwrap(); - - let pid = connection.transaction::<_, diesel::result::Error, _>(|conn| Box::pin(async move{ - use crate::schema::users::dsl::*; - - diesel::insert_into(users) - .values(&new_account) - .returning(User::as_returning()) - .get_result(conn) - .await?; - - - Ok(()) - })).await; + + Ok( + Xml(AccountCreationResponseData{ + pid + }) + ) } #[cfg(test)] diff --git a/src/nnid/mod.rs b/src/nnid/mod.rs index d4deed5..db8b7ca 100644 --- a/src/nnid/mod.rs +++ b/src/nnid/mod.rs @@ -3,4 +3,6 @@ pub mod agreements; pub mod timezones; pub mod person_exists; pub mod email; -mod create_account; \ No newline at end of file +pub mod create_account; +pub mod oauth; +mod pid_distribution; diff --git a/src/nnid/oauth/generate_token.rs b/src/nnid/oauth/generate_token.rs new file mode 100644 index 0000000..9f4458a --- /dev/null +++ b/src/nnid/oauth/generate_token.rs @@ -0,0 +1,52 @@ +use rocket::{post, FromForm, State}; +use rocket::form::Form; +use serde::Deserialize; +use crate::account::account::User; +use crate::error::{Error, Errors}; +use crate::Pool; + +const ACCOUNT_ID_OR_PASSWORD_ERRORS: Errors = Errors{ + error: &[ + Error{ + code: "0106", + message: "Invalid account ID or password" + } + ] +}; + +const ACCOUNT_BANNED_ERRORS: Errors = Errors{ + error: &[ + Error{ + code: "0122", + message: "Device has been banned by game server" + } + ] +}; + +#[derive(FromForm)] +pub struct TokenRequestData<'a>{ + grant_type: &'a str, + user_id: &'a str, + password: &'a str, + password_type: &'a str, +} + +#[post("/v1/api/oauth20/access_token/generate", data="")] +pub async fn generate_token(pool: &State, data: Form>) -> Result<(), Option>>{ + let pool = pool.inner(); + + let user = User::get_by_username(data.user_id, pool).await + .ok_or(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS))?; + + if !user.verify_hashed_password(&data.password).is_some_and(|v| v){ + return Err(Some(ACCOUNT_ID_OR_PASSWORD_ERRORS)); + } + + if user.account_level < 0{ + return Err(Some(ACCOUNT_BANNED_ERRORS)); + } + + + + Ok(()) +} \ No newline at end of file diff --git a/src/nnid/oauth/mod.rs b/src/nnid/oauth/mod.rs new file mode 100644 index 0000000..b734bad --- /dev/null +++ b/src/nnid/oauth/mod.rs @@ -0,0 +1 @@ +pub mod generate_token; \ No newline at end of file diff --git a/src/nnid/person_exists.rs b/src/nnid/person_exists.rs index cc4f913..ddc5c98 100644 --- a/src/nnid/person_exists.rs +++ b/src/nnid/person_exists.rs @@ -4,16 +4,19 @@ use crate::error::{Error, Errors}; use crate::Pool; use crate::xml::Xml; + #[get("/v1/api/people/")] pub async fn person_exists(database: &State, username: &str) -> Result<(), Errors<'static>>{ let database = database.inner(); - let exists: bool = sqlx::query_as!( - bool, - "SELECT EXISTS(SELECT 1 FROM users.users WHERE username = ? )", + let exists = sqlx::query!( + "SELECT EXISTS(SELECT 1 FROM users WHERE username = $1 ) as exists", username ).fetch_one(database) .await + .ok() + .map(|v| v.exists) + .flatten() .unwrap_or(true); if exists { diff --git a/src/nnid/pid_distribution.rs b/src/nnid/pid_distribution.rs new file mode 100644 index 0000000..eb6ad00 --- /dev/null +++ b/src/nnid/pid_distribution.rs @@ -0,0 +1,29 @@ +use crate::Pool; + +pub async fn next_pid(pool: &Pool) -> i32{ + loop { + let next_pid = sqlx::query!("SELECT nextval('pid_counter') as pid") + .fetch_one(pool) + .await + .expect("unable to get next pid") + .pid + .expect("unable to get next pid") as i32; + + let already_exists = sqlx::query!( + "SELECT EXISTS(select 1 from users where pid = $1)", + next_pid + ).fetch_one(pool) + .await + .ok() + .map(|v| v.exists) + .flatten() + .unwrap_or(true); + + if !already_exists{ + break next_pid; + } + } + + + +} \ No newline at end of file