Add sign up
This commit is contained in:
parent
70cb64a2e0
commit
9805a96510
@ -15,6 +15,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
mod email_check;
|
mod email_check;
|
||||||
mod sign_in;
|
mod sign_in;
|
||||||
|
mod sign_up;
|
||||||
mod social_auth;
|
mod social_auth;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
@ -27,7 +28,8 @@ pub fn configure(http_client: reqwest::Client, config: &mut ServiceConfig) {
|
|||||||
config
|
config
|
||||||
.service(email_check::email_check)
|
.service(email_check::email_check)
|
||||||
.service(oauth)
|
.service(oauth)
|
||||||
.service(sign_in::sign_in);
|
.service(sign_in::sign_in)
|
||||||
|
.service(sign_up::sign_up);
|
||||||
social_auth::configure(http_client, config);
|
social_auth::configure(http_client, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,25 +77,27 @@ async fn try_sign_in(
|
|||||||
tracing::error!("Invalid email address: {e}");
|
tracing::error!("Invalid email address: {e}");
|
||||||
return Err(JsonError::new("Please provide a valid email address."));
|
return Err(JsonError::new("Please provide a valid email address."));
|
||||||
}
|
}
|
||||||
|
if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? {
|
||||||
let (user, was_created) = match Users::find()
|
|
||||||
.filter(entities::users::Column::Email.eq(&email))
|
|
||||||
.one(&mut *db)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(Some(user)) => (user, false),
|
|
||||||
Ok(None) if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? => {
|
|
||||||
return Err(JsonError::new(
|
return Err(JsonError::new(
|
||||||
"New account creation is disabled. Please contact your site administrator",
|
"New account creation is disabled. Please contact your site administrator",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(None) => (create_user(&req, &email, &password, &mut *db).await?, true),
|
|
||||||
|
match Users::find()
|
||||||
|
.filter(entities::users::Column::Email.eq(&email))
|
||||||
|
.one(&mut *db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(None) => {}
|
||||||
|
Ok(Some(user)) => return Err(JsonError::new("User with this email already exists")),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to load user for sign-in: {e}");
|
tracing::error!("Failed to load user for sign-in: {e}");
|
||||||
return Ok(Error::DatabaseError.error_response());
|
return Ok(Error::DatabaseError.error_response());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let user = create_user(&req, &email, &password, db).await?;
|
||||||
|
|
||||||
let user_id = user.id;
|
let user_id = user.id;
|
||||||
let mut user: UserModel = user.into();
|
let mut user: UserModel = user.into();
|
||||||
|
|
||||||
|
144
crates/jet-api/src/http/api/authentication/sign_up.rs
Normal file
144
crates/jet-api/src/http/api/authentication/sign_up.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
use actix_jwt_session::SessionStorage;
|
||||||
|
use actix_web::web::{Data, Json};
|
||||||
|
use actix_web::ResponseError;
|
||||||
|
use actix_web::{post, HttpRequest, HttpResponse};
|
||||||
|
use entities::prelude::Users;
|
||||||
|
use entities::users::ActiveModel as UserModel;
|
||||||
|
use jet_contract::event_bus::{Msg, SignInMedium, Topic, UserMsg};
|
||||||
|
use jet_contract::UserId;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use rumqttc::QoS;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
|
use sea_orm::*;
|
||||||
|
use validators::models::Host;
|
||||||
|
use validators::prelude::*;
|
||||||
|
|
||||||
|
use crate::config::ApplicationConfig;
|
||||||
|
use crate::extractors::RequireInstanceConfigured;
|
||||||
|
use crate::http::api::authentication::{auth_http_response, create_user, has_workspace_invites};
|
||||||
|
use crate::models::{Error, JsonError};
|
||||||
|
|
||||||
|
#[post("/sign-up")]
|
||||||
|
pub async fn sign_up(
|
||||||
|
_: RequireInstanceConfigured,
|
||||||
|
req: HttpRequest,
|
||||||
|
payload: Json<SignUpInput>,
|
||||||
|
db: Data<DatabaseConnection>,
|
||||||
|
config: Data<ApplicationConfig>,
|
||||||
|
event_bus: Data<crate::EventBusClient>,
|
||||||
|
session: Data<SessionStorage>,
|
||||||
|
) -> Result<HttpResponse, JsonError> {
|
||||||
|
let mut t = db.begin().await.map_err(|e| {
|
||||||
|
tracing::error!("Failed to start transaction for sign-in: {e}");
|
||||||
|
Error::DatabaseError
|
||||||
|
})?;
|
||||||
|
let res = try_sign_in(req, payload, &mut t, config, event_bus, session).await?;
|
||||||
|
t.commit().await.map_err(|e| {
|
||||||
|
tracing::error!("Failed to commit transaction for sign-in: {e}");
|
||||||
|
Error::DatabaseError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_sign_in(
|
||||||
|
req: HttpRequest,
|
||||||
|
payload: Json<SignUpInput>,
|
||||||
|
db: &mut DatabaseTransaction,
|
||||||
|
config: Data<ApplicationConfig>,
|
||||||
|
event_bus: Data<crate::EventBusClient>,
|
||||||
|
session: Data<SessionStorage>,
|
||||||
|
) -> Result<HttpResponse, JsonError> {
|
||||||
|
if payload.email.trim().is_empty() || payload.password.trim().is_empty() {
|
||||||
|
return Err(JsonError::new("Both email and password are required"));
|
||||||
|
}
|
||||||
|
let email = payload.email.trim().to_lowercase();
|
||||||
|
|
||||||
|
#[derive(Validator)]
|
||||||
|
#[validator(email(
|
||||||
|
comment(Allow),
|
||||||
|
ip(Allow),
|
||||||
|
local(Allow),
|
||||||
|
at_least_two_labels(Allow),
|
||||||
|
non_ascii(Allow)
|
||||||
|
))]
|
||||||
|
pub struct EmailAllowComment {
|
||||||
|
pub local_part: String,
|
||||||
|
pub need_quoted: bool,
|
||||||
|
pub domain_part: Host,
|
||||||
|
pub comment_before_local_part: Option<String>,
|
||||||
|
pub comment_after_local_part: Option<String>,
|
||||||
|
pub comment_before_domain_part: Option<String>,
|
||||||
|
pub comment_after_domain_part: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let password = payload.password.clone();
|
||||||
|
if let Err(e) = EmailAllowComment::validate_str(&email) {
|
||||||
|
tracing::error!("Invalid email address: {e}");
|
||||||
|
return Err(JsonError::new("Please provide a valid email address."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (user, was_created) = match Users::find()
|
||||||
|
.filter(entities::users::Column::Email.eq(&email))
|
||||||
|
.one(&mut *db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Some(user)) => (user, false),
|
||||||
|
Ok(None) if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? => {
|
||||||
|
return Err(JsonError::new(
|
||||||
|
"New account creation is disabled. Please contact your site administrator",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(None) => (create_user(&req, &email, &password, &mut *db).await?, true),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to load user for sign-in: {e}");
|
||||||
|
return Ok(Error::DatabaseError.error_response());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = user.id;
|
||||||
|
let mut user: UserModel = user.into();
|
||||||
|
|
||||||
|
let (ip, user_agent, _current_site) = crate::utils::extract_req_info(&req)?;
|
||||||
|
|
||||||
|
user.is_active = Set(true);
|
||||||
|
user.last_active = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||||
|
user.last_login_time = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||||
|
user.last_login_ip = Set(ip.clone());
|
||||||
|
user.last_login_uagent = Set(user_agent.clone());
|
||||||
|
user.token_updated_at = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||||
|
let user = Users::update(user).exec(&mut *db).await.map_err(|e| {
|
||||||
|
tracing::error!("Failed to update account for {email:?}: {e}");
|
||||||
|
Error::DatabaseError
|
||||||
|
})?;
|
||||||
|
crate::utils::invites_to_membership(user_id, &email, None, &mut *db).await?;
|
||||||
|
|
||||||
|
if let Err(e) = event_bus
|
||||||
|
.publish(
|
||||||
|
Topic::User,
|
||||||
|
Msg::User(UserMsg::SignIn {
|
||||||
|
user_id: UserId::new(user_id),
|
||||||
|
email,
|
||||||
|
user_agent,
|
||||||
|
ip,
|
||||||
|
medium: SignInMedium::Email,
|
||||||
|
first_time: false,
|
||||||
|
}),
|
||||||
|
QoS::AtLeastOnce,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::warn!("Failed to publish sign-in msg after sign in: {e}");
|
||||||
|
};
|
||||||
|
|
||||||
|
auth_http_response(user, session, StatusCode::OK)
|
||||||
|
.await
|
||||||
|
.map_err(JsonError::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct SignUpInput {
|
||||||
|
email: String,
|
||||||
|
password: String,
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use actix_web::{HttpRequest, HttpResponse};
|
use actix_web::HttpRequest;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use entities::project_member_invites::{
|
use entities::project_member_invites::{
|
||||||
Column as ProjectMemberInviteColumn, Model as ProjectMemberInvite,
|
Column as ProjectMemberInviteColumn, Model as ProjectMemberInvite,
|
||||||
@ -21,7 +21,6 @@ use tracing::error;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::http::OAuthError;
|
use crate::http::OAuthError;
|
||||||
use crate::models::JsonError;
|
|
||||||
use crate::{http::AuthError, models::Error};
|
use crate::{http::AuthError, models::Error};
|
||||||
|
|
||||||
pub fn extract_req_info(req: &HttpRequest) -> Result<(String, String, String), Error> {
|
pub fn extract_req_info(req: &HttpRequest) -> Result<(String, String, String), Error> {
|
||||||
|
Loading…
Reference in New Issue
Block a user