feat(api): implemented password criteria checking on registration

This commit is contained in:
antifallobst 2023-09-10 03:53:50 +02:00
parent 4bccfeef96
commit 441dc842f4
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
5 changed files with 53 additions and 13 deletions

View File

@ -15,6 +15,14 @@ This verification link will time out after 10 minutes.
| password | The password used for authentication. |
| email | The email address used for validation. |
The password has to meet the following criteria:
- minimum length: 12 characters
- numbers
- special characters
- lowercase letters
- uppercase letters
## Responses
### 200 - Success
The verification request was sent.
@ -26,8 +34,15 @@ Blocked for security reasons.
The requested username or email is already taken.
__Content - JSON:__
| Field | Description |
|----------|----------------------------------------------------------------------|
| conflict | Can be `username` or `email`, depending on what caused the conflict. |
### 422 - Error: Unprocessable Entity
Malformed email address.
The email is malformed, or the password does not meet the criteria.
__Content - JSON:__
| Field | Description |
|---------|---------------------------------------------------------------------|
| problem | Can be `email` or `password`, depending on what caused the problem. |

View File

@ -13,7 +13,7 @@ async fn register(
Ok(resp) => match resp {
data::RegisterResponse::Success => HttpResponse::Ok().finish(),
data::RegisterResponse::Conflict(b) => HttpResponse::Conflict().json(web::Json(b)),
data::RegisterResponse::MalformedEmail => HttpResponse::UnprocessableEntity().finish(),
data::RegisterResponse::Unprocessable(b) => HttpResponse::UnprocessableEntity().json(web::Json(b)),
data::RegisterResponse::Blocked => HttpResponse::Forbidden().finish(),
},
Err(e) => {

View File

@ -14,11 +14,18 @@ pub enum RegisterConflict {
Email,
}
#[derive(Debug, Serialize)]
#[serde(tag = "conflict", rename_all = "snake_case")]
pub enum RegisterUnprocessable {
Email,
Password,
}
#[derive(Debug)]
pub enum RegisterResponse {
Success,
Conflict(RegisterConflict),
MalformedEmail,
Unprocessable(RegisterUnprocessable),
Blocked,
}

View File

@ -1,7 +1,7 @@
use crate::{
accounts::Account,
api::account::data,
security::{is_sql_injection, AlphaExt},
security::{is_sql_injection, AlphaExt, meet_password_criteria},
tokens::{AuthToken, VerificationToken},
};
use anyhow::Result;
@ -13,18 +13,10 @@ pub async fn register(
pool: &PgPool,
request: data::RegisterRequest,
) -> Result<data::RegisterResponse> {
if is_sql_injection(&request.username) || is_sql_injection(&request.email) {
if is_sql_injection(&request.username) {
return Ok(data::RegisterResponse::Blocked);
}
let email_regex = regex::Regex::new(
r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})",
)?;
if !email_regex.is_match(&request.email) {
return Ok(data::RegisterResponse::MalformedEmail);
}
// Check if the username is already taken
if let Some(account) = Account::from_username(pool, &request.username).await? {
// Check if the account that has taken the username is verified or has an open verification request.
@ -55,6 +47,18 @@ pub async fn register(
account.delete(pool).await?;
}
let email_regex = regex::Regex::new(
r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})",
)?;
if !email_regex.is_match(&request.email) {
return Ok(data::RegisterResponse::Unprocessable(data::RegisterUnprocessable::Email));
}
if !meet_password_criteria(&request.password) {
return Ok(data::RegisterResponse::Unprocessable(data::RegisterUnprocessable::Password));
}
let account = Account::new(pool, &request.username, &request.email, &request.password).await?;
let token = VerificationToken::new(pool, &account.id, chrono::Duration::minutes(10)).await?;

View File

@ -5,6 +5,20 @@ pub fn is_sql_injection(string: &String) -> bool {
}
}
pub fn meet_password_criteria(string: &String) -> bool {
if string.len() < 12 ||
!string.chars().any(|c| c.is_numeric()) ||
!string.chars().any(|c| c.is_lowercase()) ||
!string.chars().any(|c| c.is_uppercase()) ||
!string.chars().any(|c|{
let x = c as u8;
(x >= 32 && x <= 47) || (x >= 58 && x <= 64) || (x >= 91 && x <= 96) || (x >= 123 && x <= 126)
}) {
return false;
}
true
}
pub trait AlphaExt {
fn is_alpha(&self) -> bool;
}