Merge pull request #137 from SpecificProtagonist/server-side-validation

Remove some client-side validation
This commit is contained in:
Flori 2023-06-27 00:05:52 +02:00 committed by GitHub
commit 028c7701a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 53 additions and 355 deletions

View File

@ -26,16 +26,12 @@ impl Instance {
self, self,
&mut cloned_limits, &mut cloned_limits,
) )
.await; .await?;
if response.is_err() {
return Err(ChorusLibError::NoResponse);
}
let response_unwrap = response.unwrap(); let status = response.status();
let status = response_unwrap.status(); let response_text = response.text().await.unwrap();
let response_text_string = response_unwrap.text().await.unwrap();
if status.is_client_error() { if status.is_client_error() {
let json: ErrorResponse = serde_json::from_str(&response_text_string).unwrap(); let json: ErrorResponse = serde_json::from_str(&response_text).unwrap();
let error_type = json.errors.errors.iter().next().unwrap().0.to_owned(); let error_type = json.errors.errors.iter().next().unwrap().0.to_owned();
let mut error = "".to_string(); let mut error = "".to_string();
for (_, value) in json.errors.errors.iter() { for (_, value) in json.errors.errors.iter() {
@ -47,11 +43,8 @@ impl Instance {
} }
let cloned_limits = self.limits.clone(); let cloned_limits = self.limits.clone();
let login_result: LoginResult = from_str(&response_text_string).unwrap(); let login_result: LoginResult = from_str(&response_text).unwrap();
let object = self let object = self.get_user(login_result.token.clone(), None).await?;
.get_user(login_result.token.clone(), None)
.await
.unwrap();
let user = UserMeta::new( let user = UserMeta::new(
Rc::new(RefCell::new(self.clone())), Rc::new(RefCell::new(self.clone())),
login_result.token, login_result.token,

View File

@ -39,15 +39,11 @@ impl Instance {
self, self,
&mut cloned_limits, &mut cloned_limits,
) )
.await; .await?;
if response.is_err() {
return Err(ChorusLibError::NoResponse);
}
let response_unwrap = response.unwrap(); let status = response.status();
let status = response_unwrap.status(); let response_text = response.text().await.unwrap();
let response_unwrap_text = response_unwrap.text().await.unwrap(); let token = from_str::<Token>(&response_text).unwrap();
let token = from_str::<Token>(&response_unwrap_text).unwrap();
let token = token.token; let token = token.token;
if status.is_client_error() { if status.is_client_error() {
let json: ErrorResponse = serde_json::from_str(&token).unwrap(); let json: ErrorResponse = serde_json::from_str(&token).unwrap();
@ -61,9 +57,7 @@ impl Instance {
return Err(ChorusLibError::InvalidFormBodyError { error_type, error }); return Err(ChorusLibError::InvalidFormBodyError { error_type, error });
} }
let user_object = self.get_user(token.clone(), None).await.unwrap(); let user_object = self.get_user(token.clone(), None).await.unwrap();
let settings = UserMeta::get_settings(&token, &self.urls.api.clone(), self) let settings = UserMeta::get_settings(&token, &self.urls.api.clone(), self).await?;
.await
.unwrap();
let user = UserMeta::new( let user = UserMeta::new(
Rc::new(RefCell::new(self.clone())), Rc::new(RefCell::new(self.clone())),
token.clone(), token.clone(),

View File

@ -1879,7 +1879,7 @@ mod example {
#[derive(Debug)] #[derive(Debug)]
struct Consumer { struct Consumer {
name: String, _name: String,
events_received: AtomicI32, events_received: AtomicI32,
} }
@ -1900,13 +1900,13 @@ mod example {
}; };
let consumer = Arc::new(Consumer { let consumer = Arc::new(Consumer {
name: "first".into(), _name: "first".into(),
events_received: 0.into(), events_received: 0.into(),
}); });
event.subscribe(consumer.clone()); event.subscribe(consumer.clone());
let second_consumer = Arc::new(Consumer { let second_consumer = Arc::new(Consumer {
name: "second".into(), _name: "second".into(),
events_received: 0.into(), events_received: 0.into(),
}); });
event.subscribe(second_consumer.clone()); event.subscribe(second_consumer.clone());

View File

@ -1,122 +1,8 @@
use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::FieldFormatError; #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
/**
A struct that represents a well-formed email address.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthEmail {
pub email: String,
}
impl AuthEmail {
/**
Returns a new [`Result<AuthEmail, FieldFormatError>`].
## Arguments
The email address you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The email address is not in a valid format.
*/
pub fn new(email: String) -> Result<AuthEmail, FieldFormatError> {
let regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
if !regex.is_match(email.as_str()) {
return Err(FieldFormatError::EmailError);
}
Ok(AuthEmail { email })
}
}
/**
A struct that represents a well-formed username.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthUsername {
pub username: String,
}
impl AuthUsername {
/**
Returns a new [`Result<AuthUsername, FieldFormatError>`].
## Arguments
The username you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
*/
pub fn new(username: String) -> Result<AuthUsername, FieldFormatError> {
if username.len() < 2 || username.len() > 32 {
Err(FieldFormatError::UsernameError)
} else {
Ok(AuthUsername { username })
}
}
}
/**
A struct that represents a well-formed password.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The password is not between 1 and 72 characters.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthPassword {
pub password: String,
}
impl AuthPassword {
/**
Returns a new [`Result<AuthPassword, FieldFormatError>`].
## Arguments
The password you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The password is not between 1 and 72 characters.
*/
pub fn new(password: String) -> Result<AuthPassword, FieldFormatError> {
if password.is_empty() || password.len() > 72 {
Err(FieldFormatError::PasswordError)
} else {
Ok(AuthPassword { password })
}
}
}
/**
A struct that represents a well-formed register request.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
- The password is not between 1 and 72 characters.
*/
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct RegisterSchema { pub struct RegisterSchema {
username: String,
password: Option<String>,
consent: bool,
email: Option<String>,
fingerprint: Option<String>,
invite: Option<String>,
date_of_birth: Option<String>,
gift_code_sku_id: Option<String>,
captcha_key: Option<String>,
promotional_email_opt_in: Option<bool>,
}
pub struct RegisterSchemaOptions {
pub username: String, pub username: String,
pub password: Option<String>, pub password: Option<String>,
pub consent: bool, pub consent: bool,
@ -129,123 +15,21 @@ pub struct RegisterSchemaOptions {
pub promotional_email_opt_in: Option<bool>, pub promotional_email_opt_in: Option<bool>,
} }
impl RegisterSchema {
pub fn builder(username: impl Into<String>, consent: bool) -> RegisterSchemaOptions {
RegisterSchemaOptions {
username: username.into(),
password: None,
consent,
email: None,
fingerprint: None,
invite: None,
date_of_birth: None,
gift_code_sku_id: None,
captcha_key: None,
promotional_email_opt_in: None,
}
}
}
impl RegisterSchemaOptions {
/**
Create a new [`RegisterSchema`].
## Arguments
All but "String::username" and "bool::consent" are optional.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is less than 2 or more than 32 characters in length
- You supply a `password` which is less than 1 or more than 72 characters in length.
These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/)
*/
pub fn build(self) -> Result<RegisterSchema, FieldFormatError> {
let username = AuthUsername::new(self.username)?.username;
let email = if let Some(email) = self.email {
Some(AuthEmail::new(email)?.email)
} else {
None
};
let password = if let Some(password) = self.password {
Some(AuthPassword::new(password)?.password)
} else {
None
};
if !self.consent {
return Err(FieldFormatError::ConsentError);
}
Ok(RegisterSchema {
username,
password,
consent: self.consent,
email,
fingerprint: self.fingerprint,
invite: self.invite,
date_of_birth: self.date_of_birth,
gift_code_sku_id: self.gift_code_sku_id,
captcha_key: self.captcha_key,
promotional_email_opt_in: self.promotional_email_opt_in,
})
}
}
/**
A struct that represents a well-formed login request.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
- The password is not between 1 and 72 characters.
*/
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct LoginSchema { pub struct LoginSchema {
/// For Discord, usernames must be between 2 and 32 characters,
/// but other servers may have different limits.
pub login: String, pub login: String,
pub password: Option<String>, /// For Discord, must be between 1 and 72 characters,
/// but other servers may have different limits.
pub password: String,
pub undelete: Option<bool>, pub undelete: Option<bool>,
pub captcha_key: Option<String>, pub captcha_key: Option<String>,
pub login_source: Option<String>, pub login_source: Option<String>,
pub gift_code_sku_id: Option<String>, pub gift_code_sku_id: Option<String>,
} }
impl LoginSchema {
/**
Returns a new [`Result<LoginSchema, FieldFormatError>`].
## Arguments
login: The username you want to login with.
password: The password you want to login with.
undelete: Honestly no idea what this is for.
captcha_key: The captcha key you want to login with.
login_source: The login source.
gift_code_sku_id: The gift code sku id.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is less than 2 or more than 32 characters in length
*/
pub fn new(
login: String,
password: Option<String>,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
) -> Result<LoginSchema, FieldFormatError> {
Ok(LoginSchema {
login,
password,
undelete,
captcha_key,
login_source,
gift_code_sku_id,
})
}
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct TotpSchema { pub struct TotpSchema {

View File

@ -15,76 +15,3 @@ mod message;
mod relationship; mod relationship;
mod role; mod role;
mod user; mod user;
#[cfg(test)]
mod schemas_tests {
use crate::errors::FieldFormatError;
use super::*;
#[test]
fn password_too_short() {
assert_eq!(
AuthPassword::new("".to_string()),
Err(FieldFormatError::PasswordError)
);
}
#[test]
fn password_too_long() {
let mut long_pw = String::new();
for _ in 0..73 {
long_pw += "a";
}
assert_eq!(
AuthPassword::new(long_pw),
Err(FieldFormatError::PasswordError)
);
}
#[test]
fn username_too_short() {
assert_eq!(
AuthUsername::new("T".to_string()),
Err(FieldFormatError::UsernameError)
);
}
#[test]
fn username_too_long() {
let mut long_un = String::new();
for _ in 0..33 {
long_un += "a";
}
assert_eq!(
AuthUsername::new(long_un),
Err(FieldFormatError::UsernameError)
);
}
#[test]
fn consent_false() {
assert_eq!(
RegisterSchema::builder("Test", false).build(),
Err(FieldFormatError::ConsentError)
);
}
#[test]
fn invalid_email() {
assert_eq!(
AuthEmail::new("p@p.p".to_string()),
Err(FieldFormatError::EmailError)
)
}
#[test]
fn valid_email() {
let reg = RegisterSchemaOptions {
email: Some("me@mail.de".to_string()),
..RegisterSchema::builder("Testy", true)
}
.build();
assert_ne!(reg, Err(FieldFormatError::EmailError));
}
}

View File

@ -1,16 +1,16 @@
use chorus::types::{RegisterSchema, RegisterSchemaOptions}; use chorus::types::RegisterSchema;
mod common; mod common;
#[tokio::test] #[tokio::test]
async fn test_registration() { async fn test_registration() {
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let reg = RegisterSchemaOptions { let reg = RegisterSchema {
username: "Hiiii".into(),
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("Hiiii", true) consent: true,
} ..Default::default()
.build() };
.unwrap();
bundle.instance.register_account(&reg).await.unwrap(); bundle.instance.register_account(&reg).await.unwrap();
common::teardown(bundle).await; common::teardown(bundle).await;
} }

View File

@ -2,7 +2,7 @@ use chorus::{
instance::{Instance, UserMeta}, instance::{Instance, UserMeta},
types::{ types::{
Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema, Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema,
RegisterSchemaOptions, RoleCreateModifySchema, RoleObject, RoleCreateModifySchema, RoleObject,
}, },
UrlBundle, UrlBundle,
}; };
@ -26,12 +26,12 @@ pub async fn setup() -> TestBundle {
); );
let mut instance = Instance::new(urls.clone()).await.unwrap(); let mut instance = Instance::new(urls.clone()).await.unwrap();
// Requires the existance of the below user. // Requires the existance of the below user.
let reg = RegisterSchemaOptions { let reg = RegisterSchema {
username: "integrationtestuser".into(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("integrationtestuser", true) ..Default::default()
} };
.build()
.unwrap();
let guild_create_schema = GuildCreateSchema { let guild_create_schema = GuildCreateSchema {
name: Some("Test-Guild!".to_string()), name: Some("Test-Guild!".to_string()),
region: None, region: None,

View File

@ -1,15 +1,15 @@
use chorus::types::{self, RegisterSchema, RegisterSchemaOptions, Relationship, RelationshipType}; use chorus::types::{self, RegisterSchema, Relationship, RelationshipType};
mod common; mod common;
#[tokio::test] #[tokio::test]
async fn test_get_mutual_relationships() { async fn test_get_mutual_relationships() {
let register_schema = RegisterSchemaOptions { let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("integrationtestuser2", true) ..Default::default()
} };
.build()
.unwrap();
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let belongs_to = &mut bundle.instance;
@ -30,12 +30,12 @@ async fn test_get_mutual_relationships() {
#[tokio::test] #[tokio::test]
async fn test_get_relationships() { async fn test_get_relationships() {
let register_schema = RegisterSchemaOptions { let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("integrationtestuser2", true) ..Default::default()
} };
.build()
.unwrap();
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let belongs_to = &mut bundle.instance;
@ -53,12 +53,12 @@ async fn test_get_relationships() {
#[tokio::test] #[tokio::test]
async fn test_modify_relationship_friends() { async fn test_modify_relationship_friends() {
let register_schema = RegisterSchemaOptions { let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("integrationtestuser2", true) ..Default::default()
} };
.build()
.unwrap();
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let belongs_to = &mut bundle.instance;
@ -101,12 +101,12 @@ async fn test_modify_relationship_friends() {
#[tokio::test] #[tokio::test]
async fn test_modify_relationship_block() { async fn test_modify_relationship_block() {
let register_schema = RegisterSchemaOptions { let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()), date_of_birth: Some("2000-01-01".to_string()),
..RegisterSchema::builder("integrationtestuser2", true) ..Default::default()
} };
.build()
.unwrap();
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let belongs_to = &mut bundle.instance;