Use graphemes for input length checks instead of number of bytes.
This commit is contained in:
parent
1540153b67
commit
48a1233534
2 changed files with 35 additions and 4 deletions
|
@ -81,10 +81,9 @@ pub async fn post_create_user(
|
||||||
if password != verify {
|
if password != verify {
|
||||||
return Err(CreateUserErrorKind::PasswordMismatch.into());
|
return Err(CreateUserErrorKind::PasswordMismatch.into());
|
||||||
}
|
}
|
||||||
|
let pwlen = password.graphemes(true).size_hint().1.unwrap_or(0);
|
||||||
let password = password.trim();
|
|
||||||
let password = password.as_bytes();
|
let password = password.as_bytes();
|
||||||
if !(4..=50).contains(&password.len()) {
|
if !(4..=50).contains(&pwlen) {
|
||||||
return Err(CreateUserErrorKind::BadPassword.into());
|
return Err(CreateUserErrorKind::BadPassword.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +351,35 @@ mod test {
|
||||||
assert_eq!(&expected, body);
|
assert_eq!(&expected, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn multibyte_password_too_short() {
|
||||||
|
let pw = "🤡";
|
||||||
|
// min length is 4
|
||||||
|
assert_eq!(pw.len(), 4);
|
||||||
|
|
||||||
|
let pool = get_db_pool().await;
|
||||||
|
let server = server_with_pool(&pool).await;
|
||||||
|
let form =
|
||||||
|
format!("username=test_user&displayname=Test+User&password={pw}&pw_verify={pw}");
|
||||||
|
let body = massage(&form);
|
||||||
|
|
||||||
|
let resp = server
|
||||||
|
.post("/signup")
|
||||||
|
// failure to sign up is not failure to submit the request
|
||||||
|
.expect_success()
|
||||||
|
.bytes(body)
|
||||||
|
.content_type(FORM_CONTENT_TYPE)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// no user in db
|
||||||
|
let user = User::try_get("test_user", &pool).await;
|
||||||
|
assert!(user.is_err());
|
||||||
|
|
||||||
|
let body = std::str::from_utf8(resp.bytes()).unwrap();
|
||||||
|
let expected = CreateUserError(CreateUserErrorKind::BadPassword).to_string();
|
||||||
|
assert_eq!(&expected, body);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn username_short() {
|
async fn username_short() {
|
||||||
let pool = get_db_pool().await;
|
let pool = get_db_pool().await;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{error::Error, ops::Range};
|
use std::{error::Error, ops::Range};
|
||||||
|
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub fn validate_optional_length<E: Error>(
|
pub fn validate_optional_length<E: Error>(
|
||||||
opt: &Option<String>,
|
opt: &Option<String>,
|
||||||
len_range: Range<usize>,
|
len_range: Range<usize>,
|
||||||
|
@ -7,7 +9,8 @@ pub fn validate_optional_length<E: Error>(
|
||||||
) -> Result<Option<String>, E> {
|
) -> Result<Option<String>, E> {
|
||||||
if let Some(opt) = opt {
|
if let Some(opt) = opt {
|
||||||
let opt = opt.trim();
|
let opt = opt.trim();
|
||||||
if !len_range.contains(&opt.len()) {
|
let len = opt.graphemes(true).size_hint().1.unwrap();
|
||||||
|
if !len_range.contains(&len) {
|
||||||
Err(err)
|
Err(err)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(opt.to_string()))
|
Ok(Some(opt.to_string()))
|
||||||
|
|
Loading…
Reference in a new issue