Merge pull request #9 from polyphony-chat/feature/register-login

Bring main up-to-date
This commit is contained in:
Flori 2023-04-23 14:05:05 +02:00 committed by GitHub
commit 3d2e1120d4
5 changed files with 278 additions and 96 deletions

View File

@ -1 +1,13 @@
pub mod login {}
pub mod login {
use crate::api::schemas::schemas::{LoginResult, LoginSchema};
use crate::errors::InstanceServerError;
use crate::instance::Instance;
/* impl Instance {
pub async fn login_account(
&mut self,
login_schema: &LoginSchema,
) -> Result<LoginResult, InstanceServerError> {
}
} */
}

View File

@ -1,6 +1,6 @@
pub mod register {
use reqwest::Client;
use serde_json::{from_str, json, Value};
use serde_json::json;
use crate::{
api::{
@ -19,7 +19,7 @@ pub mod register {
# Errors
* [`InstanceServerError`] - If the server does not respond.
*/
pub async fn register(
pub async fn register_account(
&mut self,
register_schema: &RegisterSchema,
) -> Result<Token, InstanceServerError> {
@ -31,7 +31,7 @@ pub mod register {
let response = limited_requester
.send_request(request_builder, LimitType::AuthRegister)
.await;
if response.is_none() {
if !response.is_ok() {
return Err(InstanceServerError::NoResponse);
}
@ -58,7 +58,7 @@ pub mod register {
#[cfg(test)]
mod test {
use crate::api::schemas::schemas::RegisterSchema;
use crate::api::schemas::schemas::{AuthEmail, AuthPassword, AuthUsername, RegisterSchema};
use crate::errors::InstanceServerError;
use crate::instance::Instance;
use crate::limit::LimitedRequester;
@ -75,10 +75,10 @@ mod test {
.await
.unwrap();
let reg = RegisterSchema::new(
"aaa".to_string(),
AuthUsername::new("hiiii".to_string()).unwrap(),
None,
true,
Some("me@mail.xy".to_string()),
Some(AuthEmail::new("me@mail.xy".to_string()).unwrap()),
None,
None,
None,
@ -92,7 +92,7 @@ mod test {
error_type: "date_of_birth".to_string(),
error: "This field is required (BASE_TYPE_REQUIRED)".to_string()
},
test_instance.register(&reg).await.err().unwrap()
test_instance.register_account(&reg).await.err().unwrap()
);
}
@ -108,10 +108,10 @@ mod test {
.await
.unwrap();
let reg = RegisterSchema::new(
"Hiiii".to_string(),
Some("mysupersecurepass123!".to_string()),
AuthUsername::new("Hiiii".to_string()).unwrap(),
Some(AuthPassword::new("mysupersecurepass123!".to_string()).unwrap()),
true,
Some("flori@mail.xyz".to_string()),
Some(AuthEmail::new("flori@aaaa.xyz".to_string()).unwrap()),
None,
None,
Some("2000-01-01".to_string()),
@ -120,7 +120,7 @@ mod test {
None,
)
.unwrap();
let token = test_instance.register(&reg).await.unwrap().token;
let token = test_instance.register_account(&reg).await.unwrap().token;
println!("{}", token);
}
}

View File

@ -1,10 +1,109 @@
pub mod schemas {
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::{collections::HashMap, fmt};
use crate::errors::FieldFormatError;
/**
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.clone().as_str()) {
return Err(FieldFormatError::EmailError);
}
return 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 {
return Err(FieldFormatError::UsernameError);
} else {
return 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.len() < 1 || password.len() > 72 {
return Err(FieldFormatError::PasswordError);
} else {
return 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")]
pub struct RegisterSchema {
@ -34,10 +133,10 @@ pub mod schemas {
These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/)
*/
pub fn new(
username: String,
password: Option<String>,
username: AuthUsername,
password: Option<AuthPassword>,
consent: bool,
email: Option<String>,
email: Option<AuthEmail>,
fingerprint: Option<String>,
invite: Option<String>,
date_of_birth: Option<String>,
@ -45,28 +144,31 @@ pub mod schemas {
captcha_key: Option<String>,
promotional_email_opt_in: Option<bool>,
) -> Result<RegisterSchema, FieldFormatError> {
if username.len() < 2 || username.len() > 32 {
return Err(FieldFormatError::UsernameError);
let username = username.username;
let email_addr;
if email.is_some() {
email_addr = Some(email.unwrap().email);
} else {
email_addr = None;
}
if password.is_some()
&& (password.as_ref().unwrap().len() < 1 || password.as_ref().unwrap().len() > 72)
{
return Err(FieldFormatError::PasswordError);
let has_password;
if password.is_some() {
has_password = Some(password.unwrap().password);
} else {
has_password = None;
}
if !consent {
return Err(FieldFormatError::ConsentError);
}
let regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
if email.clone().is_some() && !regex.is_match(email.clone().unwrap().as_str()) {
return Err(FieldFormatError::EmailError);
}
return Ok(RegisterSchema {
username,
password,
password: has_password,
consent,
email,
email: email_addr,
fingerprint,
invite,
date_of_birth,
@ -77,6 +179,15 @@ pub mod schemas {
}
}
/**
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)]
#[serde(rename_all = "snake_case")]
pub struct LoginSchema {
@ -88,6 +199,97 @@ pub mod schemas {
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: AuthUsername,
password: String,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
) -> Result<LoginSchema, FieldFormatError> {
let login = login.username;
return Ok(LoginSchema {
login,
password,
undelete,
captcha_key,
login_source,
gift_code_sku_id,
});
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LoginResult {
token: String,
settings: UserSettings,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserSettings {
afk_timeout: i32,
allow_accessibility_detection: bool,
animate_emoji: bool,
animate_stickers: i32,
contact_sync_enabled: bool,
convert_emoticons: bool,
custom_status: Option<String>,
default_guilds_restricted: bool,
detect_platform_accounts: bool,
developer_mode: bool,
disable_games_tab: bool,
enable_tts_command: bool,
explicit_content_filter: i32,
friend_source_flags: FriendSourceFlags,
friend_discovery_flags: Option<i32>,
gateway_connected: bool,
gif_auto_play: bool,
guild_folders: Vec<GuildFolder>,
guild_positions: Vec<i64>,
inline_attachment_media: bool,
inline_embed_media: bool,
locale: String,
message_display_compact: bool,
native_phone_integration_enabled: bool,
render_embeds: bool,
render_reactions: bool,
restricted_guilds: Vec<i64>,
show_current_game: bool,
status: String,
stream_notifications_enabled: bool,
theme: String,
timezone_offset: i32,
view_nsfw_guilds: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FriendSourceFlags {
all: Option<bool>,
mutual_friends: Option<bool>,
mutual_guilds: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GuildFolder {
id: String,
guild_ids: Vec<i64>,
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct TotpSchema {
@ -97,6 +299,9 @@ pub mod schemas {
login_source: Option<String>,
}
/**
Represents the result you get from GET: /api/instance/policies/.
*/
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct InstancePoliciesSchema {
@ -187,18 +392,7 @@ mod schemas_tests {
#[test]
fn password_too_short() {
assert_eq!(
RegisterSchema::new(
"Test".to_string(),
Some("".to_string()),
true,
None,
None,
None,
None,
None,
None,
None,
),
AuthPassword::new("".to_string()),
Err(FieldFormatError::PasswordError)
);
}
@ -210,18 +404,7 @@ mod schemas_tests {
long_pw = long_pw + "a";
}
assert_eq!(
RegisterSchema::new(
"Test".to_string(),
Some(long_pw),
true,
None,
None,
None,
None,
None,
None,
None,
),
AuthPassword::new(long_pw),
Err(FieldFormatError::PasswordError)
);
}
@ -229,18 +412,7 @@ mod schemas_tests {
#[test]
fn username_too_short() {
assert_eq!(
RegisterSchema::new(
"T".to_string(),
None,
true,
None,
None,
None,
None,
None,
None,
None,
),
AuthUsername::new("T".to_string()),
Err(FieldFormatError::UsernameError)
);
}
@ -252,7 +424,7 @@ mod schemas_tests {
long_un = long_un + "a";
}
assert_eq!(
RegisterSchema::new(long_un, None, true, None, None, None, None, None, None, None,),
AuthUsername::new(long_un),
Err(FieldFormatError::UsernameError)
);
}
@ -261,7 +433,7 @@ mod schemas_tests {
fn consent_false() {
assert_eq!(
RegisterSchema::new(
"Test".to_string(),
AuthUsername::new("Test".to_string()).unwrap(),
None,
false,
None,
@ -279,18 +451,7 @@ mod schemas_tests {
#[test]
fn invalid_email() {
assert_eq!(
RegisterSchema::new(
"Test".to_string(),
None,
true,
Some("p@p.p".to_string()),
None,
None,
None,
None,
None,
None,
),
AuthEmail::new("p@p.p".to_string()),
Err(FieldFormatError::EmailError)
)
}
@ -298,10 +459,10 @@ mod schemas_tests {
#[test]
fn valid_email() {
let reg = RegisterSchema::new(
"Test".to_string(),
AuthUsername::new("Testy".to_string()).unwrap(),
None,
true,
Some("me@mail.xy".to_string()),
Some(AuthEmail::new("me@mail.de".to_string()).unwrap()),
None,
None,
None,

View File

@ -17,4 +17,5 @@ custom_error! {
ReceivedErrorCodeError{error_code:String} = "Received the following error code while requesting from the route: {error_code}",
CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}",
InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}",
RateLimited = "Ratelimited.",
}

View File

@ -1,4 +1,7 @@
use crate::api::limits::{Limit, LimitType, Limits};
use crate::{
api::limits::{Limit, LimitType, Limits},
errors::InstanceServerError,
};
use reqwest::{Client, RequestBuilder, Response};
use std::collections::{HashMap, VecDeque};
@ -67,7 +70,7 @@ impl LimitedRequester {
&mut self,
request: RequestBuilder,
limit_type: LimitType,
) -> Option<Response> {
) -> Result<Response, InstanceServerError> {
if self.can_send_request(limit_type) {
let built_request = request
.build()
@ -78,13 +81,13 @@ impl LimitedRequester {
Err(e) => panic!("An error occured while processing the response: {}", e),
};
self.update_limits(&response, limit_type);
return Some(response);
return Ok(response);
} else {
self.requests.push_back(TypedRequest {
request: request,
limit_type: limit_type,
});
return None;
return Err(InstanceServerError::RateLimited);
}
}
@ -264,20 +267,25 @@ mod rate_limit {
String::from("http://localhost:3001/cdn"),
);
let mut requester = LimitedRequester::new(urls.api.clone()).await;
let mut request: Option<Response>;
request = None;
let mut request: Option<Result<Response, InstanceServerError>> = None;
for _ in 0..50 {
for _ in 0..=50 {
let request_path = urls.api.clone() + "/some/random/nonexisting/path";
let request_builder = requester.http.get(request_path);
request = requester
.send_request(request_builder, LimitType::Channel)
.await;
request = Some(
requester
.send_request(request_builder, LimitType::Channel)
.await,
);
}
match request {
Some(_) => assert!(false),
None => assert!(true),
if request.is_some() {
match request.unwrap() {
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
} else {
assert!(false)
}
}
@ -295,8 +303,8 @@ mod rate_limit {
.send_request(request_builder, LimitType::Channel)
.await;
let result = match request {
Some(result) => result,
None => panic!("Request failed"),
Ok(result) => result,
Err(_) => panic!("Request failed"),
};
let config: Config = from_str(result.text().await.unwrap().as_str()).unwrap();
println!("{:?}", config);