diff --git a/backend/.gitignore b/backend/.gitignore index 317753a..40b942a 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,4 +1,4 @@ /target .vscode -app.toml +App.toml *.sqlite diff --git a/backend/app.toml.example b/backend/App.toml.example similarity index 100% rename from backend/app.toml.example rename to backend/App.toml.example diff --git a/backend/Cargo.lock b/backend/Cargo.lock index bfa916f..2bd89f1 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -397,6 +406,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fsevent" version = "0.4.0" @@ -538,6 +557,7 @@ dependencies = [ "rocket_dyn_templates", "rocket_sync_db_pools", "serde", + "validator", ] [[package]] @@ -701,6 +721,23 @@ dependencies = [ "want", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "indexmap" version = "1.8.0" @@ -872,6 +909,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.4.1" @@ -1240,6 +1283,30 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1368,6 +1435,8 @@ version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1924,6 +1993,21 @@ dependencies = [ "syn", ] +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tokio" version = "1.17.0" @@ -2093,6 +2177,21 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.2" @@ -2105,6 +2204,61 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "validator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0f08911ab0fee2c5009580f04615fa868898ee57de10692a45da0c3bcc3e5e" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", + "validator_types", +] + +[[package]] +name = "validator_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85135714dba11a1bd0b3eb1744169266f1a38977bf4e3ff5e2e1acb8c2b7eee" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index a6ee3fa..319a427 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -20,4 +20,5 @@ argon2 = "0.4" rand_core = { version = "0.6", features = ["std"] } diesel-derive-enum = { version = "1.1", features = ["sqlite"] } jsonwebtoken = "8.1" +validator = { version = "0.14", features = ["derive"] } diff --git a/backend/requests/gamenights.sh b/backend/requests/gamenights.sh old mode 100644 new mode 100755 diff --git a/backend/requests/login.sh b/backend/requests/login.sh old mode 100644 new mode 100755 diff --git a/backend/requests/register.sh b/backend/requests/register.sh old mode 100644 new mode 100755 index 3e1d7d3..689bfbd --- a/backend/requests/register.sh +++ b/backend/requests/register.sh @@ -1 +1 @@ -curl -X POST -H "Content-Type: application/json" -d '{"username": "a", "email": "b", "password": "c", "password_repeat": "d"}' localhost:8000/api/register +curl -X POST -H "Content-Type: application/json" -d '{"username": "roflin", "email": "user@example.com", "password": "oreokoekje123", "password_repeat": "oreokoekje123"}' localhost:8000/api/register diff --git a/backend/src/api.rs b/backend/src/api.rs index fbe1c4d..f655e06 100644 --- a/backend/src/api.rs +++ b/backend/src/api.rs @@ -1,3 +1,4 @@ +use validator::ValidateArgs; use crate::AppConfig; use rocket::request::Outcome; use jsonwebtoken::decode; @@ -141,7 +142,17 @@ pub async fn gamenight_post_json(conn: DbConn, user: Option, gamen #[post("/register", format = "application/json", data = "")] pub async fn register_post_json(conn: DbConn, register_json: Json) -> ApiResponseVariant { - match schema::insert_user(conn, register_json.into_inner()).await { + + let register = register_json.into_inner(); + let register_clone = register.clone(); + match conn.run(move |c| { + register_clone.validate_args((c,c)) + }).await { + Ok(()) => (), + Err(error) => return ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string()))) + } + + match schema::insert_user(conn, register).await { Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)), Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))) } diff --git a/backend/src/schema.rs b/backend/src/schema.rs index 6ced9e9..6d5aaa3 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -1,3 +1,5 @@ +use diesel::dsl::count; +use std::ops::Deref; use argon2::PasswordVerifier; use argon2::PasswordHash; use diesel_derive_enum::DbEnum; @@ -17,10 +19,19 @@ use argon2::{ Argon2 }; use argon2::password_hash::SaltString; +use validator::{Validate, ValidationError}; #[database("gamenight_database")] pub struct DbConn(diesel::SqliteConnection); +impl Deref for DbConn { + type Target = rocket_sync_db_pools::Connection; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + table! { gamenight (id) { id -> Integer, @@ -183,6 +194,28 @@ pub async fn get_user(conn: DbConn, id: i32) -> User { }).await } +pub fn unique_username(username: &String, conn: &diesel::SqliteConnection) -> Result<(), ValidationError> { + match user::table + .select(count(user::username)) + .filter(user::username.eq(username)) + .execute(conn) { + Ok(0) => Ok(()), + Ok(_) => Err(ValidationError::new("User already exists")), + Err(_) => Err(ValidationError::new("Database error while validating user")) + } +} + +pub fn unique_email(email: &String, conn: &diesel::SqliteConnection) -> Result<(), ValidationError> { + match user::table + .select(count(user::email)) + .filter(user::email.eq(email)) + .execute(conn) { + Ok(0) => Ok(()), + Ok(_) => Err(ValidationError::new("email already exists")), + Err(_) => Err(ValidationError::new("Database error while validating email")) + } +} + pub async fn run_migrations(rocket: Rocket) -> Rocket { // This macro from `diesel_migrations` defines an `embedded_migrations` // module containing a function named `run`. This allows the example to be @@ -236,10 +269,13 @@ pub struct GameNight { pub datetime : String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Validate, Clone)] pub struct Register { + #[validate(length(min = 1), custom( function = "unique_username", arg = "&'v_a diesel::SqliteConnection"))] pub username: String, + #[validate(email, custom( function = "unique_email", arg = "&'v_a diesel::SqliteConnection"))] pub email: String, + #[validate(length(min = 10), must_match = "password_repeat")] pub password: String, pub password_repeat: String, }