mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2026-02-11 10:05:41 +00:00
Compare commits
4 Commits
v0.1.0-rc1
...
wip-mail-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f029d2945 | ||
|
|
e7b01a5b06 | ||
|
|
0cfffed52e | ||
|
|
c53fe2e3ff |
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -873,6 +873,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3392,6 +3393,16 @@ version = "0.7.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"serde 1.0.143",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "validator"
|
name = "validator"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ serde = { version = "1", features = ["derive"]}
|
|||||||
url = { version = "2.2.2", features = ["serde"] }
|
url = { version = "2.2.2", features = ["serde"] }
|
||||||
#libmcaptcha = { version = "0.2.2", git = "https://github.com/mCaptcha/libmcaptcha", features = ["minimal"], default-features = false, tag = "0.2.2"}
|
#libmcaptcha = { version = "0.2.2", git = "https://github.com/mCaptcha/libmcaptcha", features = ["minimal"], default-features = false, tag = "0.2.2"}
|
||||||
libmcaptcha = { branch = "master", git = "https://github.com/mCaptcha/libmcaptcha", features = ["full"] }
|
libmcaptcha = { branch = "master", git = "https://github.com/mCaptcha/libmcaptcha", features = ["full"] }
|
||||||
|
uuid = { version = "1.3.3", features = ["v4", "serde"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -31,7 +31,10 @@
|
|||||||
//! - [errors](crate::auth): error data structures used in this crate
|
//! - [errors](crate::auth): error data structures used in this crate
|
||||||
//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting
|
//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting
|
||||||
//! connection from pool
|
//! connection from pool
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub use libmcaptcha::defense::Level;
|
pub use libmcaptcha::defense::Level;
|
||||||
|
|
||||||
@@ -97,6 +100,73 @@ pub struct NameHash {
|
|||||||
pub hash: String,
|
pub hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||||
|
/// Email challenge reason
|
||||||
|
pub enum ChallengeReason {
|
||||||
|
/// challenge created to verify a newly registered user
|
||||||
|
EmailVerification,
|
||||||
|
/// Challenge created to verify a password reset request
|
||||||
|
PasswordReset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChallengeReason {
|
||||||
|
pub fn to_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::EmailVerification => "email_verification",
|
||||||
|
Self::PasswordReset => "password_resset",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for ChallengeReason {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.to_str().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ChallengeReason {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
for reason in [Self::PasswordReset, Self::EmailVerification].iter() {
|
||||||
|
if s == reason.to_str() {
|
||||||
|
return Ok(reason.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
/// challenge unique identifier
|
||||||
|
pub challenge: Uuid,
|
||||||
|
/// reason why the challenge was create
|
||||||
|
pub reason: ChallengeReason,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Challenge {
|
||||||
|
/// create new Challenge instance for a given reason. Challenge text is auto-generated
|
||||||
|
pub fn new(reason: ChallengeReason) -> Self {
|
||||||
|
let challenge = Uuid::new_v4();
|
||||||
|
Self { challenge, reason }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate new ID (useful when ID clashes)
|
||||||
|
pub fn new_id(&mut self) {
|
||||||
|
self.challenge = Uuid::new_v4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
/// mCaptcha's database requirements. To implement support for $Database, kindly implement this
|
/// mCaptcha's database requirements. To implement support for $Database, kindly implement this
|
||||||
/// trait.
|
/// trait.
|
||||||
@@ -250,6 +320,19 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
|
|||||||
|
|
||||||
/// fetch PoWConfig confirms
|
/// fetch PoWConfig confirms
|
||||||
async fn fetch_confirm(&self, user: &str, key: &str) -> DBResult<Vec<i64>>;
|
async fn fetch_confirm(&self, user: &str, key: &str) -> DBResult<Vec<i64>>;
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
async fn new_challenge(&self, user: &str, challenge: &mut Challenge)
|
||||||
|
-> DBResult<()>;
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
async fn fetch_challenge_user(
|
||||||
|
&self,
|
||||||
|
challenge: &Challenge,
|
||||||
|
) -> DBResult<ChallengeUser>;
|
||||||
|
|
||||||
|
/// Delete a challenge from database
|
||||||
|
async fn delete_challenge(&self, challenge: &Challenge) -> DBResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
|||||||
@@ -295,4 +295,13 @@ pub async fn database_works<'a, T: MCDatabase>(
|
|||||||
// delete captcha; updated key = p.username so invoke delete with it
|
// delete captcha; updated key = p.username so invoke delete with it
|
||||||
db.delete_captcha(p.username, p.username).await.unwrap();
|
db.delete_captcha(p.username, p.username).await.unwrap();
|
||||||
assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap());
|
assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap());
|
||||||
|
|
||||||
|
let mut challenge = Challenge::new(ChallengeReason::PasswordReset);
|
||||||
|
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_user(&challenge).await.is_err())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS mcaptcha_challenge_reason (
|
||||||
|
id INT auto_increment,
|
||||||
|
PRIMARY KEY(id),
|
||||||
|
name VARCHAR(40) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mcaptcha_challenge (
|
||||||
|
id INT auto_increment,
|
||||||
|
PRIMARY KEY(id),
|
||||||
|
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)
|
||||||
|
REFERENCES mcaptcha_challenge_reason (id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
|
||||||
|
);
|
||||||
@@ -1,5 +1,53 @@
|
|||||||
{
|
{
|
||||||
"db": "MySQL",
|
"db": "MySQL",
|
||||||
|
"04e79a67bc8c1b18eca95fc4d2602ed5dd41b6d864796f034540efec3da05fa8": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"nullable": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": {
|
"1367dceb151a766a901b5dd771d0b75d0bc61d2fef17a94a90c8ffa0065e2c44": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
@@ -247,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;"
|
"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": {
|
"74d68a86f852d3d85957e94ed04e8acd8e6144744f7b13e383ebcb2bcf3360ae": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@@ -873,6 +931,16 @@
|
|||||||
},
|
},
|
||||||
"query": "SELECT name, password FROM mcaptcha_users WHERE email = ?"
|
"query": "SELECT name, password FROM mcaptcha_users WHERE email = ?"
|
||||||
},
|
},
|
||||||
|
"f47c05c0a7da41a2176f08a44c6c945dabb84558a4d09369b6108bfce8b9d2bf": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"nullable": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "DELETE\n FROM mcaptcha_challenge\n WHERE\n challenge_id = ?\n AND reason = (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?);"
|
||||||
|
},
|
||||||
"fc717ff0827ccfaa1cc61a71cc7f71c348ebb03d35895c54b011c03121ad2385": {
|
"fc717ff0827ccfaa1cc61a71cc7f71c348ebb03d35895c54b011c03121ad2385": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
|
|||||||
@@ -95,6 +95,22 @@ impl Migrate for Database {
|
|||||||
.run(&self.pool)
|
.run(&self.pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||||
|
|
||||||
|
for reason in [
|
||||||
|
ChallengeReason::EmailVerification,
|
||||||
|
ChallengeReason::PasswordReset,
|
||||||
|
] {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT IGNORE INTO
|
||||||
|
mcaptcha_challenge_reason (name)
|
||||||
|
VALUES (?)",
|
||||||
|
reason.to_str()
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -895,6 +911,93 @@ impl MCDatabase for Database {
|
|||||||
|
|
||||||
Ok(Date::dates_to_unix(records))
|
Ok(Date::dates_to_unix(records))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
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, 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(),
|
||||||
|
user
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await;
|
||||||
|
if let Err(Error::Database(err)) = res {
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
if err.code() == Some(Cow::from("23505")) {
|
||||||
|
let msg = err.message();
|
||||||
|
if msg.contains("for key 'challenge_id'") {
|
||||||
|
challenge.new_id();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
async fn fetch_challenge_user(
|
||||||
|
&self,
|
||||||
|
challenge: &Challenge,
|
||||||
|
) -> DBResult<ChallengeUser> {
|
||||||
|
struct C {
|
||||||
|
name: String,
|
||||||
|
email: Option<String>,
|
||||||
|
}
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
C,
|
||||||
|
"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(ChallengeUser {
|
||||||
|
username: res.name,
|
||||||
|
email: res.email.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a challenge from database
|
||||||
|
async fn delete_challenge(&self, challenge: &Challenge) -> DBResult<()> {
|
||||||
|
let _ = sqlx::query!(
|
||||||
|
"DELETE
|
||||||
|
FROM mcaptcha_challenge
|
||||||
|
WHERE
|
||||||
|
challenge_id = ?
|
||||||
|
AND reason = (SELECT id FROM mcaptcha_challenge_reason WHERE name = ?);",
|
||||||
|
&challenge.challenge.to_string(),
|
||||||
|
challenge.reason.to_str(),
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS mcaptcha_challenge_reason (
|
||||||
|
id SERIAL PRIMARY KEY NOT NULL,
|
||||||
|
name VARCHAR(40) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
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()
|
||||||
|
);
|
||||||
@@ -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 );"
|
"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": {
|
"16864df9cf9a69c299d9ab68bac559c48f4fc433541a10f7c1b60717df2b820e": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"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) "
|
"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": {
|
"1e9fe69b23e4bfa7bb369455753100307e334e8dbaf02ff37cda08992fe95910": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
@@ -427,6 +469,19 @@
|
|||||||
},
|
},
|
||||||
"query": "SELECT time FROM mcaptcha_pow_solved_stats \n WHERE config_id = (\n SELECT config_id FROM mcaptcha_config \n WHERE \n key = $1\n AND\n user_id = (\n SELECT \n ID FROM mcaptcha_users WHERE name = $2)) \n ORDER BY time DESC"
|
"query": "SELECT time FROM mcaptcha_pow_solved_stats \n WHERE config_id = (\n SELECT config_id FROM mcaptcha_config \n WHERE \n key = $1\n AND\n user_id = (\n SELECT \n ID FROM mcaptcha_users WHERE name = $2)) \n ORDER BY time DESC"
|
||||||
},
|
},
|
||||||
|
"8a624372ec26200acdbc1c6c330dad841581e9abad586fa7f5a117a7cd289bd9": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"nullable": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "DELETE\n FROM mcaptcha_challenge\n WHERE\n challenge_id = $1\n AND reason = (SELECT ID FROM mcaptcha_challenge_reason WHERE name = $2);"
|
||||||
|
},
|
||||||
"9753721856a47438c5e72f28fd9d149db10c48e677b4613bf3f1e8487908aac8": {
|
"9753721856a47438c5e72f28fd9d149db10c48e677b4613bf3f1e8487908aac8": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
@@ -453,6 +508,18 @@
|
|||||||
},
|
},
|
||||||
"query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = ($1)\n ) ORDER BY difficulty_factor ASC;"
|
"query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = ($1)\n ) ORDER BY difficulty_factor ASC;"
|
||||||
},
|
},
|
||||||
|
"a209d14eb2c2eba8a750d66f74f8edcdbb02cf7c6c5249b226db30f52541a79b": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"nullable": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "INSERT INTO\n mcaptcha_challenge_reason (name)\n VALUES ($1) ON CONFLICT DO NOTHING\n "
|
||||||
|
},
|
||||||
"ad196ab3ef9dc32f6de2313577ccd6c26eae9ab19df5f71ce182651983efb99a": {
|
"ad196ab3ef9dc32f6de2313577ccd6c26eae9ab19df5f71ce182651983efb99a": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
|
|||||||
@@ -95,6 +95,23 @@ impl Migrate for Database {
|
|||||||
.run(&self.pool)
|
.run(&self.pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||||
|
|
||||||
|
for reason in [
|
||||||
|
ChallengeReason::EmailVerification,
|
||||||
|
ChallengeReason::PasswordReset,
|
||||||
|
] {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO
|
||||||
|
mcaptcha_challenge_reason (name)
|
||||||
|
VALUES ($1) ON CONFLICT DO NOTHING
|
||||||
|
",
|
||||||
|
reason.to_str()
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,6 +918,99 @@ impl MCDatabase for Database {
|
|||||||
|
|
||||||
Ok(Date::dates_to_unix(records))
|
Ok(Date::dates_to_unix(records))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
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, 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(),
|
||||||
|
user
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await;
|
||||||
|
if let Err(Error::Database(err)) = res {
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
if err.code() == Some(Cow::from("23505")) {
|
||||||
|
let msg = err.message();
|
||||||
|
if msg.contains("mcaptcha_challenge_challenge_id_key") {
|
||||||
|
challenge.new_id();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record challenge in database
|
||||||
|
async fn fetch_challenge_user(
|
||||||
|
&self,
|
||||||
|
challenge: &Challenge,
|
||||||
|
) -> DBResult<ChallengeUser> {
|
||||||
|
struct U {
|
||||||
|
name: String,
|
||||||
|
email: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
U,
|
||||||
|
"SELECT
|
||||||
|
email, name
|
||||||
|
FROM
|
||||||
|
mcaptcha_users
|
||||||
|
WHERE
|
||||||
|
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(ChallengeUser {
|
||||||
|
username: res.name,
|
||||||
|
email: res.email.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a challenge from database
|
||||||
|
async fn delete_challenge(&self, challenge: &Challenge) -> DBResult<()> {
|
||||||
|
let _ = sqlx::query!(
|
||||||
|
"DELETE
|
||||||
|
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(),
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user