mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 17:47:54 +08:00
feat(user): add support for resend invite (#3523)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -12,8 +12,8 @@ use crate::user::{
|
|||||||
},
|
},
|
||||||
AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
|
AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
|
||||||
DashboardEntryResponse, ForgotPasswordRequest, GetUsersResponse, InviteUserRequest,
|
DashboardEntryResponse, ForgotPasswordRequest, GetUsersResponse, InviteUserRequest,
|
||||||
InviteUserResponse, ResetPasswordRequest, SendVerifyEmailRequest, SignInResponse,
|
InviteUserResponse, ReInviteUserRequest, ResetPasswordRequest, SendVerifyEmailRequest,
|
||||||
SignUpRequest, SignUpWithMerchantIdRequest, SwitchMerchantIdRequest,
|
SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, SwitchMerchantIdRequest,
|
||||||
UpdateUserAccountDetailsRequest, UserMerchantCreate, VerifyEmailRequest,
|
UpdateUserAccountDetailsRequest, UserMerchantCreate, VerifyEmailRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,6 +54,7 @@ common_utils::impl_misc_api_event_type!(
|
|||||||
ResetPasswordRequest,
|
ResetPasswordRequest,
|
||||||
InviteUserRequest,
|
InviteUserRequest,
|
||||||
InviteUserResponse,
|
InviteUserResponse,
|
||||||
|
ReInviteUserRequest,
|
||||||
VerifyEmailRequest,
|
VerifyEmailRequest,
|
||||||
SendVerifyEmailRequest,
|
SendVerifyEmailRequest,
|
||||||
SignInResponse,
|
SignInResponse,
|
||||||
|
|||||||
@ -113,6 +113,11 @@ pub struct InviteMultipleUserResponse {
|
|||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct ReInviteUserRequest {
|
||||||
|
pub email: pii::Email,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct SwitchMerchantIdRequest {
|
pub struct SwitchMerchantIdRequest {
|
||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
|
|||||||
@ -706,6 +706,63 @@ async fn handle_new_user_invitation(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
pub async fn resend_invite(
|
||||||
|
state: AppState,
|
||||||
|
user_from_token: auth::UserFromToken,
|
||||||
|
request: user_api::ReInviteUserRequest,
|
||||||
|
) -> UserResponse<()> {
|
||||||
|
let invitee_email = domain::UserEmail::from_pii_email(request.email)?;
|
||||||
|
let user: domain::UserFromStorage = state
|
||||||
|
.store
|
||||||
|
.find_user_by_email(invitee_email.clone().get_secret().expose().as_str())
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
if e.current_context().is_db_not_found() {
|
||||||
|
e.change_context(UserErrors::InvalidRoleOperation)
|
||||||
|
.attach_printable("User not found in the records")
|
||||||
|
} else {
|
||||||
|
e.change_context(UserErrors::InternalServerError)
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.into();
|
||||||
|
let user_role = state
|
||||||
|
.store
|
||||||
|
.find_user_role_by_user_id_merchant_id(user.get_user_id(), &user_from_token.merchant_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
if e.current_context().is_db_not_found() {
|
||||||
|
e.change_context(UserErrors::InvalidRoleOperation)
|
||||||
|
.attach_printable("User role with given UserId MerchantId not found")
|
||||||
|
} else {
|
||||||
|
e.change_context(UserErrors::InternalServerError)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !matches!(user_role.status, UserStatus::InvitationSent) {
|
||||||
|
return Err(UserErrors::InvalidRoleOperation.into())
|
||||||
|
.attach_printable("Invalid Status for Reinvitation");
|
||||||
|
}
|
||||||
|
|
||||||
|
let email_contents = email_types::InviteUser {
|
||||||
|
recipient_email: invitee_email,
|
||||||
|
user_name: domain::UserName::new(user.get_name())?,
|
||||||
|
settings: state.conf.clone(),
|
||||||
|
subject: "You have been invited to join Hyperswitch Community!",
|
||||||
|
merchant_id: user_from_token.merchant_id,
|
||||||
|
};
|
||||||
|
state
|
||||||
|
.email_client
|
||||||
|
.compose_and_send_email(
|
||||||
|
Box::new(email_contents),
|
||||||
|
state.conf.proxy.https_url.as_ref(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::StatusOk)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_internal_user(
|
pub async fn create_internal_user(
|
||||||
state: AppState,
|
state: AppState,
|
||||||
request: user_api::CreateInternalUserRequest,
|
request: user_api::CreateInternalUserRequest,
|
||||||
|
|||||||
@ -976,41 +976,12 @@ 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("/permission_info").route(web::get().to(get_authorization_info)))
|
.service(web::resource("/permission_info").route(web::get().to(get_authorization_info)))
|
||||||
.service(web::resource("/update").route(web::post().to(update_user_account_details)))
|
.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)),
|
|
||||||
)
|
|
||||||
.service(
|
.service(
|
||||||
web::resource("/data")
|
web::resource("/data")
|
||||||
.route(web::get().to(get_multiple_dashboard_metadata))
|
.route(web::get().to(get_multiple_dashboard_metadata))
|
||||||
.route(web::post().to(set_dashboard_metadata)),
|
.route(web::post().to(set_dashboard_metadata)),
|
||||||
)
|
|
||||||
.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(
|
|
||||||
web::resource("/sample_data")
|
|
||||||
.route(web::post().to(generate_sample_data))
|
|
||||||
.route(web::delete().to(delete_sample_data)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
{
|
{
|
||||||
route = route
|
route = route
|
||||||
@ -1031,12 +1002,43 @@ impl User {
|
|||||||
.service(
|
.service(
|
||||||
web::resource("/verify_email_request")
|
web::resource("/verify_email_request")
|
||||||
.route(web::post().to(verify_email_request)),
|
.route(web::post().to(verify_email_request)),
|
||||||
);
|
)
|
||||||
|
.service(web::resource("/user/resend_invite").route(web::post().to(resend_invite)));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "email"))]
|
#[cfg(not(feature = "email"))]
|
||||||
{
|
{
|
||||||
route = route.service(web::resource("/signup").route(web::post().to(user_signup)))
|
route = route.service(web::resource("/signup").route(web::post().to(user_signup)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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_multiple").route(web::post().to(invite_multiple_user)),
|
||||||
|
)
|
||||||
|
.service(web::resource("/invite/accept").route(web::post().to(accept_invitation)))
|
||||||
|
.service(web::resource("/update_role").route(web::post().to(update_user_role)))
|
||||||
|
.service(web::resource("/delete").route(web::delete().to(delete_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(
|
||||||
|
web::resource("/sample_data")
|
||||||
|
.route(web::post().to(generate_sample_data))
|
||||||
|
.route(web::delete().to(delete_sample_data)),
|
||||||
|
)
|
||||||
|
}
|
||||||
route
|
route
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,6 +182,7 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::ResetPassword
|
| Flow::ResetPassword
|
||||||
| Flow::InviteUser
|
| Flow::InviteUser
|
||||||
| Flow::InviteMultipleUser
|
| Flow::InviteMultipleUser
|
||||||
|
| Flow::ReInviteUser
|
||||||
| Flow::UserSignUpWithMerchantId
|
| Flow::UserSignUpWithMerchantId
|
||||||
| Flow::VerifyEmailWithoutInviteChecks
|
| Flow::VerifyEmailWithoutInviteChecks
|
||||||
| Flow::VerifyEmail
|
| Flow::VerifyEmail
|
||||||
|
|||||||
@ -401,6 +401,25 @@ pub async fn invite_multiple_user(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "email")]
|
||||||
|
pub async fn resend_invite(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
payload: web::Json<user_api::ReInviteUserRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::ReInviteUser;
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state.clone(),
|
||||||
|
&req,
|
||||||
|
payload.into_inner(),
|
||||||
|
user_core::resend_invite,
|
||||||
|
&auth::JWTAuth(Permission::UsersWrite),
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
pub async fn verify_email_without_invite_checks(
|
pub async fn verify_email_without_invite_checks(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
|
|||||||
@ -331,6 +331,8 @@ pub enum Flow {
|
|||||||
InviteUser,
|
InviteUser,
|
||||||
/// Invite multiple users
|
/// Invite multiple users
|
||||||
InviteMultipleUser,
|
InviteMultipleUser,
|
||||||
|
/// Reinvite user
|
||||||
|
ReInviteUser,
|
||||||
/// Delete user role
|
/// Delete user role
|
||||||
DeleteUserRole,
|
DeleteUserRole,
|
||||||
/// Incremental Authorization flow
|
/// Incremental Authorization flow
|
||||||
|
|||||||
Reference in New Issue
Block a user