feat(api): implemented password criteria checking on registration
This commit is contained in:
parent
4bccfeef96
commit
441dc842f4
|
@ -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. |
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue