mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(users): Signin and Verify Email changes for User Invitation changes (#3420)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -12,9 +12,9 @@ use crate::user::{
|
||||
},
|
||||
AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
|
||||
DashboardEntryResponse, ForgotPasswordRequest, GetUsersResponse, InviteUserRequest,
|
||||
InviteUserResponse, ResetPasswordRequest, SendVerifyEmailRequest, SignUpRequest,
|
||||
SignUpWithMerchantIdRequest, SwitchMerchantIdRequest, UpdateUserAccountDetailsRequest,
|
||||
UserMerchantCreate, VerifyEmailRequest,
|
||||
InviteUserResponse, ResetPasswordRequest, SendVerifyEmailRequest, SignInResponse,
|
||||
SignUpRequest, SignUpWithMerchantIdRequest, SwitchMerchantIdRequest,
|
||||
UpdateUserAccountDetailsRequest, UserMerchantCreate, VerifyEmailRequest,
|
||||
};
|
||||
|
||||
impl ApiEventMetric for DashboardEntryResponse {
|
||||
@ -56,6 +56,7 @@ common_utils::impl_misc_api_event_type!(
|
||||
InviteUserResponse,
|
||||
VerifyEmailRequest,
|
||||
SendVerifyEmailRequest,
|
||||
SignInResponse,
|
||||
UpdateUserAccountDetailsRequest
|
||||
);
|
||||
|
||||
|
||||
@ -39,7 +39,21 @@ pub struct DashboardEntryResponse {
|
||||
|
||||
pub type SignInRequest = SignUpRequest;
|
||||
|
||||
pub type SignInResponse = DashboardEntryResponse;
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(tag = "flow_type", rename_all = "snake_case")]
|
||||
pub enum SignInResponse {
|
||||
MerchantSelect(MerchantSelectResponse),
|
||||
DashboardEntry(DashboardEntryResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct MerchantSelectResponse {
|
||||
pub token: Secret<String>,
|
||||
pub name: Secret<String>,
|
||||
pub email: pii::Email,
|
||||
pub verification_days_left: Option<i64>,
|
||||
pub merchants: Vec<UserMerchantAccount>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
|
||||
pub struct ConnectAccountRequest {
|
||||
@ -138,7 +152,7 @@ pub struct VerifyEmailRequest {
|
||||
pub token: Secret<String>,
|
||||
}
|
||||
|
||||
pub type VerifyEmailResponse = DashboardEntryResponse;
|
||||
pub type VerifyEmailResponse = SignInResponse;
|
||||
|
||||
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
||||
pub struct SendVerifyEmailRequest {
|
||||
@ -149,6 +163,7 @@ pub struct SendVerifyEmailRequest {
|
||||
pub struct UserMerchantAccount {
|
||||
pub merchant_id: String,
|
||||
pub merchant_name: OptionalEncryptableName,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "recon")]
|
||||
|
||||
@ -58,6 +58,8 @@ pub enum UserErrors {
|
||||
InvalidDeleteOperation,
|
||||
#[error("MaxInvitationsError")]
|
||||
MaxInvitationsError,
|
||||
#[error("RoleNotFound")]
|
||||
RoleNotFound,
|
||||
}
|
||||
|
||||
impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for UserErrors {
|
||||
@ -146,6 +148,9 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon
|
||||
Self::MaxInvitationsError => {
|
||||
AER::BadRequest(ApiError::new(sub_code, 31, self.get_error_message(), None))
|
||||
}
|
||||
Self::RoleNotFound => {
|
||||
AER::BadRequest(ApiError::new(sub_code, 32, self.get_error_message(), None))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,6 +183,7 @@ impl UserErrors {
|
||||
Self::ChangePasswordError => "Old and new password cannot be the same",
|
||||
Self::InvalidDeleteOperation => "Delete Operation Not Supported",
|
||||
Self::MaxInvitationsError => "Maximum invite count per request exceeded",
|
||||
Self::RoleNotFound => "Role Not Found",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,10 +97,10 @@ pub async fn signup(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn signin(
|
||||
pub async fn signin_without_invite_checks(
|
||||
state: AppState,
|
||||
request: user_api::SignInRequest,
|
||||
) -> UserResponse<user_api::SignInResponse> {
|
||||
) -> UserResponse<user_api::DashboardEntryResponse> {
|
||||
let user_from_db: domain::UserFromStorage = state
|
||||
.store
|
||||
.find_user_by_email(request.email.clone().expose().expose().as_str())
|
||||
@ -124,6 +124,50 @@ pub async fn signin(
|
||||
))
|
||||
}
|
||||
|
||||
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 signin_strategy =
|
||||
if let Some(preferred_merchant_id) = user_from_db.get_preferred_merchant_id() {
|
||||
let preferred_role = user_from_db
|
||||
.get_role_from_db_by_merchant_id(&state, preferred_merchant_id.as_str())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("User role with preferred_merchant_id not found")?;
|
||||
domain::SignInWithRoleStrategyType::SingleRole(domain::SignInWithSingleRoleStrategy {
|
||||
user: user_from_db,
|
||||
user_role: preferred_role,
|
||||
})
|
||||
} else {
|
||||
let user_roles = user_from_db.get_roles_from_db(&state).await?;
|
||||
domain::SignInWithRoleStrategyType::decide_signin_strategy_by_user_roles(
|
||||
user_from_db,
|
||||
user_roles,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
signin_strategy.get_signin_response(&state).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn connect_account(
|
||||
state: AppState,
|
||||
@ -832,22 +876,22 @@ pub async fn list_merchant_ids_for_user(
|
||||
state: AppState,
|
||||
user: auth::UserFromToken,
|
||||
) -> UserResponse<Vec<user_api::UserMerchantAccount>> {
|
||||
let merchant_ids = utils::user_role::get_merchant_ids_for_user(&state, &user.user_id).await?;
|
||||
let user_roles =
|
||||
utils::user_role::get_active_user_roles_for_user(&state, &user.user_id).await?;
|
||||
|
||||
let merchant_accounts = state
|
||||
.store
|
||||
.list_multiple_merchant_accounts(merchant_ids)
|
||||
.list_multiple_merchant_accounts(
|
||||
user_roles
|
||||
.iter()
|
||||
.map(|role| role.merchant_id.clone())
|
||||
.collect(),
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
merchant_accounts
|
||||
.into_iter()
|
||||
.map(|acc| user_api::UserMerchantAccount {
|
||||
merchant_id: acc.merchant_id,
|
||||
merchant_name: acc.merchant_name,
|
||||
})
|
||||
.collect(),
|
||||
utils::user::get_multiple_merchant_details_with_status(user_roles, merchant_accounts)?,
|
||||
))
|
||||
}
|
||||
|
||||
@ -868,11 +912,38 @@ pub async fn get_users_for_merchant_account(
|
||||
Ok(ApplicationResponse::Json(user_api::GetUsersResponse(users)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn verify_email_without_invite_checks(
|
||||
state: AppState,
|
||||
req: user_api::VerifyEmailRequest,
|
||||
) -> UserResponse<user_api::DashboardEntryResponse> {
|
||||
let token = auth::decode_jwt::<email_types::EmailToken>(&req.token.clone().expose(), &state)
|
||||
.await
|
||||
.change_context(UserErrors::LinkInvalid)?;
|
||||
let user = state
|
||||
.store
|
||||
.find_user_by_email(token.get_email())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
let user = state
|
||||
.store
|
||||
.update_user_by_user_id(user.user_id.as_str(), storage_user::UserUpdate::VerifyUser)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
let user_from_db: domain::UserFromStorage = user.into();
|
||||
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(&state, user_from_db, user_role, token)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn verify_email(
|
||||
state: AppState,
|
||||
req: user_api::VerifyEmailRequest,
|
||||
) -> UserResponse<user_api::VerifyEmailResponse> {
|
||||
) -> UserResponse<user_api::SignInResponse> {
|
||||
let token = auth::decode_jwt::<email_types::EmailToken>(&req.token.clone().expose(), &state)
|
||||
.await
|
||||
.change_context(UserErrors::LinkInvalid)?;
|
||||
@ -890,11 +961,29 @@ pub async fn verify_email(
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
let user_from_db: domain::UserFromStorage = user.into();
|
||||
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?;
|
||||
|
||||
let signin_strategy =
|
||||
if let Some(preferred_merchant_id) = user_from_db.get_preferred_merchant_id() {
|
||||
let preferred_role = user_from_db
|
||||
.get_role_from_db_by_merchant_id(&state, preferred_merchant_id.as_str())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("User role with preferred_merchant_id not found")?;
|
||||
domain::SignInWithRoleStrategyType::SingleRole(domain::SignInWithSingleRoleStrategy {
|
||||
user: user_from_db,
|
||||
user_role: preferred_role,
|
||||
})
|
||||
} else {
|
||||
let user_roles = user_from_db.get_roles_from_db(&state).await?;
|
||||
domain::SignInWithRoleStrategyType::decide_signin_strategy_by_user_roles(
|
||||
user_from_db,
|
||||
user_roles,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
||||
signin_strategy.get_signin_response(&state).await?,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@ -952,7 +952,10 @@ impl User {
|
||||
let mut route = web::scope("/user").app_data(web::Data::new(state));
|
||||
|
||||
route = route
|
||||
.service(web::resource("/signin").route(web::post().to(user_signin)))
|
||||
.service(
|
||||
web::resource("/signin").route(web::post().to(user_signin_without_invite_checks)),
|
||||
)
|
||||
.service(web::resource("/v2/signin").route(web::post().to(user_signin)))
|
||||
.service(web::resource("/change_password").route(web::post().to(change_password)))
|
||||
.service(web::resource("/internal_signup").route(web::post().to(internal_user_signup)))
|
||||
.service(web::resource("/switch_merchant").route(web::post().to(switch_merchant_id)))
|
||||
@ -961,14 +964,7 @@ impl User {
|
||||
.route(web::post().to(user_merchant_account_create)),
|
||||
)
|
||||
.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("/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("/role/list").route(web::get().to(list_roles)))
|
||||
.service(web::resource("/role").route(web::get().to(get_role_from_token)))
|
||||
.service(web::resource("/role/{role_id}").route(web::get().to(get_role)))
|
||||
.service(web::resource("/user/invite").route(web::post().to(invite_user)))
|
||||
.service(web::resource("/user/invite/accept").route(web::post().to(accept_invitation)))
|
||||
.service(web::resource("/update").route(web::post().to(update_user_account_details)))
|
||||
.service(
|
||||
web::resource("/user/invite_multiple").route(web::post().to(invite_multiple_user)),
|
||||
@ -980,6 +976,23 @@ impl User {
|
||||
)
|
||||
.service(web::resource("/user/delete").route(web::delete().to(delete_user_role)));
|
||||
|
||||
// User management
|
||||
route = route.service(
|
||||
web::scope("/user")
|
||||
.service(web::resource("/list").route(web::get().to(get_user_details)))
|
||||
.service(web::resource("/invite").route(web::post().to(invite_user)))
|
||||
.service(web::resource("/invite/accept").route(web::post().to(accept_invitation)))
|
||||
.service(web::resource("/update_role").route(web::post().to(update_user_role))),
|
||||
);
|
||||
|
||||
// Role information
|
||||
route = route.service(
|
||||
web::scope("/role")
|
||||
.service(web::resource("").route(web::get().to(get_role_from_token)))
|
||||
.service(web::resource("/list").route(web::get().to(list_all_roles)))
|
||||
.service(web::resource("/{role_id}").route(web::get().to(get_role))),
|
||||
);
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
{
|
||||
route = route.service(
|
||||
@ -1000,7 +1013,11 @@ impl User {
|
||||
web::resource("/signup_with_merchant_id")
|
||||
.route(web::post().to(user_signup_with_merchant_id)),
|
||||
)
|
||||
.service(web::resource("/verify_email").route(web::post().to(verify_email)))
|
||||
.service(
|
||||
web::resource("/verify_email")
|
||||
.route(web::post().to(verify_email_without_invite_checks)),
|
||||
)
|
||||
.service(web::resource("/v2/verify_email").route(web::post().to(verify_email)))
|
||||
.service(
|
||||
web::resource("/verify_email_request")
|
||||
.route(web::post().to(verify_email_request)),
|
||||
|
||||
@ -161,6 +161,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
|
||||
Flow::UserConnectAccount
|
||||
| Flow::UserSignUp
|
||||
| Flow::UserSignInWithoutInviteChecks
|
||||
| Flow::UserSignIn
|
||||
| Flow::ChangePassword
|
||||
| Flow::SetDashboardMetadata
|
||||
@ -179,6 +180,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::InviteMultipleUser
|
||||
| Flow::DeleteUser
|
||||
| Flow::UserSignUpWithMerchantId
|
||||
| Flow::VerifyEmailWithoutInviteChecks
|
||||
| Flow::VerifyEmail
|
||||
| Flow::VerifyEmailRequest
|
||||
| Flow::UpdateUserAccountDetails => Self::User,
|
||||
|
||||
@ -58,6 +58,25 @@ pub async fn user_signup(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn user_signin_without_invite_checks(
|
||||
state: web::Data<AppState>,
|
||||
http_req: HttpRequest,
|
||||
json_payload: web::Json<user_api::SignInRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::UserSignInWithoutInviteChecks;
|
||||
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_without_invite_checks(state, req_body),
|
||||
&auth::NoAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn user_signin(
|
||||
state: web::Data<AppState>,
|
||||
http_req: HttpRequest,
|
||||
@ -368,6 +387,25 @@ pub async fn invite_multiple_user(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn verify_email_without_invite_checks(
|
||||
state: web::Data<AppState>,
|
||||
http_req: HttpRequest,
|
||||
json_payload: web::Json<user_api::VerifyEmailRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::VerifyEmailWithoutInviteChecks;
|
||||
Box::pin(api::server_wrap(
|
||||
flow.clone(),
|
||||
state,
|
||||
&http_req,
|
||||
json_payload.into_inner(),
|
||||
|state, _, req_payload| user_core::verify_email_without_invite_checks(state, req_payload),
|
||||
&auth::NoAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn verify_email(
|
||||
state: web::Data<AppState>,
|
||||
|
||||
@ -29,7 +29,7 @@ pub async fn get_authorization_info(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_roles(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
pub async fn list_all_roles(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
let flow = Flow::ListRoles;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
|
||||
@ -26,11 +26,12 @@ use crate::{
|
||||
db::StorageInterface,
|
||||
routes::AppState,
|
||||
services::{
|
||||
authentication as auth,
|
||||
authentication::UserFromToken,
|
||||
authorization::{info, predefined_permissions},
|
||||
},
|
||||
types::transformers::ForeignFrom,
|
||||
utils::user::password,
|
||||
utils::{self, user::password},
|
||||
};
|
||||
|
||||
pub mod dashboard_metadata;
|
||||
@ -733,7 +734,15 @@ impl UserFromStorage {
|
||||
pub async fn get_role_from_db(&self, state: AppState) -> UserResult<UserRole> {
|
||||
state
|
||||
.store
|
||||
.find_user_role_by_user_id(self.get_user_id())
|
||||
.find_user_role_by_user_id(&self.0.user_id)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
|
||||
pub async fn get_roles_from_db(&self, state: &AppState) -> UserResult<Vec<UserRole>> {
|
||||
state
|
||||
.store
|
||||
.list_user_roles_by_user_id(&self.0.user_id)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
@ -760,6 +769,29 @@ impl UserFromStorage {
|
||||
let days_left_for_verification = last_date_for_verification - today;
|
||||
Ok(Some(days_left_for_verification.whole_days()))
|
||||
}
|
||||
|
||||
pub fn get_preferred_merchant_id(&self) -> Option<String> {
|
||||
self.0.preferred_merchant_id.clone()
|
||||
}
|
||||
|
||||
pub async fn get_role_from_db_by_merchant_id(
|
||||
&self,
|
||||
state: &AppState,
|
||||
merchant_id: &str,
|
||||
) -> UserResult<UserRole> {
|
||||
state
|
||||
.store
|
||||
.find_user_role_by_user_id_merchant_id(self.get_user_id(), merchant_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_not_found() {
|
||||
UserErrors::RoleNotFound
|
||||
} else {
|
||||
UserErrors::InternalServerError
|
||||
}
|
||||
})
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<info::ModuleInfo> for user_role_api::ModuleInfo {
|
||||
@ -828,3 +860,101 @@ impl TryFrom<UserAndRoleJoined> for user_api::UserDetails {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SignInWithRoleStrategyType {
|
||||
SingleRole(SignInWithSingleRoleStrategy),
|
||||
MultipleRoles(SignInWithMultipleRolesStrategy),
|
||||
}
|
||||
|
||||
impl SignInWithRoleStrategyType {
|
||||
pub async fn decide_signin_strategy_by_user_roles(
|
||||
user: UserFromStorage,
|
||||
user_roles: Vec<UserRole>,
|
||||
) -> UserResult<Self> {
|
||||
if user_roles.is_empty() {
|
||||
return Err(UserErrors::InternalServerError.into());
|
||||
}
|
||||
|
||||
if let Some(user_role) = user_roles
|
||||
.iter()
|
||||
.find(|role| role.status == UserStatus::Active)
|
||||
{
|
||||
Ok(Self::SingleRole(SignInWithSingleRoleStrategy {
|
||||
user,
|
||||
user_role: user_role.clone(),
|
||||
}))
|
||||
} else {
|
||||
Ok(Self::MultipleRoles(SignInWithMultipleRolesStrategy {
|
||||
user,
|
||||
user_roles,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_signin_response(
|
||||
self,
|
||||
state: &AppState,
|
||||
) -> UserResult<user_api::SignInResponse> {
|
||||
match self {
|
||||
Self::SingleRole(strategy) => strategy.get_signin_response(state).await,
|
||||
Self::MultipleRoles(strategy) => strategy.get_signin_response(state).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignInWithSingleRoleStrategy {
|
||||
pub user: UserFromStorage,
|
||||
pub user_role: UserRole,
|
||||
}
|
||||
|
||||
impl SignInWithSingleRoleStrategy {
|
||||
async fn get_signin_response(self, state: &AppState) -> UserResult<user_api::SignInResponse> {
|
||||
let token =
|
||||
utils::user::generate_jwt_auth_token(state, &self.user, &self.user_role).await?;
|
||||
let dashboard_entry_response =
|
||||
utils::user::get_dashboard_entry_response(state, self.user, self.user_role, token)?;
|
||||
Ok(user_api::SignInResponse::DashboardEntry(
|
||||
dashboard_entry_response,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignInWithMultipleRolesStrategy {
|
||||
pub user: UserFromStorage,
|
||||
pub user_roles: Vec<UserRole>,
|
||||
}
|
||||
|
||||
impl SignInWithMultipleRolesStrategy {
|
||||
async fn get_signin_response(self, state: &AppState) -> UserResult<user_api::SignInResponse> {
|
||||
let merchant_accounts = state
|
||||
.store
|
||||
.list_multiple_merchant_accounts(
|
||||
self.user_roles
|
||||
.iter()
|
||||
.map(|role| role.merchant_id.clone())
|
||||
.collect(),
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
let merchant_details = utils::user::get_multiple_merchant_details_with_status(
|
||||
self.user_roles,
|
||||
merchant_accounts,
|
||||
)?;
|
||||
|
||||
Ok(user_api::SignInResponse::MerchantSelect(
|
||||
user_api::MerchantSelectResponse {
|
||||
name: self.user.get_name(),
|
||||
email: self.user.get_email(),
|
||||
token: auth::UserAuthToken::new_token(
|
||||
self.user.get_user_id().to_string(),
|
||||
&state.conf,
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
merchants: merchant_details,
|
||||
verification_days_left: utils::user::get_verification_days_left(state, &self.user)?,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use api_models::user as user_api;
|
||||
use diesel_models::user_role::UserRole;
|
||||
use diesel_models::{enums::UserStatus, user_role::UserRole};
|
||||
use error_stack::ResultExt;
|
||||
use masking::Secret;
|
||||
|
||||
@ -118,3 +120,29 @@ pub fn get_verification_days_left(
|
||||
#[cfg(not(feature = "email"))]
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
pub fn get_multiple_merchant_details_with_status(
|
||||
user_roles: Vec<UserRole>,
|
||||
merchant_accounts: Vec<MerchantAccount>,
|
||||
) -> UserResult<Vec<user_api::UserMerchantAccount>> {
|
||||
let roles: HashMap<_, _> = user_roles
|
||||
.into_iter()
|
||||
.map(|user_role| (user_role.merchant_id.clone(), user_role))
|
||||
.collect();
|
||||
|
||||
merchant_accounts
|
||||
.into_iter()
|
||||
.map(|merchant| {
|
||||
let role = roles
|
||||
.get(merchant.merchant_id.as_str())
|
||||
.ok_or(UserErrors::InternalServerError.into())
|
||||
.attach_printable("Merchant exists but user role doesn't")?;
|
||||
|
||||
Ok(user_api::UserMerchantAccount {
|
||||
merchant_id: merchant.merchant_id.clone(),
|
||||
merchant_name: merchant.merchant_name.clone(),
|
||||
is_active: role.status == UserStatus::Active,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use api_models::user_role as user_role_api;
|
||||
use diesel_models::enums::UserStatus;
|
||||
use diesel_models::{enums::UserStatus, user_role::UserRole};
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use crate::{
|
||||
@ -17,19 +17,17 @@ pub fn is_internal_role(role_id: &str) -> bool {
|
||||
|| role_id == consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER
|
||||
}
|
||||
|
||||
pub async fn get_merchant_ids_for_user(state: &AppState, user_id: &str) -> UserResult<Vec<String>> {
|
||||
pub async fn get_active_user_roles_for_user(
|
||||
state: &AppState,
|
||||
user_id: &str,
|
||||
) -> UserResult<Vec<UserRole>> {
|
||||
Ok(state
|
||||
.store
|
||||
.list_user_roles_by_user_id(user_id)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.filter_map(|ele| {
|
||||
if ele.status == UserStatus::Active {
|
||||
return Some(ele.merchant_id);
|
||||
}
|
||||
None
|
||||
})
|
||||
.filter(|ele| ele.status == UserStatus::Active)
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
||||
@ -267,6 +267,8 @@ pub enum Flow {
|
||||
UserSignUp,
|
||||
/// User Sign Up
|
||||
UserSignUpWithMerchantId,
|
||||
/// User Sign In without invite checks
|
||||
UserSignInWithoutInviteChecks,
|
||||
/// User Sign In
|
||||
UserSignIn,
|
||||
/// User connect account
|
||||
@ -333,6 +335,8 @@ pub enum Flow {
|
||||
SyncOnboardingStatus,
|
||||
/// Reset tracking id
|
||||
ResetTrackingId,
|
||||
/// Verify email token without invite checks
|
||||
VerifyEmailWithoutInviteChecks,
|
||||
/// Verify email Token
|
||||
VerifyEmail,
|
||||
/// Send verify email
|
||||
|
||||
Reference in New Issue
Block a user