diff --git a/db/db-core/src/lib.rs b/db/db-core/src/lib.rs index 70a292a9..e13dbe89 100644 --- a/db/db-core/src/lib.rs +++ b/db/db-core/src/lib.rs @@ -136,6 +136,15 @@ impl FromStr for ChallengeReason { } } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +/// Minimal user representation for use in challenge verification +pub struct ChallengeUser { + /// username of the user + pub username: String, + /// email ID of the user + pub email: String, +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] /// Email challenge pub struct Challenge { @@ -313,10 +322,14 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase { async fn fetch_confirm(&self, user: &str, key: &str) -> DBResult>; /// Record challenge in database - async fn new_challenge(&self, challenge: &mut Challenge) -> DBResult<()>; + async fn new_challenge(&self, user: &str, challenge: &mut Challenge) + -> DBResult<()>; /// Record challenge in database - async fn fetch_challenge(&self, challenge: &Challenge) -> DBResult; + async fn fetch_challenge_user( + &self, + challenge: &Challenge, + ) -> DBResult; /// Delete a challenge from database async fn delete_challenge(&self, challenge: &Challenge) -> DBResult<()>; diff --git a/db/db-core/src/tests.rs b/db/db-core/src/tests.rs index 543d600f..84b2fef3 100644 --- a/db/db-core/src/tests.rs +++ b/db/db-core/src/tests.rs @@ -295,15 +295,13 @@ pub async fn database_works<'a, T: MCDatabase>( // delete captcha; updated key = p.username so invoke delete with it db.delete_captcha(p.username, p.username).await.unwrap(); assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap()); -} -/// test all challenge routines -pub async fn challenges_works<'a, T: MCDatabase>(db: &T) { let mut challenge = Challenge::new(ChallengeReason::PasswordReset); - db.new_challenge(&mut challenge).await.unwrap(); - db.new_challenge(&mut challenge).await.unwrap(); - let c = db.fetch_challenge(&challenge).await.unwrap(); - assert_eq!(c, challenge); + db.new_challenge(p.username, &mut challenge).await.unwrap(); + db.new_challenge(p.username, &mut challenge).await.unwrap(); + let c = db.fetch_challenge_user(&challenge).await.unwrap(); + assert_eq!(c.username, p.username); + assert_eq!(&c.email, p.email.as_ref().unwrap()); db.delete_challenge(&challenge).await.unwrap(); - assert!(db.fetch_challenge(&challenge).await.is_err()) + assert!(db.fetch_challenge_user(&challenge).await.is_err()) } diff --git a/db/db-sqlx-maria/migrations/20230610113728_mcaptcha_challenge.sql b/db/db-sqlx-maria/migrations/20230610113728_mcaptcha_challenge.sql index 6d5b05aa..650ea941 100644 --- a/db/db-sqlx-maria/migrations/20230610113728_mcaptcha_challenge.sql +++ b/db/db-sqlx-maria/migrations/20230610113728_mcaptcha_challenge.sql @@ -10,6 +10,13 @@ CREATE TABLE IF NOT EXISTS mcaptcha_challenge ( reason INT NOT NULL, challenge_id varchar(40) NOT NULL UNIQUE, received timestamp NOT NULL DEFAULT now(), + user_id INT NOT NULL, + + CONSTRAINT `fk_mcaptcha_challenge_user` + FOREIGN KEY (user_id) + REFERENCES mcaptcha_users (ID) + ON DELETE CASCADE + ON UPDATE CASCADE, CONSTRAINT `fk_mcaptcha_mcaptcha_challenge_reason` FOREIGN KEY (reason) diff --git a/db/db-sqlx-maria/sqlx-data.json b/db/db-sqlx-maria/sqlx-data.json index 11a7d6a6..ab308135 100644 --- a/db/db-sqlx-maria/sqlx-data.json +++ b/db/db-sqlx-maria/sqlx-data.json @@ -10,6 +10,44 @@ }, "query": "INSERT IGNORE INTO\n mcaptcha_challenge_reason (name)\n VALUES (?)" }, + "12a7d765fb683c8134d032563f2d101e2fd70c261e71696e7a90387507e0ef43": { + "describe": { + "columns": [ + { + "name": "name", + "ordinal": 0, + "type_info": { + "char_set": 224, + "flags": { + "bits": 4101 + }, + "max_size": 400, + "type": "VarString" + } + }, + { + "name": "email", + "ordinal": 1, + "type_info": { + "char_set": 224, + "flags": { + "bits": 4 + }, + "max_size": 400, + "type": "VarString" + } + } + ], + "nullable": [ + false, + true + ], + "parameters": { + "Right": 2 + } + }, + "query": "SELECT name, email\n FROM mcaptcha_users\n WHERE ID = (SELECT user_id \n FROM mcaptcha_challenge\n WHERE\n challenge_id = ?\n AND reason = (\n SELECT id FROM mcaptcha_challenge_reason WHERE name = ?\n )\n );" + }, "1367dceb151a766a901b5dd771d0b75d0bc61d2fef17a94a90c8ffa0065e2c44": { "describe": { "columns": [ @@ -209,16 +247,6 @@ }, "query": "UPDATE mcaptcha_users set name = ?\n WHERE name = ?" }, - "6a31a6745dc005449f742b516979f195674848222b16c72c48da553a379a4e6f": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 3 - } - }, - "query": "INSERT INTO mcaptcha_challenge (challenge_id, received, reason)\n VALUES (?, ?, (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?));\n " - }, "6d1b6e5e58ca2ba285cab7b050bbdc43de1f3e46cf7d420bc95c124a1c7c9d1f": { "describe": { "columns": [], @@ -267,6 +295,16 @@ }, "query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config where captcha_key= (?)\n ) ORDER BY difficulty_factor ASC;" }, + "740ed2dab8c07c718c1b0e8e4262251bbf2501cdebfc4872fb903f70ec3d0dc8": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 4 + } + }, + "query": "INSERT INTO mcaptcha_challenge (challenge_id, received, reason, user_id)\n VALUES (?, ?,\n (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?),\n (SELECT id FROM mcaptcha_users WHERE name = ?)\n );\n " + }, "74d68a86f852d3d85957e94ed04e8acd8e6144744f7b13e383ebcb2bcf3360ae": { "describe": { "columns": [], @@ -352,31 +390,6 @@ }, "query": "insert into mcaptcha_users \n (name , password, email, secret) values (?, ?, ?, ?)" }, - "900a1f8c30fed90f8e56f88c4d0a2e81a7ea6af24d90c3a76764ee411b01af73": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": { - "char_set": 63, - "flags": { - "bits": 515 - }, - "max_size": 11, - "type": "Long" - } - } - ], - "nullable": [ - false - ], - "parameters": { - "Right": 2 - } - }, - "query": "SELECT id\n FROM mcaptcha_challenge\n WHERE\n challenge_id = ?\n AND reason = (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?);" - }, "9c435148ed5655e79dd1e73e3566ce23b7c6d38edcedbb988c95813c5da893ed": { "describe": { "columns": [ diff --git a/db/db-sqlx-maria/src/lib.rs b/db/db-sqlx-maria/src/lib.rs index f6843628..57c5e2a4 100644 --- a/db/db-sqlx-maria/src/lib.rs +++ b/db/db-sqlx-maria/src/lib.rs @@ -913,16 +913,24 @@ impl MCDatabase for Database { } /// Record challenge in database - async fn new_challenge(&self, challenge: &mut Challenge) -> DBResult<()> { + async fn new_challenge( + &self, + user: &str, + challenge: &mut Challenge, + ) -> DBResult<()> { let now = now_unix_time_stamp(); loop { let res = sqlx::query!( - "INSERT INTO mcaptcha_challenge (challenge_id, received, reason) - VALUES (?, ?, (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?)); + "INSERT INTO mcaptcha_challenge (challenge_id, received, reason, user_id) + VALUES (?, ?, + (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?), + (SELECT id FROM mcaptcha_users WHERE name = ?) + ); ", &challenge.challenge.to_string(), now, - challenge.reason.to_str() + challenge.reason.to_str(), + user ) .execute(&self.pool) .await; @@ -943,24 +951,36 @@ impl MCDatabase for Database { } /// Record challenge in database - async fn fetch_challenge(&self, challenge: &Challenge) -> DBResult { + async fn fetch_challenge_user( + &self, + challenge: &Challenge, + ) -> DBResult { struct C { - id: i32, + name: String, + email: Option, } - sqlx::query_as!( + let res = sqlx::query_as!( C, - "SELECT id - FROM mcaptcha_challenge - WHERE - challenge_id = ? - AND reason = (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?);", + "SELECT name, email + FROM mcaptcha_users + WHERE ID = (SELECT user_id + FROM mcaptcha_challenge + WHERE + challenge_id = ? + AND reason = ( + SELECT id FROM mcaptcha_challenge_reason WHERE name = ? + ) + );", &challenge.challenge.to_string(), challenge.reason.to_str(), ) .fetch_one(&self.pool) .await .map_err(map_register_err)?; - Ok(challenge.clone()) + Ok(ChallengeUser { + username: res.name, + email: res.email.unwrap(), + }) } /// Delete a challenge from database diff --git a/db/db-sqlx-maria/src/tests.rs b/db/db-sqlx-maria/src/tests.rs index 4b9ea63e..62fe290f 100644 --- a/db/db-sqlx-maria/src/tests.rs +++ b/db/db-sqlx-maria/src/tests.rs @@ -89,5 +89,4 @@ async fn everyting_works() { description: CAPTCHA_DESCRIPTION, }; database_works(&db, &p, &c, &LEVELS, &TRAFFIC_PATTERN, &ADD_NOTIFICATION).await; - challenges_works(&db).await; } diff --git a/db/db-sqlx-postgres/migrations/20230610105810_mcaptcha_challenge.sql b/db/db-sqlx-postgres/migrations/20230610105810_mcaptcha_challenge.sql index f372749a..7deb9a81 100644 --- a/db/db-sqlx-postgres/migrations/20230610105810_mcaptcha_challenge.sql +++ b/db/db-sqlx-postgres/migrations/20230610105810_mcaptcha_challenge.sql @@ -6,6 +6,7 @@ CREATE TABLE IF NOT EXISTS mcaptcha_challenge_reason ( CREATE TABLE IF NOT EXISTS mcaptcha_challenge ( id SERIAL PRIMARY KEY NOT NULL, reason INTEGER NOT NULL references mcaptcha_challenge_reason(ID) ON DELETE CASCADE, + user_id INTEGER NOT NULL references mcaptcha_users(ID) ON DELETE CASCADE, challenge_id varchar(40) NOT NULL UNIQUE, received timestamptz NOT NULL DEFAULT now() ); diff --git a/db/db-sqlx-postgres/sqlx-data.json b/db/db-sqlx-postgres/sqlx-data.json index c220e512..b6f958b6 100644 --- a/db/db-sqlx-postgres/sqlx-data.json +++ b/db/db-sqlx-postgres/sqlx-data.json @@ -81,6 +81,33 @@ }, "query": "DELETE FROM mcaptcha_sitekey_user_provided_avg_traffic\n WHERE config_id = (\n SELECT config_id \n FROM \n mcaptcha_config \n WHERE\n key = ($1) \n AND \n user_id = (SELECT ID FROM mcaptcha_users WHERE name = $2)\n );" }, + "0fe29ca10e9a83f2064b1b98f570161d339891a74c637077b94d138a4360340e": { + "describe": { + "columns": [ + { + "name": "email", + "ordinal": 0, + "type_info": "Varchar" + }, + { + "name": "name", + "ordinal": 1, + "type_info": "Varchar" + } + ], + "nullable": [ + true, + false + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + } + }, + "query": "SELECT\n email, name\n FROM\n mcaptcha_users\n WHERE\n ID = (\n SELECT\n user_id\n FROM\n mcaptcha_challenge\n WHERE\n challenge_id = $1\n AND reason = (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $2)\n );" + }, "16864df9cf9a69c299d9ab68bac559c48f4fc433541a10f7c1b60717df2b820e": { "describe": { "columns": [ @@ -119,6 +146,21 @@ }, "query": "SELECT key, name, config_id, duration FROM mcaptcha_config WHERE\n user_id = (SELECT ID FROM mcaptcha_users WHERE name = $1) " }, + "1e08fab612b17ab3cf3f76cd1543fb4d4006f7c20e09ecb58e1a1cfd5a7e70a2": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar", + "Timestamptz", + "Text", + "Text" + ] + } + }, + "query": "INSERT INTO mcaptcha_challenge (challenge_id, received, reason, user_id)\n VALUES ($1, $2, \n (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $3),\n (SELECT ID FROM mcaptcha_users WHERE name = $4)\n );\n " + }, "1e9fe69b23e4bfa7bb369455753100307e334e8dbaf02ff37cda08992fe95910": { "describe": { "columns": [], @@ -478,41 +520,6 @@ }, "query": "INSERT INTO\n mcaptcha_challenge_reason (name)\n VALUES ($1) ON CONFLICT DO NOTHING\n " }, - "aacf81df0a8ca303428d51345cd6f72e015808103516b9f32723aadf302afcdc": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int4" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - } - }, - "query": "SELECT ID\n FROM mcaptcha_challenge\n WHERE\n challenge_id = $1\n AND reason = (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $2);" - }, - "ab2a6711d8a457f055ce005ff971e9fd6f4d77f425d4eb26cc5ae050aac647f1": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Varchar", - "Timestamptz", - "Text" - ] - } - }, - "query": "INSERT INTO mcaptcha_challenge (challenge_id, received, reason)\n VALUES ($1, $2, (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $3));\n " - }, "ad196ab3ef9dc32f6de2313577ccd6c26eae9ab19df5f71ce182651983efb99a": { "describe": { "columns": [ diff --git a/db/db-sqlx-postgres/src/lib.rs b/db/db-sqlx-postgres/src/lib.rs index 493bafeb..e3322775 100644 --- a/db/db-sqlx-postgres/src/lib.rs +++ b/db/db-sqlx-postgres/src/lib.rs @@ -920,16 +920,24 @@ impl MCDatabase for Database { } /// Record challenge in database - async fn new_challenge(&self, challenge: &mut Challenge) -> DBResult<()> { + async fn new_challenge( + &self, + user: &str, + challenge: &mut Challenge, + ) -> DBResult<()> { let now = now_unix_time_stamp(); loop { let res = sqlx::query!( - "INSERT INTO mcaptcha_challenge (challenge_id, received, reason) - VALUES ($1, $2, (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $3)); + "INSERT INTO mcaptcha_challenge (challenge_id, received, reason, user_id) + VALUES ($1, $2, + (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $3), + (SELECT ID FROM mcaptcha_users WHERE name = $4) + ); ", &challenge.challenge.to_string(), now, - challenge.reason.to_str() + challenge.reason.to_str(), + user ) .execute(&self.pool) .await; @@ -950,24 +958,42 @@ impl MCDatabase for Database { } /// Record challenge in database - async fn fetch_challenge(&self, challenge: &Challenge) -> DBResult { - struct C { - id: i32, + async fn fetch_challenge_user( + &self, + challenge: &Challenge, + ) -> DBResult { + struct U { + name: String, + email: Option, } - sqlx::query_as!( - C, - "SELECT ID - FROM mcaptcha_challenge + + let res = sqlx::query_as!( + U, + "SELECT + email, name + FROM + mcaptcha_users WHERE - challenge_id = $1 - AND reason = (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $2);", - &challenge.challenge.to_string(), + ID = ( + SELECT + user_id + FROM + mcaptcha_challenge + WHERE + challenge_id = $1 + AND reason = (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $2) + );", + challenge.challenge.to_string(), challenge.reason.to_str(), ) .fetch_one(&self.pool) .await .map_err(map_register_err)?; - Ok(challenge.clone()) + + Ok(ChallengeUser { + username: res.name, + email: res.email.unwrap(), + }) } /// Delete a challenge from database diff --git a/db/db-sqlx-postgres/src/tests.rs b/db/db-sqlx-postgres/src/tests.rs index cce6e0b2..ac098278 100644 --- a/db/db-sqlx-postgres/src/tests.rs +++ b/db/db-sqlx-postgres/src/tests.rs @@ -89,6 +89,4 @@ async fn everyting_works() { description: CAPTCHA_DESCRIPTION, }; database_works(&db, &p, &c, &LEVELS, &TRAFFIC_PATTERN, &ADD_NOTIFICATION).await; - - challenges_works(&db).await; }