mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 05:17:02 +08:00
feat(users): add global support in user roles (#6458)
This commit is contained in:
@ -1343,6 +1343,8 @@ diesel::table! {
|
||||
#[max_length = 64]
|
||||
entity_type -> Nullable<Varchar>,
|
||||
version -> UserRoleVersion,
|
||||
#[max_length = 64]
|
||||
tenant_id -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1289,6 +1289,8 @@ diesel::table! {
|
||||
#[max_length = 64]
|
||||
entity_type -> Nullable<Varchar>,
|
||||
version -> UserRoleVersion,
|
||||
#[max_length = 64]
|
||||
tenant_id -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ pub struct UserRole {
|
||||
pub entity_id: Option<String>,
|
||||
pub entity_type: Option<EntityType>,
|
||||
pub version: enums::UserRoleVersion,
|
||||
pub tenant_id: String,
|
||||
}
|
||||
|
||||
impl UserRole {
|
||||
@ -87,6 +88,7 @@ pub struct UserRoleNew {
|
||||
pub entity_id: Option<String>,
|
||||
pub entity_type: Option<EntityType>,
|
||||
pub version: enums::UserRoleVersion,
|
||||
pub tenant_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
|
||||
|
||||
@ -1853,7 +1853,7 @@ pub mod routes {
|
||||
return Err(OpenSearchError::AccessForbiddenError)?;
|
||||
}
|
||||
let user_roles: HashSet<UserRole> = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: &auth.user_id,
|
||||
org_id: Some(&auth.org_id),
|
||||
@ -1976,7 +1976,7 @@ pub mod routes {
|
||||
return Err(OpenSearchError::AccessForbiddenError)?;
|
||||
}
|
||||
let user_roles: HashSet<UserRole> = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: &auth.user_id,
|
||||
org_id: Some(&auth.org_id),
|
||||
|
||||
@ -94,6 +94,8 @@ pub enum UserErrors {
|
||||
MaxTotpAttemptsReached,
|
||||
#[error("Maximum attempts reached for Recovery Code")]
|
||||
MaxRecoveryCodeAttemptsReached,
|
||||
#[error("Forbidden tenant id")]
|
||||
ForbiddenTenantId,
|
||||
}
|
||||
|
||||
impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for UserErrors {
|
||||
@ -239,6 +241,9 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon
|
||||
Self::MaxRecoveryCodeAttemptsReached => {
|
||||
AER::BadRequest(ApiError::new(sub_code, 49, self.get_error_message(), None))
|
||||
}
|
||||
Self::ForbiddenTenantId => {
|
||||
AER::BadRequest(ApiError::new(sub_code, 50, self.get_error_message(), None))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,6 +294,7 @@ impl UserErrors {
|
||||
Self::AuthConfigParsingError => "Auth config parsing error",
|
||||
Self::SSOFailed => "Invalid SSO request",
|
||||
Self::JwtProfileIdMissing => "profile_id missing in JWT",
|
||||
Self::ForbiddenTenantId => "Forbidden tenant id",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +94,7 @@ pub async fn generate_recon_token(
|
||||
&state.conf,
|
||||
user.org_id.clone(),
|
||||
user.profile_id.clone(),
|
||||
user.tenant_id,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
|
||||
@ -598,7 +598,7 @@ async fn handle_existing_user_invitation(
|
||||
let now = common_utils::date_time::now();
|
||||
|
||||
if state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
invitee_user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -614,7 +614,7 @@ async fn handle_existing_user_invitation(
|
||||
}
|
||||
|
||||
if state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
invitee_user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -650,6 +650,10 @@ async fn handle_existing_user_invitation(
|
||||
EntityType::Organization => {
|
||||
user_role
|
||||
.add_entity(domain::OrganizationLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
})
|
||||
.insert_in_v2(state)
|
||||
@ -658,6 +662,10 @@ async fn handle_existing_user_invitation(
|
||||
EntityType::Merchant => {
|
||||
user_role
|
||||
.add_entity(domain::MerchantLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
})
|
||||
@ -671,6 +679,10 @@ async fn handle_existing_user_invitation(
|
||||
.ok_or(UserErrors::InternalServerError)?;
|
||||
user_role
|
||||
.add_entity(domain::ProfileLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
profile_id: profile_id.clone(),
|
||||
@ -777,6 +789,10 @@ async fn handle_new_user_invitation(
|
||||
EntityType::Organization => {
|
||||
user_role
|
||||
.add_entity(domain::OrganizationLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
})
|
||||
.insert_in_v2(state)
|
||||
@ -785,6 +801,10 @@ async fn handle_new_user_invitation(
|
||||
EntityType::Merchant => {
|
||||
user_role
|
||||
.add_entity(domain::MerchantLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
})
|
||||
@ -798,6 +818,10 @@ async fn handle_new_user_invitation(
|
||||
.ok_or(UserErrors::InternalServerError)?;
|
||||
user_role
|
||||
.add_entity(domain::ProfileLevel {
|
||||
tenant_id: user_from_token
|
||||
.tenant_id
|
||||
.clone()
|
||||
.unwrap_or(state.tenant.tenant_id.clone()),
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
profile_id: profile_id.clone(),
|
||||
@ -864,6 +888,7 @@ async fn handle_new_user_invitation(
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
role_id: request.role_id.clone(),
|
||||
profile_id: None,
|
||||
tenant_id: user_from_token.tenant_id.clone(),
|
||||
};
|
||||
|
||||
let set_metadata_request = SetMetaDataRequest::IsChangePasswordRequired;
|
||||
@ -909,7 +934,7 @@ pub async fn resend_invite(
|
||||
.into();
|
||||
|
||||
let user_role = match state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -932,7 +957,7 @@ pub async fn resend_invite(
|
||||
let user_role = match user_role {
|
||||
Some(user_role) => user_role,
|
||||
None => state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -1102,6 +1127,13 @@ pub async fn create_internal_user(
|
||||
}
|
||||
})?;
|
||||
|
||||
let default_tenant_id = common_utils::consts::DEFAULT_TENANT.to_string();
|
||||
|
||||
if state.tenant.tenant_id != default_tenant_id {
|
||||
return Err(UserErrors::ForbiddenTenantId)
|
||||
.attach_printable("Operation allowed only for the default tenant.");
|
||||
}
|
||||
|
||||
let internal_merchant_id = common_utils::id_type::MerchantId::get_internal_user_merchant_id(
|
||||
consts::user_role::INTERNAL_USER_MERCHANT_ID,
|
||||
);
|
||||
@ -1142,6 +1174,7 @@ pub async fn create_internal_user(
|
||||
UserStatus::Active,
|
||||
)
|
||||
.add_entity(domain::MerchantLevel {
|
||||
tenant_id: default_tenant_id,
|
||||
org_id: internal_merchant.organization_id,
|
||||
merchant_id: internal_merchant_id,
|
||||
})
|
||||
@ -1195,7 +1228,7 @@ pub async fn list_user_roles_details(
|
||||
}
|
||||
|
||||
let user_roles_set = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: required_user.get_user_id(),
|
||||
org_id: Some(&user_from_token.org_id),
|
||||
@ -2372,7 +2405,7 @@ pub async fn list_orgs_for_user(
|
||||
}
|
||||
|
||||
let orgs = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: user_from_token.user_id.as_str(),
|
||||
org_id: None,
|
||||
@ -2437,7 +2470,7 @@ pub async fn list_merchants_for_user_in_org(
|
||||
.change_context(UserErrors::InternalServerError)?,
|
||||
EntityType::Merchant | EntityType::Profile => {
|
||||
let merchant_ids = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: user_from_token.user_id.as_str(),
|
||||
org_id: Some(&user_from_token.org_id),
|
||||
@ -2516,7 +2549,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
|
||||
.change_context(UserErrors::InternalServerError)?,
|
||||
EntityType::Profile => {
|
||||
let profile_ids = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: user_from_token.user_id.as_str(),
|
||||
org_id: Some(&user_from_token.org_id),
|
||||
@ -2592,7 +2625,7 @@ pub async fn switch_org_for_user(
|
||||
}
|
||||
|
||||
let user_role = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: &user_from_token.user_id,
|
||||
org_id: Some(&request.org_id),
|
||||
@ -2621,6 +2654,7 @@ pub async fn switch_org_for_user(
|
||||
request.org_id.clone(),
|
||||
user_role.role_id.clone(),
|
||||
profile_id.clone(),
|
||||
user_from_token.tenant_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -2764,7 +2798,7 @@ pub async fn switch_merchant_for_user_in_org(
|
||||
|
||||
EntityType::Merchant | EntityType::Profile => {
|
||||
let user_role = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: &user_from_token.user_id,
|
||||
org_id: Some(&user_from_token.org_id),
|
||||
@ -2805,6 +2839,7 @@ pub async fn switch_merchant_for_user_in_org(
|
||||
org_id.clone(),
|
||||
role_id.clone(),
|
||||
profile_id,
|
||||
user_from_token.tenant_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -2879,7 +2914,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant(
|
||||
|
||||
EntityType::Profile => {
|
||||
let user_role = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload{
|
||||
user_id:&user_from_token.user_id,
|
||||
org_id: Some(&user_from_token.org_id),
|
||||
@ -2910,6 +2945,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant(
|
||||
user_from_token.org_id.clone(),
|
||||
role_id.clone(),
|
||||
profile_id,
|
||||
user_from_token.tenant_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ pub async fn update_user_role(
|
||||
let mut is_updated = false;
|
||||
|
||||
let v2_user_role_to_be_updated = match state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user_to_be_updated.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -210,7 +210,7 @@ pub async fn update_user_role(
|
||||
}
|
||||
|
||||
state
|
||||
.store
|
||||
.global_store
|
||||
.update_user_role_by_user_id_and_lineage(
|
||||
user_to_be_updated.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -229,7 +229,7 @@ pub async fn update_user_role(
|
||||
}
|
||||
|
||||
let v1_user_role_to_be_updated = match state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user_to_be_updated.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -283,7 +283,7 @@ pub async fn update_user_role(
|
||||
}
|
||||
|
||||
state
|
||||
.store
|
||||
.global_store
|
||||
.update_user_role_by_user_id_and_lineage(
|
||||
user_to_be_updated.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -470,7 +470,7 @@ pub async fn delete_user_role(
|
||||
|
||||
// Find in V2
|
||||
let user_role_v2 = match state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -517,7 +517,7 @@ pub async fn delete_user_role(
|
||||
|
||||
user_role_deleted_flag = true;
|
||||
state
|
||||
.store
|
||||
.global_store
|
||||
.delete_user_role_by_user_id_and_lineage(
|
||||
user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -532,7 +532,7 @@ pub async fn delete_user_role(
|
||||
|
||||
// Find in V1
|
||||
let user_role_v1 = match state
|
||||
.store
|
||||
.global_store
|
||||
.find_user_role_by_user_id_and_lineage(
|
||||
user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -579,7 +579,7 @@ pub async fn delete_user_role(
|
||||
|
||||
user_role_deleted_flag = true;
|
||||
state
|
||||
.store
|
||||
.global_store
|
||||
.delete_user_role_by_user_id_and_lineage(
|
||||
user_from_db.get_user_id(),
|
||||
&user_from_token.org_id,
|
||||
@ -599,7 +599,7 @@ pub async fn delete_user_role(
|
||||
|
||||
// Check if user has any more role associations
|
||||
let remaining_roles = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: user_from_db.get_user_id(),
|
||||
org_id: None,
|
||||
@ -780,7 +780,7 @@ pub async fn list_invitations_for_user(
|
||||
user_from_token: auth::UserIdFromAuth,
|
||||
) -> UserResponse<Vec<user_role_api::ListInvitationForUserResponse>> {
|
||||
let user_roles = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: &user_from_token.user_id,
|
||||
org_id: None,
|
||||
|
||||
@ -123,7 +123,6 @@ pub trait StorageInterface:
|
||||
+ routing_algorithm::RoutingAlgorithmInterface
|
||||
+ gsm::GsmInterface
|
||||
+ unified_translations::UnifiedTranslationsInterface
|
||||
+ user_role::UserRoleInterface
|
||||
+ authorization::AuthorizationInterface
|
||||
+ user::sample_data::BatchSampleDataInterface
|
||||
+ health_check::HealthCheckDbInterface
|
||||
@ -144,6 +143,7 @@ pub trait GlobalStorageInterface:
|
||||
+ Sync
|
||||
+ dyn_clone::DynClone
|
||||
+ user::UserInterface
|
||||
+ user_role::UserRoleInterface
|
||||
+ user_key_store::UserKeyStoreInterface
|
||||
+ 'static
|
||||
{
|
||||
|
||||
@ -234,6 +234,7 @@ impl UserRoleInterface for MockDb {
|
||||
entity_id: None,
|
||||
entity_type: None,
|
||||
version: enums::UserRoleVersion::V1,
|
||||
tenant_id: user_role.tenant_id,
|
||||
};
|
||||
db_user_roles.push(user_role.clone());
|
||||
Ok(user_role)
|
||||
|
||||
@ -179,6 +179,7 @@ pub struct UserFromSinglePurposeToken {
|
||||
pub user_id: String,
|
||||
pub origin: domain::Origin,
|
||||
pub path: Vec<TokenPurpose>,
|
||||
pub tenant_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
@ -189,6 +190,7 @@ pub struct SinglePurposeToken {
|
||||
pub origin: domain::Origin,
|
||||
pub path: Vec<TokenPurpose>,
|
||||
pub exp: u64,
|
||||
pub tenant_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
@ -199,6 +201,7 @@ impl SinglePurposeToken {
|
||||
origin: domain::Origin,
|
||||
settings: &Settings,
|
||||
path: Vec<TokenPurpose>,
|
||||
tenant_id: Option<String>,
|
||||
) -> UserResult<String> {
|
||||
let exp_duration =
|
||||
std::time::Duration::from_secs(consts::SINGLE_PURPOSE_TOKEN_TIME_IN_SECS);
|
||||
@ -209,6 +212,7 @@ impl SinglePurposeToken {
|
||||
origin,
|
||||
exp,
|
||||
path,
|
||||
tenant_id,
|
||||
};
|
||||
jwt::generate_jwt(&token_payload, settings).await
|
||||
}
|
||||
@ -222,6 +226,7 @@ pub struct AuthToken {
|
||||
pub exp: u64,
|
||||
pub org_id: id_type::OrganizationId,
|
||||
pub profile_id: Option<id_type::ProfileId>,
|
||||
pub tenant_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
@ -233,6 +238,7 @@ impl AuthToken {
|
||||
settings: &Settings,
|
||||
org_id: id_type::OrganizationId,
|
||||
profile_id: Option<id_type::ProfileId>,
|
||||
tenant_id: Option<String>,
|
||||
) -> UserResult<String> {
|
||||
let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS);
|
||||
let exp = jwt::generate_exp(exp_duration)?.as_secs();
|
||||
@ -243,6 +249,7 @@ impl AuthToken {
|
||||
exp,
|
||||
org_id,
|
||||
profile_id,
|
||||
tenant_id,
|
||||
};
|
||||
jwt::generate_jwt(&token_payload, settings).await
|
||||
}
|
||||
@ -255,6 +262,7 @@ pub struct UserFromToken {
|
||||
pub role_id: String,
|
||||
pub org_id: id_type::OrganizationId,
|
||||
pub profile_id: Option<id_type::ProfileId>,
|
||||
pub tenant_id: Option<String>,
|
||||
}
|
||||
|
||||
pub struct UserIdFromAuth {
|
||||
@ -268,6 +276,7 @@ pub struct SinglePurposeOrLoginToken {
|
||||
pub role_id: Option<String>,
|
||||
pub purpose: Option<TokenPurpose>,
|
||||
pub exp: u64,
|
||||
pub tenant_id: Option<String>,
|
||||
}
|
||||
|
||||
pub trait AuthInfo {
|
||||
@ -748,6 +757,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
if self.0 != payload.purpose {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
@ -758,6 +771,7 @@ where
|
||||
user_id: payload.user_id.clone(),
|
||||
origin: payload.origin.clone(),
|
||||
path: payload.path,
|
||||
tenant_id: payload.tenant_id,
|
||||
},
|
||||
AuthenticationType::SinglePurposeJwt {
|
||||
user_id: payload.user_id,
|
||||
@ -782,6 +796,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
if self.0 != payload.purpose {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
@ -792,6 +810,7 @@ where
|
||||
user_id: payload.user_id.clone(),
|
||||
origin: payload.origin.clone(),
|
||||
path: payload.path,
|
||||
tenant_id: payload.tenant_id,
|
||||
}),
|
||||
AuthenticationType::SinglePurposeJwt {
|
||||
user_id: payload.user_id,
|
||||
@ -821,6 +840,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let is_purpose_equal = payload
|
||||
.purpose
|
||||
@ -1448,6 +1471,11 @@ where
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
|
||||
@ -1476,6 +1504,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
@ -1487,6 +1519,7 @@ where
|
||||
org_id: payload.org_id,
|
||||
role_id: payload.role_id,
|
||||
profile_id: payload.profile_id,
|
||||
tenant_id: payload.tenant_id,
|
||||
},
|
||||
AuthenticationType::MerchantJwt {
|
||||
merchant_id: payload.merchant_id,
|
||||
@ -1511,6 +1544,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
@ -1570,6 +1607,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.required_permission, &role_info)?;
|
||||
@ -1611,6 +1652,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.required_permission, &role_info)?;
|
||||
@ -1647,6 +1692,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.required_permission, &role_info)?;
|
||||
|
||||
@ -1792,6 +1841,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.required_permission, &role_info)?;
|
||||
@ -1825,6 +1878,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
if payload.merchant_id != self.merchant_id {
|
||||
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
|
||||
@ -1964,6 +2021,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
if payload.merchant_id != self.merchant_id {
|
||||
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
|
||||
@ -2039,6 +2100,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.required_permission, &role_info)?;
|
||||
@ -2205,6 +2270,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
@ -2262,6 +2331,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let profile_id = HeaderMapStruct::new(request_headers)
|
||||
.get_id_type_from_header::<id_type::ProfileId>(headers::X_PROFILE_ID)?;
|
||||
@ -2334,6 +2407,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
@ -2393,6 +2470,11 @@ where
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
UserFromToken {
|
||||
user_id: payload.user_id.clone(),
|
||||
@ -2400,6 +2482,7 @@ where
|
||||
org_id: payload.org_id,
|
||||
role_id: payload.role_id,
|
||||
profile_id: payload.profile_id,
|
||||
tenant_id: payload.tenant_id,
|
||||
},
|
||||
AuthenticationType::MerchantJwt {
|
||||
merchant_id: payload.merchant_id,
|
||||
@ -2424,6 +2507,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
Ok(((), AuthenticationType::NoAuth))
|
||||
}
|
||||
@ -2441,6 +2528,11 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(AuthenticationData, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
|
||||
let key_manager_state = &(&state.session_state()).into();
|
||||
let key_store = state
|
||||
.store()
|
||||
@ -2809,6 +2901,10 @@ where
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
authorization::check_tenant(
|
||||
payload.tenant_id.clone(),
|
||||
&state.session_state().tenant.tenant_id,
|
||||
)?;
|
||||
let role_info = authorization::get_role_info(state, &payload).await?;
|
||||
authorization::check_permission(&self.permission, &role_info)?;
|
||||
|
||||
|
||||
@ -112,6 +112,18 @@ pub fn check_permission(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_tenant(token_tenant_id: Option<String>, header_tenant_id: &str) -> RouterResult<()> {
|
||||
if let Some(tenant_id) = token_tenant_id {
|
||||
if tenant_id != header_tenant_id {
|
||||
return Err(ApiErrorResponse::InvalidJwtToken).attach_printable(format!(
|
||||
"Token tenant ID: '{}' does not match Header tenant ID: '{}'",
|
||||
tenant_id, header_tenant_id
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_redis_connection<A: SessionStateInfo>(state: &A) -> RouterResult<Arc<RedisConnectionPool>> {
|
||||
state
|
||||
.store()
|
||||
|
||||
@ -689,7 +689,10 @@ impl NewUser {
|
||||
|
||||
let org_user_role = self
|
||||
.get_no_level_user_role(role_id, user_status)
|
||||
.add_entity(OrganizationLevel { org_id });
|
||||
.add_entity(OrganizationLevel {
|
||||
tenant_id: state.tenant.tenant_id.clone(),
|
||||
org_id,
|
||||
});
|
||||
|
||||
org_user_role.insert_in_v2(&state).await
|
||||
}
|
||||
@ -1116,17 +1119,20 @@ pub struct NoLevel;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OrganizationLevel {
|
||||
pub tenant_id: String,
|
||||
pub org_id: id_type::OrganizationId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MerchantLevel {
|
||||
pub tenant_id: String,
|
||||
pub org_id: id_type::OrganizationId,
|
||||
pub merchant_id: id_type::MerchantId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProfileLevel {
|
||||
pub tenant_id: String,
|
||||
pub org_id: id_type::OrganizationId,
|
||||
pub merchant_id: id_type::MerchantId,
|
||||
pub profile_id: id_type::ProfileId,
|
||||
@ -1163,6 +1169,7 @@ impl NewUserRole<NoLevel> {
|
||||
}
|
||||
|
||||
pub struct EntityInfo {
|
||||
tenant_id: String,
|
||||
org_id: id_type::OrganizationId,
|
||||
merchant_id: Option<id_type::MerchantId>,
|
||||
profile_id: Option<id_type::ProfileId>,
|
||||
@ -1175,6 +1182,7 @@ impl From<OrganizationLevel> for EntityInfo {
|
||||
Self {
|
||||
entity_id: value.org_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Organization,
|
||||
tenant_id: value.tenant_id,
|
||||
org_id: value.org_id,
|
||||
merchant_id: None,
|
||||
profile_id: None,
|
||||
@ -1187,6 +1195,7 @@ impl From<MerchantLevel> for EntityInfo {
|
||||
Self {
|
||||
entity_id: value.merchant_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Merchant,
|
||||
tenant_id: value.tenant_id,
|
||||
org_id: value.org_id,
|
||||
profile_id: None,
|
||||
merchant_id: Some(value.merchant_id),
|
||||
@ -1199,6 +1208,7 @@ impl From<ProfileLevel> for EntityInfo {
|
||||
Self {
|
||||
entity_id: value.profile_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Profile,
|
||||
tenant_id: value.tenant_id,
|
||||
org_id: value.org_id,
|
||||
merchant_id: Some(value.merchant_id),
|
||||
profile_id: Some(value.profile_id),
|
||||
@ -1225,6 +1235,7 @@ where
|
||||
entity_id: Some(entity.entity_id),
|
||||
entity_type: Some(entity.entity_type),
|
||||
version: UserRoleVersion::V2,
|
||||
tenant_id: entity.tenant_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1234,7 +1245,7 @@ where
|
||||
let new_v2_role = self.convert_to_new_v2_role(entity.into());
|
||||
|
||||
state
|
||||
.store
|
||||
.global_store
|
||||
.insert_user_role(new_v2_role)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
|
||||
@ -65,7 +65,7 @@ impl SPTFlow {
|
||||
.is_password_rotate_required(state)
|
||||
.map(|rotate_required| rotate_required && !path.contains(&TokenPurpose::SSO)),
|
||||
Self::MerchantSelect => Ok(state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: user.get_user_id(),
|
||||
org_id: None,
|
||||
@ -93,6 +93,7 @@ impl SPTFlow {
|
||||
next_flow.origin.clone(),
|
||||
&state.conf,
|
||||
next_flow.path.to_vec(),
|
||||
Some(state.tenant.tenant_id.clone()),
|
||||
)
|
||||
.await
|
||||
.map(|token| token.into())
|
||||
@ -132,6 +133,7 @@ impl JWTFlow {
|
||||
.ok_or(report!(UserErrors::InternalServerError))
|
||||
.attach_printable("org_id not found")?,
|
||||
Some(profile_id),
|
||||
Some(user_role.tenant_id.clone()),
|
||||
)
|
||||
.await
|
||||
.map(|token| token.into())
|
||||
@ -299,7 +301,7 @@ impl NextFlow {
|
||||
self.user.get_verification_days_left(state)?;
|
||||
}
|
||||
let user_role = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id: self.user.get_user_id(),
|
||||
org_id: None,
|
||||
|
||||
@ -92,6 +92,7 @@ pub async fn generate_jwt_auth_token_with_attributes(
|
||||
org_id: id_type::OrganizationId,
|
||||
role_id: String,
|
||||
profile_id: id_type::ProfileId,
|
||||
tenant_id: Option<String>,
|
||||
) -> UserResult<Secret<String>> {
|
||||
let token = AuthToken::new_token(
|
||||
user_id,
|
||||
@ -100,6 +101,7 @@ pub async fn generate_jwt_auth_token_with_attributes(
|
||||
&state.conf,
|
||||
org_id,
|
||||
Some(profile_id),
|
||||
tenant_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(Secret::new(token))
|
||||
|
||||
@ -142,7 +142,7 @@ pub async fn update_v1_and_v2_user_roles_in_db(
|
||||
Result<UserRole, Report<StorageError>>,
|
||||
) {
|
||||
let updated_v1_role = state
|
||||
.store
|
||||
.global_store
|
||||
.update_user_role_by_user_id_and_lineage(
|
||||
user_id,
|
||||
org_id,
|
||||
@ -158,7 +158,7 @@ pub async fn update_v1_and_v2_user_roles_in_db(
|
||||
});
|
||||
|
||||
let updated_v2_role = state
|
||||
.store
|
||||
.global_store
|
||||
.update_user_role_by_user_id_and_lineage(
|
||||
user_id,
|
||||
org_id,
|
||||
@ -228,7 +228,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
|
||||
};
|
||||
|
||||
let user_roles = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id,
|
||||
org_id: Some(&org_id),
|
||||
@ -272,7 +272,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
|
||||
};
|
||||
|
||||
let user_roles = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id,
|
||||
org_id: None,
|
||||
@ -317,7 +317,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
|
||||
};
|
||||
|
||||
let user_roles = state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||
user_id,
|
||||
org_id: None,
|
||||
@ -407,7 +407,7 @@ pub async fn fetch_user_roles_by_payload(
|
||||
request_entity_type: Option<EntityType>,
|
||||
) -> UserResult<HashSet<UserRole>> {
|
||||
Ok(state
|
||||
.store
|
||||
.global_store
|
||||
.list_user_roles_by_org_id(payload)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE user_roles DROP COLUMN IF EXISTS tenant_id;
|
||||
@ -0,0 +1,2 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE user_roles ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) NOT NULL DEFAULT 'public';
|
||||
Reference in New Issue
Block a user