From d69558604c05c87f17b7a5e9198088a288f6c7dc Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Thu, 30 Nov 2023 15:08:10 +0530 Subject: [PATCH] feat: add routes and endpoints for roles --- crates/api_models/src/events.rs | 1 + crates/api_models/src/events/user.rs | 12 +- crates/api_models/src/events/user_role.rs | 15 ++ crates/api_models/src/user.rs | 17 ++ crates/api_models/src/user_role.rs | 34 ---- crates/router/src/core/user.rs | 232 +++++++++++++++++++--- crates/router/src/core/user_role.rs | 183 +---------------- crates/router/src/routes.rs | 2 + crates/router/src/routes/app.rs | 15 +- crates/router/src/routes/lock_utils.rs | 12 +- crates/router/src/routes/user.rs | 63 +++++- crates/router/src/routes/user_role.rs | 83 ++++++++ crates/router/src/types/domain/user.rs | 19 +- crates/router_env/src/logger/types.rs | 14 ++ 14 files changed, 442 insertions(+), 260 deletions(-) create mode 100644 crates/api_models/src/events/user_role.rs create mode 100644 crates/router/src/routes/user_role.rs diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 345f827daeac..ac7cdeb83d94 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -7,6 +7,7 @@ pub mod payouts; pub mod refund; pub mod routing; pub mod user; +pub mod user_role; use common_utils::{ events::{ApiEventMetric, ApiEventsType}, diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 4e9f2f284173..d15464e0468f 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -1,6 +1,9 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -use crate::user::{ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse}; +use crate::user::{ + ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse, + CreateInternalUserRequest, SwitchMerchantIdRequest, UserMerchantCreate, +}; impl ApiEventMetric for ConnectAccountResponse { fn get_api_event_type(&self) -> Option { @@ -13,4 +16,9 @@ impl ApiEventMetric for ConnectAccountResponse { impl ApiEventMetric for ConnectAccountRequest {} -common_utils::impl_misc_api_event_type!(ChangePasswordRequest); +common_utils::impl_misc_api_event_type!( + ChangePasswordRequest, + SwitchMerchantIdRequest, + CreateInternalUserRequest, + UserMerchantCreate +); diff --git a/crates/api_models/src/events/user_role.rs b/crates/api_models/src/events/user_role.rs new file mode 100644 index 000000000000..64c0eb34e8b4 --- /dev/null +++ b/crates/api_models/src/events/user_role.rs @@ -0,0 +1,15 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::user_role::{ + AuthorizationInfoResponse, GetRoleRequest, GetUsersResponse, ListRolesResponse, + RoleInfoResponse, UpdateUserRoleRequest, +}; + +common_utils::impl_misc_api_event_type!( + ListRolesResponse, + RoleInfoResponse, + GetRoleRequest, + AuthorizationInfoResponse, + GetUsersResponse, + UpdateUserRoleRequest +); diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 41ea9cc5193a..00f57a7be296 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -25,3 +25,20 @@ pub struct ChangePasswordRequest { pub new_password: Secret, pub old_password: Secret, } + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct SwitchMerchantIdRequest { + pub merchant_id: String, +} + +#[derive(serde::Deserialize, Debug, serde::Serialize)] +pub struct CreateInternalUserRequest { + pub name: Secret, + pub email: pii::Email, + pub password: Secret, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct UserMerchantCreate { + pub company_name: String, +} diff --git a/crates/api_models/src/user_role.rs b/crates/api_models/src/user_role.rs index b4d7256bf994..e7926f27f571 100644 --- a/crates/api_models/src/user_role.rs +++ b/crates/api_models/src/user_role.rs @@ -1,35 +1,6 @@ use common_utils::pii; use masking::Secret; -pub struct CreateInternalUserRequest { - pub name: Secret, - pub email: pii::Email, - pub password: Secret, -} - -#[derive(serde::Deserialize, Debug, serde::Serialize)] -pub struct UserListRequest { - #[serde(skip_deserializing)] - pub onboarding_steps: String, - pub subscribed: bool, - pub start_date: String, - pub last_date: Option, - pub last_modified: Option, -} - -#[derive(serde::Serialize, Clone, Debug)] -pub struct UserListResponse { - email: pii::Email, - user_id: String, - onboarding_modified_at: time::PrimitiveDateTime, - onboarding_step: i32, -} - -#[derive(Debug, serde::Deserialize, serde::Serialize)] -pub struct SwitchMerchantIdRequest { - pub merchant_id: String, -} - #[derive(Debug, serde::Serialize)] pub struct ListRolesResponse(pub Vec); @@ -149,8 +120,3 @@ pub struct UpdateUserRoleRequest { pub user_id: String, pub role_id: String, } - -#[derive(Debug, serde::Deserialize, serde::Serialize)] -pub struct UserMerchantCreate { - pub company_name: String, -} diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 5239ca1586d0..eb1a5ad44576 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,5 +1,5 @@ -use api_models::user as api; -use diesel_models::enums::UserStatus; +use api_models::user as user_api; +use diesel_models::{enums::UserStatus, user as storage_user}; use error_stack::{IntoReport, ResultExt}; use masking::{ExposeInterface, Secret}; use router_env::env; @@ -9,14 +9,15 @@ use crate::{ consts, db::user::UserInterface, routes::AppState, - services::{authentication::UserFromToken, ApplicationResponse}, + services::{authentication as auth, ApplicationResponse}, types::domain, + utils, }; pub async fn connect_account( state: AppState, - request: api::ConnectAccountRequest, -) -> UserResponse { + request: user_api::ConnectAccountRequest, +) -> UserResponse { let find_user = state .store .find_user_by_email(request.email.clone().expose().expose().as_str()) @@ -32,15 +33,17 @@ pub async fn connect_account( .get_jwt_auth_token(state.clone(), user_role.org_id) .await?; - return Ok(ApplicationResponse::Json(api::ConnectAccountResponse { - token: Secret::new(jwt_token), - merchant_id: user_role.merchant_id, - name: user_from_db.get_name(), - email: user_from_db.get_email(), - verification_days_left: None, - user_role: user_role.role_id, - user_id: user_from_db.get_user_id().to_string(), - })); + return Ok(ApplicationResponse::Json( + user_api::ConnectAccountResponse { + token: Secret::new(jwt_token), + merchant_id: user_role.merchant_id, + name: user_from_db.get_name(), + email: user_from_db.get_email(), + verification_days_left: None, + user_role: user_role.role_id, + user_id: user_from_db.get_user_id().to_string(), + }, + )); } else if find_user .map_err(|e| e.current_context().is_db_not_found()) .err() @@ -92,15 +95,17 @@ pub async fn connect_account( logger::info!(?send_email_result); } - return Ok(ApplicationResponse::Json(api::ConnectAccountResponse { - token: Secret::new(jwt_token), - merchant_id: user_role.merchant_id, - name: user_from_db.get_name(), - email: user_from_db.get_email(), - verification_days_left: None, - user_role: user_role.role_id, - user_id: user_from_db.get_user_id().to_string(), - })); + return Ok(ApplicationResponse::Json( + user_api::ConnectAccountResponse { + token: Secret::new(jwt_token), + merchant_id: user_role.merchant_id, + name: user_from_db.get_name(), + email: user_from_db.get_email(), + verification_days_left: None, + user_role: user_role.role_id, + user_id: user_from_db.get_user_id().to_string(), + }, + )); } else { Err(UserErrors::InternalServerError.into()) } @@ -108,8 +113,8 @@ pub async fn connect_account( pub async fn change_password( state: AppState, - request: api::ChangePasswordRequest, - user_from_token: UserFromToken, + request: user_api::ChangePasswordRequest, + user_from_token: auth::UserFromToken, ) -> UserResponse<()> { let user: domain::UserFromStorage = UserInterface::find_user_by_id(&*state.store, &user_from_token.user_id) @@ -137,3 +142,180 @@ pub async fn change_password( Ok(ApplicationResponse::StatusOk) } + +pub async fn create_internal_user( + state: AppState, + request: user_api::CreateInternalUserRequest, +) -> UserResponse<()> { + let new_user = domain::NewUser::try_from(request)?; + + let mut store_user: storage_user::UserNew = new_user.clone().try_into()?; + store_user.set_is_verified(true); + + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + consts::user_role::INTERNAL_USER_MERCHANT_ID, + &state.store.get_master_key().to_vec().into(), + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(UserErrors::MerchantIdNotFound) + } else { + e.change_context(UserErrors::InternalServerError) + } + })?; + + state + .store + .find_merchant_account_by_merchant_id( + consts::user_role::INTERNAL_USER_MERCHANT_ID, + &key_store, + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(UserErrors::MerchantIdNotFound) + } else { + e.change_context(UserErrors::InternalServerError) + } + })?; + + state + .store + .insert_user(store_user) + .await + .map_err(|e| { + if e.current_context().is_db_unique_violation() { + e.change_context(UserErrors::UserExists) + } else { + e.change_context(UserErrors::InternalServerError) + } + }) + .map(domain::user::UserFromStorage::from)?; + + new_user + .insert_user_role_in_db( + state, + consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), + UserStatus::Active, + ) + .await?; + + Ok(ApplicationResponse::StatusOk) +} + +pub async fn switch_merchant_id( + state: AppState, + request: user_api::SwitchMerchantIdRequest, + user_from_token: auth::UserFromToken, +) -> UserResponse { + if !utils::user_role::is_internal_role(&user_from_token.role_id) { + let merchant_list = + utils::user_role::get_merchant_ids_for_user(state.clone(), &user_from_token.user_id) + .await?; + if !merchant_list.contains(&request.merchant_id) { + return Err(UserErrors::InvalidRoleOperation.into()) + .attach_printable("User doesn't have access to switch"); + } + } + + if user_from_token.merchant_id == request.merchant_id { + return Err(UserErrors::InvalidRoleOperation.into()) + .attach_printable("User switch to same merchant id."); + } + + let user = state + .store + .find_user_by_id(&user_from_token.user_id) + .await + .change_context(UserErrors::InternalServerError)?; + + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + request.merchant_id.as_str(), + &state.store.get_master_key().to_vec().into(), + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(UserErrors::MerchantIdNotFound) + } else { + e.change_context(UserErrors::InternalServerError) + } + })?; + + let org_id = state + .store + .find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(UserErrors::MerchantIdNotFound) + } else { + e.change_context(UserErrors::InternalServerError) + } + })? + .organization_id; + + let user = domain::UserFromStorage::from(user); + let user_role = state + .store + .find_user_role_by_user_id(user.get_user_id()) + .await + .change_context(UserErrors::InternalServerError)?; + + let token = Box::pin(user.get_jwt_auth_token_with_custom_merchant_id( + state.clone(), + request.merchant_id.clone(), + org_id, + )) + .await? + .into(); + + Ok(ApplicationResponse::Json( + user_api::ConnectAccountResponse { + merchant_id: request.merchant_id, + token, + name: user.get_name(), + email: user.get_email(), + user_id: user.get_user_id().to_string(), + verification_days_left: None, + user_role: user_role.role_id, + }, + )) +} + +pub async fn create_merchant_account( + state: AppState, + user_from_token: auth::UserFromToken, + req: user_api::UserMerchantCreate, +) -> UserResponse<()> { + let user_from_db: domain::UserFromStorage = + user_from_token.get_user(state.clone()).await?.into(); + + let new_user = domain::NewUser::try_from((user_from_db, req, user_from_token))?; + let new_merchant = new_user.get_new_merchant(); + new_merchant + .create_new_merchant_and_insert_in_db(state.to_owned()) + .await?; + + let role_insertion_res = new_user + .insert_user_role_in_db( + state.clone(), + consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), + UserStatus::Active, + ) + .await; + if let Err(e) = role_insertion_res { + let _ = state + .store + .delete_merchant_account_by_merchant_id(new_merchant.get_merchant_id().as_str()) + .await; + return Err(e); + } + + Ok(ApplicationResponse::StatusOk) +} diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index e67d55f4aa91..2b7752d1904b 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -1,9 +1,8 @@ -use api_models::{user as user_api, user_role as user_role_api}; -use diesel_models::{enums::UserStatus, user as storage, user_role::UserRoleUpdate}; +use api_models::user_role as user_role_api; +use diesel_models::user_role::UserRoleUpdate; use error_stack::ResultExt; use crate::{ - consts, core::errors::{UserErrors, UserResponse}, routes::AppState, services::{ @@ -11,155 +10,9 @@ use crate::{ authorization::{info, predefined_permissions}, ApplicationResponse, }, - types::domain, utils, }; -pub async fn create_internal_user( - state: AppState, - request: user_role_api::CreateInternalUserRequest, -) -> UserResponse<()> { - let new_user = domain::NewUser::try_from(request)?; - - let mut store_user: storage::UserNew = new_user.clone().try_into()?; - store_user.set_is_verified(true); - - let key_store = state - .store - .get_merchant_key_store_by_merchant_id( - consts::user_role::INTERNAL_USER_MERCHANT_ID, - &state.store.get_master_key().to_vec().into(), - ) - .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(UserErrors::MerchantIdNotFound) - } else { - e.change_context(UserErrors::InternalServerError) - } - })?; - - state - .store - .find_merchant_account_by_merchant_id( - consts::user_role::INTERNAL_USER_MERCHANT_ID, - &key_store, - ) - .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(UserErrors::MerchantIdNotFound) - } else { - e.change_context(UserErrors::InternalServerError) - } - })?; - - state - .store - .insert_user(store_user) - .await - .map_err(|e| { - if e.current_context().is_db_unique_violation() { - e.change_context(UserErrors::UserExists) - } else { - e.change_context(UserErrors::InternalServerError) - } - }) - .map(domain::user::UserFromStorage::from)?; - - new_user - .insert_user_role_in_db( - state, - consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), - UserStatus::Active, - ) - .await?; - - Ok(ApplicationResponse::StatusOk) -} - -pub async fn switch_merchant_id( - state: AppState, - request: user_role_api::SwitchMerchantIdRequest, - user_from_token: auth::UserFromToken, -) -> UserResponse { - if !utils::user_role::is_internal_role(&user_from_token.role_id) { - let merchant_list = - utils::user_role::get_merchant_ids_for_user(state.clone(), &user_from_token.user_id) - .await?; - if !merchant_list.contains(&request.merchant_id) { - return Err(UserErrors::InvalidRoleOperation.into()) - .attach_printable("User doesn't have access to switch"); - } - } - - if user_from_token.merchant_id == request.merchant_id { - return Err(UserErrors::InvalidRoleOperation.into()) - .attach_printable("User switch to same merchant id."); - } - - let user = state - .store - .find_user_by_id(&user_from_token.user_id) - .await - .change_context(UserErrors::InternalServerError)?; - - let key_store = state - .store - .get_merchant_key_store_by_merchant_id( - request.merchant_id.as_str(), - &state.store.get_master_key().to_vec().into(), - ) - .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(UserErrors::MerchantIdNotFound) - } else { - e.change_context(UserErrors::InternalServerError) - } - })?; - - let org_id = state - .store - .find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store) - .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(UserErrors::MerchantIdNotFound) - } else { - e.change_context(UserErrors::InternalServerError) - } - })? - .organization_id; - - let user = domain::UserFromStorage::from(user); - let user_role = state - .store - .find_user_role_by_user_id(user.get_user_id()) - .await - .change_context(UserErrors::InternalServerError)?; - - let token = Box::pin(user.get_jwt_auth_token_with_custom_merchant_id( - state.clone(), - request.merchant_id.clone(), - org_id, - )) - .await? - .into(); - - Ok(ApplicationResponse::Json( - user_api::ConnectAccountResponse { - merchant_id: request.merchant_id, - token, - name: user.get_name(), - email: user.get_email(), - user_id: user.get_user_id().to_string(), - verification_days_left: None, - user_role: user_role.role_id, - }, - )) -} - pub async fn get_authorization_info( _state: AppState, ) -> UserResponse { @@ -246,35 +99,3 @@ pub async fn update_user_role( Ok(ApplicationResponse::StatusOk) } - -pub async fn create_merchant_account( - state: AppState, - user_from_token: auth::UserFromToken, - req: user_role_api::UserMerchantCreate, -) -> UserResponse<()> { - let user_from_db: domain::UserFromStorage = - user_from_token.get_user(state.clone()).await?.into(); - - let new_user = domain::NewUser::try_from((user_from_db, req, user_from_token))?; - let new_merchant = new_user.get_new_merchant(); - new_merchant - .create_new_merchant_and_insert_in_db(state.to_owned()) - .await?; - - let role_insertion_res = new_user - .insert_user_role_in_db( - state.clone(), - consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), - UserStatus::Active, - ) - .await; - if let Err(e) = role_insertion_res { - let _ = state - .store - .delete_merchant_account_by_merchant_id(new_merchant.get_merchant_id().as_str()) - .await; - return Err(e); - } - - Ok(ApplicationResponse::StatusOk) -} diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 22c2610d3255..b19ef5d7016b 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -27,6 +27,8 @@ pub mod refunds; pub mod routing; #[cfg(feature = "olap")] pub mod user; +#[cfg(feature = "olap")] +pub mod user_role; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; #[cfg(feature = "olap")] diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 2a7e1ab61905..0f5c9c945e9b 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -23,7 +23,7 @@ use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_ve #[cfg(feature = "olap")] use super::{ admin::*, api_keys::*, disputes::*, files::*, gsm::*, locker_migration, payment_link::*, - user::*, + user::*, user_role::*, }; use super::{cache::*, health::*}; #[cfg(any(feature = "olap", feature = "oltp"))] @@ -522,7 +522,7 @@ impl MerchantAccount { pub fn server(state: AppState) -> Scope { web::scope("/accounts") .app_data(web::Data::new(state)) - .service(web::resource("").route(web::post().to(merchant_account_create))) + .service(web::resource("").route(web::post().to(user_merchant_account_create))) .service(web::resource("/list").route(web::get().to(merchant_account_list))) .service( web::resource("/{id}/kv") @@ -807,6 +807,17 @@ impl User { .service(web::resource("/v2/signin").route(web::post().to(user_connect_account))) .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) .service(web::resource("/change_password").route(web::post().to(change_password))) + .service(web::resource("/internal_signup").route(web::post().to(internal_user_signup))) + .service(web::resource("/switch_merchant").route(web::post().to(switch_merchant_id))) + .service( + web::resource("/create_merchant") + .route(web::post().to(user_merchant_account_create)), + ) + // User Role APIs + .service(web::resource("/permission_info").route(web::get().to(get_authorization_info))) + .service(web::resource("/user/update_role").route(web::post().to(update_user_role))) + .service(web::resource("/role/list").route(web::get().to(list_roles))) + .service(web::resource("/role/{role_id}").route(web::get().to(get_role))) } } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index c7369b9e4d52..aa74552aa671 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -27,6 +27,7 @@ pub enum ApiIdentifier { RustLockerMigration, Gsm, User, + UserRole, } impl From for ApiIdentifier { @@ -147,8 +148,15 @@ impl From for ApiIdentifier { | Flow::GsmRuleUpdate | Flow::GsmRuleDelete => Self::Gsm, - Flow::UserConnectAccount | Flow::ChangePassword | Flow::VerifyPaymentConnector => { - Self::User + Flow::UserConnectAccount + | Flow::ChangePassword + | Flow::VerifyPaymentConnector + | Flow::InternalUserSignup + | Flow::SwitchMerchant + | Flow::UserMerchantAccountCreate => Self::User, + + Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => { + Self::UserRole } } } diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 7d3d183eda76..e27a030785ae 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -4,10 +4,11 @@ use router_env::Flow; use super::AppState; use crate::{ - core::{api_locking, user}, + core::{api_locking, user as user_core}, services::{ api, authentication::{self as auth}, + authorization::permissions::Permission, }, }; @@ -23,7 +24,7 @@ pub async fn user_connect_account( state, &http_req, req_payload.clone(), - |state, _, req_body| user::connect_account(state, req_body), + |state, _, req_body| user_core::connect_account(state, req_body), &auth::NoAuth, api_locking::LockAction::NotApplicable, )) @@ -41,9 +42,65 @@ pub async fn change_password( state.clone(), &http_req, json_payload.into_inner(), - |state, user, req| user::change_password(state, req, user), + |state, user, req| user_core::change_password(state, req, user), &auth::DashboardNoPermissionAuth, api_locking::LockAction::NotApplicable, )) .await } + +pub async fn internal_user_signup( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::InternalUserSignup; + Box::pin(api::server_wrap( + flow, + state.clone(), + &http_req, + json_payload.into_inner(), + |state, _, req| user_core::create_internal_user(state, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn switch_merchant_id( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::SwitchMerchant; + Box::pin(api::server_wrap( + flow, + state.clone(), + &http_req, + json_payload.into_inner(), + |state, user, req| user_core::switch_merchant_id(state, req, user), + &auth::DashboardNoPermissionAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn user_merchant_account_create( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserMerchantAccountCreate; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth: auth::UserFromToken, json_payload| { + user_core::create_merchant_account(state, auth, json_payload) + }, + &auth::JWTAuth(Permission::MerchantAccountCreate), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/user_role.rs b/crates/router/src/routes/user_role.rs new file mode 100644 index 000000000000..7e4fc8678227 --- /dev/null +++ b/crates/router/src/routes/user_role.rs @@ -0,0 +1,83 @@ +use super::AppState; +use crate::{ + core::{api_locking, user_role as user_role_core}, + services::{ + api, + authentication::{self as auth}, + authorization::permissions::Permission, + }, +}; +use actix_web::{web, HttpRequest, HttpResponse}; +use api_models::user_role as user_role_api; +use router_env::Flow; + +pub async fn get_authorization_info( + state: web::Data, + http_req: HttpRequest, +) -> HttpResponse { + let flow = Flow::GetAuthorizationInfo; + Box::pin(api::server_wrap( + flow, + state.clone(), + &http_req, + (), + |state, _: (), _| user_role_core::get_authorization_info(state), + &auth::JWTAuth(Permission::UsersRead), + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn list_roles(state: web::Data, req: HttpRequest) -> HttpResponse { + let flow = Flow::ListRoles; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + (), + |state, _: (), _| user_role_core::list_roles(state), + &auth::JWTAuth(Permission::UsersRead), + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn get_role( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::GetRole; + let request_payload = user_role_api::GetRoleRequest { + role_id: path.into_inner(), + }; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + request_payload, + |state, _: (), req| user_role_core::get_role(state, req), + &auth::JWTAuth(Permission::UsersRead), + api_locking::LockAction::NotApplicable, + )) + .await +} + +pub async fn update_user_role( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UpdateUserRole; + let payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload, + user_role_core::update_user_role, + &auth::JWTAuth(Permission::UsersWrite), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 3c82f27733b2..10f751d90862 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -221,8 +221,8 @@ impl From for NewUserOrganization { } } -impl From for NewUserOrganization { - fn from(_value: user_role_api::CreateInternalUserRequest) -> Self { +impl From for NewUserOrganization { + fn from(_value: user_api::CreateInternalUserRequest) -> Self { let new_organization = api_org::OrganizationNew::new(None); let db_organization = ForeignFrom::foreign_from(new_organization); Self(db_organization) @@ -350,10 +350,10 @@ impl TryFrom for NewUserMerchant { } } -impl TryFrom for NewUserMerchant { +impl TryFrom for NewUserMerchant { type Error = error_stack::Report; - fn try_from(value: user_role_api::CreateInternalUserRequest) -> UserResult { + fn try_from(value: user_api::CreateInternalUserRequest) -> UserResult { let merchant_id = MerchantId::new(consts::user_role::INTERNAL_USER_MERCHANT_ID.to_string())?; let new_organization = NewUserOrganization::from(value); @@ -366,11 +366,8 @@ impl TryFrom for NewUserMerchant { } } -type UserMerchantCreateRequestWithToken = ( - UserFromStorage, - user_role_api::UserMerchantCreate, - UserFromToken, -); +type UserMerchantCreateRequestWithToken = + (UserFromStorage, user_api::UserMerchantCreate, UserFromToken); impl TryFrom for NewUserMerchant { type Error = error_stack::Report; @@ -516,10 +513,10 @@ impl TryFrom for NewUser { } } -impl TryFrom for NewUser { +impl TryFrom for NewUser { type Error = error_stack::Report; - fn try_from(value: user_role_api::CreateInternalUserRequest) -> UserResult { + fn try_from(value: user_api::CreateInternalUserRequest) -> UserResult { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.email.clone().try_into()?; let name = UserName::new(value.name.clone())?; diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index c254f89b4eef..02f3e5381dfa 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -261,6 +261,20 @@ pub enum Flow { ChangePassword, /// Payment Connector Verify VerifyPaymentConnector, + /// Internal user signup + InternalUserSignup, + /// Switch merchant + SwitchMerchant, + /// Get permission info + GetAuthorizationInfo, + /// List roles + ListRoles, + /// Get role + GetRole, + /// Update user role + UpdateUserRole, + /// Create merchant account for user in a org + UserMerchantAccountCreate, } ///