mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
refactor(users): Separate signup and signin (#2921)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -6,11 +6,12 @@ use crate::user::{
|
|||||||
dashboard_metadata::{
|
dashboard_metadata::{
|
||||||
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
|
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
|
||||||
},
|
},
|
||||||
ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse,
|
AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
|
||||||
CreateInternalUserRequest, GetUsersResponse, SwitchMerchantIdRequest, UserMerchantCreate,
|
DashboardEntryResponse, GetUsersResponse, SignUpRequest, SignUpWithMerchantIdRequest,
|
||||||
|
SwitchMerchantIdRequest, UserMerchantCreate,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ApiEventMetric for ConnectAccountResponse {
|
impl ApiEventMetric for DashboardEntryResponse {
|
||||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
Some(ApiEventsType::User {
|
Some(ApiEventsType::User {
|
||||||
merchant_id: self.merchant_id.clone(),
|
merchant_id: self.merchant_id.clone(),
|
||||||
@ -19,9 +20,9 @@ impl ApiEventMetric for ConnectAccountResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiEventMetric for ConnectAccountRequest {}
|
|
||||||
|
|
||||||
common_utils::impl_misc_api_event_type!(
|
common_utils::impl_misc_api_event_type!(
|
||||||
|
SignUpRequest,
|
||||||
|
SignUpWithMerchantIdRequest,
|
||||||
ChangePasswordRequest,
|
ChangePasswordRequest,
|
||||||
GetMultipleMetaDataPayload,
|
GetMultipleMetaDataPayload,
|
||||||
GetMetaDataResponse,
|
GetMetaDataResponse,
|
||||||
@ -30,7 +31,9 @@ common_utils::impl_misc_api_event_type!(
|
|||||||
SwitchMerchantIdRequest,
|
SwitchMerchantIdRequest,
|
||||||
CreateInternalUserRequest,
|
CreateInternalUserRequest,
|
||||||
UserMerchantCreate,
|
UserMerchantCreate,
|
||||||
GetUsersResponse
|
GetUsersResponse,
|
||||||
|
AuthorizeResponse,
|
||||||
|
ConnectAccountRequest
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "dummy_connector")]
|
#[cfg(feature = "dummy_connector")]
|
||||||
|
|||||||
@ -7,13 +7,25 @@ pub mod dashboard_metadata;
|
|||||||
pub mod sample_data;
|
pub mod sample_data;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
|
#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
|
||||||
pub struct ConnectAccountRequest {
|
pub struct SignUpWithMerchantIdRequest {
|
||||||
|
pub name: Secret<String>,
|
||||||
|
pub email: pii::Email,
|
||||||
|
pub password: Secret<String>,
|
||||||
|
pub company_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SignUpWithMerchantIdResponse = AuthorizeResponse;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
|
||||||
|
pub struct SignUpRequest {
|
||||||
pub email: pii::Email,
|
pub email: pii::Email,
|
||||||
pub password: Secret<String>,
|
pub password: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SignUpResponse = DashboardEntryResponse;
|
||||||
|
|
||||||
#[derive(serde::Serialize, Debug, Clone)]
|
#[derive(serde::Serialize, Debug, Clone)]
|
||||||
pub struct ConnectAccountResponse {
|
pub struct DashboardEntryResponse {
|
||||||
pub token: Secret<String>,
|
pub token: Secret<String>,
|
||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
pub name: Secret<String>,
|
pub name: Secret<String>,
|
||||||
@ -25,6 +37,28 @@ pub struct ConnectAccountResponse {
|
|||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SignInRequest = SignUpRequest;
|
||||||
|
|
||||||
|
pub type SignInResponse = DashboardEntryResponse;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
|
||||||
|
pub struct ConnectAccountRequest {
|
||||||
|
pub email: pii::Email,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ConnectAccountResponse = AuthorizeResponse;
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Debug, Clone)]
|
||||||
|
pub struct AuthorizeResponse {
|
||||||
|
pub is_email_sent: bool,
|
||||||
|
//this field is added for audit/debug reasons
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub user_id: String,
|
||||||
|
//this field is added for audit/debug reasons
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub merchant_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
||||||
pub struct ChangePasswordRequest {
|
pub struct ChangePasswordRequest {
|
||||||
pub new_password: Secret<String>,
|
pub new_password: Secret<String>,
|
||||||
@ -36,6 +70,8 @@ pub struct SwitchMerchantIdRequest {
|
|||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SwitchMerchantResponse = DashboardEntryResponse;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
||||||
pub struct CreateInternalUserRequest {
|
pub struct CreateInternalUserRequest {
|
||||||
pub name: Secret<String>,
|
pub name: Secret<String>,
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
use api_models::user as user_api;
|
use api_models::user as user_api;
|
||||||
use diesel_models::{enums::UserStatus, user as storage_user};
|
use diesel_models::{enums::UserStatus, user as storage_user};
|
||||||
use error_stack::{IntoReport, ResultExt};
|
#[cfg(feature = "email")]
|
||||||
use masking::{ExposeInterface, Secret};
|
use error_stack::IntoReport;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use masking::ExposeInterface;
|
||||||
|
#[cfg(feature = "email")]
|
||||||
use router_env::env;
|
use router_env::env;
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
use router_env::logger;
|
||||||
|
|
||||||
use super::errors::{UserErrors, UserResponse};
|
use super::errors::{UserErrors, UserResponse};
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
use crate::services::email::types as email_types;
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
db::user::UserInterface,
|
db::user::UserInterface,
|
||||||
@ -13,11 +20,112 @@ use crate::{
|
|||||||
types::domain,
|
types::domain,
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
pub mod dashboard_metadata;
|
||||||
#[cfg(feature = "dummy_connector")]
|
#[cfg(feature = "dummy_connector")]
|
||||||
pub mod sample_data;
|
pub mod sample_data;
|
||||||
|
|
||||||
pub mod dashboard_metadata;
|
#[cfg(feature = "email")]
|
||||||
|
pub async fn signup_with_merchant_id(
|
||||||
|
state: AppState,
|
||||||
|
request: user_api::SignUpWithMerchantIdRequest,
|
||||||
|
) -> UserResponse<user_api::SignUpWithMerchantIdResponse> {
|
||||||
|
let new_user = domain::NewUser::try_from(request.clone())?;
|
||||||
|
new_user
|
||||||
|
.get_new_merchant()
|
||||||
|
.get_new_organization()
|
||||||
|
.insert_org_in_db(state.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let user_from_db = new_user
|
||||||
|
.insert_user_and_merchant_in_db(state.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let user_role = new_user
|
||||||
|
.insert_user_role_in_db(
|
||||||
|
state.clone(),
|
||||||
|
consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||||
|
UserStatus::Active,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let email_contents = email_types::ResetPassword {
|
||||||
|
recipient_email: user_from_db.get_email().try_into()?,
|
||||||
|
user_name: domain::UserName::new(user_from_db.get_name())?,
|
||||||
|
settings: state.conf.clone(),
|
||||||
|
subject: "Get back to Hyperswitch - Reset Your Password Now",
|
||||||
|
};
|
||||||
|
|
||||||
|
let send_email_result = state
|
||||||
|
.email_client
|
||||||
|
.compose_and_send_email(
|
||||||
|
Box::new(email_contents),
|
||||||
|
state.conf.proxy.https_url.as_ref(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
logger::info!(?send_email_result);
|
||||||
|
Ok(ApplicationResponse::Json(user_api::AuthorizeResponse {
|
||||||
|
is_email_sent: send_email_result.is_ok(),
|
||||||
|
user_id: user_from_db.get_user_id().to_string(),
|
||||||
|
merchant_id: user_role.merchant_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signup(
|
||||||
|
state: AppState,
|
||||||
|
request: user_api::SignUpRequest,
|
||||||
|
) -> UserResponse<user_api::SignUpResponse> {
|
||||||
|
let new_user = domain::NewUser::try_from(request)?;
|
||||||
|
new_user
|
||||||
|
.get_new_merchant()
|
||||||
|
.get_new_organization()
|
||||||
|
.insert_org_in_db(state.clone())
|
||||||
|
.await?;
|
||||||
|
let user_from_db = new_user
|
||||||
|
.insert_user_and_merchant_in_db(state.clone())
|
||||||
|
.await?;
|
||||||
|
let user_role = new_user
|
||||||
|
.insert_user_role_in_db(
|
||||||
|
state.clone(),
|
||||||
|
consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||||
|
UserStatus::Active,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let token = utils::user::generate_jwt_auth_token(state, &user_from_db, &user_role).await?;
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::Json(
|
||||||
|
utils::user::get_dashboard_entry_response(user_from_db, user_role, token),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signin(
|
||||||
|
state: AppState,
|
||||||
|
request: user_api::SignInRequest,
|
||||||
|
) -> UserResponse<user_api::SignInResponse> {
|
||||||
|
let user_from_db: domain::UserFromStorage = state
|
||||||
|
.store
|
||||||
|
.find_user_by_email(request.email.clone().expose().expose().as_str())
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
if e.current_context().is_db_not_found() {
|
||||||
|
e.change_context(UserErrors::InvalidCredentials)
|
||||||
|
} else {
|
||||||
|
e.change_context(UserErrors::InternalServerError)
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
user_from_db.compare_password(request.password)?;
|
||||||
|
|
||||||
|
let user_role = user_from_db.get_role_from_db(state.clone()).await?;
|
||||||
|
let token = utils::user::generate_jwt_auth_token(state, &user_from_db, &user_role).await?;
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::Json(
|
||||||
|
utils::user::get_dashboard_entry_response(user_from_db, user_role, token),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "email")]
|
||||||
pub async fn connect_account(
|
pub async fn connect_account(
|
||||||
state: AppState,
|
state: AppState,
|
||||||
request: user_api::ConnectAccountRequest,
|
request: user_api::ConnectAccountRequest,
|
||||||
@ -29,26 +137,34 @@ pub async fn connect_account(
|
|||||||
|
|
||||||
if let Ok(found_user) = find_user {
|
if let Ok(found_user) = find_user {
|
||||||
let user_from_db: domain::UserFromStorage = found_user.into();
|
let user_from_db: domain::UserFromStorage = found_user.into();
|
||||||
|
|
||||||
user_from_db.compare_password(request.password)?;
|
|
||||||
|
|
||||||
let user_role = user_from_db.get_role_from_db(state.clone()).await?;
|
let user_role = user_from_db.get_role_from_db(state.clone()).await?;
|
||||||
let jwt_token = user_from_db
|
|
||||||
.get_jwt_auth_token(state.clone(), user_role.org_id)
|
let email_contents = email_types::MagicLink {
|
||||||
.await?;
|
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
||||||
|
settings: state.conf.clone(),
|
||||||
|
user_name: domain::UserName::new(user_from_db.get_name())?,
|
||||||
|
subject: "Unlock Hyperswitch: Use Your Magic Link to Sign In",
|
||||||
|
};
|
||||||
|
|
||||||
|
let send_email_result = state
|
||||||
|
.email_client
|
||||||
|
.compose_and_send_email(
|
||||||
|
Box::new(email_contents),
|
||||||
|
state.conf.proxy.https_url.as_ref(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
logger::info!(?send_email_result);
|
||||||
|
|
||||||
return Ok(ApplicationResponse::Json(
|
return Ok(ApplicationResponse::Json(
|
||||||
user_api::ConnectAccountResponse {
|
user_api::ConnectAccountResponse {
|
||||||
token: Secret::new(jwt_token),
|
is_email_sent: send_email_result.is_ok(),
|
||||||
merchant_id: user_role.merchant_id,
|
|
||||||
name: user_from_db.get_name(),
|
|
||||||
email: user_from_db.get_email(),
|
|
||||||
verification_days_left: None,
|
|
||||||
user_role: user_role.role_id,
|
|
||||||
user_id: user_from_db.get_user_id().to_string(),
|
user_id: user_from_db.get_user_id().to_string(),
|
||||||
|
merchant_id: user_role.merchant_id,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else if find_user
|
} else if find_user
|
||||||
|
.as_ref()
|
||||||
.map_err(|e| e.current_context().is_db_not_found())
|
.map_err(|e| e.current_context().is_db_not_found())
|
||||||
.err()
|
.err()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
@ -73,46 +189,35 @@ pub async fn connect_account(
|
|||||||
UserStatus::Active,
|
UserStatus::Active,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let jwt_token = user_from_db
|
|
||||||
.get_jwt_auth_token(state.clone(), user_role.org_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
#[cfg(feature = "email")]
|
let email_contents = email_types::VerifyEmail {
|
||||||
{
|
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
||||||
use router_env::logger;
|
settings: state.conf.clone(),
|
||||||
|
subject: "Welcome to the Hyperswitch community!",
|
||||||
|
};
|
||||||
|
|
||||||
use crate::services::email::types as email_types;
|
let send_email_result = state
|
||||||
|
.email_client
|
||||||
|
.compose_and_send_email(
|
||||||
|
Box::new(email_contents),
|
||||||
|
state.conf.proxy.https_url.as_ref(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let email_contents = email_types::VerifyEmail {
|
logger::info!(?send_email_result);
|
||||||
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
|
||||||
settings: state.conf.clone(),
|
|
||||||
subject: "Welcome to the Hyperswitch community!",
|
|
||||||
};
|
|
||||||
|
|
||||||
let send_email_result = state
|
|
||||||
.email_client
|
|
||||||
.compose_and_send_email(
|
|
||||||
Box::new(email_contents),
|
|
||||||
state.conf.proxy.https_url.as_ref(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
logger::info!(?send_email_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(ApplicationResponse::Json(
|
return Ok(ApplicationResponse::Json(
|
||||||
user_api::ConnectAccountResponse {
|
user_api::ConnectAccountResponse {
|
||||||
token: Secret::new(jwt_token),
|
is_email_sent: send_email_result.is_ok(),
|
||||||
merchant_id: user_role.merchant_id,
|
|
||||||
name: user_from_db.get_name(),
|
|
||||||
email: user_from_db.get_email(),
|
|
||||||
verification_days_left: None,
|
|
||||||
user_role: user_role.role_id,
|
|
||||||
user_id: user_from_db.get_user_id().to_string(),
|
user_id: user_from_db.get_user_id().to_string(),
|
||||||
|
merchant_id: user_role.merchant_id,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
Err(UserErrors::InternalServerError.into())
|
Err(find_user
|
||||||
|
.err()
|
||||||
|
.map(|e| e.change_context(UserErrors::InternalServerError))
|
||||||
|
.unwrap_or(UserErrors::InternalServerError.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +320,7 @@ pub async fn switch_merchant_id(
|
|||||||
state: AppState,
|
state: AppState,
|
||||||
request: user_api::SwitchMerchantIdRequest,
|
request: user_api::SwitchMerchantIdRequest,
|
||||||
user_from_token: auth::UserFromToken,
|
user_from_token: auth::UserFromToken,
|
||||||
) -> UserResponse<user_api::ConnectAccountResponse> {
|
) -> UserResponse<user_api::SwitchMerchantResponse> {
|
||||||
if !utils::user_role::is_internal_role(&user_from_token.role_id) {
|
if !utils::user_role::is_internal_role(&user_from_token.role_id) {
|
||||||
let merchant_list =
|
let merchant_list =
|
||||||
utils::user_role::get_merchant_ids_for_user(state.clone(), &user_from_token.user_id)
|
utils::user_role::get_merchant_ids_for_user(state.clone(), &user_from_token.user_id)
|
||||||
@ -252,7 +357,7 @@ pub async fn switch_merchant_id(
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let org_id = state
|
let _org_id = state
|
||||||
.store
|
.store
|
||||||
.find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store)
|
.find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store)
|
||||||
.await
|
.await
|
||||||
@ -272,23 +377,23 @@ pub async fn switch_merchant_id(
|
|||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
let token = Box::pin(user.get_jwt_auth_token_with_custom_merchant_id(
|
let token = utils::user::generate_jwt_auth_token_with_custom_merchant_id(
|
||||||
state.clone(),
|
state,
|
||||||
|
&user,
|
||||||
|
&user_role,
|
||||||
request.merchant_id.clone(),
|
request.merchant_id.clone(),
|
||||||
org_id,
|
)
|
||||||
))
|
.await?;
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
Ok(ApplicationResponse::Json(
|
||||||
user_api::ConnectAccountResponse {
|
user_api::SwitchMerchantResponse {
|
||||||
merchant_id: request.merchant_id,
|
|
||||||
token,
|
token,
|
||||||
name: user.get_name(),
|
name: user.get_name(),
|
||||||
email: user.get_email(),
|
email: user.get_email(),
|
||||||
user_id: user.get_user_id().to_string(),
|
user_id: user.get_user_id().to_string(),
|
||||||
verification_days_left: None,
|
verification_days_left: None,
|
||||||
user_role: user_role.role_id,
|
user_role: user_role.role_id,
|
||||||
|
merchant_id: user_role.merchant_id,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -823,10 +823,7 @@ impl User {
|
|||||||
let mut route = web::scope("/user").app_data(web::Data::new(state));
|
let mut route = web::scope("/user").app_data(web::Data::new(state));
|
||||||
|
|
||||||
route = route
|
route = route
|
||||||
.service(web::resource("/signin").route(web::post().to(user_connect_account)))
|
.service(web::resource("/signin").route(web::post().to(user_signin)))
|
||||||
.service(web::resource("/signup").route(web::post().to(user_connect_account)))
|
|
||||||
.service(web::resource("/v2/signin").route(web::post().to(user_connect_account)))
|
|
||||||
.service(web::resource("/v2/signup").route(web::post().to(user_connect_account)))
|
|
||||||
.service(web::resource("/change_password").route(web::post().to(change_password)))
|
.service(web::resource("/change_password").route(web::post().to(change_password)))
|
||||||
.service(
|
.service(
|
||||||
web::resource("/data/merchant")
|
web::resource("/data/merchant")
|
||||||
@ -841,7 +838,6 @@ impl User {
|
|||||||
)
|
)
|
||||||
.service(web::resource("/switch/list").route(web::get().to(list_merchant_ids_for_user)))
|
.service(web::resource("/switch/list").route(web::get().to(list_merchant_ids_for_user)))
|
||||||
.service(web::resource("/user/list").route(web::get().to(get_user_details)))
|
.service(web::resource("/user/list").route(web::get().to(get_user_details)))
|
||||||
// User Role APIs
|
|
||||||
.service(web::resource("/permission_info").route(web::get().to(get_authorization_info)))
|
.service(web::resource("/permission_info").route(web::get().to(get_authorization_info)))
|
||||||
.service(web::resource("/user/update_role").route(web::post().to(update_user_role)))
|
.service(web::resource("/user/update_role").route(web::post().to(update_user_role)))
|
||||||
.service(web::resource("/role/list").route(web::get().to(list_roles)))
|
.service(web::resource("/role/list").route(web::get().to(list_roles)))
|
||||||
@ -855,6 +851,21 @@ impl User {
|
|||||||
.route(web::delete().to(delete_sample_data)),
|
.route(web::delete().to(delete_sample_data)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
{
|
||||||
|
route = route
|
||||||
|
.service(
|
||||||
|
web::resource("/connect_account").route(web::post().to(user_connect_account)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/signup_with_merchant_id")
|
||||||
|
.route(web::post().to(user_signup_with_merchant_id)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "email"))]
|
||||||
|
{
|
||||||
|
route = route.service(web::resource("/signup").route(web::post().to(user_signup)))
|
||||||
|
}
|
||||||
route
|
route
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,6 +149,8 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::GsmRuleDelete => Self::Gsm,
|
| Flow::GsmRuleDelete => Self::Gsm,
|
||||||
|
|
||||||
Flow::UserConnectAccount
|
Flow::UserConnectAccount
|
||||||
|
| Flow::UserSignUp
|
||||||
|
| Flow::UserSignIn
|
||||||
| Flow::ChangePassword
|
| Flow::ChangePassword
|
||||||
| Flow::SetDashboardMetadata
|
| Flow::SetDashboardMetadata
|
||||||
| Flow::GetMutltipleDashboardMetadata
|
| Flow::GetMutltipleDashboardMetadata
|
||||||
@ -159,7 +161,8 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::GenerateSampleData
|
| Flow::GenerateSampleData
|
||||||
| Flow::DeleteSampleData
|
| Flow::DeleteSampleData
|
||||||
| Flow::UserMerchantAccountList
|
| Flow::UserMerchantAccountList
|
||||||
| Flow::GetUserDetails => Self::User,
|
| Flow::GetUserDetails
|
||||||
|
| Flow::UserSignUpWithMerchantId => Self::User,
|
||||||
|
|
||||||
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
||||||
Self::UserRole
|
Self::UserRole
|
||||||
|
|||||||
@ -19,6 +19,65 @@ use crate::{
|
|||||||
utils::user::dashboard_metadata::{parse_string_to_enums, set_ip_address_if_required},
|
utils::user::dashboard_metadata::{parse_string_to_enums, set_ip_address_if_required},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
pub async fn user_signup_with_merchant_id(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
http_req: HttpRequest,
|
||||||
|
json_payload: web::Json<user_api::SignUpWithMerchantIdRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::UserSignUpWithMerchantId;
|
||||||
|
let req_payload = json_payload.into_inner();
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow.clone(),
|
||||||
|
state,
|
||||||
|
&http_req,
|
||||||
|
req_payload.clone(),
|
||||||
|
|state, _, req_body| user_core::signup_with_merchant_id(state, req_body),
|
||||||
|
&auth::NoAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn user_signup(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
http_req: HttpRequest,
|
||||||
|
json_payload: web::Json<user_api::SignUpRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::UserSignUp;
|
||||||
|
let req_payload = json_payload.into_inner();
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow.clone(),
|
||||||
|
state,
|
||||||
|
&http_req,
|
||||||
|
req_payload.clone(),
|
||||||
|
|state, _, req_body| user_core::signup(state, req_body),
|
||||||
|
&auth::NoAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn user_signin(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
http_req: HttpRequest,
|
||||||
|
json_payload: web::Json<user_api::SignInRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::UserSignIn;
|
||||||
|
let req_payload = json_payload.into_inner();
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow.clone(),
|
||||||
|
state,
|
||||||
|
&http_req,
|
||||||
|
req_payload.clone(),
|
||||||
|
|state, _, req_body| user_core::signin(state, req_body),
|
||||||
|
&auth::NoAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "email")]
|
||||||
pub async fn user_connect_account(
|
pub async fn user_connect_account(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
http_req: HttpRequest,
|
http_req: HttpRequest,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ use crate::{
|
|||||||
db::StorageInterface,
|
db::StorageInterface,
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services::{
|
services::{
|
||||||
authentication::{AuthToken, UserFromToken},
|
authentication::UserFromToken,
|
||||||
authorization::{info, predefined_permissions},
|
authorization::{info, predefined_permissions},
|
||||||
},
|
},
|
||||||
types::transformers::ForeignFrom,
|
types::transformers::ForeignFrom,
|
||||||
@ -215,6 +215,25 @@ impl NewUserOrganization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<user_api::SignUpWithMerchantIdRequest> for NewUserOrganization {
|
||||||
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult<Self> {
|
||||||
|
let new_organization = api_org::OrganizationNew::new(Some(
|
||||||
|
UserCompanyName::new(value.company_name)?.get_secret(),
|
||||||
|
));
|
||||||
|
let db_organization = ForeignFrom::foreign_from(new_organization);
|
||||||
|
Ok(Self(db_organization))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<user_api::SignUpRequest> for NewUserOrganization {
|
||||||
|
fn from(_value: user_api::SignUpRequest) -> Self {
|
||||||
|
let new_organization = api_org::OrganizationNew::new(None);
|
||||||
|
let db_organization = ForeignFrom::foreign_from(new_organization);
|
||||||
|
Self(db_organization)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<user_api::ConnectAccountRequest> for NewUserOrganization {
|
impl From<user_api::ConnectAccountRequest> for NewUserOrganization {
|
||||||
fn from(_value: user_api::ConnectAccountRequest) -> Self {
|
fn from(_value: user_api::ConnectAccountRequest) -> Self {
|
||||||
let new_organization = api_org::OrganizationNew::new(None);
|
let new_organization = api_org::OrganizationNew::new(None);
|
||||||
@ -334,6 +353,24 @@ impl NewUserMerchant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<user_api::SignUpRequest> for NewUserMerchant {
|
||||||
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
|
fn try_from(value: user_api::SignUpRequest) -> UserResult<Self> {
|
||||||
|
let merchant_id = MerchantId::new(format!(
|
||||||
|
"merchant_{}",
|
||||||
|
common_utils::date_time::now_unix_timestamp()
|
||||||
|
))?;
|
||||||
|
let new_organization = NewUserOrganization::from(value);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
company_name: None,
|
||||||
|
merchant_id,
|
||||||
|
new_organization,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<user_api::ConnectAccountRequest> for NewUserMerchant {
|
impl TryFrom<user_api::ConnectAccountRequest> for NewUserMerchant {
|
||||||
type Error = error_stack::Report<UserErrors>;
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
@ -352,6 +389,21 @@ impl TryFrom<user_api::ConnectAccountRequest> for NewUserMerchant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<user_api::SignUpWithMerchantIdRequest> for NewUserMerchant {
|
||||||
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult<Self> {
|
||||||
|
let company_name = Some(UserCompanyName::new(value.company_name.clone())?);
|
||||||
|
let merchant_id = MerchantId::new(value.company_name.clone())?;
|
||||||
|
let new_organization = NewUserOrganization::try_from(value)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
company_name,
|
||||||
|
merchant_id,
|
||||||
|
new_organization,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<user_api::CreateInternalUserRequest> for NewUserMerchant {
|
impl TryFrom<user_api::CreateInternalUserRequest> for NewUserMerchant {
|
||||||
type Error = error_stack::Report<UserErrors>;
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
@ -434,10 +486,23 @@ impl NewUser {
|
|||||||
.attach_printable("Error while inserting user")
|
.attach_printable("Error while inserting user")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn check_if_already_exists_in_db(&self, state: AppState) -> UserResult<()> {
|
||||||
|
if state
|
||||||
|
.store
|
||||||
|
.find_user_by_email(self.get_email().into_inner().expose().expose().as_str())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return Err(UserErrors::UserExists).into_report();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_user_and_merchant_in_db(
|
pub async fn insert_user_and_merchant_in_db(
|
||||||
&self,
|
&self,
|
||||||
state: AppState,
|
state: AppState,
|
||||||
) -> UserResult<UserFromStorage> {
|
) -> UserResult<UserFromStorage> {
|
||||||
|
self.check_if_already_exists_in_db(state.clone()).await?;
|
||||||
let db = state.store.as_ref();
|
let db = state.store.as_ref();
|
||||||
let merchant_id = self.get_new_merchant().get_merchant_id();
|
let merchant_id = self.get_new_merchant().get_merchant_id();
|
||||||
self.new_merchant
|
self.new_merchant
|
||||||
@ -495,6 +560,46 @@ impl TryFrom<NewUser> for storage_user::UserNew {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<user_api::SignUpWithMerchantIdRequest> for NewUser {
|
||||||
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
|
fn try_from(value: user_api::SignUpWithMerchantIdRequest) -> UserResult<Self> {
|
||||||
|
let email = value.email.clone().try_into()?;
|
||||||
|
let name = UserName::new(value.name.clone())?;
|
||||||
|
let password = UserPassword::new(value.password.clone())?;
|
||||||
|
let user_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let new_merchant = NewUserMerchant::try_from(value)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
user_id,
|
||||||
|
new_merchant,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<user_api::SignUpRequest> for NewUser {
|
||||||
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
|
fn try_from(value: user_api::SignUpRequest) -> UserResult<Self> {
|
||||||
|
let user_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
let email = value.email.clone().try_into()?;
|
||||||
|
let name = UserName::try_from(value.email.clone())?;
|
||||||
|
let password = UserPassword::new(value.password.clone())?;
|
||||||
|
let new_merchant = NewUserMerchant::try_from(value)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
user_id,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
new_merchant,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<user_api::ConnectAccountRequest> for NewUser {
|
impl TryFrom<user_api::ConnectAccountRequest> for NewUser {
|
||||||
type Error = error_stack::Report<UserErrors>;
|
type Error = error_stack::Report<UserErrors>;
|
||||||
|
|
||||||
@ -502,7 +607,7 @@ impl TryFrom<user_api::ConnectAccountRequest> for NewUser {
|
|||||||
let user_id = uuid::Uuid::new_v4().to_string();
|
let user_id = uuid::Uuid::new_v4().to_string();
|
||||||
let email = value.email.clone().try_into()?;
|
let email = value.email.clone().try_into()?;
|
||||||
let name = UserName::try_from(value.email.clone())?;
|
let name = UserName::try_from(value.email.clone())?;
|
||||||
let password = UserPassword::new(value.password.clone())?;
|
let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?;
|
||||||
let new_merchant = NewUserMerchant::try_from(value)?;
|
let new_merchant = NewUserMerchant::try_from(value)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -582,41 +687,6 @@ impl UserFromStorage {
|
|||||||
self.0.email.clone()
|
self.0.email.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_jwt_auth_token(&self, state: AppState, org_id: String) -> UserResult<String> {
|
|
||||||
let role_id = self.get_role_from_db(state.clone()).await?.role_id;
|
|
||||||
let merchant_id = state
|
|
||||||
.store
|
|
||||||
.find_user_role_by_user_id(self.get_user_id())
|
|
||||||
.await
|
|
||||||
.change_context(UserErrors::InternalServerError)?
|
|
||||||
.merchant_id;
|
|
||||||
AuthToken::new_token(
|
|
||||||
self.0.user_id.clone(),
|
|
||||||
merchant_id,
|
|
||||||
role_id,
|
|
||||||
&state.conf,
|
|
||||||
org_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_jwt_auth_token_with_custom_merchant_id(
|
|
||||||
&self,
|
|
||||||
state: AppState,
|
|
||||||
merchant_id: String,
|
|
||||||
org_id: String,
|
|
||||||
) -> UserResult<String> {
|
|
||||||
let role_id = self.get_role_from_db(state.clone()).await?.role_id;
|
|
||||||
AuthToken::new_token(
|
|
||||||
self.0.user_id.clone(),
|
|
||||||
merchant_id,
|
|
||||||
role_id,
|
|
||||||
&state.conf,
|
|
||||||
org_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_role_from_db(&self, state: AppState) -> UserResult<UserRole> {
|
pub async fn get_role_from_db(&self, state: AppState) -> UserResult<UserRole> {
|
||||||
state
|
state
|
||||||
.store
|
.store
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
use diesel_models::enums::UserStatus;
|
use api_models::user as user_api;
|
||||||
|
use diesel_models::{enums::UserStatus, user_role::UserRole};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
|
use masking::Secret;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{UserErrors, UserResult},
|
core::errors::{UserErrors, UserResult},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services::authentication::UserFromToken,
|
services::authentication::{AuthToken, UserFromToken},
|
||||||
types::domain::MerchantAccount,
|
types::domain::{MerchantAccount, UserFromStorage},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod dashboard_metadata;
|
pub mod dashboard_metadata;
|
||||||
@ -68,3 +70,52 @@ pub async fn get_merchant_ids_for_user(state: AppState, user_id: &str) -> UserRe
|
|||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn generate_jwt_auth_token(
|
||||||
|
state: AppState,
|
||||||
|
user: &UserFromStorage,
|
||||||
|
user_role: &UserRole,
|
||||||
|
) -> UserResult<Secret<String>> {
|
||||||
|
let token = AuthToken::new_token(
|
||||||
|
user.get_user_id().to_string(),
|
||||||
|
user_role.merchant_id.clone(),
|
||||||
|
user_role.role_id.clone(),
|
||||||
|
&state.conf,
|
||||||
|
user_role.org_id.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(Secret::new(token))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_jwt_auth_token_with_custom_merchant_id(
|
||||||
|
state: AppState,
|
||||||
|
user: &UserFromStorage,
|
||||||
|
user_role: &UserRole,
|
||||||
|
merchant_id: String,
|
||||||
|
) -> UserResult<Secret<String>> {
|
||||||
|
let token = AuthToken::new_token(
|
||||||
|
user.get_user_id().to_string(),
|
||||||
|
merchant_id,
|
||||||
|
user_role.role_id.clone(),
|
||||||
|
&state.conf,
|
||||||
|
user_role.org_id.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(Secret::new(token))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dashboard_entry_response(
|
||||||
|
user: UserFromStorage,
|
||||||
|
user_role: UserRole,
|
||||||
|
token: Secret<String>,
|
||||||
|
) -> user_api::DashboardEntryResponse {
|
||||||
|
user_api::DashboardEntryResponse {
|
||||||
|
merchant_id: user_role.merchant_id,
|
||||||
|
token,
|
||||||
|
name: user.get_name(),
|
||||||
|
email: user.get_email(),
|
||||||
|
user_id: user.get_user_id().to_string(),
|
||||||
|
verification_days_left: None,
|
||||||
|
user_role: user_role.role_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -249,6 +249,12 @@ pub enum Flow {
|
|||||||
GsmRuleUpdate,
|
GsmRuleUpdate,
|
||||||
/// Gsm Rule Delete flow
|
/// Gsm Rule Delete flow
|
||||||
GsmRuleDelete,
|
GsmRuleDelete,
|
||||||
|
/// User Sign Up
|
||||||
|
UserSignUp,
|
||||||
|
/// User Sign Up
|
||||||
|
UserSignUpWithMerchantId,
|
||||||
|
/// User Sign In
|
||||||
|
UserSignIn,
|
||||||
/// User connect account
|
/// User connect account
|
||||||
UserConnectAccount,
|
UserConnectAccount,
|
||||||
/// Upsert Decision Manager Config
|
/// Upsert Decision Manager Config
|
||||||
|
|||||||
Reference in New Issue
Block a user