mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2025-11-25 15:05:54 +00:00
suplicate errors and database fkey null correction
This commit is contained in:
parent
a73725eb39
commit
6be10af6fd
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1179,6 +1179,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"m_captcha",
|
"m_captcha",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
"rand 0.8.3",
|
||||||
"serde 1.0.124",
|
"serde 1.0.124",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
|||||||
@ -20,6 +20,8 @@ path = "./src/tests-migrate.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "3"
|
actix-web = "3"
|
||||||
actix = "0.10"
|
actix = "0.10"
|
||||||
|
actix-identity = "0.3"
|
||||||
|
actix-http = "2.2"
|
||||||
|
|
||||||
sqlx = { version = "0.4.0", features = [ "runtime-actix-rustls", "postgres" ] }
|
sqlx = { version = "0.4.0", features = [ "runtime-actix-rustls", "postgres" ] }
|
||||||
argon2-creds = { version = "0.2", git = "https://github.com/realaravinth/argon2-creds", commit = "61f2d1d" }
|
argon2-creds = { version = "0.2", git = "https://github.com/realaravinth/argon2-creds", commit = "61f2d1d" }
|
||||||
@ -40,11 +42,11 @@ log = "0.4"
|
|||||||
|
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
||||||
actix-identity = "0.3"
|
|
||||||
actix-http = "2.2"
|
|
||||||
|
|
||||||
m_captcha = { version = "0.1.0", git = "https://github.com/mCaptcha/mCaptcha", tag = "0.1.0" }
|
m_captcha = { version = "0.1.0", git = "https://github.com/mCaptcha/mCaptcha", tag = "0.1.0" }
|
||||||
|
|
||||||
|
rand = "0.8"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1"
|
actix-rt = "1"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE IF NOT EXISTS mcaptcha_domains (
|
CREATE TABLE IF NOT EXISTS mcaptcha_domains (
|
||||||
name VARCHAR(100) PRIMARY KEY NOT NULL UNIQUE,
|
name VARCHAR(100) PRIMARY KEY NOT NULL UNIQUE,
|
||||||
ID INTEGER references mcaptcha_users(ID)
|
ID INTEGER references mcaptcha_users(ID) NOT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,5 +2,5 @@ CREATE TABLE IF NOT EXISTS mcaptcha_config (
|
|||||||
config_id SERIAL PRIMARY KEY NOT NULL,
|
config_id SERIAL PRIMARY KEY NOT NULL,
|
||||||
ID INTEGER references mcaptcha_users(ID),
|
ID INTEGER references mcaptcha_users(ID),
|
||||||
key VARCHAR(100) NOT NULL UNIQUE,
|
key VARCHAR(100) NOT NULL UNIQUE,
|
||||||
duration INTEGER NOT NULL
|
duration INTEGER NOT NULL DEFAULT 30
|
||||||
);
|
);
|
||||||
|
|||||||
@ -48,15 +48,19 @@ pub async fn signup(
|
|||||||
let username = data.creds.username(&payload.username)?;
|
let username = data.creds.username(&payload.username)?;
|
||||||
let hash = data.creds.password(&payload.password)?;
|
let hash = data.creds.password(&payload.password)?;
|
||||||
data.creds.email(Some(&payload.email))?;
|
data.creds.email(Some(&payload.email))?;
|
||||||
sqlx::query!(
|
let res = sqlx::query!(
|
||||||
"INSERT INTO mcaptcha_users (name , password, email) VALUES ($1, $2, $3)",
|
"INSERT INTO mcaptcha_users (name , password, email) VALUES ($1, $2, $3)",
|
||||||
username,
|
username,
|
||||||
hash,
|
hash,
|
||||||
&payload.email
|
&payload.email
|
||||||
)
|
)
|
||||||
.execute(&data.db)
|
.execute(&data.db)
|
||||||
.await?;
|
.await;
|
||||||
Ok(HttpResponse::Ok())
|
|
||||||
|
match res {
|
||||||
|
Err(e) => Err(dup_error(e, ServiceError::UsernameTaken)),
|
||||||
|
Ok(_) => Ok(HttpResponse::Ok()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/api/v1/signin")]
|
#[post("/api/v1/signin")]
|
||||||
@ -129,6 +133,8 @@ pub async fn delete_account(
|
|||||||
.fetch_one(&data.db)
|
.fetch_one(&data.db)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
id.forget();
|
||||||
|
|
||||||
match rec {
|
match rec {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
if Config::verify(&s.password, &payload.password)? {
|
if Config::verify(&s.password, &payload.password)? {
|
||||||
|
|||||||
@ -38,7 +38,13 @@ pub async fn add_domain(
|
|||||||
is_authenticated(&id)?;
|
is_authenticated(&id)?;
|
||||||
let url = Url::parse(&payload.name)?;
|
let url = Url::parse(&payload.name)?;
|
||||||
if let Some(host) = url.host_str() {
|
if let Some(host) = url.host_str() {
|
||||||
sqlx::query!("INSERT INTO mcaptcha_domains (name) VALUES ($1)", host,)
|
let user = id.identity().unwrap();
|
||||||
|
sqlx::query!(
|
||||||
|
"insert into mcaptcha_domains (name, ID) values
|
||||||
|
($1, (select ID from mcaptcha_users where name = ($2) ));",
|
||||||
|
host,
|
||||||
|
user
|
||||||
|
)
|
||||||
.execute(&data.db)
|
.execute(&data.db)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(HttpResponse::Ok())
|
Ok(HttpResponse::Ok())
|
||||||
@ -65,6 +71,60 @@ pub async fn delete_domain(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct TokenName {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct TokenKeyPair {
|
||||||
|
pub name: String,
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[post("/api/v1/mcaptcha/domain/token/add")]
|
||||||
|
//pub async fn add_mcaptcha(
|
||||||
|
// payload: web::Json<Domain>,
|
||||||
|
// data: web::Data<Data>,
|
||||||
|
// id: Identity,
|
||||||
|
//) -> ServiceResult<impl Responder> {
|
||||||
|
// is_authenticated(&id)?;
|
||||||
|
// let key = get_random(32);
|
||||||
|
// let res = sqlx::query!(
|
||||||
|
// "INSERT INTO mcaptcha_config (name, key) VALUES ($1, $2)",
|
||||||
|
// &payload.name,
|
||||||
|
// &key,
|
||||||
|
// )
|
||||||
|
// .execute(&data.db)
|
||||||
|
// .await;
|
||||||
|
//
|
||||||
|
// match res {
|
||||||
|
// Err(e) => Err(dup_error(e, ServiceError::UsernameTaken)),
|
||||||
|
// Ok(_) => {
|
||||||
|
// let resp = TokenKeyPair {
|
||||||
|
// key,
|
||||||
|
// name: payload.name,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// Ok(HttpResponse::Ok().json(resp))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
fn get_random(len: usize) -> String {
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
|
||||||
|
|
||||||
|
let mut rng: ThreadRng = thread_rng();
|
||||||
|
|
||||||
|
iter::repeat(())
|
||||||
|
.map(|()| rng.sample(Alphanumeric))
|
||||||
|
.map(char::from)
|
||||||
|
.take(len)
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::http::{header, StatusCode};
|
use actix_web::http::{header, StatusCode};
|
||||||
@ -77,7 +137,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn add_domains_work() {
|
async fn add_domains_work() {
|
||||||
const NAME: &str = "testuserdomain";
|
const NAME: &str = "testuserdomainn";
|
||||||
const PASSWORD: &str = "longpassworddomain";
|
const PASSWORD: &str = "longpassworddomain";
|
||||||
const EMAIL: &str = "testuserdomain@a.com";
|
const EMAIL: &str = "testuserdomain@a.com";
|
||||||
const DOMAIN: &str = "http://example.com";
|
const DOMAIN: &str = "http://example.com";
|
||||||
|
|||||||
@ -37,8 +37,12 @@ use std::convert::From;
|
|||||||
pub enum ServiceError {
|
pub enum ServiceError {
|
||||||
#[display(fmt = "internal server error")]
|
#[display(fmt = "internal server error")]
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
|
|
||||||
#[display(fmt = "The value you entered for email is not an email")] //405j
|
#[display(fmt = "The value you entered for email is not an email")] //405j
|
||||||
NotAnEmail,
|
NotAnEmail,
|
||||||
|
#[display(fmt = "The value you entered for URL is not a URL")] //405j
|
||||||
|
NotAUrl,
|
||||||
|
|
||||||
#[display(fmt = "Wrong password")]
|
#[display(fmt = "Wrong password")]
|
||||||
WrongPassword,
|
WrongPassword,
|
||||||
#[display(fmt = "Username not found")]
|
#[display(fmt = "Username not found")]
|
||||||
@ -54,22 +58,24 @@ pub enum ServiceError {
|
|||||||
/// see [blacklist](https://github.com/shuttlecraft/The-Big-Username-Blacklist)
|
/// see [blacklist](https://github.com/shuttlecraft/The-Big-Username-Blacklist)
|
||||||
#[display(fmt = "Username contains blacklisted words")]
|
#[display(fmt = "Username contains blacklisted words")]
|
||||||
BlacklistError,
|
BlacklistError,
|
||||||
|
|
||||||
/// when the value passed contains characters not present
|
/// when the value passed contains characters not present
|
||||||
/// in [UsernameCaseMapped](https://tools.ietf.org/html/rfc8265#page-7)
|
/// in [UsernameCaseMapped](https://tools.ietf.org/html/rfc8265#page-7)
|
||||||
/// profile
|
/// profile
|
||||||
#[display(fmt = "username_case_mapped violation")]
|
#[display(fmt = "username_case_mapped violation")]
|
||||||
UsernameCaseMappedError,
|
UsernameCaseMappedError,
|
||||||
|
|
||||||
/// when the value passed contains profainity
|
|
||||||
#[display(fmt = "Username not available")]
|
|
||||||
UsernameTaken,
|
|
||||||
#[display(fmt = "Passsword too short")]
|
#[display(fmt = "Passsword too short")]
|
||||||
PasswordTooShort,
|
PasswordTooShort,
|
||||||
#[display(fmt = "Username too long")]
|
#[display(fmt = "Username too long")]
|
||||||
PasswordTooLong,
|
PasswordTooLong,
|
||||||
#[display(fmt = "The value you entered for URL is not a URL")] //405j
|
|
||||||
NotAUrl,
|
/// when the a username is already taken
|
||||||
|
#[display(fmt = "Username not available")]
|
||||||
|
UsernameTaken,
|
||||||
|
|
||||||
|
/// when the a token name is already taken
|
||||||
|
#[display(fmt = "token name not available")]
|
||||||
|
TokenNameTaken,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -104,6 +110,7 @@ impl ResponseError for ServiceError {
|
|||||||
ServiceError::PasswordTooLong => StatusCode::BAD_REQUEST,
|
ServiceError::PasswordTooLong => StatusCode::BAD_REQUEST,
|
||||||
ServiceError::UsernameCaseMappedError => StatusCode::BAD_REQUEST,
|
ServiceError::UsernameCaseMappedError => StatusCode::BAD_REQUEST,
|
||||||
ServiceError::UsernameTaken => StatusCode::BAD_REQUEST,
|
ServiceError::UsernameTaken => StatusCode::BAD_REQUEST,
|
||||||
|
ServiceError::TokenNameTaken => StatusCode::BAD_REQUEST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +149,6 @@ impl From<sqlx::Error> for ServiceError {
|
|||||||
fn from(e: sqlx::Error) -> Self {
|
fn from(e: sqlx::Error) -> Self {
|
||||||
use sqlx::error::Error;
|
use sqlx::error::Error;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
debug!("{:?}", &e);
|
|
||||||
if let Error::Database(err) = e {
|
if let Error::Database(err) = e {
|
||||||
if err.code() == Some(Cow::from("23505")) {
|
if err.code() == Some(Cow::from("23505")) {
|
||||||
return ServiceError::UsernameTaken;
|
return ServiceError::UsernameTaken;
|
||||||
@ -153,5 +159,19 @@ impl From<sqlx::Error> for ServiceError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dup_error(e: sqlx::Error, dup_error: ServiceError) -> ServiceError {
|
||||||
|
use sqlx::error::Error;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
if let Error::Database(err) = e {
|
||||||
|
if err.code() == Some(Cow::from("23505")) {
|
||||||
|
dup_error
|
||||||
|
} else {
|
||||||
|
ServiceError::InternalServerError
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ServiceError::InternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
|
pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user