mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
refactor(user_role): Remove V1 insertion for user_roles and allow Invites for org_admins (#6185)
This commit is contained in:
@ -5,7 +5,6 @@ use diesel::{
|
||||
BoolExpressionMethods, ExpressionMethods, QueryDsl,
|
||||
};
|
||||
use error_stack::{report, ResultExt};
|
||||
use router_env::logger;
|
||||
|
||||
use crate::{
|
||||
enums::{UserRoleVersion, UserStatus},
|
||||
@ -23,21 +22,6 @@ impl UserRoleNew {
|
||||
}
|
||||
|
||||
impl UserRole {
|
||||
pub async fn insert_multiple_user_roles(
|
||||
conn: &PgPooledConn,
|
||||
user_roles: Vec<UserRoleNew>,
|
||||
) -> StorageResult<Vec<Self>> {
|
||||
let query = diesel::insert_into(<Self>::table()).values(user_roles);
|
||||
|
||||
logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());
|
||||
|
||||
query
|
||||
.get_results_async(conn)
|
||||
.await
|
||||
.change_context(errors::DatabaseError::Others)
|
||||
.attach_printable("Error while inserting user_roles")
|
||||
}
|
||||
|
||||
pub async fn find_by_user_id(
|
||||
conn: &PgPooledConn,
|
||||
user_id: String,
|
||||
|
||||
@ -67,7 +67,6 @@ pub async fn signup_with_merchant_id(
|
||||
state.clone(),
|
||||
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||
UserStatus::Active,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -146,7 +145,6 @@ pub async fn signup_token_only_flow(
|
||||
state.clone(),
|
||||
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||
UserStatus::Active,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -247,7 +245,6 @@ pub async fn connect_account(
|
||||
state.clone(),
|
||||
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||
UserStatus::Active,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -657,7 +654,7 @@ async fn handle_existing_user_invitation(
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
})
|
||||
.insert_in_v1_and_v2(state)
|
||||
.insert_in_v2(state)
|
||||
.await?
|
||||
}
|
||||
EntityType::Profile => {
|
||||
@ -767,14 +764,21 @@ async fn handle_new_user_invitation(
|
||||
};
|
||||
|
||||
let _user_role = match role_info.get_entity_type() {
|
||||
EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()),
|
||||
EntityType::Organization => {
|
||||
user_role
|
||||
.add_entity(domain::OrganizationLevel {
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
})
|
||||
.insert_in_v2(state)
|
||||
.await?
|
||||
}
|
||||
EntityType::Merchant => {
|
||||
user_role
|
||||
.add_entity(domain::MerchantLevel {
|
||||
org_id: user_from_token.org_id.clone(),
|
||||
merchant_id: user_from_token.merchant_id.clone(),
|
||||
})
|
||||
.insert_in_v1_and_v2(state)
|
||||
.insert_in_v2(state)
|
||||
.await?
|
||||
}
|
||||
EntityType::Profile => {
|
||||
@ -1128,7 +1132,7 @@ pub async fn create_internal_user(
|
||||
org_id: internal_merchant.organization_id,
|
||||
merchant_id: internal_merchant_id,
|
||||
})
|
||||
.insert_in_v1_and_v2(&state)
|
||||
.insert_in_v2(&state)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
@ -1142,28 +1146,11 @@ pub async fn create_merchant_account(
|
||||
) -> UserResponse<()> {
|
||||
let user_from_db = user_from_token.get_user_from_db(&state).await?;
|
||||
|
||||
let new_user = domain::NewUser::try_from((user_from_db, req, user_from_token))?;
|
||||
let new_merchant = new_user.get_new_merchant();
|
||||
let new_merchant = domain::NewUserMerchant::try_from((user_from_db, req, user_from_token))?;
|
||||
new_merchant
|
||||
.create_new_merchant_and_insert_in_db(state.to_owned())
|
||||
.await?;
|
||||
|
||||
let role_insertion_res = new_user
|
||||
.insert_org_level_user_role_in_db(
|
||||
state.clone(),
|
||||
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
||||
UserStatus::Active,
|
||||
Some(UserRoleVersion::V1),
|
||||
)
|
||||
.await;
|
||||
if let Err(e) = role_insertion_res {
|
||||
let _ = state
|
||||
.store
|
||||
.delete_merchant_account_by_merchant_id(&new_merchant.get_merchant_id())
|
||||
.await;
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
|
||||
@ -37,10 +37,7 @@ use super::{
|
||||
user::{sample_data::BatchSampleDataInterface, UserInterface},
|
||||
user_authentication_method::UserAuthenticationMethodInterface,
|
||||
user_key_store::UserKeyStoreInterface,
|
||||
user_role::{
|
||||
InsertUserRolePayload, ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload,
|
||||
UserRoleInterface,
|
||||
},
|
||||
user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload, UserRoleInterface},
|
||||
};
|
||||
#[cfg(feature = "payouts")]
|
||||
use crate::services::kafka::payout::KafkaPayout;
|
||||
@ -3018,8 +3015,8 @@ impl RedisConnInterface for KafkaStore {
|
||||
impl UserRoleInterface for KafkaStore {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: InsertUserRolePayload,
|
||||
) -> CustomResult<Vec<user_storage::UserRole>, errors::StorageError> {
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<user_storage::UserRole, errors::StorageError> {
|
||||
self.diesel_store.insert_user_role(user_role).await
|
||||
}
|
||||
|
||||
|
||||
@ -13,21 +13,6 @@ use crate::{
|
||||
services::Store,
|
||||
};
|
||||
|
||||
pub enum InsertUserRolePayload {
|
||||
OnlyV1(storage::UserRoleNew),
|
||||
OnlyV2(storage::UserRoleNew),
|
||||
V1AndV2(Box<[storage::UserRoleNew; 2]>),
|
||||
}
|
||||
|
||||
impl InsertUserRolePayload {
|
||||
fn convert_to_vec(self) -> Vec<storage::UserRoleNew> {
|
||||
match self {
|
||||
Self::OnlyV1(user_role) | Self::OnlyV2(user_role) => vec![user_role],
|
||||
Self::V1AndV2(user_roles) => user_roles.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListUserRolesByOrgIdPayload<'a> {
|
||||
pub user_id: Option<&'a String>,
|
||||
pub org_id: &'a id_type::OrganizationId,
|
||||
@ -51,8 +36,8 @@ pub struct ListUserRolesByUserIdPayload<'a> {
|
||||
pub trait UserRoleInterface {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: InsertUserRolePayload,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||
|
||||
async fn find_user_role_by_user_id_and_lineage(
|
||||
&self,
|
||||
@ -98,11 +83,12 @@ impl UserRoleInterface for Store {
|
||||
#[instrument(skip_all)]
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: InsertUserRolePayload,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
|
||||
storage::UserRole::insert_multiple_user_roles(&conn, user_role.convert_to_vec())
|
||||
user_role
|
||||
.insert(&conn)
|
||||
.await
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||
}
|
||||
@ -217,44 +203,38 @@ impl UserRoleInterface for Store {
|
||||
impl UserRoleInterface for MockDb {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: InsertUserRolePayload,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let mut db_user_roles = self.user_roles.lock().await;
|
||||
|
||||
user_role
|
||||
.convert_to_vec()
|
||||
.into_iter()
|
||||
.map(|user_role| {
|
||||
if db_user_roles
|
||||
.iter()
|
||||
.any(|user_role_inner| user_role_inner.user_id == user_role.user_id)
|
||||
{
|
||||
Err(errors::StorageError::DuplicateValue {
|
||||
entity: "user_id",
|
||||
key: None,
|
||||
})?
|
||||
}
|
||||
let user_role = storage::UserRole {
|
||||
id: i32::try_from(db_user_roles.len())
|
||||
.change_context(errors::StorageError::MockDbError)?,
|
||||
user_id: user_role.user_id,
|
||||
merchant_id: user_role.merchant_id,
|
||||
role_id: user_role.role_id,
|
||||
status: user_role.status,
|
||||
created_by: user_role.created_by,
|
||||
created_at: user_role.created_at,
|
||||
last_modified: user_role.last_modified,
|
||||
last_modified_by: user_role.last_modified_by,
|
||||
org_id: user_role.org_id,
|
||||
profile_id: None,
|
||||
entity_id: None,
|
||||
entity_type: None,
|
||||
version: enums::UserRoleVersion::V1,
|
||||
};
|
||||
db_user_roles.push(user_role.clone());
|
||||
Ok(user_role)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
if db_user_roles
|
||||
.iter()
|
||||
.any(|user_role_inner| user_role_inner.user_id == user_role.user_id)
|
||||
{
|
||||
Err(errors::StorageError::DuplicateValue {
|
||||
entity: "user_id",
|
||||
key: None,
|
||||
})?
|
||||
}
|
||||
let user_role = storage::UserRole {
|
||||
id: i32::try_from(db_user_roles.len())
|
||||
.change_context(errors::StorageError::MockDbError)?,
|
||||
user_id: user_role.user_id,
|
||||
merchant_id: user_role.merchant_id,
|
||||
role_id: user_role.role_id,
|
||||
status: user_role.status,
|
||||
created_by: user_role.created_by,
|
||||
created_at: user_role.created_at,
|
||||
last_modified: user_role.last_modified,
|
||||
last_modified_by: user_role.last_modified_by,
|
||||
org_id: user_role.org_id,
|
||||
profile_id: None,
|
||||
entity_id: None,
|
||||
entity_type: None,
|
||||
version: enums::UserRoleVersion::V1,
|
||||
};
|
||||
db_user_roles.push(user_role.clone());
|
||||
Ok(user_role)
|
||||
}
|
||||
|
||||
async fn find_user_role_by_user_id_and_lineage(
|
||||
|
||||
@ -83,9 +83,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
|
||||
role_name: "organization_admin".to_string(),
|
||||
scope: RoleScope::Organization,
|
||||
entity_type: EntityType::Organization,
|
||||
is_invitable: false,
|
||||
is_deletable: false,
|
||||
is_updatable: false,
|
||||
is_invitable: true,
|
||||
is_deletable: true,
|
||||
is_updatable: true,
|
||||
is_internal: false,
|
||||
},
|
||||
);
|
||||
|
||||
@ -30,7 +30,7 @@ use crate::{
|
||||
admin,
|
||||
errors::{UserErrors, UserResult},
|
||||
},
|
||||
db::{user_role::InsertUserRolePayload, GlobalStorageInterface},
|
||||
db::GlobalStorageInterface,
|
||||
routes::SessionState,
|
||||
services::{self, authentication::UserFromToken},
|
||||
types::transformers::ForeignFrom,
|
||||
@ -661,26 +661,17 @@ impl NewUser {
|
||||
state: SessionState,
|
||||
role_id: String,
|
||||
user_status: UserStatus,
|
||||
version: Option<UserRoleVersion>,
|
||||
) -> UserResult<UserRole> {
|
||||
let org_id = self
|
||||
.get_new_merchant()
|
||||
.get_new_organization()
|
||||
.get_organization_id();
|
||||
let merchant_id = self.get_new_merchant().get_merchant_id();
|
||||
|
||||
let org_user_role = self
|
||||
.get_no_level_user_role(role_id, user_status)
|
||||
.add_entity(OrganizationLevel {
|
||||
org_id,
|
||||
merchant_id,
|
||||
});
|
||||
.add_entity(OrganizationLevel { org_id });
|
||||
|
||||
match version {
|
||||
Some(UserRoleVersion::V1) => org_user_role.insert_in_v1(&state).await,
|
||||
Some(UserRoleVersion::V2) => org_user_role.insert_in_v2(&state).await,
|
||||
None => org_user_role.insert_in_v1_and_v2(&state).await,
|
||||
}
|
||||
org_user_role.insert_in_v2(&state).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,8 +1079,6 @@ pub struct NoLevel;
|
||||
#[derive(Clone)]
|
||||
pub struct OrganizationLevel {
|
||||
pub org_id: id_type::OrganizationId,
|
||||
// Keeping this to allow insertion of org_admins in V1
|
||||
pub merchant_id: id_type::MerchantId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -1143,32 +1132,46 @@ pub struct EntityInfo {
|
||||
entity_type: EntityType,
|
||||
}
|
||||
|
||||
impl<E> NewUserRole<E>
|
||||
where
|
||||
E: Clone,
|
||||
{
|
||||
fn convert_to_new_v1_role(
|
||||
self,
|
||||
org_id: id_type::OrganizationId,
|
||||
merchant_id: id_type::MerchantId,
|
||||
) -> UserRoleNew {
|
||||
UserRoleNew {
|
||||
user_id: self.user_id,
|
||||
role_id: self.role_id,
|
||||
status: self.status,
|
||||
created_by: self.created_by,
|
||||
last_modified_by: self.last_modified_by,
|
||||
created_at: self.created_at,
|
||||
last_modified: self.last_modified,
|
||||
org_id: Some(org_id),
|
||||
merchant_id: Some(merchant_id),
|
||||
impl From<OrganizationLevel> for EntityInfo {
|
||||
fn from(value: OrganizationLevel) -> Self {
|
||||
Self {
|
||||
entity_id: value.org_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Organization,
|
||||
org_id: value.org_id,
|
||||
merchant_id: None,
|
||||
profile_id: None,
|
||||
entity_id: None,
|
||||
entity_type: None,
|
||||
version: UserRoleVersion::V1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MerchantLevel> for EntityInfo {
|
||||
fn from(value: MerchantLevel) -> Self {
|
||||
Self {
|
||||
entity_id: value.merchant_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Merchant,
|
||||
org_id: value.org_id,
|
||||
profile_id: None,
|
||||
merchant_id: Some(value.merchant_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProfileLevel> for EntityInfo {
|
||||
fn from(value: ProfileLevel) -> Self {
|
||||
Self {
|
||||
entity_id: value.profile_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Profile,
|
||||
org_id: value.org_id,
|
||||
merchant_id: Some(value.merchant_id),
|
||||
profile_id: Some(value.profile_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> NewUserRole<E>
|
||||
where
|
||||
E: Clone + Into<EntityInfo>,
|
||||
{
|
||||
fn convert_to_new_v2_role(self, entity: EntityInfo) -> UserRoleNew {
|
||||
UserRoleNew {
|
||||
user_id: self.user_id,
|
||||
@ -1187,116 +1190,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn insert_v1_and_v2_in_db_and_get_v2(
|
||||
state: &SessionState,
|
||||
v1_role: UserRoleNew,
|
||||
v2_role: UserRoleNew,
|
||||
) -> UserResult<UserRole> {
|
||||
let inserted_roles = state
|
||||
.store
|
||||
.insert_user_role(InsertUserRolePayload::V1AndV2(Box::new([v1_role, v2_role])))
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
inserted_roles
|
||||
.into_iter()
|
||||
.find(|role| role.version == UserRoleVersion::V2)
|
||||
.ok_or(report!(UserErrors::InternalServerError))
|
||||
}
|
||||
}
|
||||
|
||||
impl NewUserRole<OrganizationLevel> {
|
||||
pub async fn insert_in_v1(self, state: &SessionState) -> UserResult<UserRole> {
|
||||
let entity = self.entity.clone();
|
||||
|
||||
let new_v1_role = self
|
||||
.clone()
|
||||
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
|
||||
|
||||
state
|
||||
.store
|
||||
.insert_user_role(InsertUserRolePayload::OnlyV1(new_v1_role))
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.pop()
|
||||
.ok_or(report!(UserErrors::InternalServerError))
|
||||
}
|
||||
|
||||
pub async fn insert_in_v2(self, state: &SessionState) -> UserResult<UserRole> {
|
||||
let entity = self.entity.clone();
|
||||
|
||||
let new_v2_role = self.convert_to_new_v2_role(EntityInfo {
|
||||
org_id: entity.org_id.clone(),
|
||||
merchant_id: None,
|
||||
profile_id: None,
|
||||
entity_id: entity.org_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Organization,
|
||||
});
|
||||
let new_v2_role = self.convert_to_new_v2_role(entity.into());
|
||||
|
||||
state
|
||||
.store
|
||||
.insert_user_role(InsertUserRolePayload::OnlyV2(new_v2_role))
|
||||
.insert_user_role(new_v2_role)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.pop()
|
||||
.ok_or(report!(UserErrors::InternalServerError))
|
||||
}
|
||||
|
||||
pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult<UserRole> {
|
||||
let entity = self.entity.clone();
|
||||
|
||||
let new_v1_role = self
|
||||
.clone()
|
||||
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
|
||||
|
||||
let new_v2_role = self.clone().convert_to_new_v2_role(EntityInfo {
|
||||
org_id: entity.org_id.clone(),
|
||||
merchant_id: None,
|
||||
profile_id: None,
|
||||
entity_id: entity.org_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Organization,
|
||||
});
|
||||
|
||||
Self::insert_v1_and_v2_in_db_and_get_v2(state, new_v1_role, new_v2_role).await
|
||||
}
|
||||
}
|
||||
|
||||
impl NewUserRole<MerchantLevel> {
|
||||
pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult<UserRole> {
|
||||
let entity = self.entity.clone();
|
||||
|
||||
let new_v1_role = self
|
||||
.clone()
|
||||
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
|
||||
|
||||
let new_v2_role = self.clone().convert_to_new_v2_role(EntityInfo {
|
||||
org_id: entity.org_id.clone(),
|
||||
merchant_id: Some(entity.merchant_id.clone()),
|
||||
profile_id: None,
|
||||
entity_id: entity.merchant_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Merchant,
|
||||
});
|
||||
|
||||
Self::insert_v1_and_v2_in_db_and_get_v2(state, new_v1_role, new_v2_role).await
|
||||
}
|
||||
}
|
||||
|
||||
impl NewUserRole<ProfileLevel> {
|
||||
pub async fn insert_in_v2(self, state: &SessionState) -> UserResult<UserRole> {
|
||||
let entity = self.entity.clone();
|
||||
|
||||
let new_v2_role = self.convert_to_new_v2_role(EntityInfo {
|
||||
org_id: entity.org_id.clone(),
|
||||
merchant_id: Some(entity.merchant_id.clone()),
|
||||
profile_id: Some(entity.profile_id.clone()),
|
||||
entity_id: entity.profile_id.get_string_repr().to_owned(),
|
||||
entity_type: EntityType::Profile,
|
||||
});
|
||||
state
|
||||
.store
|
||||
.insert_user_role(InsertUserRolePayload::OnlyV2(new_v2_role))
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.pop()
|
||||
.ok_or(report!(UserErrors::InternalServerError))
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user