diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 590179c052c9..8d1ae60b3257 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -16,11 +16,11 @@ use crate::user::{ GetSsoAuthUrlRequest, GetUserAuthenticationMethodsRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, GetUserRoleDetailsResponse, InviteUserRequest, ListUsersResponse, ReInviteUserRequest, RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest, - SendVerifyEmailRequest, SignInResponse, SignUpRequest, SignUpWithMerchantIdRequest, - SsoSignInRequest, SwitchMerchantRequest, SwitchOrganizationRequest, SwitchProfileRequest, - TokenOrPayloadResponse, TokenResponse, TwoFactorAuthStatusResponse, - UpdateUserAccountDetailsRequest, UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, - UserMerchantCreate, VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest, + SendVerifyEmailRequest, SignUpRequest, SignUpWithMerchantIdRequest, SsoSignInRequest, + SwitchMerchantRequest, SwitchOrganizationRequest, SwitchProfileRequest, TokenResponse, + TwoFactorAuthStatusResponse, UpdateUserAccountDetailsRequest, + UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantCreate, + VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest, }; impl ApiEventMetric for DashboardEntryResponse { @@ -40,12 +40,6 @@ impl ApiEventMetric for VerifyTokenResponse { } } -impl ApiEventMetric for TokenOrPayloadResponse { - fn get_api_event_type(&self) -> Option { - Some(ApiEventsType::Miscellaneous) - } -} - common_utils::impl_api_event_type!( Miscellaneous, ( @@ -72,7 +66,6 @@ common_utils::impl_api_event_type!( VerifyEmailRequest, SendVerifyEmailRequest, AcceptInviteFromEmailRequest, - SignInResponse, UpdateUserAccountDetailsRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest, diff --git a/crates/api_models/src/events/user_role.rs b/crates/api_models/src/events/user_role.rs index 5b5da0ec50a7..f88c318477aa 100644 --- a/crates/api_models/src/events/user_role.rs +++ b/crates/api_models/src/events/user_role.rs @@ -2,9 +2,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; use crate::user_role::{ role::{ - CreateRoleRequest, GetRoleFromTokenResponse, GetRoleRequest, ListRolesResponse, - RoleInfoResponse, RoleInfoWithGroupsResponse, RoleInfoWithPermissionsResponse, - UpdateRoleRequest, + CreateRoleRequest, GetRoleRequest, ListRolesResponse, RoleInfoWithGroupsResponse, + RoleInfoWithPermissionsResponse, UpdateRoleRequest, }, AcceptInvitationRequest, AuthorizationInfoResponse, DeleteUserRoleRequest, MerchantSelectRequest, UpdateUserRoleRequest, @@ -23,8 +22,6 @@ common_utils::impl_api_event_type!( CreateRoleRequest, UpdateRoleRequest, ListRolesResponse, - RoleInfoResponse, - GetRoleFromTokenResponse, RoleInfoWithGroupsResponse ) ); diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 97d0c63fdf1c..abc09c8bbee7 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -23,8 +23,6 @@ pub struct SignUpRequest { pub password: Secret, } -pub type SignUpResponse = DashboardEntryResponse; - #[derive(serde::Serialize, Debug, Clone)] pub struct DashboardEntryResponse { pub token: Secret, @@ -40,22 +38,6 @@ pub struct DashboardEntryResponse { pub type SignInRequest = SignUpRequest; -#[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, - pub name: Secret, - pub email: pii::Email, - pub verification_days_left: Option, - pub merchants: Vec, -} - #[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] pub struct ConnectAccountRequest { pub email: pii::Email, @@ -202,8 +184,6 @@ pub struct VerifyEmailRequest { pub token: Secret, } -pub type VerifyEmailResponse = SignInResponse; - #[derive(serde::Deserialize, Debug, serde::Serialize)] pub struct SendVerifyEmailRequest { pub email: pii::Email, @@ -232,11 +212,6 @@ pub struct UpdateUserAccountDetailsRequest { pub preferred_merchant_id: Option, } -#[derive(Debug, serde::Deserialize, serde::Serialize)] -pub struct TokenOnlyQueryParam { - pub token_only: Option, -} - #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct SkipTwoFactorAuthQueryParam { pub skip_two_factor_auth: Option, @@ -254,12 +229,6 @@ pub struct TwoFactorAuthStatusResponse { pub recovery_code: bool, } -#[derive(Debug, serde::Serialize)] -#[serde(untagged)] -pub enum TokenOrPayloadResponse { - Token(TokenResponse), - Payload(T), -} #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct UserFromEmailRequest { pub token: Secret, diff --git a/crates/api_models/src/user_role.rs b/crates/api_models/src/user_role.rs index c9f222cb7bec..ed6911016b16 100644 --- a/crates/api_models/src/user_role.rs +++ b/crates/api_models/src/user_role.rs @@ -121,9 +121,8 @@ pub enum UserStatus { #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct MerchantSelectRequest { pub merchant_ids: Vec, - // TODO: Remove this once the token only api is being used - pub need_dashboard_entry_response: Option, } + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct AcceptInvitationRequest { pub merchant_ids: Vec, diff --git a/crates/api_models/src/user_role/role.rs b/crates/api_models/src/user_role/role.rs index 73b25c84af51..acbad9152ae3 100644 --- a/crates/api_models/src/user_role/role.rs +++ b/crates/api_models/src/user_role/role.rs @@ -1,4 +1,5 @@ -use common_enums::{EntityType, PermissionGroup, RoleScope}; +pub use common_enums::PermissionGroup; +use common_enums::{EntityType, RoleScope}; use super::Permission; @@ -17,26 +18,7 @@ pub struct UpdateRoleRequest { } #[derive(Debug, serde::Serialize)] -pub struct ListRolesResponse(pub Vec); - -#[derive(Debug, serde::Deserialize)] -pub struct GetGroupsQueryParam { - pub groups: Option, -} - -#[derive(Debug, serde::Serialize)] -#[serde(untagged)] -pub enum GetRoleFromTokenResponse { - Permissions(Vec), - Groups(Vec), -} - -#[derive(Debug, serde::Serialize)] -#[serde(untagged)] -pub enum RoleInfoResponse { - Permissions(RoleInfoWithPermissionsResponse), - Groups(RoleInfoWithGroupsResponse), -} +pub struct ListRolesResponse(pub Vec); #[derive(Debug, serde::Serialize)] pub struct RoleInfoWithPermissionsResponse { diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index ace8c3babc6e..93a557d56882 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -121,42 +121,10 @@ pub async fn get_user_details( )) } -pub async fn signup( - state: SessionState, - request: user_api::SignUpRequest, -) -> UserResponse> { - 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_org_level_user_role_in_db( - state.clone(), - common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), - UserStatus::Active, - None, - ) - .await?; - utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - - let token = - utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) - .await?; - let response = - utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?; - - auth::cookies::set_cookie_response(user_api::TokenOrPayloadResponse::Payload(response), token) -} - pub async fn signup_token_only_flow( state: SessionState, request: user_api::SignUpRequest, -) -> UserResponse> { +) -> UserResponse { let new_user = domain::NewUser::try_from(request)?; new_user .get_new_merchant() @@ -182,61 +150,17 @@ pub async fn signup_token_only_flow( .get_token_with_user_role(&state, &user_role) .await?; - let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + let response = user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), - }); + }; auth::cookies::set_cookie_response(response, token) } -pub async fn signin( - state: SessionState, - request: user_api::SignInRequest, -) -> UserResponse> { - let user_from_db: domain::UserFromStorage = state - .global_store - .find_user_by_email(&request.email) - .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) - .await - .to_not_found_response(UserErrors::InternalServerError) - .attach_printable("User role with preferred_merchant_id not found")?; - domain::SignInWithRoleStrategyType::SingleRole(domain::SignInWithSingleRoleStrategy { - user: user_from_db, - user_role: Box::new(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? - }; - - let response = signin_strategy.get_signin_response(&state).await?; - let token = utils::user::get_token_from_signin_response(&response); - auth::cookies::set_cookie_response(user_api::TokenOrPayloadResponse::Payload(response), token) -} - pub async fn signin_token_only_flow( state: SessionState, request: user_api::SignInRequest, -) -> UserResponse> { +) -> UserResponse { let user_from_db: domain::UserFromStorage = state .global_store .find_user_by_email(&request.email) @@ -251,10 +175,10 @@ pub async fn signin_token_only_flow( let token = next_flow.get_token(&state).await?; - let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + let response = user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), - }); + }; auth::cookies::set_cookie_response(response, token) } @@ -573,105 +497,11 @@ pub async fn reset_password_token_only_flow( auth::cookies::remove_cookie_response() } -#[cfg(feature = "email")] -pub async fn reset_password( - state: SessionState, - request: user_api::ResetPasswordRequest, -) -> UserResponse<()> { - let token = request.token.expose(); - let email_token = auth::decode_jwt::(&token, &state) - .await - .change_context(UserErrors::LinkInvalid)?; - - auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; - - let password = domain::UserPassword::new(request.password)?; - let hash_password = utils::user::password::generate_password_hash(password.get_secret())?; - - let user = state - .global_store - .update_user_by_email( - &email_token - .get_email() - .change_context(UserErrors::InternalServerError)?, - storage_user::UserUpdate::PasswordUpdate { - password: hash_password, - }, - ) - .await - .change_context(UserErrors::InternalServerError)?; - - if let Some(inviter_merchant_id) = email_token.get_merchant_id() { - let key_manager_state = &(&state).into(); - - let key_store = state - .store - .get_merchant_key_store_by_merchant_id( - key_manager_state, - inviter_merchant_id, - &state.store.get_master_key().to_vec().into(), - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("merchant_key_store not found")?; - - let merchant_account = state - .store - .find_merchant_account_by_merchant_id( - key_manager_state, - inviter_merchant_id, - &key_store, - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("merchant_account not found")?; - - let (update_v1_result, update_v2_result) = - utils::user_role::update_v1_and_v2_user_roles_in_db( - &state, - user.user_id.clone().as_str(), - &merchant_account.organization_id, - inviter_merchant_id, - None, - UserRoleUpdate::UpdateStatus { - status: UserStatus::Active, - modified_by: user.user_id.clone(), - }, - ) - .await; - - if update_v1_result - .as_ref() - .is_err_and(|err| !err.current_context().is_db_not_found()) - || update_v2_result - .as_ref() - .is_err_and(|err| !err.current_context().is_db_not_found()) - { - return Err(report!(UserErrors::InternalServerError)); - } - - if update_v1_result.is_err() && update_v2_result.is_err() { - return Err(report!(UserErrors::InvalidRoleOperation)) - .attach_printable("User not found in the organization")?; - } - } - - let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) - .await - .map_err(|error| logger::error!(?error)); - let _ = auth::blacklist::insert_user_in_blacklist(&state, &user.user_id) - .await - .map_err(|error| logger::error!(?error)); - - auth::cookies::remove_cookie_response() -} - pub async fn invite_multiple_user( state: SessionState, user_from_token: auth::UserFromToken, requests: Vec, req_state: ReqState, - is_token_only: Option, auth_id: Option, ) -> UserResponse> { if requests.len() > 10 { @@ -680,16 +510,7 @@ pub async fn invite_multiple_user( } let responses = futures::future::join_all(requests.iter().map(|request| async { - match handle_invitation( - &state, - &user_from_token, - request, - &req_state, - is_token_only, - &auth_id, - ) - .await - { + match handle_invitation(&state, &user_from_token, request, &req_state, &auth_id).await { Ok(response) => response, Err(error) => InviteMultipleUserResponse { email: request.email.clone(), @@ -709,7 +530,6 @@ async fn handle_invitation( user_from_token: &auth::UserFromToken, request: &user_api::InviteUserRequest, req_state: &ReqState, - is_token_only: Option, auth_id: &Option, ) -> UserResult { let inviter_user = user_from_token.get_user_from_db(state).await?; @@ -756,15 +576,8 @@ async fn handle_invitation( .err() .unwrap_or(false) { - handle_new_user_invitation( - state, - user_from_token, - request, - req_state.clone(), - is_token_only, - auth_id, - ) - .await + handle_new_user_invitation(state, user_from_token, request, req_state.clone(), auth_id) + .await } else { Err(UserErrors::InternalServerError.into()) } @@ -877,7 +690,6 @@ async fn handle_new_user_invitation( user_from_token: &auth::UserFromToken, request: &user_api::InviteUserRequest, req_state: ReqState, - is_token_only: Option, auth_id: &Option, ) -> UserResult { let new_user = domain::NewUser::try_from((request.clone(), user_from_token.clone()))?; @@ -912,8 +724,6 @@ async fn handle_new_user_invitation( .await?; 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")] { @@ -921,8 +731,7 @@ async fn handle_new_user_invitation( // Will be adding actual usage for this variable later let _ = req_state.clone(); let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; - let email_contents: Box = if let Some(true) = is_token_only - { + let email_contents: Box = Box::new(email_types::InviteRegisteredUser { recipient_email: invitee_email, user_name: domain::UserName::new(new_user.get_name())?, @@ -930,17 +739,7 @@ async fn handle_new_user_invitation( subject: "You have been invited to join Hyperswitch Community!", merchant_id: user_from_token.merchant_id.clone(), auth_id: auth_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(), - auth_id: auth_id.clone(), - }) - }; + }); let send_email_result = state .email_client .compose_and_send_email(email_contents, state.conf.proxy.https_url.as_ref()) @@ -1047,115 +846,12 @@ pub async fn resend_invite( Ok(ApplicationResponse::StatusOk) } -#[cfg(feature = "email")] -pub async fn accept_invite_from_email( - state: SessionState, - request: user_api::AcceptInviteFromEmailRequest, -) -> UserResponse { - let token = request.token.expose(); - - let email_token = auth::decode_jwt::(&token, &state) - .await - .change_context(UserErrors::LinkInvalid)?; - - auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; - - let user: domain::UserFromStorage = state - .global_store - .find_user_by_email( - &email_token - .get_email() - .change_context(UserErrors::InternalServerError)?, - ) - .await - .change_context(UserErrors::InternalServerError)? - .into(); - - let merchant_id = email_token - .get_merchant_id() - .ok_or(UserErrors::InternalServerError)?; - - let key_manager_state = &(&state).into(); - - let key_store = state - .store - .get_merchant_key_store_by_merchant_id( - key_manager_state, - merchant_id, - &state.store.get_master_key().to_vec().into(), - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("merchant_key_store not found")?; - - let merchant_account = state - .store - .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("merchant_account not found")?; - - let (update_v1_result, update_v2_result) = utils::user_role::update_v1_and_v2_user_roles_in_db( - &state, - user.get_user_id(), - &merchant_account.organization_id, - merchant_id, - None, - UserRoleUpdate::UpdateStatus { - status: UserStatus::Active, - modified_by: user.get_user_id().to_string(), - }, - ) - .await; - - if update_v1_result - .as_ref() - .is_err_and(|err| !err.current_context().is_db_not_found()) - || update_v2_result - .as_ref() - .is_err_and(|err| !err.current_context().is_db_not_found()) - { - return Err(report!(UserErrors::InternalServerError)); - } - - if update_v1_result.is_err() && update_v2_result.is_err() { - return Err(report!(UserErrors::InvalidRoleOperation)) - .attach_printable("User not found in the organization")?; - } - - let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) - .await - .map_err(|error| logger::error!(?error)); - - let user_from_db: domain::UserFromStorage = state - .global_store - .update_user_by_user_id(user.get_user_id(), storage_user::UserUpdate::VerifyUser) - .await - .change_context(UserErrors::InternalServerError)? - .into(); - - let user_role = user_from_db - .get_preferred_or_active_user_role_from_db(&state) - .await - .change_context(UserErrors::InternalServerError)?; - - let token = - utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) - .await?; - utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - - let response = - utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?; - - auth::cookies::set_cookie_response(response, token) -} - #[cfg(feature = "email")] pub async fn accept_invite_from_email_token_only_flow( state: SessionState, user_token: auth::UserFromSinglePurposeToken, request: user_api::AcceptInviteFromEmailRequest, -) -> UserResponse> { +) -> UserResponse { let token = request.token.expose(); let email_token = auth::decode_jwt::(&token, &state) @@ -1261,10 +957,10 @@ pub async fn accept_invite_from_email_token_only_flow( .get_token_with_user_role(&state, &user_role) .await?; - let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + let response = user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), - }); + }; auth::cookies::set_cookie_response(response, token) } @@ -1503,7 +1199,7 @@ pub async fn list_merchants_for_user( .await .change_context(UserErrors::InternalServerError)?; - let merchant_accounts = state + let merchant_accounts_map = state .store .list_multiple_merchant_accounts( &(&state).into(), @@ -1517,17 +1213,62 @@ pub async fn list_merchants_for_user( .collect::, _>>()?, ) .await - .change_context(UserErrors::InternalServerError)?; + .change_context(UserErrors::InternalServerError)? + .into_iter() + .map(|merchant_account| (merchant_account.get_id().clone(), merchant_account)) + .collect::>(); - let roles = - utils::user_role::get_multiple_role_info_for_user_roles(&state, &user_roles).await?; + let roles_map = futures::future::try_join_all(user_roles.iter().map(|user_role| async { + let Some(merchant_id) = &user_role.merchant_id else { + return Err(report!(UserErrors::InternalServerError)) + .attach_printable("merchant_id not found for user_role"); + }; + let Some(org_id) = &user_role.org_id else { + return Err(report!(UserErrors::InternalServerError) + .attach_printable("org_id not found in user_role")); + }; + roles::RoleInfo::from_role_id(&state, &user_role.role_id, merchant_id, org_id) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable("Unable to find role info for user role") + })) + .await? + .into_iter() + .map(|role_info| (role_info.get_role_id().to_owned(), role_info)) + .collect::>(); Ok(ApplicationResponse::Json( - utils::user::get_multiple_merchant_details_with_status( - user_roles, - merchant_accounts, - roles, - )?, + user_roles + .into_iter() + .map(|user_role| { + let Some(merchant_id) = &user_role.merchant_id else { + return Err(report!(UserErrors::InternalServerError)) + .attach_printable("merchant_id not found for user_role"); + }; + let Some(org_id) = &user_role.org_id else { + return Err(report!(UserErrors::InternalServerError) + .attach_printable("org_id not found in user_role")); + }; + let merchant_account = merchant_accounts_map + .get(merchant_id) + .ok_or(UserErrors::InternalServerError) + .attach_printable("Merchant account for user role doesn't exist")?; + + let role_info = roles_map + .get(&user_role.role_id) + .ok_or(UserErrors::InternalServerError) + .attach_printable("Role info for user role doesn't exist")?; + + Ok(user_api::UserMerchantAccount { + merchant_id: merchant_id.to_owned(), + merchant_name: merchant_account.merchant_name.clone(), + is_active: user_role.status == UserStatus::Active, + role_id: user_role.role_id, + role_name: role_info.get_role_name().to_string(), + org_id: org_id.to_owned(), + }) + }) + .collect::, _>>()?, )) } @@ -1650,71 +1391,12 @@ pub async fn list_users_for_merchant_account( ))) } -#[cfg(feature = "email")] -pub async fn verify_email( - state: SessionState, - req: user_api::VerifyEmailRequest, -) -> UserResponse { - let token = req.token.clone().expose(); - let email_token = auth::decode_jwt::(&token, &state) - .await - .change_context(UserErrors::LinkInvalid)?; - - auth::blacklist::check_email_token_in_blacklist(&state, &token).await?; - - let user = state - .global_store - .find_user_by_email( - &email_token - .get_email() - .change_context(UserErrors::InternalServerError)?, - ) - .await - .change_context(UserErrors::InternalServerError)?; - - let user = state - .global_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 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) - .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: Box::new(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? - }; - - let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token) - .await - .map_err(|error| logger::error!(?error)); - - let response = signin_strategy.get_signin_response(&state).await?; - let token = utils::user::get_token_from_signin_response(&response); - auth::cookies::set_cookie_response(response, token) -} - #[cfg(feature = "email")] pub async fn verify_email_token_only_flow( state: SessionState, user_token: auth::UserFromSinglePurposeToken, req: user_api::VerifyEmailRequest, -) -> UserResponse> { +) -> UserResponse { let token = req.token.clone().expose(); let email_token = auth::decode_jwt::(&token, &state) .await @@ -1754,10 +1436,10 @@ pub async fn verify_email_token_only_flow( let next_flow = current_flow.next(user_from_db, &state).await?; let token = next_flow.get_token(&state).await?; - let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + let response = user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), - }); + }; auth::cookies::set_cookie_response(response, token) } @@ -2839,24 +2521,7 @@ pub async fn switch_org_for_user( ))? .to_owned(); - let merchant_id = if let Some(merchant_id) = &user_role.merchant_id { - merchant_id.clone() - } else { - state - .store - .list_merchant_accounts_by_organization_id( - key_manager_state, - request.org_id.get_string_repr(), - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list merchant accounts by organization_id")? - .first() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No merchant account found for the given organization_id")? - .get_id() - .clone() - }; + let merchant_id = utils::user_role::get_single_merchant_id(&state, &user_role).await?; let profile_id = if let Some(profile_id) = &user_role.profile_id { profile_id.clone() diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 971b34b06671..4204e91678d8 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -24,20 +24,6 @@ pub mod role; use common_enums::{EntityType, PermissionGroup}; use strum::IntoEnumIterator; -// TODO: To be deprecated once groups are stable -pub async fn get_authorization_info_with_modules( - _state: SessionState, -) -> UserResponse { - Ok(ApplicationResponse::Json( - user_role_api::AuthorizationInfoResponse( - info::get_module_authorization_info() - .into_iter() - .map(|module_info| user_role_api::AuthorizationInfo::Module(module_info.into())) - .collect(), - ), - )) -} - pub async fn get_authorization_info_with_groups( _state: SessionState, ) -> UserResponse { @@ -50,6 +36,7 @@ pub async fn get_authorization_info_with_groups( ), )) } + pub async fn get_authorization_info_with_group_tag( ) -> UserResponse { static GROUPS_WITH_PARENT_TAGS: Lazy> = Lazy::new(|| { @@ -309,85 +296,11 @@ pub async fn accept_invitation( Ok(ApplicationResponse::StatusOk) } -pub async fn merchant_select( - state: SessionState, - user_token: auth::UserFromSinglePurposeToken, - req: user_role_api::MerchantSelectRequest, -) -> UserResponse> { - let merchant_accounts = state - .store - .list_multiple_merchant_accounts(&(&state).into(), req.merchant_ids) - .await - .change_context(UserErrors::InternalServerError)?; - - let update_result = - futures::future::join_all(merchant_accounts.iter().map(|merchant_account| async { - let (update_v1_result, update_v2_result) = - utils::user_role::update_v1_and_v2_user_roles_in_db( - &state, - user_token.user_id.as_str(), - &merchant_account.organization_id, - merchant_account.get_id(), - None, - UserRoleUpdate::UpdateStatus { - status: UserStatus::Active, - modified_by: user_token.user_id.clone(), - }, - ) - .await; - - if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found()) - || update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found()) - { - Err(report!(UserErrors::InternalServerError)) - } else { - Ok(()) - } - })) - .await; - - if update_result.iter().all(Result::is_err) { - return Err(UserErrors::MerchantIdNotFound.into()); - } - - if let Some(true) = req.need_dashboard_entry_response { - let user_from_db: domain::UserFromStorage = state - .global_store - .find_user_by_id(user_token.user_id.as_str()) - .await - .change_context(UserErrors::InternalServerError)? - .into(); - - let user_role = user_from_db - .get_preferred_or_active_user_role_from_db(&state) - .await - .change_context(UserErrors::InternalServerError)?; - - utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - - let token = - utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) - .await?; - let response = utils::user::get_dashboard_entry_response( - &state, - user_from_db, - user_role, - token.clone(), - )?; - return auth::cookies::set_cookie_response( - user_api::TokenOrPayloadResponse::Payload(response), - token, - ); - } - - Ok(ApplicationResponse::StatusOk) -} - pub async fn merchant_select_token_only_flow( state: SessionState, user_token: auth::UserFromSinglePurposeToken, req: user_role_api::MerchantSelectRequest, -) -> UserResponse> { +) -> UserResponse { let merchant_accounts = state .store .list_multiple_merchant_accounts(&(&state).into(), req.merchant_ids) @@ -444,10 +357,10 @@ pub async fn merchant_select_token_only_flow( .get_token_with_user_role(&state, &user_role) .await?; - let response = user_api::TokenOrPayloadResponse::Token(user_api::TokenResponse { + let response = user_api::TokenResponse { token: token.clone(), token_type: next_flow.get_flow().into(), - }); + }; auth::cookies::set_cookie_response(response, token) } diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs index d79b3136422e..7a2c70205126 100644 --- a/crates/router/src/core/user_role/role.rs +++ b/crates/router/src/core/user_role/role.rs @@ -16,30 +16,10 @@ use crate::{ utils, }; -pub async fn get_role_from_token_with_permissions( - state: SessionState, - user_from_token: UserFromToken, -) -> UserResponse { - let role_info = user_from_token - .get_role_info_from_db(&state) - .await - .attach_printable("Invalid role_id in JWT")?; - - let permissions = role_info - .get_permissions_set() - .into_iter() - .map(Into::into) - .collect(); - - Ok(ApplicationResponse::Json( - role_api::GetRoleFromTokenResponse::Permissions(permissions), - )) -} - pub async fn get_role_from_token_with_groups( state: SessionState, user_from_token: UserFromToken, -) -> UserResponse { +) -> UserResponse> { let role_info = user_from_token .get_role_info_from_db(&state) .await @@ -47,9 +27,7 @@ pub async fn get_role_from_token_with_groups( let permissions = role_info.get_permission_groups().to_vec(); - Ok(ApplicationResponse::Json( - role_api::GetRoleFromTokenResponse::Groups(permissions), - )) + Ok(ApplicationResponse::Json(permissions)) } pub async fn create_role( @@ -105,56 +83,6 @@ pub async fn create_role( )) } -// TODO: To be deprecated once groups are stable -pub async fn list_invitable_roles_with_permissions( - state: SessionState, - user_from_token: UserFromToken, -) -> UserResponse { - let predefined_roles_map = PREDEFINED_ROLES - .iter() - .filter(|(_, role_info)| role_info.is_invitable()) - .map(|(role_id, role_info)| { - role_api::RoleInfoResponse::Permissions(role_api::RoleInfoWithPermissionsResponse { - permissions: role_info - .get_permissions_set() - .into_iter() - .map(Into::into) - .collect(), - role_id: role_id.to_string(), - role_name: role_info.get_role_name().to_string(), - role_scope: role_info.get_scope(), - }) - }); - - let custom_roles_map = state - .store - .list_all_roles(&user_from_token.merchant_id, &user_from_token.org_id) - .await - .change_context(UserErrors::InternalServerError)? - .into_iter() - .filter_map(|role| { - let role_info = roles::RoleInfo::from(role); - role_info - .is_invitable() - .then_some(role_api::RoleInfoResponse::Permissions( - role_api::RoleInfoWithPermissionsResponse { - permissions: role_info - .get_permissions_set() - .into_iter() - .map(Into::into) - .collect(), - role_id: role_info.get_role_id().to_string(), - role_name: role_info.get_role_name().to_string(), - role_scope: role_info.get_scope(), - }, - )) - }); - - Ok(ApplicationResponse::Json(role_api::ListRolesResponse( - predefined_roles_map.chain(custom_roles_map).collect(), - ))) -} - pub async fn list_invitable_roles_with_groups( state: SessionState, user_from_token: UserFromToken, @@ -162,14 +90,14 @@ pub async fn list_invitable_roles_with_groups( let predefined_roles_map = PREDEFINED_ROLES .iter() .filter(|(_, role_info)| role_info.is_invitable()) - .map(|(role_id, role_info)| { - role_api::RoleInfoResponse::Groups(role_api::RoleInfoWithGroupsResponse { + .map( + |(role_id, role_info)| role_api::RoleInfoWithGroupsResponse { groups: role_info.get_permission_groups().to_vec(), role_id: role_id.to_string(), role_name: role_info.get_role_name().to_string(), role_scope: role_info.get_scope(), - }) - }); + }, + ); let custom_roles_map = state .store @@ -181,14 +109,12 @@ pub async fn list_invitable_roles_with_groups( let role_info = roles::RoleInfo::from(role); role_info .is_invitable() - .then_some(role_api::RoleInfoResponse::Groups( - role_api::RoleInfoWithGroupsResponse { - groups: role_info.get_permission_groups().to_vec(), - role_id: role_info.get_role_id().to_string(), - role_name: role_info.get_role_name().to_string(), - role_scope: role_info.get_scope(), - }, - )) + .then_some(role_api::RoleInfoWithGroupsResponse { + groups: role_info.get_permission_groups().to_vec(), + role_id: role_info.get_role_id().to_string(), + role_name: role_info.get_role_name().to_string(), + role_scope: role_info.get_scope(), + }) }); Ok(ApplicationResponse::Json(role_api::ListRolesResponse( @@ -196,46 +122,11 @@ pub async fn list_invitable_roles_with_groups( ))) } -// TODO: To be deprecated once groups are stable -pub async fn get_role_with_permissions( - state: SessionState, - user_from_token: UserFromToken, - role: role_api::GetRoleRequest, -) -> UserResponse { - let role_info = roles::RoleInfo::from_role_id( - &state, - &role.role_id, - &user_from_token.merchant_id, - &user_from_token.org_id, - ) - .await - .to_not_found_response(UserErrors::InvalidRoleId)?; - - if role_info.is_internal() { - return Err(UserErrors::InvalidRoleId.into()); - } - - let permissions = role_info - .get_permissions_set() - .into_iter() - .map(Into::into) - .collect(); - - Ok(ApplicationResponse::Json( - role_api::RoleInfoResponse::Permissions(role_api::RoleInfoWithPermissionsResponse { - permissions, - role_id: role.role_id, - role_name: role_info.get_role_name().to_string(), - role_scope: role_info.get_scope(), - }), - )) -} - pub async fn get_role_with_groups( state: SessionState, user_from_token: UserFromToken, role: role_api::GetRoleRequest, -) -> UserResponse { +) -> UserResponse { let role_info = roles::RoleInfo::from_role_id( &state, &role.role_id, @@ -250,12 +141,12 @@ pub async fn get_role_with_groups( } Ok(ApplicationResponse::Json( - role_api::RoleInfoResponse::Groups(role_api::RoleInfoWithGroupsResponse { + role_api::RoleInfoWithGroupsResponse { groups: role_info.get_permission_groups().to_vec(), role_id: role.role_id, role_name: role_info.get_role_name().to_string(), role_scope: role_info.get_scope(), - }), + }, )) } diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 570906327257..eadd1ef4a5bc 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -357,11 +357,7 @@ impl UserRoleInterface for MockDb { for user_role in user_roles.iter() { let Some(user_role_merchant_id) = &user_role.merchant_id else { - return Err(errors::StorageError::DatabaseError( - report!(errors::DatabaseError::Others) - .attach_printable("merchant_id not found for user_role"), - ) - .into()); + continue; }; if user_role.user_id == user_id && user_role_merchant_id == merchant_id diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 8201380cc29f..42f46e39d2ec 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1681,6 +1681,7 @@ impl User { route = route .service(web::resource("").route(web::get().to(get_user_details))) + .service(web::resource("/signin").route(web::post().to(user_signin))) .service(web::resource("/v2/signin").route(web::post().to(user_signin))) // signin/signup with sso using openidconnect .service(web::resource("/oidc").route(web::post().to(sso_sign))) @@ -1796,6 +1797,7 @@ 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("/v2/verify_email").route(web::post().to(verify_email))) .service( web::resource("/verify_email_request") diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index c52c5d1321c7..1d5e3d601b68 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -62,22 +62,16 @@ pub async fn user_signup( state: web::Data, http_req: HttpRequest, json_payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::UserSignUp; let req_payload = json_payload.into_inner(); - let is_token_only = query.into_inner().token_only; Box::pin(api::server_wrap( flow.clone(), state, &http_req, req_payload.clone(), |state, _: (), req_body, _| async move { - if let Some(true) = is_token_only { - user_core::signup_token_only_flow(state, req_body).await - } else { - user_core::signup(state, req_body).await - } + user_core::signup_token_only_flow(state, req_body).await }, &auth::NoAuth, api_locking::LockAction::NotApplicable, @@ -89,22 +83,16 @@ pub async fn user_signin( state: web::Data, http_req: HttpRequest, json_payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::UserSignIn; let req_payload = json_payload.into_inner(); - let is_token_only = query.into_inner().token_only; Box::pin(api::server_wrap( flow.clone(), state, &http_req, req_payload.clone(), |state, _: (), req_body, _| async move { - if let Some(true) = is_token_only { - user_core::signin_token_only_flow(state, req_body).await - } else { - user_core::signin(state, req_body).await - } + user_core::signin_token_only_flow(state, req_body).await }, &auth::NoAuth, api_locking::LockAction::NotApplicable, @@ -409,46 +397,27 @@ pub async fn reset_password( state: web::Data, req: HttpRequest, payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::ResetPassword; - let is_token_only = query.into_inner().token_only; - if let Some(true) = is_token_only { - Box::pin(api::server_wrap( - flow, - state.clone(), - &req, - payload.into_inner(), - |state, user, payload, _| { - user_core::reset_password_token_only_flow(state, user, payload) - }, - &auth::SinglePurposeJWTAuth(TokenPurpose::ResetPassword), - api_locking::LockAction::NotApplicable, - )) - .await - } else { - Box::pin(api::server_wrap( - flow, - state.clone(), - &req, - payload.into_inner(), - |state, _: (), payload, _| user_core::reset_password(state, payload), - &auth::NoAuth, - api_locking::LockAction::NotApplicable, - )) - .await - } + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload.into_inner(), + |state, user, payload, _| user_core::reset_password_token_only_flow(state, user, payload), + &auth::SinglePurposeJWTAuth(TokenPurpose::ResetPassword), + api_locking::LockAction::NotApplicable, + )) + .await } pub async fn invite_multiple_user( state: web::Data, req: HttpRequest, payload: web::Json>, - token_only_query_param: web::Query, auth_id_query_param: web::Query, ) -> HttpResponse { let flow = Flow::InviteMultipleUser; - let is_token_only = token_only_query_param.into_inner().token_only; let auth_id = auth_id_query_param.into_inner().auth_id; Box::pin(api::server_wrap( flow, @@ -456,14 +425,7 @@ pub async fn invite_multiple_user( &req, payload.into_inner(), |state, user, payload, req_state| { - user_core::invite_multiple_user( - state, - user, - payload, - req_state, - is_token_only, - auth_id.clone(), - ) + user_core::invite_multiple_user(state, user, payload, req_state, auth_id.clone()) }, &auth::JWTAuth(Permission::UsersWrite), api_locking::LockAction::NotApplicable, @@ -499,37 +461,20 @@ pub async fn accept_invite_from_email( state: web::Data, req: HttpRequest, payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::AcceptInviteFromEmail; - let is_token_only = query.into_inner().token_only; - if let Some(true) = is_token_only { - Box::pin(api::server_wrap( - flow.clone(), - state, - &req, - payload.into_inner(), - |state, user, req_payload, _| { - user_core::accept_invite_from_email_token_only_flow(state, user, req_payload) - }, - &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvitationFromEmail), - api_locking::LockAction::NotApplicable, - )) - .await - } else { - Box::pin(api::server_wrap( - flow, - state.clone(), - &req, - payload.into_inner(), - |state, _: (), request_payload, _| { - user_core::accept_invite_from_email(state, request_payload) - }, - &auth::NoAuth, - api_locking::LockAction::NotApplicable, - )) - .await - } + Box::pin(api::server_wrap( + flow.clone(), + state, + &req, + payload.into_inner(), + |state, user, req_payload, _| { + user_core::accept_invite_from_email_token_only_flow(state, user, req_payload) + }, + &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvitationFromEmail), + api_locking::LockAction::NotApplicable, + )) + .await } #[cfg(feature = "email")] @@ -537,35 +482,20 @@ pub async fn verify_email( state: web::Data, http_req: HttpRequest, json_payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::VerifyEmail; - let is_token_only = query.into_inner().token_only; - if let Some(true) = is_token_only { - Box::pin(api::server_wrap( - flow.clone(), - state, - &http_req, - json_payload.into_inner(), - |state, user, req_payload, _| { - user_core::verify_email_token_only_flow(state, user, req_payload) - }, - &auth::SinglePurposeJWTAuth(TokenPurpose::VerifyEmail), - api_locking::LockAction::NotApplicable, - )) - .await - } else { - Box::pin(api::server_wrap( - flow.clone(), - state, - &http_req, - json_payload.into_inner(), - |state, _: (), req_payload, _| user_core::verify_email(state, req_payload), - &auth::NoAuth, - api_locking::LockAction::NotApplicable, - )) - .await - } + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + json_payload.into_inner(), + |state, user, req_payload, _| { + user_core::verify_email_token_only_flow(state, user, req_payload) + }, + &auth::SinglePurposeJWTAuth(TokenPurpose::VerifyEmail), + api_locking::LockAction::NotApplicable, + )) + .await } #[cfg(feature = "email")] diff --git a/crates/router/src/routes/user_role.rs b/crates/router/src/routes/user_role.rs index a730321e1909..5f1e4301d45b 100644 --- a/crates/router/src/routes/user_role.rs +++ b/crates/router/src/routes/user_role.rs @@ -1,8 +1,5 @@ use actix_web::{web, HttpRequest, HttpResponse}; -use api_models::{ - user as user_api, - user_role::{self as user_role_api, role as role_api}, -}; +use api_models::user_role::{self as user_role_api, role as role_api}; use common_enums::TokenPurpose; use router_env::Flow; @@ -22,22 +19,15 @@ use crate::{ pub async fn get_authorization_info( state: web::Data, http_req: HttpRequest, - query: web::Query, ) -> HttpResponse { let flow = Flow::GetAuthorizationInfo; - let respond_with_groups = query.into_inner().groups.unwrap_or(false); Box::pin(api::server_wrap( flow, state.clone(), &http_req, (), |state, _: (), _, _| async move { - // TODO: Permissions to be deprecated once groups are stable - if respond_with_groups { - user_role_core::get_authorization_info_with_groups(state).await - } else { - user_role_core::get_authorization_info_with_modules(state).await - } + user_role_core::get_authorization_info_with_groups(state).await }, &auth::JWTAuth(Permission::UsersRead), api_locking::LockAction::NotApplicable, @@ -45,13 +35,8 @@ pub async fn get_authorization_info( .await } -pub async fn get_role_from_token( - state: web::Data, - req: HttpRequest, - query: web::Query, -) -> HttpResponse { +pub async fn get_role_from_token(state: web::Data, req: HttpRequest) -> HttpResponse { let flow = Flow::GetRoleFromToken; - let respond_with_groups = query.into_inner().groups.unwrap_or(false); Box::pin(api::server_wrap( flow, @@ -59,12 +44,7 @@ pub async fn get_role_from_token( &req, (), |state, user, _, _| async move { - // TODO: Permissions to be deprecated once groups are stable - if respond_with_groups { - role_core::get_role_from_token_with_groups(state, user).await - } else { - role_core::get_role_from_token_with_permissions(state, user).await - } + role_core::get_role_from_token_with_groups(state, user).await }, &auth::DashboardNoPermissionAuth, api_locking::LockAction::NotApplicable, @@ -90,25 +70,15 @@ pub async fn create_role( .await } -pub async fn list_all_roles( - state: web::Data, - req: HttpRequest, - query: web::Query, -) -> HttpResponse { +pub async fn list_all_roles(state: web::Data, req: HttpRequest) -> HttpResponse { let flow = Flow::ListRoles; - let respond_with_groups = query.into_inner().groups.unwrap_or(false); Box::pin(api::server_wrap( flow, state.clone(), &req, (), |state, user, _, _| async move { - // TODO: Permissions to be deprecated once groups are stable - if respond_with_groups { - role_core::list_invitable_roles_with_groups(state, user).await - } else { - role_core::list_invitable_roles_with_permissions(state, user).await - } + role_core::list_invitable_roles_with_groups(state, user).await }, &auth::JWTAuth(Permission::UsersRead), api_locking::LockAction::NotApplicable, @@ -120,25 +90,18 @@ pub async fn get_role( state: web::Data, req: HttpRequest, path: web::Path, - query: web::Query, ) -> HttpResponse { let flow = Flow::GetRole; let request_payload = user_role_api::role::GetRoleRequest { role_id: path.into_inner(), }; - let respond_with_groups = query.into_inner().groups.unwrap_or(false); Box::pin(api::server_wrap( flow, state.clone(), &req, request_payload, |state, user, payload, _| async move { - // TODO: Permissions to be deprecated once groups are stable - if respond_with_groups { - role_core::get_role_with_groups(state, user, payload).await - } else { - role_core::get_role_with_permissions(state, user, payload).await - } + role_core::get_role_with_groups(state, user, payload).await }, &auth::JWTAuth(Permission::UsersRead), api_locking::LockAction::NotApplicable, @@ -209,22 +172,16 @@ pub async fn merchant_select( state: web::Data, req: HttpRequest, json_payload: web::Json, - query: web::Query, ) -> HttpResponse { let flow = Flow::MerchantSelect; let payload = json_payload.into_inner(); - let is_token_only = query.into_inner().token_only; Box::pin(api::server_wrap( flow, state.clone(), &req, payload, |state, user, req_body, _| async move { - if let Some(true) = is_token_only { - user_role_core::merchant_select_token_only_flow(state, user, req_body).await - } else { - user_role_core::merchant_select(state, user, req_body).await - } + user_role_core::merchant_select_token_only_flow(state, user, req_body).await }, &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvite), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/services/authorization/predefined_permissions.rs b/crates/router/src/services/authorization/predefined_permissions.rs deleted file mode 100644 index 50f9a7196b8e..000000000000 --- a/crates/router/src/services/authorization/predefined_permissions.rs +++ /dev/null @@ -1,346 +0,0 @@ -use std::collections::HashMap; - -#[cfg(feature = "olap")] -use error_stack::ResultExt; -use once_cell::sync::Lazy; - -use super::permissions::Permission; -use crate::consts; -#[cfg(feature = "olap")] -use crate::core::errors::{UserErrors, UserResult}; - -#[allow(dead_code)] -pub struct RoleInfo { - permissions: Vec, - name: Option<&'static str>, - is_invitable: bool, - is_deletable: bool, - is_updatable: bool, -} - -impl RoleInfo { - pub fn get_permissions(&self) -> &Vec { - &self.permissions - } - - pub fn get_name(&self) -> Option<&'static str> { - self.name - } - - pub fn is_invitable(&self) -> bool { - self.is_invitable - } -} - -pub static PREDEFINED_PERMISSIONS: Lazy> = Lazy::new(|| { - let mut roles = HashMap::new(); - roles.insert( - consts::user_role::ROLE_ID_INTERNAL_ADMIN, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::PaymentWrite, - Permission::RefundRead, - Permission::RefundWrite, - Permission::ApiKeyRead, - Permission::ApiKeyWrite, - Permission::MerchantAccountRead, - Permission::MerchantAccountWrite, - Permission::MerchantConnectorAccountRead, - Permission::MerchantConnectorAccountWrite, - Permission::RoutingRead, - Permission::RoutingWrite, - Permission::ThreeDsDecisionManagerWrite, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerWrite, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::DisputeWrite, - Permission::MandateRead, - Permission::MandateWrite, - Permission::CustomerRead, - Permission::CustomerWrite, - Permission::Analytics, - Permission::UsersRead, - Permission::UsersWrite, - Permission::MerchantAccountCreate, - Permission::PayoutRead, - Permission::PayoutWrite, - ], - name: None, - is_invitable: false, - is_deletable: false, - is_updatable: false, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::RefundRead, - Permission::ApiKeyRead, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::RoutingRead, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerRead, - Permission::Analytics, - Permission::DisputeRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::UsersRead, - Permission::PayoutRead, - ], - name: None, - is_invitable: false, - is_deletable: false, - is_updatable: false, - }, - ); - - roles.insert( - consts::user_role::ROLE_ID_ORGANIZATION_ADMIN, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::PaymentWrite, - Permission::RefundRead, - Permission::RefundWrite, - Permission::ApiKeyRead, - Permission::ApiKeyWrite, - Permission::MerchantAccountRead, - Permission::MerchantAccountWrite, - Permission::MerchantConnectorAccountRead, - Permission::MerchantConnectorAccountWrite, - Permission::RoutingRead, - Permission::RoutingWrite, - Permission::ThreeDsDecisionManagerWrite, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerWrite, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::DisputeWrite, - Permission::MandateRead, - Permission::MandateWrite, - Permission::CustomerRead, - Permission::CustomerWrite, - Permission::Analytics, - Permission::UsersRead, - Permission::UsersWrite, - Permission::MerchantAccountCreate, - Permission::PayoutRead, - Permission::PayoutWrite, - ], - name: Some("Organization Admin"), - is_invitable: false, - is_deletable: false, - is_updatable: false, - }, - ); - - // MERCHANT ROLES - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_ADMIN, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::PaymentWrite, - Permission::RefundRead, - Permission::RefundWrite, - Permission::ApiKeyRead, - Permission::ApiKeyWrite, - Permission::MerchantAccountRead, - Permission::MerchantAccountWrite, - Permission::MerchantConnectorAccountRead, - Permission::MerchantConnectorAccountWrite, - Permission::RoutingRead, - Permission::RoutingWrite, - Permission::ThreeDsDecisionManagerWrite, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerWrite, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::DisputeWrite, - Permission::MandateRead, - Permission::MandateWrite, - Permission::CustomerRead, - Permission::CustomerWrite, - Permission::Analytics, - Permission::UsersRead, - Permission::UsersWrite, - Permission::PayoutRead, - Permission::PayoutWrite, - ], - name: Some("Admin"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_VIEW_ONLY, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::RefundRead, - Permission::ApiKeyRead, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::RoutingRead, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::Analytics, - Permission::UsersRead, - Permission::PayoutRead, - ], - name: Some("View Only"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_IAM_ADMIN, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::RefundRead, - Permission::ApiKeyRead, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::RoutingRead, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::Analytics, - Permission::UsersRead, - Permission::UsersWrite, - Permission::PayoutRead, - ], - name: Some("IAM"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_DEVELOPER, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::RefundRead, - Permission::ApiKeyRead, - Permission::ApiKeyWrite, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::RoutingRead, - Permission::ThreeDsDecisionManagerRead, - Permission::SurchargeDecisionManagerRead, - Permission::DisputeRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::Analytics, - Permission::UsersRead, - Permission::PayoutRead, - ], - name: Some("Developer"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_OPERATOR, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::PaymentWrite, - Permission::RefundRead, - Permission::RefundWrite, - Permission::ApiKeyRead, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::MerchantConnectorAccountWrite, - Permission::RoutingRead, - Permission::RoutingWrite, - Permission::ThreeDsDecisionManagerRead, - Permission::ThreeDsDecisionManagerWrite, - Permission::SurchargeDecisionManagerRead, - Permission::SurchargeDecisionManagerWrite, - Permission::DisputeRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::Analytics, - Permission::UsersRead, - Permission::PayoutRead, - Permission::PayoutWrite, - ], - name: Some("Operator"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles.insert( - consts::user_role::ROLE_ID_MERCHANT_CUSTOMER_SUPPORT, - RoleInfo { - permissions: vec![ - Permission::PaymentRead, - Permission::RefundRead, - Permission::RefundWrite, - Permission::DisputeRead, - Permission::DisputeWrite, - Permission::MerchantAccountRead, - Permission::MerchantConnectorAccountRead, - Permission::MandateRead, - Permission::CustomerRead, - Permission::Analytics, - Permission::PayoutRead, - ], - name: Some("Customer Support"), - is_invitable: true, - is_deletable: true, - is_updatable: true, - }, - ); - roles -}); - -pub fn get_role_name_from_id(role_id: &str) -> Option<&'static str> { - PREDEFINED_PERMISSIONS - .get(role_id) - .and_then(|role_info| role_info.name) -} - -#[cfg(feature = "olap")] -pub fn is_role_invitable(role_id: &str) -> UserResult { - PREDEFINED_PERMISSIONS - .get(role_id) - .map(|role_info| role_info.is_invitable) - .ok_or(UserErrors::InvalidRoleId.into()) - .attach_printable(format!("role_id = {} doesn't exist", role_id)) -} - -#[cfg(feature = "olap")] -pub fn is_role_deletable(role_id: &str) -> UserResult { - PREDEFINED_PERMISSIONS - .get(role_id) - .map(|role_info| role_info.is_deletable) - .ok_or(UserErrors::InvalidRoleId.into()) - .attach_printable(format!("role_id = {} doesn't exist", role_id)) -} - -#[cfg(feature = "olap")] -pub fn is_role_updatable(role_id: &str) -> UserResult { - PREDEFINED_PERMISSIONS - .get(role_id) - .map(|role_info| role_info.is_updatable) - .ok_or(UserErrors::InvalidRoleId.into()) - .attach_printable(format!("role_id = {} doesn't exist", role_id)) -} diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 42cb71458d54..58a0a68df51f 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, ops, str::FromStr}; use api_models::{ admin as admin_api, organization as api_org, user as user_api, user_role as user_role_api, }; -use common_enums::{EntityType, TokenPurpose}; +use common_enums::EntityType; use common_utils::{ crypto::Encryptable, errors::CustomResult, id_type, new_type::MerchantName, pii, type_name, types::keymanager::Identifier, @@ -32,13 +32,9 @@ use crate::{ }, db::{user_role::InsertUserRolePayload, GlobalStorageInterface}, routes::SessionState, - services::{ - self, - authentication::{self as auth, UserFromToken}, - authorization::info, - }, + services::{self, authentication::UserFromToken, authorization::info}, types::transformers::ForeignFrom, - utils::{self, user::password}, + utils::user::password, }; pub mod dashboard_metadata; @@ -1115,130 +1111,6 @@ impl From for user_role_api::PermissionModule { } } -pub enum SignInWithRoleStrategyType { - SingleRole(SignInWithSingleRoleStrategy), - MultipleRoles(SignInWithMultipleRolesStrategy), -} - -impl SignInWithRoleStrategyType { - pub async fn decide_signin_strategy_by_user_roles( - user: UserFromStorage, - user_roles: Vec, - ) -> UserResult { - 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: Box::new(user_role.clone()), - })) - } else { - Ok(Self::MultipleRoles(SignInWithMultipleRolesStrategy { - user, - user_roles, - })) - } - } - - pub async fn get_signin_response( - self, - state: &SessionState, - ) -> UserResult { - 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: Box, -} - -impl SignInWithSingleRoleStrategy { - async fn get_signin_response( - self, - state: &SessionState, - ) -> UserResult { - let token = utils::user::generate_jwt_auth_token_without_profile( - state, - &self.user, - &self.user_role, - ) - .await?; - utils::user_role::set_role_permissions_in_cache_by_user_role(state, &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, -} - -impl SignInWithMultipleRolesStrategy { - async fn get_signin_response( - self, - state: &SessionState, - ) -> UserResult { - let merchant_accounts = state - .store - .list_multiple_merchant_accounts( - &state.into(), - self.user_roles - .iter() - .map(|role| { - role.merchant_id - .clone() - .ok_or(UserErrors::InternalServerError) - }) - .collect::, _>>()?, - ) - .await - .change_context(UserErrors::InternalServerError)?; - - let roles = - utils::user_role::get_multiple_role_info_for_user_roles(state, &self.user_roles) - .await?; - - let merchant_details = utils::user::get_multiple_merchant_details_with_status( - self.user_roles, - merchant_accounts, - roles, - )?; - - Ok(user_api::SignInResponse::MerchantSelect( - user_api::MerchantSelectResponse { - name: self.user.get_name(), - email: self.user.get_email(), - token: auth::SinglePurposeToken::new_token( - self.user.get_user_id().to_string(), - TokenPurpose::AcceptInvite, - Origin::SignIn, - &state.conf, - vec![], - ) - .await? - .into(), - merchants: merchant_details, - verification_days_left: utils::user::get_verification_days_left(state, &self.user)?, - }, - )) - } -} - impl ForeignFrom for user_role_api::UserStatus { fn foreign_from(value: UserStatus) -> Self { match value { diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 92f1d4ba5baf..4206b16df6db 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -1,6 +1,6 @@ use common_enums::TokenPurpose; use diesel_models::{enums::UserStatus, user_role::UserRole}; -use error_stack::report; +use error_stack::{report, ResultExt}; use masking::Secret; use super::UserFromStorage; @@ -109,16 +109,14 @@ impl JWTFlow { ) -> UserResult> { auth::AuthToken::new_token( next_flow.user.get_user_id().to_string(), - user_role - .merchant_id - .clone() - .ok_or(report!(UserErrors::InternalServerError))?, + utils::user_role::get_single_merchant_id(state, user_role).await?, user_role.role_id.clone(), &state.conf, user_role .org_id .clone() - .ok_or(report!(UserErrors::InternalServerError))?, + .ok_or(report!(UserErrors::InternalServerError)) + .attach_printable("org_id not found")?, None, ) .await diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index b2170e7294a0..a1bd6972dab5 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -1,11 +1,11 @@ -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use api_models::user as user_api; use common_enums::UserAuthType; use common_utils::{ encryption::Encryption, errors::CustomResult, id_type, type_name, types::keymanager::Identifier, }; -use diesel_models::{enums::UserStatus, user_role::UserRole}; +use diesel_models::user_role::UserRole; use error_stack::{report, ResultExt}; use masking::{ExposeInterface, Secret}; use redis_interface::RedisConnectionPool; @@ -128,28 +128,6 @@ pub async fn generate_jwt_auth_token_with_attributes( Ok(Secret::new(token)) } -pub fn get_dashboard_entry_response( - state: &SessionState, - user: UserFromStorage, - user_role: UserRole, - token: Secret, -) -> UserResult { - let verification_days_left = get_verification_days_left(state, &user)?; - - Ok(user_api::DashboardEntryResponse { - merchant_id: user_role.merchant_id.ok_or( - report!(UserErrors::InternalServerError) - .attach_printable("merchant_id not found for user_role"), - )?, - token, - name: user.get_name(), - email: user.get_email(), - user_id: user.get_user_id().to_string(), - verification_days_left, - user_role: user_role.role_id, - }) -} - #[allow(unused_variables)] pub fn get_verification_days_left( state: &SessionState, @@ -161,54 +139,6 @@ pub fn get_verification_days_left( return Ok(None); } -pub fn get_multiple_merchant_details_with_status( - user_roles: Vec, - merchant_accounts: Vec, - roles: Vec, -) -> UserResult> { - let merchant_account_map = merchant_accounts - .into_iter() - .map(|merchant_account| (merchant_account.get_id().clone(), merchant_account)) - .collect::>(); - - let role_map = roles - .into_iter() - .map(|role_info| (role_info.get_role_id().to_string(), role_info)) - .collect::>(); - - user_roles - .into_iter() - .map(|user_role| { - let Some(merchant_id) = &user_role.merchant_id else { - return Err(report!(UserErrors::InternalServerError)) - .attach_printable("merchant_id not found for user_role"); - }; - let Some(org_id) = &user_role.org_id else { - return Err(report!(UserErrors::InternalServerError) - .attach_printable("org_id not found in user_role")); - }; - let merchant_account = merchant_account_map - .get(merchant_id) - .ok_or(UserErrors::InternalServerError) - .attach_printable("Merchant account for user role doesn't exist")?; - - let role_info = role_map - .get(&user_role.role_id) - .ok_or(UserErrors::InternalServerError) - .attach_printable("Role info for user role doesn't exist")?; - - Ok(user_api::UserMerchantAccount { - merchant_id: merchant_id.to_owned(), - merchant_name: merchant_account.merchant_name.clone(), - is_active: user_role.status == UserStatus::Active, - role_id: user_role.role_id, - role_name: role_info.get_role_name().to_string(), - org_id: org_id.to_owned(), - }) - }) - .collect() -} - pub async fn get_user_from_db_by_email( state: &SessionState, email: domain::UserEmail, @@ -220,13 +150,6 @@ pub async fn get_user_from_db_by_email( .map(UserFromStorage::from) } -pub fn get_token_from_signin_response(resp: &user_api::SignInResponse) -> Secret { - match resp { - user_api::SignInResponse::DashboardEntry(data) => data.token.clone(), - user_api::SignInResponse::MerchantSelect(data) => data.token.clone(), - } -} - pub fn get_redis_connection(state: &SessionState) -> UserResult> { state .store diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index 1791b5308b44..dbe3ed6d5851 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use api_models::user_role as user_role_api; -use common_enums::PermissionGroup; +use common_enums::{EntityType, PermissionGroup}; use common_utils::id_type; use diesel_models::{ enums::UserRoleVersion, @@ -13,7 +13,7 @@ use storage_impl::errors::StorageError; use crate::{ consts, - core::errors::{StorageErrorExt, UserErrors, UserResult}, + core::errors::{UserErrors, UserResult}, routes::SessionState, services::authorization::{self as authz, permissions::Permission, roles}, types::domain, @@ -167,28 +167,6 @@ pub async fn set_role_permissions_in_cache_if_required( .attach_printable("Error setting permissions in redis") } -pub async fn get_multiple_role_info_for_user_roles( - state: &SessionState, - user_roles: &[UserRole], -) -> UserResult> { - futures::future::try_join_all(user_roles.iter().map(|user_role| async { - let Some(merchant_id) = &user_role.merchant_id else { - return Err(report!(UserErrors::InternalServerError)) - .attach_printable("merchant_id not found for user_role"); - }; - let Some(org_id) = &user_role.org_id else { - return Err(report!(UserErrors::InternalServerError) - .attach_printable("org_id not found in user_role")); - }; - let role = roles::RoleInfo::from_role_id(state, &user_role.role_id, merchant_id, org_id) - .await - .to_not_found_response(UserErrors::InternalServerError) - .attach_printable("Role for user role doesn't exist")?; - Ok::<_, Report>(role) - })) - .await -} - pub async fn update_v1_and_v2_user_roles_in_db( state: &SessionState, user_id: &str, @@ -234,3 +212,38 @@ pub async fn update_v1_and_v2_user_roles_in_db( (updated_v1_role, updated_v2_role) } + +pub async fn get_single_merchant_id( + state: &SessionState, + user_role: &UserRole, +) -> UserResult { + match user_role.entity_type { + Some(EntityType::Organization) => Ok(state + .store + .list_merchant_accounts_by_organization_id( + &state.into(), + user_role + .org_id + .as_ref() + .ok_or(UserErrors::InternalServerError) + .attach_printable("org_id not found")? + .get_string_repr(), + ) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to get merchant list for org")? + .first() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No merchants found for org_id")? + .get_id() + .clone()), + Some(EntityType::Merchant) + | Some(EntityType::Internal) + | Some(EntityType::Profile) + | None => user_role + .merchant_id + .clone() + .ok_or(UserErrors::InternalServerError) + .attach_printable("merchant_id not found"), + } +} diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/.meta.json b/postman/collection-dir/users/Flow Testcases/Sign In/.meta.json index 84ed2cb94944..75badf2502c2 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/.meta.json +++ b/postman/collection-dir/users/Flow Testcases/Sign In/.meta.json @@ -2,7 +2,7 @@ "childrenOrder": [ "Signin", "Signin Wrong", - "Signin Token Only", - "Signin Token Only Wrong" + "Signin V2 - To be deprecated", + "Signin V2 Wrong" ] } diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/.event.meta.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/.event.meta.json similarity index 100% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/.event.meta.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/.event.meta.json diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/event.test.js b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/event.test.js similarity index 61% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/event.test.js rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/event.test.js index 9105ce122cb4..c8d654d21e98 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/event.test.js +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/event.test.js @@ -1,24 +1,24 @@ console.log("[LOG]::x-request-id - " + pm.response.headers.get("x-request-id")); // Validate status 2xx -pm.test("[POST]::user/v2/signin?token_only=true - Status code is 2xx", function () { +pm.test("[POST]::user/v2/signin - Status code is 2xx", function () { pm.response.to.be.success; }); // Validate if response header has matching content-type -pm.test("[POST]::user/v2/signin?token_only=true - Content-Type is application/json", function () { +pm.test("[POST]::user/v2/signin - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( "application/json", ); }); // Validate if response has JSON Body -pm.test("[POST]::user/v2/signin?token_only=true - Response has JSON Body", function () { +pm.test("[POST]::user/v2/signin - Response has JSON Body", function () { pm.response.to.have.jsonBody(); }); // Validate specific JSON response content -pm.test("[POST]::user/v2/signin?token_only=true - Response contains token", function () { +pm.test("[POST]::user/v2/signin - Response contains token", function () { var jsonData = pm.response.json(); pm.expect(jsonData).to.have.property("token"); pm.expect(jsonData.token).to.be.a("string").and.to.not.be.empty; diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/request.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/request.json similarity index 75% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/request.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/request.json index 62028501a501..1d23f6e9652f 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/request.json +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/request.json @@ -18,7 +18,7 @@ } }, "url": { - "raw": "{{baseUrl}}/user/v2/signin?token_only=true", + "raw": "{{baseUrl}}/user/v2/signin", "host": [ "{{baseUrl}}" ], @@ -26,12 +26,6 @@ "user", "v2", "signin" - ], - "query": [ - { - "key": "token_only", - "value": "true" - } ] } } diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/response.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/response.json similarity index 100% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/response.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 - To be deprecated/response.json diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/.event.meta.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/.event.meta.json similarity index 100% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/.event.meta.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/.event.meta.json diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/event.test.js b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/event.test.js similarity index 57% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/event.test.js rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/event.test.js index a92975384520..3cc82ebb013c 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/event.test.js +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/event.test.js @@ -1,18 +1,18 @@ console.log("[LOG]::x-request-id - " + pm.response.headers.get("x-request-id")); // Validate status 4xx -pm.test("[POST]::/user/v2/signin?token_only=true - Status code is 401", function () { +pm.test("[POST]::/user/v2/signin - Status code is 401", function () { pm.response.to.have.status(401); }); // Validate if response header has matching content-type -pm.test("[POST]::user/v2/signin?token_only=true - Content-Type is application/json", function () { +pm.test("[POST]::user/v2/signin - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( "application/json", ); }); // Validate if response has JSON Body -pm.test("[POST]::user/v2/signin?token_only=true - Response has JSON Body", function () { +pm.test("[POST]::user/v2/signin - Response has JSON Body", function () { pm.response.to.have.jsonBody(); }); \ No newline at end of file diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/request.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/request.json similarity index 75% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/request.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/request.json index 7fee4c465c84..775b0972d57f 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only Wrong/request.json +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/request.json @@ -18,7 +18,7 @@ } }, "url": { - "raw": "{{baseUrl}}/user/v2/signin?token_only=true", + "raw": "{{baseUrl}}/user/v2/signin", "host": [ "{{baseUrl}}" ], @@ -26,12 +26,6 @@ "user", "v2", "signin" - ], - "query": [ - { - "key": "token_only", - "value": "true" - } ] } } diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/response.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/response.json similarity index 100% rename from postman/collection-dir/users/Flow Testcases/Sign In/Signin Token Only/response.json rename to postman/collection-dir/users/Flow Testcases/Sign In/Signin V2 Wrong/response.json diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/event.test.js b/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/event.test.js index 6cb6b69ba6c9..bd346bc0cb3a 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/event.test.js +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/event.test.js @@ -1,18 +1,18 @@ console.log("[LOG]::x-request-id - " + pm.response.headers.get("x-request-id")); // Validate status code is 4xx Bad Request -pm.test("[POST]::/user/v2/signin - Status code is 401", function () { +pm.test("[POST]::/user/signin - Status code is 401", function () { pm.response.to.have.status(401); }); // Validate if response header has matching content-type -pm.test("[POST]::/user/v2/signin - Content-Type is application/json", function () { +pm.test("[POST]::/user/signin - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( "application/json", ); }); // Validate if response has JSON Body -pm.test("[POST]::/user/v2/signin - Response has JSON Body", function () { +pm.test("[POST]::/user/signin - Response has JSON Body", function () { pm.response.to.have.jsonBody(); }); \ No newline at end of file diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/request.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/request.json index 775b0972d57f..0cd97ee6de1f 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/request.json +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin Wrong/request.json @@ -18,13 +18,12 @@ } }, "url": { - "raw": "{{baseUrl}}/user/v2/signin", + "raw": "{{baseUrl}}/user/signin", "host": [ "{{baseUrl}}" ], "path": [ "user", - "v2", "signin" ] } diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin/event.test.js b/postman/collection-dir/users/Flow Testcases/Sign In/Signin/event.test.js index 4724ff5981f8..23dc84eb8a90 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin/event.test.js +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin/event.test.js @@ -1,24 +1,24 @@ console.log("[LOG]::x-request-id - " + pm.response.headers.get("x-request-id")); // Validate status 2xx -pm.test("[POST]::/user/v2/signin - Status code is 2xx", function () { +pm.test("[POST]::/user/signin - Status code is 2xx", function () { pm.response.to.be.success; }); // Validate if response header has matching content-type -pm.test("[POST]::/user/v2/signin - Content-Type is application/json", function () { +pm.test("[POST]::/user/signin - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( "application/json", ); }); // Validate if response has JSON Body -pm.test("[POST]::/user/v2/signin - Response has JSON Body", function () { +pm.test("[POST]::/user/signin - Response has JSON Body", function () { pm.response.to.have.jsonBody(); }); // Validate specific JSON response content -pm.test("[POST]::/user/v2/signin - Response contains token", function () { +pm.test("[POST]::/user/signin - Response contains token", function () { var jsonData = pm.response.json(); pm.expect(jsonData).to.have.property("token"); pm.expect(jsonData.token).to.be.a("string").and.to.not.be.empty; diff --git a/postman/collection-dir/users/Flow Testcases/Sign In/Signin/request.json b/postman/collection-dir/users/Flow Testcases/Sign In/Signin/request.json index 1d23f6e9652f..17f0eccff369 100644 --- a/postman/collection-dir/users/Flow Testcases/Sign In/Signin/request.json +++ b/postman/collection-dir/users/Flow Testcases/Sign In/Signin/request.json @@ -18,13 +18,12 @@ } }, "url": { - "raw": "{{baseUrl}}/user/v2/signin", + "raw": "{{baseUrl}}/user/signin", "host": [ "{{baseUrl}}" ], "path": [ "user", - "v2", "signin" ] } diff --git a/postman/collection-json/users.postman_collection.json b/postman/collection-json/users.postman_collection.json index 142eaf3b2329..ac6ee2f8cb02 100644 --- a/postman/collection-json/users.postman_collection.json +++ b/postman/collection-json/users.postman_collection.json @@ -1,567 +1,553 @@ { - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "item": [ - { - "name": "Health check", - "item": [ - { - "name": "New Request", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/health - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/health", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "health" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Flow Testcases", - "item": [ - { - "name": "Sign Up", - "item": [ - { - "name": "Connect Account", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", - "", - "// Validate status 2xx", - "pm.test(\"[POST]::/user/connect_account - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/user/connect_account - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/user/connect_account - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Validate specific JSON response content", - "pm.test(\"[POST]::/user/connect_account - Response contains is_email_sent\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property(\"is_email_sent\");", - " pm.expect(jsonData.is_email_sent).to.be.true;", - "});", - "", - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "", - "var baseEmail = pm.environment.get('user_base_email_for_signup');", - "var emailDomain = pm.environment.get(\"user_domain_for_signup\");", - "", - "// Generate a unique email address", - "var uniqueEmail = baseEmail + new Date().getTime() + emailDomain;", - "// Set the unique email address as an environment variable", - "pm.environment.set('unique_email', uniqueEmail);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Cookie", - "value": "Cookie_1=value" - } - ], - "body": { - "mode": "raw", - "raw": "{\"email\":\"{{unique_email}}\"}" - }, - "url": { - "raw": "{{baseUrl}}/user/connect_account", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "user", - "connect_account" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Sign In", - "item": [ - { - "name": "Signin", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", - "", - "// Validate status 2xx", - "pm.test(\"[POST]::/user/v2/signin - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/user/v2/signin - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/user/v2/signin - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Validate specific JSON response content", - "pm.test(\"[POST]::/user/v2/signin - Response contains token\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property(\"token\");", - " pm.expect(jsonData.token).to.be.a(\"string\").and.to.not.be.empty;", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Cookie", - "value": "Cookie_1=value" - } - ], - "body": { - "mode": "raw", - "raw": "{\"email\":\"{{user_email}}\",\"password\":\"{{user_password}}\"}" - }, - "url": { - "raw": "{{baseUrl}}/user/v2/signin", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "user", - "v2", - "signin" - ] - } - }, - "response": [] - }, - { - "name": "Signin Wrong", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", - "", - "// Validate status code is 4xx Bad Request", - "pm.test(\"[POST]::/user/v2/signin - Status code is 401\", function () {", - " pm.response.to.have.status(401);", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/user/v2/signin - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/user/v2/signin - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Cookie", - "value": "Cookie_1=value" - } - ], - "body": { - "mode": "raw", - "raw": "{\"email\":\"{{user_email}}\",\"password\":\"{{wrong_password}}\"}" - }, - "url": { - "raw": "{{baseUrl}}/user/v2/signin", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "user", - "v2", - "signin" - ] - } - }, - "response": [] - }, - { - "name": "Signin Token Only", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", - "", - "// Validate status 2xx", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Validate specific JSON response content", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Response contains token\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property(\"token\");", - " pm.expect(jsonData.token).to.be.a(\"string\").and.to.not.be.empty;", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Cookie", - "value": "Cookie_1=value" - } - ], - "body": { - "mode": "raw", - "raw": "{\"email\":\"{{user_email}}\",\"password\":\"{{user_password}}\"}" - }, - "url": { - "raw": "{{baseUrl}}/user/v2/signin?token_only=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "user", - "v2", - "signin" - ], - "query": [ - { - "key": "token_only", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "Signin Token Only Wrong", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", - "", - "// Validate status 4xx", - "pm.test(\"[POST]::/user/v2/signin?token_only=true - Status code is 401\", function () {", - " pm.response.to.have.status(401);", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::user/v2/signin?token_only=true - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Cookie", - "value": "Cookie_1=value" - } - ], - "body": { - "mode": "raw", - "raw": "{\"email\":\"{{user_email}}\",\"password\":\"{{wrong_password}}\"}" - }, - "url": { - "raw": "{{baseUrl}}/user/v2/signin?token_only=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "user", - "v2", - "signin" - ], - "query": [ - { - "key": "token_only", - "value": "true" - } - ] - } - }, - "response": [] - } - ] - } - ] - } - ], - "info": { - "_postman_id": "b5b40c9a-7e58-42c7-8b89-0adb208c45c9", - "name": "users", - "description": "## Get started\n\nJuspay Router provides a collection of APIs that enable you to process and manage payments. Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes. \nYou can consume the APIs directly using your favorite HTTP/REST library. \nWe have a testing environment referred to \"sandbox\", which you can setup to test API calls without affecting production data.\n\n### Base URLs\n\nUse the following base URLs when making requests to the APIs:\n\n| Environment | Base URL |\n| --- | --- |\n| Sandbox | [https://sandbox.hyperswitch.io](https://sandbox.hyperswitch.io) |\n| Production | [https://router.juspay.io](https://router.juspay.io) |\n\n# Authentication\n\nWhen you sign up for an account, you are given a secret key (also referred as api-key). You may authenticate all API requests with Juspay server by providing the appropriate key in the request Authorization header. \nNever share your secret api keys. Keep them guarded and secure.\n\nContact Support: \nName: Juspay Support \nEmail: [support@juspay.in](mailto:support@juspay.in)", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "26710321" - }, - "variable": [ - { - "key": "baseUrl", - "value": "", - "type": "string" - }, - { - "key": "admin_api_key", - "value": "", - "type": "string" - }, - { - "key": "api_key", - "value": "", - "type": "string" - }, - { - "key": "merchant_id", - "value": "" - }, - { - "key": "payment_id", - "value": "" - }, - { - "key": "customer_id", - "value": "" - }, - { - "key": "mandate_id", - "value": "" - }, - { - "key": "payment_method_id", - "value": "" - }, - { - "key": "refund_id", - "value": "" - }, - { - "key": "merchant_connector_id", - "value": "" - }, - { - "key": "client_secret", - "value": "", - "type": "string" - }, - { - "key": "connector_api_key", - "value": "", - "type": "string" - }, - { - "key": "publishable_key", - "value": "", - "type": "string" - }, - { - "key": "api_key_id", - "value": "", - "type": "string" - }, - { - "key": "payment_token", - "value": "" - }, - { - "key": "gateway_merchant_id", - "value": "", - "type": "string" - }, - { - "key": "certificate", - "value": "", - "type": "string" - }, - { - "key": "certificate_keys", - "value": "", - "type": "string" - }, - { - "key": "connector_api_secret", - "value": "", - "type": "string" - }, - { - "key": "connector_key1", - "value": "", - "type": "string" - }, - { - "key": "connector_key2", - "value": "", - "type": "string" - }, - { - "key": "user_email", - "value": "", - "type": "string" - }, - { - "key": "user_password", - "value": "", - "type": "string" - }, - { - "key": "wrong_password", - "value": "", - "type": "string" - }, - { - "key": "user_base_email_for_signup", - "value": "", - "type": "string" - }, - { - "key": "user_domain_for_signup", - "value": "", - "type": "string" - } - ] -} + "info": { + "_postman_id": "b5b40c9a-7e58-42c7-8b89-0adb208c45c9", + "name": "users", + "description": "## Get started\n\nJuspay Router provides a collection of APIs that enable you to process and manage payments. Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes. \nYou can consume the APIs directly using your favorite HTTP/REST library. \nWe have a testing environment referred to \"sandbox\", which you can setup to test API calls without affecting production data.\n\n### Base URLs\n\nUse the following base URLs when making requests to the APIs:\n\n| Environment | Base URL |\n| --- | --- |\n| Sandbox | [https://sandbox.hyperswitch.io](https://sandbox.hyperswitch.io) |\n| Production | [https://router.juspay.io](https://router.juspay.io) |\n\n# Authentication\n\nWhen you sign up for an account, you are given a secret key (also referred as api-key). You may authenticate all API requests with Juspay server by providing the appropriate key in the request Authorization header. \nNever share your secret api keys. Keep them guarded and secure.\n\nContact Support: \nName: Juspay Support \nEmail: [support@juspay.in](mailto:support@juspay.in)", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "26710321" + }, + "item": [ + { + "name": "Health check", + "item": [ + { + "name": "New Request", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/health - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/health", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "health" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Flow Testcases", + "item": [ + { + "name": "Sign Up", + "item": [ + { + "name": "Connect Account", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "", + "// Validate status 2xx", + "pm.test(\"[POST]::/user/connect_account - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/user/connect_account - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/user/connect_account - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Validate specific JSON response content", + "pm.test(\"[POST]::/user/connect_account - Response contains is_email_sent\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"is_email_sent\");", + " pm.expect(jsonData.is_email_sent).to.be.true;", + "});", + "", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "", + "var baseEmail = pm.environment.get('user_base_email_for_signup');", + "var emailDomain = pm.environment.get(\"user_domain_for_signup\");", + "", + "// Generate a unique email address", + "var uniqueEmail = baseEmail + new Date().getTime() + emailDomain;", + "// Set the unique email address as an environment variable", + "pm.environment.set('unique_email', uniqueEmail);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Cookie", + "value": "Cookie_1=value" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{unique_email}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/user/connect_account", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "user", + "connect_account" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Sign In", + "item": [ + { + "name": "Signin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "", + "// Validate status 2xx", + "pm.test(\"[POST]::/user/signin - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/user/signin - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/user/signin - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Validate specific JSON response content", + "pm.test(\"[POST]::/user/signin - Response contains token\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.be.a(\"string\").and.to.not.be.empty;", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Cookie", + "value": "Cookie_1=value" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{user_email}}\",\n \"password\": \"{{user_password}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/user/signin", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "user", + "signin" + ] + } + }, + "response": [] + }, + { + "name": "Signin Wrong", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "", + "// Validate status code is 4xx Bad Request", + "pm.test(\"[POST]::/user/signin - Status code is 401\", function () {", + " pm.response.to.have.status(401);", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/user/signin - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/user/signin - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Cookie", + "value": "Cookie_1=value" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{user_email}}\",\n \"password\": \"{{wrong_password}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/user/signin", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "user", + "signin" + ] + } + }, + "response": [] + }, + { + "name": "Signin V2 - To be deprecated", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "", + "// Validate status 2xx", + "pm.test(\"[POST]::user/v2/signin - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::user/v2/signin - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::user/v2/signin - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Validate specific JSON response content", + "pm.test(\"[POST]::user/v2/signin - Response contains token\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.be.a(\"string\").and.to.not.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Cookie", + "value": "Cookie_1=value" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{user_email}}\",\n \"password\": \"{{user_password}}\" \n}" + }, + "url": { + "raw": "{{baseUrl}}/user/v2/signin", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "user", + "v2", + "signin" + ] + } + }, + "response": [] + }, + { + "name": "Signin V2 Wrong", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "", + "// Validate status 4xx", + "pm.test(\"[POST]::/user/v2/signin - Status code is 401\", function () {", + " pm.response.to.have.status(401);", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::user/v2/signin - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::user/v2/signin - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Cookie", + "value": "Cookie_1=value" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{user_email}}\",\n \"password\": \"{{wrong_password}}\" \n}" + }, + "url": { + "raw": "{{baseUrl}}/user/v2/signin", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "user", + "v2", + "signin" + ] + } + }, + "response": [] + } + ] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + }, + { + "key": "admin_api_key", + "value": "", + "type": "string" + }, + { + "key": "api_key", + "value": "", + "type": "string" + }, + { + "key": "merchant_id", + "value": "" + }, + { + "key": "payment_id", + "value": "" + }, + { + "key": "customer_id", + "value": "" + }, + { + "key": "mandate_id", + "value": "" + }, + { + "key": "payment_method_id", + "value": "" + }, + { + "key": "refund_id", + "value": "" + }, + { + "key": "merchant_connector_id", + "value": "" + }, + { + "key": "client_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_api_key", + "value": "", + "type": "string" + }, + { + "key": "publishable_key", + "value": "", + "type": "string" + }, + { + "key": "api_key_id", + "value": "", + "type": "string" + }, + { + "key": "payment_token", + "value": "" + }, + { + "key": "gateway_merchant_id", + "value": "", + "type": "string" + }, + { + "key": "certificate", + "value": "", + "type": "string" + }, + { + "key": "certificate_keys", + "value": "", + "type": "string" + }, + { + "key": "connector_api_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_key1", + "value": "", + "type": "string" + }, + { + "key": "connector_key2", + "value": "", + "type": "string" + }, + { + "key": "user_email", + "value": "", + "type": "string" + }, + { + "key": "user_password", + "value": "", + "type": "string" + }, + { + "key": "wrong_password", + "value": "", + "type": "string" + }, + { + "key": "user_base_email_for_signup", + "value": "", + "type": "string" + }, + { + "key": "user_domain_for_signup", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file