suplicate errors and database fkey null correction

This commit is contained in:
realaravinth 2021-03-11 16:45:43 +05:30
parent a73725eb39
commit 6be10af6fd
No known key found for this signature in database
GPG Key ID: AD9F0F08E855ED88
7 changed files with 107 additions and 18 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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
); );

View File

@ -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
); );

View File

@ -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)? {

View File

@ -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";

View File

@ -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>;