feat: added support for account registration

This commit is contained in:
antifallobst 2023-11-01 20:20:17 +01:00
parent 14f1c54575
commit f85081e449
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
4 changed files with 106 additions and 5 deletions

View File

@ -23,3 +23,33 @@ pub enum AuthError {
#[error("Failed to authenticate: account not verified")]
NotVerified,
}
#[derive(Debug, Error)]
pub enum RegisterError {
#[error("Failed to register: the API complains about a malformed request")]
BadRequest,
#[error("Failed to register: unprocessable API response")]
BadResponse,
#[error("Failed to register: failed to send request")]
RequestSend(reqwest::Error),
#[error("Failed to register: blocked for security reasons")]
Forbidden,
#[error("Failed to register: the username is already taken")]
UsernameTaken,
#[error("Failed to register: the email is already in use")]
EmailTaken,
#[error("Failed to register: the username does not meet the criteria")]
UsernameInvalid,
#[error("Failed to register: the email is not a valid email address")]
EmailInvalid,
#[error("Failed to register: password does not meet the criteria")]
PasswordInvalid,
}

View File

@ -2,7 +2,7 @@ mod error;
pub mod request;
pub mod response;
use crate::{error::AuthError, request::AccountAuthenticate};
use crate::error::{AuthError, RegisterError};
use anyhow::Result;
use reqwest::StatusCode;
use std::fmt::Debug;
@ -40,7 +40,7 @@ impl Session {
) -> Result<(), AuthError> {
let url = format!("{}/account/authenticate", &self.base_url);
let body = AccountAuthenticate { username, password };
let body = request::AccountAuthenticate { username, password };
let request = self.client.post(&url).json(&body);
let response = request
@ -52,7 +52,7 @@ impl Session {
StatusCode::OK => {
self.auth_token = Some(
response
.json::<response::AccountAuthenticateResponse>()
.json::<response::AccountAuthenticate>()
.await
.map_err(|_| AuthError::BadResponse)?
.token,
@ -68,6 +68,55 @@ impl Session {
}
}
pub async fn register(
&self,
username: String,
password: String,
email: String,
) -> Result<(), RegisterError> {
let url = format!("{}/account/register", &self.base_url);
let body = request::AccountRegister {
username,
password,
email,
};
let request = self.client.post(&url).json(&body);
let response = request
.send()
.await
.map_err(|e| RegisterError::RequestSend(e))?;
match response.status() {
StatusCode::OK => Ok(()),
StatusCode::BAD_REQUEST => Err(RegisterError::BadRequest),
StatusCode::FORBIDDEN => Err(RegisterError::Forbidden),
StatusCode::CONFLICT => match response
.json::<response::AccountRegisterConflict>()
.await
.map_err(|_| RegisterError::BadResponse)?
{
response::AccountRegisterConflict::Username => Err(RegisterError::UsernameTaken),
response::AccountRegisterConflict::Email => Err(RegisterError::EmailTaken),
},
StatusCode::UNPROCESSABLE_ENTITY => match response
.json::<response::AccountRegisterUnprocessable>()
.await
.map_err(|_| RegisterError::BadResponse)?
{
response::AccountRegisterUnprocessable::Username => {
Err(RegisterError::UsernameInvalid)
}
response::AccountRegisterUnprocessable::Email => Err(RegisterError::EmailInvalid),
response::AccountRegisterUnprocessable::Password => {
Err(RegisterError::PasswordInvalid)
}
},
_ => Err(RegisterError::BadResponse),
}
}
pub fn is_authenticated(&self) -> bool {
self.auth_token.is_some()
}

View File

@ -4,4 +4,11 @@ use serde::Serialize;
pub struct AccountAuthenticate {
pub username: String,
pub password: String,
}
}
#[derive(Debug, Serialize, Clone)]
pub struct AccountRegister {
pub username: String,
pub password: String,
pub email: String,
}

View File

@ -1,6 +1,21 @@
use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub struct AccountAuthenticateResponse {
pub struct AccountAuthenticate {
pub token: String,
}
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "conflict", rename_all = "snake_case")]
pub enum AccountRegisterConflict {
Username,
Email,
}
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "problem", rename_all = "snake_case")]
pub enum AccountRegisterUnprocessable {
Username,
Email,
Password,
}