diff --git a/src/api/v1/auth/login.rs b/src/api/v1/auth/login.rs index c3e8bc7..81ef117 100644 --- a/src/api/v1/auth/login.rs +++ b/src/api/v1/auth/login.rs @@ -9,10 +9,12 @@ use uuid::Uuid; use crate::{ Data, - api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX}, error::Error, schema::*, - utils::{generate_access_token, generate_refresh_token, refresh_token_cookie}, + utils::{ + EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_access_token, generate_refresh_token, + refresh_token_cookie, + }, }; use super::Response; diff --git a/src/api/v1/auth/mod.rs b/src/api/v1/auth/mod.rs index be9cc35..1689d2b 100644 --- a/src/api/v1/auth/mod.rs +++ b/src/api/v1/auth/mod.rs @@ -23,15 +23,6 @@ struct Response { access_token: String, } -static EMAIL_REGEX: LazyLock = LazyLock::new(|| { - Regex::new(r"[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+(?:\.[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?").unwrap() -}); - -static USERNAME_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^[a-z0-9_.-]+$").unwrap()); - -// Password is expected to be hashed using SHA3-384 -static PASSWORD_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"[0-9a-f]{96}").unwrap()); - pub fn web() -> Scope { web::scope("/auth") .service(register::res) diff --git a/src/api/v1/auth/register.rs b/src/api/v1/auth/register.rs index 66be90a..0e170ee 100644 --- a/src/api/v1/auth/register.rs +++ b/src/api/v1/auth/register.rs @@ -13,14 +13,16 @@ use uuid::Uuid; use super::Response; use crate::{ Data, - api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX}, error::Error, schema::{ access_tokens::{self, dsl as adsl}, refresh_tokens::{self, dsl as rdsl}, users::{self, dsl as udsl}, }, - utils::{generate_access_token, generate_refresh_token, refresh_token_cookie}, + utils::{ + EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_access_token, generate_refresh_token, + refresh_token_cookie, + }, }; #[derive(Deserialize)] @@ -68,9 +70,11 @@ pub async fn res( data: web::Data, ) -> Result { if !data.config.instance.registration { - return Err(Error::Forbidden("registration is disabled on this instance".to_string())) + return Err(Error::Forbidden( + "registration is disabled on this instance".to_string(), + )); } - + let uuid = Uuid::now_v7(); if !EMAIL_REGEX.is_match(&account_information.email) { diff --git a/src/api/v1/me/mod.rs b/src/api/v1/me/mod.rs index 58eaa02..605eb1c 100644 --- a/src/api/v1/me/mod.rs +++ b/src/api/v1/me/mod.rs @@ -1,5 +1,5 @@ use actix_multipart::form::{MultipartForm, json::Json as MpJson, tempfile::TempFile}; -use actix_web::{get, patch, web, HttpRequest, HttpResponse, Scope}; +use actix_web::{HttpRequest, HttpResponse, Scope, get, patch, web}; use serde::Deserialize; use crate::{ @@ -9,9 +9,7 @@ use crate::{ mod servers; pub fn web() -> Scope { - web::scope("/me") - .service(get) - .service(update) + web::scope("/me").service(get).service(update) } #[get("")] @@ -76,15 +74,15 @@ pub async fn update( if let Some(new_info) = form.json { if let Some(username) = &new_info.username { - todo!(); + me.set_username(&mut conn, username.clone()).await?; } if let Some(display_name) = &new_info.display_name { - todo!(); + me.set_display_name(&mut conn, display_name.clone()).await?; } if let Some(email) = &new_info.email { - todo!(); + me.set_email(&mut conn, email.to_string()).await?; } } diff --git a/src/structs.rs b/src/structs.rs index 50b5ac5..e4d2610 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -12,7 +12,10 @@ use url::Url; use uuid::Uuid; use crate::{ - error::Error, schema::*, utils::{generate_refresh_token, image_check, order_by_is_above}, Conn, Data + Conn, Data, + error::Error, + schema::*, + utils::{EMAIL_REGEX, USERNAME_REGEX, generate_refresh_token, image_check, order_by_is_above}, }; pub trait HasUuid { @@ -423,11 +426,7 @@ impl Guild { futures::future::try_join_all(guild_futures).await } - pub async fn new( - conn: &mut Conn, - name: String, - owner_uuid: Uuid, - ) -> Result { + pub async fn new(conn: &mut Conn, name: String, owner_uuid: Uuid) -> Result { let guild_uuid = Uuid::now_v7(); let guild_builder = GuildBuilder { @@ -856,6 +855,64 @@ impl Me { Ok(()) } + + pub async fn set_username( + &mut self, + conn: &mut Conn, + new_username: String, + ) -> Result<(), Error> { + if !USERNAME_REGEX.is_match(&new_username) { + return Err(Error::BadRequest("Invalid username".to_string())); + } + + use users::dsl; + update(users::table) + .filter(dsl::uuid.eq(self.uuid)) + .set(dsl::username.eq(new_username.as_str())) + .execute(conn) + .await?; + + self.username = new_username; + + Ok(()) + } + + pub async fn set_display_name( + &mut self, + conn: &mut Conn, + new_display_name: String, + ) -> Result<(), Error> { + use users::dsl; + update(users::table) + .filter(dsl::uuid.eq(self.uuid)) + .set(dsl::display_name.eq(new_display_name.as_str())) + .execute(conn) + .await?; + + self.display_name = Some(new_display_name); + + Ok(()) + } + + pub async fn set_email(&mut self, conn: &mut Conn, new_email: String) -> Result<(), Error> { + if !EMAIL_REGEX.is_match(&new_email) { + return Err(Error::BadRequest("Invalid username".to_string())); + } + + use users::dsl; + update(users::table) + .filter(dsl::uuid.eq(self.uuid)) + .set(( + dsl::email.eq(new_email.as_str()), + dsl::email_verified.eq(false), + )) + .execute(conn) + .await?; + + self.email = new_email; + + Ok(()) + } } #[derive(Deserialize)] diff --git a/src/utils.rs b/src/utils.rs index 3edaab8..143b544 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use actix_web::{ cookie::{Cookie, SameSite, time::Duration}, http::header::HeaderMap, @@ -7,9 +9,24 @@ use bindet::FileType; use getrandom::fill; use hex::encode; use redis::RedisError; +use regex::Regex; use serde::Serialize; -use crate::{error::Error, structs::{HasIsAbove, HasUuid}, Data}; +use crate::{ + Data, + error::Error, + structs::{HasIsAbove, HasUuid}, +}; + +pub static EMAIL_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+(?:\.[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?").unwrap() +}); + +pub static USERNAME_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^[a-z0-9_.-]+$").unwrap()); + +// Password is expected to be hashed using SHA3-384 +pub static PASSWORD_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"[0-9a-f]{96}").unwrap()); pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, Error> { let auth_token = headers.get(actix_web::http::header::AUTHORIZATION);