mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(users): new routes to accept invite and list merchants (#4591)
This commit is contained in:
		| @ -7,7 +7,7 @@ use crate::user_role::{ | |||||||
|         UpdateRoleRequest, |         UpdateRoleRequest, | ||||||
|     }, |     }, | ||||||
|     AcceptInvitationRequest, AuthorizationInfoResponse, DeleteUserRoleRequest, |     AcceptInvitationRequest, AuthorizationInfoResponse, DeleteUserRoleRequest, | ||||||
|     TransferOrgOwnershipRequest, UpdateUserRoleRequest, |     MerchantSelectRequest, TransferOrgOwnershipRequest, UpdateUserRoleRequest, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| common_utils::impl_misc_api_event_type!( | common_utils::impl_misc_api_event_type!( | ||||||
| @ -15,6 +15,7 @@ common_utils::impl_misc_api_event_type!( | |||||||
|     GetRoleRequest, |     GetRoleRequest, | ||||||
|     AuthorizationInfoResponse, |     AuthorizationInfoResponse, | ||||||
|     UpdateUserRoleRequest, |     UpdateUserRoleRequest, | ||||||
|  |     MerchantSelectRequest, | ||||||
|     AcceptInvitationRequest, |     AcceptInvitationRequest, | ||||||
|     DeleteUserRoleRequest, |     DeleteUserRoleRequest, | ||||||
|     TransferOrgOwnershipRequest, |     TransferOrgOwnershipRequest, | ||||||
|  | |||||||
| @ -97,11 +97,15 @@ pub enum UserStatus { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, serde::Deserialize, serde::Serialize)] | #[derive(Debug, serde::Deserialize, serde::Serialize)] | ||||||
| pub struct AcceptInvitationRequest { | pub struct MerchantSelectRequest { | ||||||
|     pub merchant_ids: Vec<String>, |     pub merchant_ids: Vec<String>, | ||||||
|     // TODO: Remove this once the token only api is being used |     // TODO: Remove this once the token only api is being used | ||||||
|     pub need_dashboard_entry_response: Option<bool>, |     pub need_dashboard_entry_response: Option<bool>, | ||||||
| } | } | ||||||
|  | #[derive(Debug, serde::Deserialize, serde::Serialize)] | ||||||
|  | pub struct AcceptInvitationRequest { | ||||||
|  |     pub merchant_ids: Vec<String>, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug, serde::Deserialize, serde::Serialize)] | #[derive(Debug, serde::Deserialize, serde::Serialize)] | ||||||
| pub struct DeleteUserRoleRequest { | pub struct DeleteUserRoleRequest { | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ use diesel_models::{ | |||||||
|     user_role::UserRoleNew, |     user_role::UserRoleNew, | ||||||
| }; | }; | ||||||
| use error_stack::{report, ResultExt}; | use error_stack::{report, ResultExt}; | ||||||
|  | #[cfg(feature = "email")] | ||||||
|  | use external_services::email::EmailData; | ||||||
| use masking::{ExposeInterface, PeekInterface}; | use masking::{ExposeInterface, PeekInterface}; | ||||||
| #[cfg(feature = "email")] | #[cfg(feature = "email")] | ||||||
| use router_env::env; | use router_env::env; | ||||||
| @ -570,6 +572,7 @@ pub async fn invite_multiple_user( | |||||||
|     user_from_token: auth::UserFromToken, |     user_from_token: auth::UserFromToken, | ||||||
|     requests: Vec<user_api::InviteUserRequest>, |     requests: Vec<user_api::InviteUserRequest>, | ||||||
|     req_state: ReqState, |     req_state: ReqState, | ||||||
|  |     is_token_only: Option<bool>, | ||||||
| ) -> UserResponse<Vec<InviteMultipleUserResponse>> { | ) -> UserResponse<Vec<InviteMultipleUserResponse>> { | ||||||
|     if requests.len() > 10 { |     if requests.len() > 10 { | ||||||
|         return Err(report!(UserErrors::MaxInvitationsError)) |         return Err(report!(UserErrors::MaxInvitationsError)) | ||||||
| @ -577,7 +580,8 @@ pub async fn invite_multiple_user( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     let responses = futures::future::join_all(requests.iter().map(|request| async { |     let responses = futures::future::join_all(requests.iter().map(|request| async { | ||||||
|         match handle_invitation(&state, &user_from_token, request, &req_state).await { |         match handle_invitation(&state, &user_from_token, request, &req_state, is_token_only).await | ||||||
|  |         { | ||||||
|             Ok(response) => response, |             Ok(response) => response, | ||||||
|             Err(error) => InviteMultipleUserResponse { |             Err(error) => InviteMultipleUserResponse { | ||||||
|                 email: request.email.clone(), |                 email: request.email.clone(), | ||||||
| @ -597,6 +601,7 @@ async fn handle_invitation( | |||||||
|     user_from_token: &auth::UserFromToken, |     user_from_token: &auth::UserFromToken, | ||||||
|     request: &user_api::InviteUserRequest, |     request: &user_api::InviteUserRequest, | ||||||
|     req_state: &ReqState, |     req_state: &ReqState, | ||||||
|  |     is_token_only: Option<bool>, | ||||||
| ) -> UserResult<InviteMultipleUserResponse> { | ) -> UserResult<InviteMultipleUserResponse> { | ||||||
|     let inviter_user = user_from_token.get_user_from_db(state).await?; |     let inviter_user = user_from_token.get_user_from_db(state).await?; | ||||||
|  |  | ||||||
| @ -635,7 +640,14 @@ async fn handle_invitation( | |||||||
|         .err() |         .err() | ||||||
|         .unwrap_or(false) |         .unwrap_or(false) | ||||||
|     { |     { | ||||||
|         handle_new_user_invitation(state, user_from_token, request, req_state.clone()).await |         handle_new_user_invitation( | ||||||
|  |             state, | ||||||
|  |             user_from_token, | ||||||
|  |             request, | ||||||
|  |             req_state.clone(), | ||||||
|  |             is_token_only, | ||||||
|  |         ) | ||||||
|  |         .await | ||||||
|     } else { |     } else { | ||||||
|         Err(UserErrors::InternalServerError.into()) |         Err(UserErrors::InternalServerError.into()) | ||||||
|     } |     } | ||||||
| @ -718,6 +730,7 @@ async fn handle_new_user_invitation( | |||||||
|     user_from_token: &auth::UserFromToken, |     user_from_token: &auth::UserFromToken, | ||||||
|     request: &user_api::InviteUserRequest, |     request: &user_api::InviteUserRequest, | ||||||
|     req_state: ReqState, |     req_state: ReqState, | ||||||
|  |     is_token_only: Option<bool>, | ||||||
| ) -> UserResult<InviteMultipleUserResponse> { | ) -> UserResult<InviteMultipleUserResponse> { | ||||||
|     let new_user = domain::NewUser::try_from((request.clone(), user_from_token.clone()))?; |     let new_user = domain::NewUser::try_from((request.clone(), user_from_token.clone()))?; | ||||||
|  |  | ||||||
| @ -756,25 +769,36 @@ async fn handle_new_user_invitation( | |||||||
|         })?; |         })?; | ||||||
|  |  | ||||||
|     let is_email_sent; |     let is_email_sent; | ||||||
|  |     // TODO: Adding this to avoid clippy lints, remove this once the token only flow is being used | ||||||
|  |     let _ = is_token_only; | ||||||
|  |  | ||||||
|     #[cfg(feature = "email")] |     #[cfg(feature = "email")] | ||||||
|     { |     { | ||||||
|         // TODO: Adding this to avoid clippy lints |         // TODO: Adding this to avoid clippy lints | ||||||
|         // Will be adding actual usage for this variable later |         // Will be adding actual usage for this variable later | ||||||
|         let _ = req_state.clone(); |         let _ = req_state.clone(); | ||||||
|         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; |         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; | ||||||
|         let email_contents = email_types::InviteUser { |         let email_contents: Box<dyn EmailData + Send + 'static> = if let Some(true) = is_token_only | ||||||
|  |         { | ||||||
|  |             Box::new(email_types::InviteRegisteredUser { | ||||||
|                 recipient_email: invitee_email, |                 recipient_email: invitee_email, | ||||||
|                 user_name: domain::UserName::new(new_user.get_name())?, |                 user_name: domain::UserName::new(new_user.get_name())?, | ||||||
|                 settings: state.conf.clone(), |                 settings: state.conf.clone(), | ||||||
|                 subject: "You have been invited to join Hyperswitch Community!", |                 subject: "You have been invited to join Hyperswitch Community!", | ||||||
|                 merchant_id: user_from_token.merchant_id.clone(), |                 merchant_id: user_from_token.merchant_id.clone(), | ||||||
|  |             }) | ||||||
|  |         } else { | ||||||
|  |             Box::new(email_types::InviteUser { | ||||||
|  |                 recipient_email: invitee_email, | ||||||
|  |                 user_name: domain::UserName::new(new_user.get_name())?, | ||||||
|  |                 settings: state.conf.clone(), | ||||||
|  |                 subject: "You have been invited to join Hyperswitch Community!", | ||||||
|  |                 merchant_id: user_from_token.merchant_id.clone(), | ||||||
|  |             }) | ||||||
|         }; |         }; | ||||||
|         let send_email_result = state |         let send_email_result = state | ||||||
|             .email_client |             .email_client | ||||||
|             .compose_and_send_email( |             .compose_and_send_email(email_contents, state.conf.proxy.https_url.as_ref()) | ||||||
|                 Box::new(email_contents), |  | ||||||
|                 state.conf.proxy.https_url.as_ref(), |  | ||||||
|             ) |  | ||||||
|             .await; |             .await; | ||||||
|         logger::info!(?send_email_result); |         logger::info!(?send_email_result); | ||||||
|         is_email_sent = send_email_result.is_ok(); |         is_email_sent = send_email_result.is_ok(); | ||||||
| @ -1203,11 +1227,11 @@ pub async fn create_merchant_account( | |||||||
|  |  | ||||||
| pub async fn list_merchants_for_user( | pub async fn list_merchants_for_user( | ||||||
|     state: AppState, |     state: AppState, | ||||||
|     user_from_token: auth::UserFromToken, |     user_from_token: Box<dyn auth::GetUserIdFromAuth>, | ||||||
| ) -> UserResponse<Vec<user_api::UserMerchantAccount>> { | ) -> UserResponse<Vec<user_api::UserMerchantAccount>> { | ||||||
|     let user_roles = state |     let user_roles = state | ||||||
|         .store |         .store | ||||||
|         .list_user_roles_by_user_id(user_from_token.user_id.as_str()) |         .list_user_roles_by_user_id(user_from_token.get_user_id().as_str()) | ||||||
|         .await |         .await | ||||||
|         .change_context(UserErrors::InternalServerError)?; |         .change_context(UserErrors::InternalServerError)?; | ||||||
|  |  | ||||||
|  | |||||||
| @ -170,8 +170,38 @@ pub async fn transfer_org_ownership( | |||||||
|  |  | ||||||
| pub async fn accept_invitation( | pub async fn accept_invitation( | ||||||
|     state: AppState, |     state: AppState, | ||||||
|     user_token: auth::UserFromSinglePurposeToken, |     user_token: auth::UserFromToken, | ||||||
|     req: user_role_api::AcceptInvitationRequest, |     req: user_role_api::AcceptInvitationRequest, | ||||||
|  | ) -> UserResponse<()> { | ||||||
|  |     futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { | ||||||
|  |         state | ||||||
|  |             .store | ||||||
|  |             .update_user_role_by_user_id_merchant_id( | ||||||
|  |                 user_token.user_id.as_str(), | ||||||
|  |                 merchant_id, | ||||||
|  |                 UserRoleUpdate::UpdateStatus { | ||||||
|  |                     status: UserStatus::Active, | ||||||
|  |                     modified_by: user_token.user_id.clone(), | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| { | ||||||
|  |                 logger::error!("Error while accepting invitation {}", e); | ||||||
|  |             }) | ||||||
|  |             .ok() | ||||||
|  |     })) | ||||||
|  |     .await | ||||||
|  |     .into_iter() | ||||||
|  |     .reduce(Option::or) | ||||||
|  |     .flatten() | ||||||
|  |     .ok_or(UserErrors::MerchantIdNotFound.into()) | ||||||
|  |     .map(|_| ApplicationResponse::StatusOk) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub async fn merchant_select( | ||||||
|  |     state: AppState, | ||||||
|  |     user_token: auth::UserFromSinglePurposeToken, | ||||||
|  |     req: user_role_api::MerchantSelectRequest, | ||||||
| ) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> { | ) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> { | ||||||
|     let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { |     let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { | ||||||
|         state |         state | ||||||
| @ -207,7 +237,6 @@ pub async fn accept_invitation( | |||||||
|         utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; |         utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; | ||||||
|  |  | ||||||
|         let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; |         let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; | ||||||
|  |  | ||||||
|         let response = utils::user::get_dashboard_entry_response( |         let response = utils::user::get_dashboard_entry_response( | ||||||
|             &state, |             &state, | ||||||
|             user_from_db, |             user_from_db, | ||||||
| @ -223,10 +252,10 @@ pub async fn accept_invitation( | |||||||
|     Ok(ApplicationResponse::StatusOk) |     Ok(ApplicationResponse::StatusOk) | ||||||
| } | } | ||||||
|  |  | ||||||
| pub async fn accept_invitation_token_only_flow( | pub async fn merchant_select_token_only_flow( | ||||||
|     state: AppState, |     state: AppState, | ||||||
|     user_token: auth::UserFromSinglePurposeToken, |     user_token: auth::UserFromSinglePurposeToken, | ||||||
|     req: user_role_api::AcceptInvitationRequest, |     req: user_role_api::MerchantSelectRequest, | ||||||
| ) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> { | ) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> { | ||||||
|     let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { |     let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async { | ||||||
|         state |         state | ||||||
|  | |||||||
| @ -1192,6 +1192,11 @@ impl User { | |||||||
|             // TODO: Remove this endpoint once migration to /merchants/list is done |             // TODO: Remove this endpoint once migration to /merchants/list is done | ||||||
|             .service(web::resource("/switch/list").route(web::get().to(list_merchants_for_user))) |             .service(web::resource("/switch/list").route(web::get().to(list_merchants_for_user))) | ||||||
|             .service(web::resource("/merchants/list").route(web::get().to(list_merchants_for_user))) |             .service(web::resource("/merchants/list").route(web::get().to(list_merchants_for_user))) | ||||||
|  |             // The route is utilized to select an invitation from a list of merchants in an intermediate state | ||||||
|  |             .service( | ||||||
|  |                 web::resource("/merchants_select/list") | ||||||
|  |                     .route(web::get().to(list_merchants_for_user_with_spt)), | ||||||
|  |             ) | ||||||
|             .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( |             .service( | ||||||
| @ -1241,7 +1246,11 @@ impl User { | |||||||
|                 .service( |                 .service( | ||||||
|                     web::resource("/invite_multiple").route(web::post().to(invite_multiple_user)), |                     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("/invite/accept") | ||||||
|  |                         .route(web::post().to(merchant_select)) | ||||||
|  |                         .route(web::put().to(accept_invitation)), | ||||||
|  |                 ) | ||||||
|                 .service(web::resource("/update_role").route(web::post().to(update_user_role))) |                 .service(web::resource("/update_role").route(web::post().to(update_user_role))) | ||||||
|                 .service( |                 .service( | ||||||
|                     web::resource("/transfer_ownership") |                     web::resource("/transfer_ownership") | ||||||
|  | |||||||
| @ -220,6 +220,7 @@ impl From<Flow> for ApiIdentifier { | |||||||
|             | Flow::UpdateUserRole |             | Flow::UpdateUserRole | ||||||
|             | Flow::GetAuthorizationInfo |             | Flow::GetAuthorizationInfo | ||||||
|             | Flow::AcceptInvitation |             | Flow::AcceptInvitation | ||||||
|  |             | Flow::MerchantSelect | ||||||
|             | Flow::DeleteUserRole |             | Flow::DeleteUserRole | ||||||
|             | Flow::TransferOrgOwnership |             | Flow::TransferOrgOwnership | ||||||
|             | Flow::CreateRole |             | Flow::CreateRole | ||||||
|  | |||||||
| @ -324,6 +324,23 @@ pub async fn list_merchants_for_user(state: web::Data<AppState>, req: HttpReques | |||||||
|     .await |     .await | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub async fn list_merchants_for_user_with_spt( | ||||||
|  |     state: web::Data<AppState>, | ||||||
|  |     req: HttpRequest, | ||||||
|  | ) -> HttpResponse { | ||||||
|  |     let flow = Flow::UserMerchantAccountList; | ||||||
|  |     Box::pin(api::server_wrap( | ||||||
|  |         flow, | ||||||
|  |         state, | ||||||
|  |         &req, | ||||||
|  |         (), | ||||||
|  |         |state, user, _, _| user_core::list_merchants_for_user(state, user), | ||||||
|  |         &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvite), | ||||||
|  |         api_locking::LockAction::NotApplicable, | ||||||
|  |     )) | ||||||
|  |     .await | ||||||
|  | } | ||||||
|  |  | ||||||
| pub async fn get_user_role_details( | pub async fn get_user_role_details( | ||||||
|     state: web::Data<AppState>, |     state: web::Data<AppState>, | ||||||
|     req: HttpRequest, |     req: HttpRequest, | ||||||
| @ -435,14 +452,18 @@ pub async fn invite_multiple_user( | |||||||
|     state: web::Data<AppState>, |     state: web::Data<AppState>, | ||||||
|     req: HttpRequest, |     req: HttpRequest, | ||||||
|     payload: web::Json<Vec<user_api::InviteUserRequest>>, |     payload: web::Json<Vec<user_api::InviteUserRequest>>, | ||||||
|  |     query: web::Query<user_api::TokenOnlyQueryParam>, | ||||||
| ) -> HttpResponse { | ) -> HttpResponse { | ||||||
|     let flow = Flow::InviteMultipleUser; |     let flow = Flow::InviteMultipleUser; | ||||||
|  |     let is_token_only = query.into_inner().token_only; | ||||||
|     Box::pin(api::server_wrap( |     Box::pin(api::server_wrap( | ||||||
|         flow, |         flow, | ||||||
|         state.clone(), |         state.clone(), | ||||||
|         &req, |         &req, | ||||||
|         payload.into_inner(), |         payload.into_inner(), | ||||||
|         user_core::invite_multiple_user, |         |state, user, payload, req_state| { | ||||||
|  |             user_core::invite_multiple_user(state, user, payload, req_state, is_token_only) | ||||||
|  |         }, | ||||||
|         &auth::JWTAuth(Permission::UsersWrite), |         &auth::JWTAuth(Permission::UsersWrite), | ||||||
|         api_locking::LockAction::NotApplicable, |         api_locking::LockAction::NotApplicable, | ||||||
|     )) |     )) | ||||||
|  | |||||||
| @ -209,10 +209,29 @@ pub async fn accept_invitation( | |||||||
|     state: web::Data<AppState>, |     state: web::Data<AppState>, | ||||||
|     req: HttpRequest, |     req: HttpRequest, | ||||||
|     json_payload: web::Json<user_role_api::AcceptInvitationRequest>, |     json_payload: web::Json<user_role_api::AcceptInvitationRequest>, | ||||||
|     query: web::Query<user_api::TokenOnlyQueryParam>, |  | ||||||
| ) -> HttpResponse { | ) -> HttpResponse { | ||||||
|     let flow = Flow::AcceptInvitation; |     let flow = Flow::AcceptInvitation; | ||||||
|     let payload = json_payload.into_inner(); |     let payload = json_payload.into_inner(); | ||||||
|  |     Box::pin(api::server_wrap( | ||||||
|  |         flow, | ||||||
|  |         state.clone(), | ||||||
|  |         &req, | ||||||
|  |         payload, | ||||||
|  |         |state, user, req_body, _| user_role_core::accept_invitation(state, user, req_body), | ||||||
|  |         &auth::DashboardNoPermissionAuth, | ||||||
|  |         api_locking::LockAction::NotApplicable, | ||||||
|  |     )) | ||||||
|  |     .await | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub async fn merchant_select( | ||||||
|  |     state: web::Data<AppState>, | ||||||
|  |     req: HttpRequest, | ||||||
|  |     json_payload: web::Json<user_role_api::MerchantSelectRequest>, | ||||||
|  |     query: web::Query<user_api::TokenOnlyQueryParam>, | ||||||
|  | ) -> HttpResponse { | ||||||
|  |     let flow = Flow::MerchantSelect; | ||||||
|  |     let payload = json_payload.into_inner(); | ||||||
|     let is_token_only = query.into_inner().token_only; |     let is_token_only = query.into_inner().token_only; | ||||||
|     Box::pin(api::server_wrap( |     Box::pin(api::server_wrap( | ||||||
|         flow, |         flow, | ||||||
| @ -221,9 +240,9 @@ pub async fn accept_invitation( | |||||||
|         payload, |         payload, | ||||||
|         |state, user, req_body, _| async move { |         |state, user, req_body, _| async move { | ||||||
|             if let Some(true) = is_token_only { |             if let Some(true) = is_token_only { | ||||||
|                 user_role_core::accept_invitation_token_only_flow(state, user, req_body).await |                 user_role_core::merchant_select_token_only_flow(state, user, req_body).await | ||||||
|             } else { |             } else { | ||||||
|                 user_role_core::accept_invitation(state, user, req_body).await |                 user_role_core::merchant_select(state, user, req_body).await | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvite), |         &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvite), | ||||||
|  | |||||||
| @ -206,6 +206,23 @@ impl AuthInfo for AuthenticationData { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub trait GetUserIdFromAuth { | ||||||
|  |     fn get_user_id(&self) -> String; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl GetUserIdFromAuth for UserFromToken { | ||||||
|  |     fn get_user_id(&self) -> String { | ||||||
|  |         self.user_id.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "olap")] | ||||||
|  | impl GetUserIdFromAuth for UserFromSinglePurposeToken { | ||||||
|  |     fn get_user_id(&self) -> String { | ||||||
|  |         self.user_id.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait AuthenticateAndFetch<T, A> | pub trait AuthenticateAndFetch<T, A> | ||||||
| where | where | ||||||
| @ -347,6 +364,39 @@ where | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "olap")] | ||||||
|  | #[async_trait] | ||||||
|  | impl<A> AuthenticateAndFetch<Box<dyn GetUserIdFromAuth>, A> for SinglePurposeJWTAuth | ||||||
|  | where | ||||||
|  |     A: AppStateInfo + Sync, | ||||||
|  | { | ||||||
|  |     async fn authenticate_and_fetch( | ||||||
|  |         &self, | ||||||
|  |         request_headers: &HeaderMap, | ||||||
|  |         state: &A, | ||||||
|  |     ) -> RouterResult<(Box<dyn GetUserIdFromAuth>, AuthenticationType)> { | ||||||
|  |         let payload = parse_jwt_payload::<A, SinglePurposeToken>(request_headers, state).await?; | ||||||
|  |         if payload.check_in_blacklist(state).await? { | ||||||
|  |             return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if self.0 != payload.purpose { | ||||||
|  |             return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(( | ||||||
|  |             Box::new(UserFromSinglePurposeToken { | ||||||
|  |                 user_id: payload.user_id.clone(), | ||||||
|  |                 origin: payload.origin.clone(), | ||||||
|  |             }), | ||||||
|  |             AuthenticationType::SinglePurposeJWT { | ||||||
|  |                 user_id: payload.user_id, | ||||||
|  |                 purpose: payload.purpose, | ||||||
|  |             }, | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct AdminApiAuth; | pub struct AdminApiAuth; | ||||||
|  |  | ||||||
| @ -786,6 +836,37 @@ where | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "olap")] | ||||||
|  | #[async_trait] | ||||||
|  | impl<A> AuthenticateAndFetch<Box<dyn GetUserIdFromAuth>, A> for DashboardNoPermissionAuth | ||||||
|  | where | ||||||
|  |     A: AppStateInfo + Sync, | ||||||
|  | { | ||||||
|  |     async fn authenticate_and_fetch( | ||||||
|  |         &self, | ||||||
|  |         request_headers: &HeaderMap, | ||||||
|  |         state: &A, | ||||||
|  |     ) -> RouterResult<(Box<dyn GetUserIdFromAuth>, AuthenticationType)> { | ||||||
|  |         let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?; | ||||||
|  |         if payload.check_in_blacklist(state).await? { | ||||||
|  |             return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(( | ||||||
|  |             Box::new(UserFromToken { | ||||||
|  |                 user_id: payload.user_id.clone(), | ||||||
|  |                 merchant_id: payload.merchant_id.clone(), | ||||||
|  |                 org_id: payload.org_id, | ||||||
|  |                 role_id: payload.role_id, | ||||||
|  |             }), | ||||||
|  |             AuthenticationType::MerchantJwt { | ||||||
|  |                 merchant_id: payload.merchant_id, | ||||||
|  |                 user_id: Some(payload.user_id), | ||||||
|  |             }, | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(feature = "olap")] | #[cfg(feature = "olap")] | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl<A> AuthenticateAndFetch<(), A> for DashboardNoPermissionAuth | impl<A> AuthenticateAndFetch<(), A> for DashboardNoPermissionAuth | ||||||
|  | |||||||
| @ -386,6 +386,8 @@ pub enum Flow { | |||||||
|     UpdateUserAccountDetails, |     UpdateUserAccountDetails, | ||||||
|     /// Accept user invitation |     /// Accept user invitation | ||||||
|     AcceptInvitation, |     AcceptInvitation, | ||||||
|  |     /// Select merchant from invitations | ||||||
|  |     MerchantSelect, | ||||||
|     /// Initiate external authentication for a payment |     /// Initiate external authentication for a payment | ||||||
|     PaymentsExternalAuthentication, |     PaymentsExternalAuthentication, | ||||||
|     /// Authorize the payment after external 3ds authentication |     /// Authorize the payment after external 3ds authentication | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Apoorv Dixit
					Apoorv Dixit