feat: add resources and granular permission groups for reconciliation (#6591)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Kashif
2024-12-04 14:58:10 +05:30
committed by GitHub
parent 35f963c2e8
commit fa21ef892d
18 changed files with 440 additions and 170 deletions

View File

@ -446,3 +446,20 @@ pub enum StripeChargeType {
pub fn convert_frm_connector(connector_name: &str) -> Option<FrmConnectors> {
FrmConnectors::from_str(connector_name).ok()
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, Hash)]
pub enum ReconPermissionScope {
#[serde(rename = "R")]
Read = 0,
#[serde(rename = "RW")]
Write = 1,
}
impl From<PermissionScope> for ReconPermissionScope {
fn from(scope: PermissionScope) -> Self {
match scope {
PermissionScope::Read => Self::Read,
PermissionScope::Write => Self::Write,
}
}
}

View File

@ -1,6 +1,9 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};
use masking::PeekInterface;
use crate::recon::{ReconStatusResponse, ReconTokenResponse, ReconUpdateMerchantRequest};
use crate::recon::{
ReconStatusResponse, ReconTokenResponse, ReconUpdateMerchantRequest, VerifyTokenResponse,
};
impl ApiEventMetric for ReconUpdateMerchantRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
@ -19,3 +22,11 @@ impl ApiEventMetric for ReconStatusResponse {
Some(ApiEventsType::Recon)
}
}
impl ApiEventMetric for VerifyTokenResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::User {
user_id: self.user_email.peek().to_string(),
})
}
}

View File

@ -1,11 +1,7 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};
#[cfg(feature = "recon")]
use masking::PeekInterface;
#[cfg(feature = "dummy_connector")]
use crate::user::sample_data::SampleDataRequest;
#[cfg(feature = "recon")]
use crate::user::VerifyTokenResponse;
use crate::user::{
dashboard_metadata::{
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
@ -23,15 +19,6 @@ use crate::user::{
VerifyTotpRequest,
};
#[cfg(feature = "recon")]
impl ApiEventMetric for VerifyTokenResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::User {
user_id: self.user_email.peek().to_string(),
})
}
}
common_utils::impl_api_event_type!(
Miscellaneous,
(

View File

@ -1,4 +1,4 @@
use common_utils::pii;
use common_utils::{id_type, pii};
use masking::Secret;
use crate::enums;
@ -18,3 +18,11 @@ pub struct ReconTokenResponse {
pub struct ReconStatusResponse {
pub recon_status: enums::ReconStatus,
}
#[derive(serde::Serialize, Debug)]
pub struct VerifyTokenResponse {
pub merchant_id: id_type::MerchantId,
pub user_email: pii::Email,
#[serde(skip_serializing_if = "Option::is_none")]
pub acl: Option<String>,
}

View File

@ -167,13 +167,6 @@ pub struct SendVerifyEmailRequest {
pub email: pii::Email,
}
#[cfg(feature = "recon")]
#[derive(serde::Serialize, Debug)]
pub struct VerifyTokenResponse {
pub merchant_id: id_type::MerchantId,
pub user_email: pii::Email,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct UpdateUserAccountDetailsRequest {
pub name: Option<Secret<String>>,

View File

@ -2819,9 +2819,15 @@ pub enum PermissionGroup {
MerchantDetailsManage,
// TODO: To be deprecated, make sure DB is migrated before removing
OrganizationManage,
ReconOps,
AccountView,
AccountManage,
ReconReportsView,
ReconReportsManage,
ReconOpsView,
// Alias is added for backward compatibility with database
// TODO: Remove alias post migration
#[serde(alias = "recon_ops")]
ReconOpsManage,
}
#[derive(Clone, Debug, serde::Serialize, PartialEq, Eq, Hash, strum::EnumIter)]
@ -2831,7 +2837,8 @@ pub enum ParentGroup {
Workflows,
Analytics,
Users,
Recon,
ReconOps,
ReconReports,
Account,
}
@ -2854,7 +2861,13 @@ pub enum Resource {
WebhookEvent,
Payout,
Report,
Recon,
ReconToken,
ReconFiles,
ReconAndSettlementAnalytics,
ReconUpload,
ReconReports,
RunRecon,
ReconConfig,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, Hash)]

View File

@ -1,24 +1,37 @@
use api_models::recon as recon_api;
#[cfg(feature = "email")]
use common_utils::ext_traits::AsyncExt;
use error_stack::ResultExt;
#[cfg(feature = "email")]
use masking::{ExposeInterface, PeekInterface, Secret};
#[cfg(feature = "email")]
use crate::{consts, services::email::types as email_types, types::domain};
use crate::{
consts,
core::errors::{self, RouterResponse, UserErrors},
services::{api as service_api, authentication, email::types as email_types},
core::errors::{self, RouterResponse, UserErrors, UserResponse},
services::{api as service_api, authentication},
types::{
api::{self as api_types, enums},
domain, storage,
storage,
transformers::ForeignTryFrom,
},
SessionState,
};
#[allow(unused_variables)]
pub async fn send_recon_request(
state: SessionState,
auth_data: authentication::AuthenticationDataWithUser,
) -> RouterResponse<recon_api::ReconStatusResponse> {
#[cfg(not(feature = "email"))]
return Ok(service_api::ApplicationResponse::Json(
recon_api::ReconStatusResponse {
recon_status: enums::ReconStatus::NotRequested,
},
));
#[cfg(feature = "email")]
{
let user_in_db = &auth_data.user;
let merchant_id = auth_data.merchant_account.get_id().clone();
@ -37,14 +50,12 @@ pub async fn send_recon_request(
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert recipient's email to UserEmail")?,
settings: state.conf.clone(),
subject: format!(
"{} {}",
consts::EMAIL_SUBJECT_DASHBOARD_FEATURE_REQUEST,
user_email.expose().peek()
),
};
state
.email_client
.compose_and_send_email(
@ -81,20 +92,22 @@ pub async fn send_recon_request(
))
})
.await
}
}
pub async fn generate_recon_token(
state: SessionState,
user: authentication::UserFromToken,
user_with_role: authentication::UserFromTokenWithRoleInfo,
) -> RouterResponse<recon_api::ReconTokenResponse> {
let token = authentication::AuthToken::new_token(
let user = user_with_role.user;
let token = authentication::ReconToken::new_token(
user.user_id.clone(),
user.merchant_id.clone(),
user.role_id.clone(),
&state.conf,
user.org_id.clone(),
user.profile_id.clone(),
user.tenant_id,
user_with_role.role_info,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
@ -138,18 +151,20 @@ pub async fn recon_merchant_account_update(
format!("Failed while updating merchant's recon status: {merchant_id:?}")
})?;
#[cfg(feature = "email")]
{
let user_email = &req.user_email.clone();
let email_contents = email_types::ReconActivation {
recipient_email: domain::UserEmail::from_pii_email(user_email.clone())
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert recipient's email to UserEmail from pii::Email")?,
.attach_printable(
"Failed to convert recipient's email to UserEmail from pii::Email",
)?,
user_name: domain::UserName::new(Secret::new("HyperSwitch User".to_string()))
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to form username")?,
settings: state.conf.clone(),
subject: consts::EMAIL_SUBJECT_APPROVAL_RECON_REQUEST,
};
if req.recon_status == enums::ReconStatus::Active {
let _ = state
.email_client
@ -158,9 +173,15 @@ pub async fn recon_merchant_account_update(
state.conf.proxy.https_url.as_ref(),
)
.await
.inspect_err(|err| {
router_env::logger::error!(
"Failed to compose and send email notifying them of recon activation: {}",
err
)
})
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to compose and send email for ReconActivation")
.is_ok();
.attach_printable("Failed to compose and send email for ReconActivation");
}
}
Ok(service_api::ApplicationResponse::Json(
@ -170,3 +191,34 @@ pub async fn recon_merchant_account_update(
})?,
))
}
pub async fn verify_recon_token(
state: SessionState,
user_with_role: authentication::UserFromTokenWithRoleInfo,
) -> UserResponse<recon_api::VerifyTokenResponse> {
let user = user_with_role.user;
let user_in_db = user
.get_user_from_db(&state)
.await
.attach_printable_lazy(|| {
format!(
"Failed to fetch the user from DB for user_id - {}",
user.user_id
)
})?;
let acl = user_with_role.role_info.get_recon_acl();
let optional_acl_str = serde_json::to_string(&acl)
.inspect_err(|err| router_env::logger::error!("Failed to serialize acl to string: {}", err))
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to serialize acl to string. Using empty ACL")
.ok();
Ok(service_api::ApplicationResponse::Json(
recon_api::VerifyTokenResponse {
merchant_id: user.merchant_id.to_owned(),
user_email: user_in_db.0.email,
acl: optional_acl_str,
},
))
}

View File

@ -1593,27 +1593,6 @@ pub async fn send_verification_mail(
Ok(ApplicationResponse::StatusOk)
}
#[cfg(feature = "recon")]
pub async fn verify_token(
state: SessionState,
user: auth::UserFromToken,
) -> UserResponse<user_api::VerifyTokenResponse> {
let user_in_db = user
.get_user_from_db(&state)
.await
.attach_printable_lazy(|| {
format!(
"Failed to fetch the user from DB for user_id - {}",
user.user_id
)
})?;
Ok(ApplicationResponse::Json(user_api::VerifyTokenResponse {
merchant_id: user.merchant_id.to_owned(),
user_email: user_in_db.0.email,
}))
}
pub async fn update_user_details(
state: SessionState,
user_token: auth::UserFromToken,

View File

@ -1216,7 +1216,10 @@ impl Recon {
.service(
web::resource("/request").route(web::post().to(recon_routes::request_for_recon)),
)
.service(web::resource("/verify_token").route(web::get().to(user::verify_recon_token)))
.service(
web::resource("/verify_token")
.route(web::get().to(recon_routes::verify_recon_token)),
)
}
}

View File

@ -38,7 +38,7 @@ pub async fn request_for_recon(state: web::Data<AppState>, http_req: HttpRequest
(),
|state, user, _, _| recon::send_recon_request(state, user),
&authentication::JWTAuth {
permission: Permission::MerchantReconWrite,
permission: Permission::MerchantAccountWrite,
},
api_locking::LockAction::NotApplicable,
))
@ -54,7 +54,24 @@ pub async fn get_recon_token(state: web::Data<AppState>, req: HttpRequest) -> Ht
(),
|state, user, _, _| recon::generate_recon_token(state, user),
&authentication::JWTAuth {
permission: Permission::MerchantReconWrite,
permission: Permission::MerchantReconTokenRead,
},
api_locking::LockAction::NotApplicable,
))
.await
}
#[cfg(feature = "recon")]
pub async fn verify_recon_token(state: web::Data<AppState>, http_req: HttpRequest) -> HttpResponse {
let flow = Flow::ReconVerifyToken;
Box::pin(api::server_wrap(
flow,
state.clone(),
&http_req,
(),
|state, user, _req, _| recon::verify_recon_token(state, user),
&authentication::JWTAuth {
permission: Permission::MerchantReconTokenRead,
},
api_locking::LockAction::NotApplicable,
))

View File

@ -487,23 +487,6 @@ pub async fn verify_email_request(
.await
}
#[cfg(feature = "recon")]
pub async fn verify_recon_token(state: web::Data<AppState>, http_req: HttpRequest) -> HttpResponse {
let flow = Flow::ReconVerifyToken;
Box::pin(api::server_wrap(
flow,
state.clone(),
&http_req,
(),
|state, user, _req, _| user_core::verify_token(state, user),
&auth::JWTAuth {
permission: Permission::MerchantReconWrite,
},
api_locking::LockAction::NotApplicable,
))
.await
}
pub async fn update_user_account_details(
state: web::Data<AppState>,
req: HttpRequest,

View File

@ -90,6 +90,12 @@ pub struct AuthenticationDataWithUser {
pub profile_id: id_type::ProfileId,
}
#[derive(Clone)]
pub struct UserFromTokenWithRoleInfo {
pub user: UserFromToken,
pub role_info: authorization::roles::RoleInfo,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(
tag = "api_auth_type",
@ -3228,3 +3234,91 @@ where
Ok((auth, auth_type))
}
}
#[cfg(feature = "recon")]
#[async_trait]
impl<A> AuthenticateAndFetch<UserFromTokenWithRoleInfo, A> for JWTAuth
where
A: SessionStateInfo + Sync,
{
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &A,
) -> RouterResult<(UserFromTokenWithRoleInfo, AuthenticationType)> {
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
if payload.check_in_blacklist(state).await? {
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
}
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)?;
let user = UserFromToken {
user_id: payload.user_id.clone(),
merchant_id: payload.merchant_id.clone(),
org_id: payload.org_id,
role_id: payload.role_id,
profile_id: payload.profile_id,
tenant_id: payload.tenant_id,
};
Ok((
UserFromTokenWithRoleInfo { user, role_info },
AuthenticationType::MerchantJwt {
merchant_id: payload.merchant_id,
user_id: Some(payload.user_id),
},
))
}
}
#[cfg(feature = "recon")]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct ReconToken {
pub user_id: String,
pub merchant_id: id_type::MerchantId,
pub role_id: String,
pub exp: u64,
pub org_id: id_type::OrganizationId,
pub profile_id: id_type::ProfileId,
pub tenant_id: Option<id_type::TenantId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub acl: Option<String>,
}
#[cfg(all(feature = "olap", feature = "recon"))]
impl ReconToken {
pub async fn new_token(
user_id: String,
merchant_id: id_type::MerchantId,
settings: &Settings,
org_id: id_type::OrganizationId,
profile_id: id_type::ProfileId,
tenant_id: Option<id_type::TenantId>,
role_info: authorization::roles::RoleInfo,
) -> 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();
let acl = role_info.get_recon_acl();
let optional_acl_str = serde_json::to_string(&acl)
.inspect_err(|err| logger::error!("Failed to serialize acl to string: {}", err))
.change_context(errors::UserErrors::InternalServerError)
.attach_printable("Failed to serialize acl to string. Using empty ACL")
.ok();
let token_payload = Self {
user_id,
merchant_id,
role_id: role_info.get_role_id().to_string(),
exp,
org_id,
profile_id,
tenant_id,
acl: optional_acl_str,
};
jwt::generate_jwt(&token_payload, settings).await
}
}

View File

@ -40,7 +40,10 @@ fn get_group_description(group: PermissionGroup) -> &'static str {
PermissionGroup::MerchantDetailsView | PermissionGroup::AccountView => "View Merchant Details",
PermissionGroup::MerchantDetailsManage | PermissionGroup::AccountManage => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
PermissionGroup::OrganizationManage => "Manage organization level tasks like create new Merchant accounts, Organization level roles, etc",
PermissionGroup::ReconOps => "View and manage reconciliation reports",
PermissionGroup::ReconReportsView => "View and access reconciliation reports and analytics",
PermissionGroup::ReconReportsManage => "Manage reconciliation reports",
PermissionGroup::ReconOpsView => "View and access reconciliation operations",
PermissionGroup::ReconOpsManage => "Manage reconciliation operations",
}
}
@ -52,6 +55,7 @@ pub fn get_parent_group_description(group: ParentGroup) -> &'static str {
ParentGroup::Analytics => "View Analytics",
ParentGroup::Users => "Manage and invite Users to the Team",
ParentGroup::Account => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
ParentGroup::Recon => "View and manage reconciliation reports",
ParentGroup::ReconOps => "View, manage reconciliation operations like upload and process files, run reconciliation etc",
ParentGroup::ReconReports => "View, manage reconciliation reports and analytics",
}
}

View File

@ -21,7 +21,9 @@ impl PermissionGroupExt for PermissionGroup {
| Self::AnalyticsView
| Self::UsersView
| Self::MerchantDetailsView
| Self::AccountView => PermissionScope::Read,
| Self::AccountView
| Self::ReconOpsView
| Self::ReconReportsView => PermissionScope::Read,
Self::OperationsManage
| Self::ConnectorsManage
@ -29,8 +31,9 @@ impl PermissionGroupExt for PermissionGroup {
| Self::UsersManage
| Self::MerchantDetailsManage
| Self::OrganizationManage
| Self::ReconOps
| Self::AccountManage => PermissionScope::Write,
| Self::AccountManage
| Self::ReconOpsManage
| Self::ReconReportsManage => PermissionScope::Write,
}
}
@ -41,12 +44,13 @@ impl PermissionGroupExt for PermissionGroup {
Self::WorkflowsView | Self::WorkflowsManage => ParentGroup::Workflows,
Self::AnalyticsView => ParentGroup::Analytics,
Self::UsersView | Self::UsersManage => ParentGroup::Users,
Self::ReconOps => ParentGroup::Recon,
Self::MerchantDetailsView
| Self::OrganizationManage
| Self::MerchantDetailsManage
| Self::AccountView
| Self::AccountManage => ParentGroup::Account,
Self::ReconOpsView | Self::ReconOpsManage => ParentGroup::ReconOps,
Self::ReconReportsView | Self::ReconReportsManage => ParentGroup::ReconReports,
}
}
@ -76,7 +80,11 @@ impl PermissionGroupExt for PermissionGroup {
vec![Self::UsersView, Self::UsersManage]
}
Self::ReconOps => vec![Self::ReconOps],
Self::ReconOpsView => vec![Self::ReconOpsView],
Self::ReconOpsManage => vec![Self::ReconOpsView, Self::ReconOpsManage],
Self::ReconReportsView => vec![Self::ReconReportsView],
Self::ReconReportsManage => vec![Self::ReconReportsView, Self::ReconReportsManage],
Self::MerchantDetailsView => vec![Self::MerchantDetailsView],
Self::MerchantDetailsManage => {
@ -108,7 +116,8 @@ impl ParentGroupExt for ParentGroup {
Self::Analytics => ANALYTICS.to_vec(),
Self::Users => USERS.to_vec(),
Self::Account => ACCOUNT.to_vec(),
Self::Recon => RECON.to_vec(),
Self::ReconOps => RECON_OPS.to_vec(),
Self::ReconReports => RECON_REPORTS.to_vec(),
}
}
@ -167,4 +176,18 @@ pub static USERS: [Resource; 2] = [Resource::User, Resource::Account];
pub static ACCOUNT: [Resource; 3] = [Resource::Account, Resource::ApiKey, Resource::WebhookEvent];
pub static RECON: [Resource; 1] = [Resource::Recon];
pub static RECON_OPS: [Resource; 7] = [
Resource::ReconToken,
Resource::ReconFiles,
Resource::ReconUpload,
Resource::RunRecon,
Resource::ReconConfig,
Resource::ReconAndSettlementAnalytics,
Resource::ReconReports,
];
pub static RECON_REPORTS: [Resource; 3] = [
Resource::ReconToken,
Resource::ReconAndSettlementAnalytics,
Resource::ReconReports,
];

View File

@ -67,8 +67,32 @@ generate_permissions! {
scopes: [Read, Write],
entities: [Merchant]
},
Recon: {
scopes: [Write],
ReconToken: {
scopes: [Read],
entities: [Merchant]
},
ReconFiles: {
scopes: [Read, Write],
entities: [Merchant]
},
ReconAndSettlementAnalytics: {
scopes: [Read],
entities: [Merchant]
},
ReconUpload: {
scopes: [Read, Write],
entities: [Merchant]
},
ReconReports: {
scopes: [Read, Write],
entities: [Merchant]
},
RunRecon: {
scopes: [Read, Write],
entities: [Merchant]
},
ReconConfig: {
scopes: [Read, Write],
entities: [Merchant]
},
]
@ -91,7 +115,13 @@ pub fn get_resource_name(resource: &Resource, entity_type: &EntityType) -> &'sta
(Resource::Report, _) => "Operation Reports",
(Resource::User, _) => "Users",
(Resource::WebhookEvent, _) => "Webhook Events",
(Resource::Recon, _) => "Reconciliation Reports",
(Resource::ReconUpload, _) => "Reconciliation File Upload",
(Resource::RunRecon, _) => "Run Reconciliation Process",
(Resource::ReconConfig, _) => "Reconciliation Configurations",
(Resource::ReconToken, _) => "Generate & Verify Reconciliation Token",
(Resource::ReconFiles, _) => "Reconciliation Process Manager",
(Resource::ReconReports, _) => "Reconciliation Reports",
(Resource::ReconAndSettlementAnalytics, _) => "Reconciliation Analytics",
(Resource::Account, EntityType::Profile) => "Business Profile Account",
(Resource::Account, EntityType::Merchant) => "Merchant Account",
(Resource::Account, EntityType::Organization) => "Organization Account",

View File

@ -1,8 +1,14 @@
#[cfg(feature = "recon")]
use std::collections::HashMap;
use std::collections::HashSet;
#[cfg(feature = "recon")]
use api_models::enums::ReconPermissionScope;
use common_enums::{EntityType, PermissionGroup, Resource, RoleScope};
use common_utils::{errors::CustomResult, id_type};
#[cfg(feature = "recon")]
use super::permission_groups::{RECON_OPS, RECON_REPORTS};
use super::{permission_groups::PermissionGroupExt, permissions::Permission};
use crate::{core::errors, routes::SessionState};
@ -78,6 +84,38 @@ impl RoleInfo {
})
}
#[cfg(feature = "recon")]
pub fn get_recon_acl(&self) -> HashMap<Resource, ReconPermissionScope> {
let mut acl: HashMap<Resource, ReconPermissionScope> = HashMap::new();
let mut recon_resources = RECON_OPS.to_vec();
recon_resources.extend(RECON_REPORTS);
let recon_internal_resources = [Resource::ReconToken];
self.get_permission_groups()
.iter()
.for_each(|permission_group| {
permission_group.resources().iter().for_each(|resource| {
if recon_resources.contains(resource)
&& !recon_internal_resources.contains(resource)
{
let scope = match resource {
Resource::ReconAndSettlementAnalytics => ReconPermissionScope::Read,
_ => ReconPermissionScope::from(permission_group.scope()),
};
acl.entry(*resource)
.and_modify(|curr_scope| {
*curr_scope = if (*curr_scope) < scope {
scope
} else {
*curr_scope
}
})
.or_insert(scope);
}
})
});
acl
}
pub async fn from_role_id_in_merchant_scope(
state: &SessionState,
role_id: &str,

View File

@ -28,7 +28,10 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::MerchantDetailsManage,
PermissionGroup::AccountManage,
PermissionGroup::OrganizationManage,
PermissionGroup::ReconOps,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconOpsManage,
PermissionGroup::ReconReportsView,
PermissionGroup::ReconReportsManage,
],
role_id: common_utils::consts::ROLE_ID_INTERNAL_ADMIN.to_string(),
role_name: "internal_admin".to_string(),
@ -51,6 +54,8 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::UsersView,
PermissionGroup::MerchantDetailsView,
PermissionGroup::AccountView,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconReportsView,
],
role_id: common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(),
role_name: "internal_view_only".to_string(),
@ -82,7 +87,10 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::MerchantDetailsManage,
PermissionGroup::AccountManage,
PermissionGroup::OrganizationManage,
PermissionGroup::ReconOps,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconOpsManage,
PermissionGroup::ReconReportsView,
PermissionGroup::ReconReportsManage,
],
role_id: common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
role_name: "organization_admin".to_string(),
@ -113,7 +121,10 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::AccountView,
PermissionGroup::MerchantDetailsManage,
PermissionGroup::AccountManage,
PermissionGroup::ReconOps,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconOpsManage,
PermissionGroup::ReconReportsView,
PermissionGroup::ReconReportsManage,
],
role_id: consts::user_role::ROLE_ID_MERCHANT_ADMIN.to_string(),
role_name: "merchant_admin".to_string(),
@ -136,6 +147,8 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::UsersView,
PermissionGroup::MerchantDetailsView,
PermissionGroup::AccountView,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconReportsView,
],
role_id: consts::user_role::ROLE_ID_MERCHANT_VIEW_ONLY.to_string(),
role_name: "merchant_view_only".to_string(),
@ -180,6 +193,8 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::AccountView,
PermissionGroup::MerchantDetailsManage,
PermissionGroup::AccountManage,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconReportsView,
],
role_id: consts::user_role::ROLE_ID_MERCHANT_DEVELOPER.to_string(),
role_name: "merchant_developer".to_string(),
@ -203,6 +218,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::UsersView,
PermissionGroup::MerchantDetailsView,
PermissionGroup::AccountView,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconOpsManage,
PermissionGroup::ReconReportsView,
],
role_id: consts::user_role::ROLE_ID_MERCHANT_OPERATOR.to_string(),
role_name: "merchant_operator".to_string(),
@ -223,6 +241,8 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
PermissionGroup::UsersView,
PermissionGroup::MerchantDetailsView,
PermissionGroup::AccountView,
PermissionGroup::ReconOpsView,
PermissionGroup::ReconReportsView,
],
role_id: consts::user_role::ROLE_ID_MERCHANT_CUSTOMER_SUPPORT.to_string(),
role_name: "customer_support".to_string(),

View File

@ -383,7 +383,6 @@ impl EmailData for InviteUser {
pub struct ReconActivation {
pub recipient_email: domain::UserEmail,
pub user_name: domain::UserName,
pub settings: std::sync::Arc<configs::Settings>,
pub subject: &'static str,
}
@ -458,7 +457,6 @@ pub struct ProFeatureRequest {
pub merchant_id: common_utils::id_type::MerchantId,
pub user_name: domain::UserName,
pub user_email: domain::UserEmail,
pub settings: std::sync::Arc<configs::Settings>,
pub subject: String,
}