From 03c0a772a99000acf4676db8ca2ce916036281d1 Mon Sep 17 00:00:00 2001
From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com>
Date: Fri, 24 Nov 2023 19:11:46 +0530
Subject: [PATCH] feat(auth): Add Authorization for JWT Authentication types
(#2973)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
---
crates/router/src/analytics/routes.rs | 29 ++-
crates/router/src/consts.rs | 2 +
crates/router/src/consts/user.rs | 6 -
crates/router/src/core/user.rs | 4 +-
crates/router/src/routes/admin.rs | 37 +++-
crates/router/src/routes/api_keys.rs | 10 +-
crates/router/src/routes/disputes.rs | 38 +++-
crates/router/src/routes/files.rs | 20 ++-
crates/router/src/routes/mandates.rs | 8 +-
crates/router/src/routes/payment_link.rs | 2 +-
crates/router/src/routes/payments.rs | 31 +++-
crates/router/src/routes/refunds.rs | 26 ++-
crates/router/src/routes/routing.rs | 158 ++++++++++++----
crates/router/src/routes/verification.rs | 14 +-
crates/router/src/services.rs | 1 +
crates/router/src/services/authentication.rs | 16 +-
crates/router/src/services/authorization.rs | 27 +++
.../router/src/services/authorization/info.rs | 168 ++++++++++++++++++
.../src/services/authorization/permissions.rs | 74 ++++++++
.../authorization/predefined_permissions.rs | 79 ++++++++
20 files changed, 659 insertions(+), 91 deletions(-)
create mode 100644 crates/router/src/services/authorization.rs
create mode 100644 crates/router/src/services/authorization/info.rs
create mode 100644 crates/router/src/services/authorization/permissions.rs
create mode 100644 crates/router/src/services/authorization/predefined_permissions.rs
diff --git a/crates/router/src/analytics/routes.rs b/crates/router/src/analytics/routes.rs
index 298ec61ec903..113312cdf10f 100644
--- a/crates/router/src/analytics/routes.rs
+++ b/crates/router/src/analytics/routes.rs
@@ -8,7 +8,10 @@ use router_env::AnalyticsFlow;
use super::{core::*, payments, refunds, types::AnalyticsDomain};
use crate::{
core::api_locking,
- services::{api, authentication as auth, authentication::AuthenticationData},
+ services::{
+ api, authentication as auth, authentication::AuthenticationData,
+ authorization::permissions::Permission,
+ },
AppState,
};
@@ -68,7 +71,11 @@ pub async fn get_payment_metrics(
|state, auth: AuthenticationData, req| {
payments::get_metrics(state.pool.clone(), auth.merchant_account, req)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::Analytics),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -98,7 +105,11 @@ pub async fn get_refunds_metrics(
|state, auth: AuthenticationData, req| {
refunds::get_metrics(state.pool.clone(), auth.merchant_account, req)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::Analytics),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -118,7 +129,11 @@ pub async fn get_payment_filters(
|state, auth: AuthenticationData, req| {
payment_filters_core(state.pool.clone(), req, auth.merchant_account)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::Analytics),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -138,7 +153,11 @@ pub async fn get_refund_filters(
|state, auth: AuthenticationData, req: GetRefundFilterRequest| {
refund_filter_core(state.pool.clone(), req, auth.merchant_account)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::Analytics),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs
index 410e3c1113b1..c5490ee00e63 100644
--- a/crates/router/src/consts.rs
+++ b/crates/router/src/consts.rs
@@ -58,3 +58,5 @@ pub const LOCKER_REDIS_EXPIRY_SECONDS: u32 = 60 * 15; // 15 minutes
#[cfg(any(feature = "olap", feature = "oltp"))]
pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days
+
+pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin";
diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs
index 3a71fed01a12..c570aca76038 100644
--- a/crates/router/src/consts/user.rs
+++ b/crates/router/src/consts/user.rs
@@ -1,8 +1,2 @@
-#[cfg(feature = "olap")]
pub const MAX_NAME_LENGTH: usize = 70;
-#[cfg(feature = "olap")]
pub const MAX_COMPANY_NAME_LENGTH: usize = 70;
-
-// USER ROLES
-#[cfg(any(feature = "olap", feature = "oltp"))]
-pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin";
diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs
index 710dc9281bfa..8b4cf45fe5ef 100644
--- a/crates/router/src/core/user.rs
+++ b/crates/router/src/core/user.rs
@@ -5,9 +5,7 @@ use masking::{ExposeInterface, Secret};
use router_env::env;
use super::errors::{UserErrors, UserResponse};
-use crate::{
- consts::user as consts, routes::AppState, services::ApplicationResponse, types::domain,
-};
+use crate::{consts, routes::AppState, services::ApplicationResponse, types::domain};
pub async fn connect_account(
state: AppState,
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index eef8cacc5f92..0586faabbf76 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -4,7 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::{admin::*, api_locking},
- services::{api, authentication as auth},
+ services::{api, authentication as auth, authorization::permissions::Permission},
types::api::admin,
};
@@ -77,7 +77,10 @@ pub async fn retrieve_merchant_account(
|state, _, req| get_merchant_account(state, req),
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantAccountRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
@@ -141,6 +144,7 @@ pub async fn update_merchant_account(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::MerchantAccountWrite,
},
req.headers(),
),
@@ -220,6 +224,7 @@ pub async fn payment_connector_create(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::MerchantConnectorAccountWrite,
},
req.headers(),
),
@@ -270,7 +275,10 @@ pub async fn payment_connector_retrieve(
},
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantConnectorAccountRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
@@ -312,7 +320,10 @@ pub async fn payment_connector_list(
|state, _, merchant_id| list_payment_connectors(state, merchant_id),
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantConnectorAccountRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
@@ -359,6 +370,7 @@ pub async fn payment_connector_update(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::MerchantConnectorAccountWrite,
},
req.headers(),
),
@@ -407,7 +419,10 @@ pub async fn payment_connector_delete(
|state, _, req| delete_payment_connector(state, req.merchant_id, req.merchant_connector_id),
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantConnectorAccountWrite,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
@@ -460,6 +475,7 @@ pub async fn business_profile_create(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::MerchantAccountWrite,
},
req.headers(),
),
@@ -484,7 +500,10 @@ pub async fn business_profile_retrieve(
|state, _, profile_id| retrieve_business_profile(state, profile_id),
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantAccountRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
@@ -511,6 +530,7 @@ pub async fn business_profile_update(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::MerchantAccountWrite,
},
req.headers(),
),
@@ -555,7 +575,10 @@ pub async fn business_profiles_list(
|state, _, merchant_id| list_business_profile(state, merchant_id),
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::MerchantAccountRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs
index 7299aa696390..5b4c047b1466 100644
--- a/crates/router/src/routes/api_keys.rs
+++ b/crates/router/src/routes/api_keys.rs
@@ -4,7 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::{api_keys, api_locking},
- services::{api, authentication as auth},
+ services::{api, authentication as auth, authorization::permissions::Permission},
types::api as api_types,
};
@@ -57,6 +57,7 @@ pub async fn api_key_create(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::ApiKeyWrite,
},
req.headers(),
),
@@ -101,6 +102,7 @@ pub async fn api_key_retrieve(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::ApiKeyRead,
},
req.headers(),
),
@@ -189,6 +191,7 @@ pub async fn api_key_revoke(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
+ required_permission: Permission::ApiKeyWrite,
},
req.headers(),
),
@@ -237,7 +240,10 @@ pub async fn api_key_list(
},
auth::auth_type(
&auth::AdminApiAuth,
- &auth::JWTAuthMerchantFromRoute { merchant_id },
+ &auth::JWTAuthMerchantFromRoute {
+ merchant_id,
+ required_permission: Permission::ApiKeyRead,
+ },
req.headers(),
),
api_locking::LockAction::NotApplicable,
diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs
index aaeb118645db..7bcd8ad35124 100644
--- a/crates/router/src/routes/disputes.rs
+++ b/crates/router/src/routes/disputes.rs
@@ -3,7 +3,7 @@ use actix_web::{web, HttpRequest, HttpResponse};
use api_models::disputes as dispute_models;
use router_env::{instrument, tracing, Flow};
-use crate::core::api_locking;
+use crate::{core::api_locking, services::authorization::permissions::Permission};
pub mod utils;
use super::app::AppState;
@@ -44,7 +44,11 @@ pub async fn retrieve_dispute(
&req,
dispute_id,
|state, auth, req| disputes::retrieve_dispute(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -87,7 +91,11 @@ pub async fn retrieve_disputes_list(
&req,
payload,
|state, auth, req| disputes::retrieve_disputes_list(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -125,7 +133,11 @@ pub async fn accept_dispute(
|state, auth, req| {
disputes::accept_dispute(state, auth.merchant_account, auth.key_store, req)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -158,7 +170,11 @@ pub async fn submit_dispute_evidence(
|state, auth, req| {
disputes::submit_evidence(state, auth.merchant_account, auth.key_store, req)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -199,7 +215,11 @@ pub async fn attach_dispute_evidence(
|state, auth, req| {
disputes::attach_evidence(state, auth.merchant_account, auth.key_store, req)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -235,7 +255,11 @@ pub async fn retrieve_dispute_evidence(
&req,
dispute_id,
|state, auth, req| disputes::retrieve_dispute_evidence(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::DisputeRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
diff --git a/crates/router/src/routes/files.rs b/crates/router/src/routes/files.rs
index bde221ebc161..95f4007cb91b 100644
--- a/crates/router/src/routes/files.rs
+++ b/crates/router/src/routes/files.rs
@@ -2,7 +2,7 @@ use actix_multipart::Multipart;
use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};
-use crate::core::api_locking;
+use crate::{core::api_locking, services::authorization::permissions::Permission};
pub mod transformers;
use super::app::AppState;
@@ -45,7 +45,11 @@ pub async fn files_create(
&req,
create_file_request,
|state, auth, req| files_create_core(state, auth.merchant_account, auth.key_store, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::FileWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -83,7 +87,11 @@ pub async fn files_delete(
&req,
file_id,
|state, auth, req| files_delete_core(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::FileWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -121,7 +129,11 @@ pub async fn files_retrieve(
&req,
file_id,
|state, auth, req| files_retrieve_core(state, auth.merchant_account, auth.key_store, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::FileRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
diff --git a/crates/router/src/routes/mandates.rs b/crates/router/src/routes/mandates.rs
index 0213d48ddca7..1e4461362975 100644
--- a/crates/router/src/routes/mandates.rs
+++ b/crates/router/src/routes/mandates.rs
@@ -4,7 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::{api_locking, mandate},
- services::{api, authentication as auth},
+ services::{api, authentication as auth, authorization::permissions::Permission},
types::api::mandates,
};
@@ -122,7 +122,11 @@ pub async fn retrieve_mandates_list(
&req,
payload,
|state, auth, req| mandate::retrieve_mandates_list(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::MandateRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs
index 4c26ea71f7d5..d45d67568b89 100644
--- a/crates/router/src/routes/payment_link.rs
+++ b/crates/router/src/routes/payment_link.rs
@@ -118,7 +118,7 @@ pub async fn payments_link_list(
&req,
payload,
|state, auth, payload| list_payment_link(state, auth.merchant_account, payload),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ &auth::ApiKeyAuth,
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs
index 81e53ade5e96..979b15a3d7f2 100644
--- a/crates/router/src/routes/payments.rs
+++ b/crates/router/src/routes/payments.rs
@@ -1,4 +1,7 @@
-use crate::core::api_locking::{self, GetLockingInput};
+use crate::{
+ core::api_locking::{self, GetLockingInput},
+ services::authorization::permissions::Permission,
+};
pub mod helpers;
use actix_web::{web, Responder};
@@ -128,7 +131,11 @@ pub async fn payments_create(
},
match env::which() {
env::Env::Production => &auth::ApiKeyAuth,
- _ => auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ _ => auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::PaymentWrite),
+ req.headers(),
+ ),
},
locking_action,
))
@@ -262,7 +269,7 @@ pub async fn payments_retrieve(
},
auth::auth_type(
&*auth_type,
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::PaymentRead),
req.headers(),
),
locking_action,
@@ -843,7 +850,11 @@ pub async fn payments_list(
&req,
payload,
|state, auth, req| payments::list_payments(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::PaymentRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -863,7 +874,11 @@ pub async fn payments_list_by_filter(
&req,
payload,
|state, auth, req| payments::apply_filters_on_payments(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::PaymentRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -883,7 +898,11 @@ pub async fn get_filters_for_payments(
&req,
payload,
|state, auth, req| payments::get_filters_for_payments(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::PaymentRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs
index d370af6b8d7a..47e9f2bf42a8 100644
--- a/crates/router/src/routes/refunds.rs
+++ b/crates/router/src/routes/refunds.rs
@@ -4,7 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::{api_locking, refunds::*},
- services::{api, authentication as auth},
+ services::{api, authentication as auth, authorization::permissions::Permission},
types::api::refunds,
};
@@ -37,7 +37,11 @@ pub async fn refunds_create(
&req,
json_payload.into_inner(),
|state, auth, req| refund_create_core(state, auth.merchant_account, auth.key_store, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RefundWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -88,7 +92,11 @@ pub async fn refunds_retrieve(
refund_retrieve_core,
)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RefundRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -202,7 +210,11 @@ pub async fn refunds_list(
&req,
payload.into_inner(),
|state, auth, req| refund_list(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RefundRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -235,7 +247,11 @@ pub async fn refunds_filter_list(
&req,
payload.into_inner(),
|state, auth, req| refund_filter_list(state, auth.merchant_account, req),
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RefundRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs
index 1d2549bb047a..e7e31cb36aeb 100644
--- a/crates/router/src/routes/routing.rs
+++ b/crates/router/src/routes/routing.rs
@@ -14,7 +14,7 @@ use router_env::{
use crate::{
core::{api_locking, conditional_config, routing, surcharge_decision_config},
routes::AppState,
- services::{api as oss_api, authentication as auth},
+ services::{api as oss_api, authentication as auth, authorization::permissions::Permission},
};
#[cfg(feature = "olap")]
@@ -34,9 +34,13 @@ pub async fn routing_create_config(
routing::create_routing_config(state, auth.merchant_account, auth.key_store, payload)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -65,9 +69,13 @@ pub async fn routing_link_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -91,9 +99,13 @@ pub async fn routing_retrieve_config(
routing::retrieve_routing_config(state, auth.merchant_account, algorithm_id)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -122,9 +134,13 @@ pub async fn routing_retrieve_dictionary(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -142,9 +158,13 @@ pub async fn routing_retrieve_dictionary(
routing::retrieve_merchant_routing_dictionary(state, auth.merchant_account)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -172,9 +192,13 @@ pub async fn routing_unlink_config(
routing::unlink_routing_config(state, auth.merchant_account, payload_req)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -192,9 +216,13 @@ pub async fn routing_unlink_config(
routing::unlink_routing_config(state, auth.merchant_account, auth.key_store)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -217,9 +245,13 @@ pub async fn routing_update_default_config(
routing::update_default_routing_config(state, auth.merchant_account, updated_config)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
)
.await
@@ -240,9 +272,13 @@ pub async fn routing_retrieve_default_config(
routing::retrieve_default_routing_config(state, auth.merchant_account)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
)
.await
@@ -270,9 +306,13 @@ pub async fn upsert_surcharge_decision_manager_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -297,9 +337,13 @@ pub async fn delete_surcharge_decision_manager_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -324,9 +368,13 @@ pub async fn retrieve_surcharge_decision_manager_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
api_locking::LockAction::NotApplicable,
)
.await
@@ -354,9 +402,13 @@ pub async fn upsert_decision_manager_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -382,9 +434,13 @@ pub async fn delete_decision_manager_config(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerWrite),
api_locking::LockAction::NotApplicable,
))
.await
@@ -406,9 +462,13 @@ pub async fn retrieve_decision_manager_config(
conditional_config::retrieve_conditional_config(state, auth.merchant_account)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::SurchargeDecisionManagerRead),
api_locking::LockAction::NotApplicable,
)
.await
@@ -434,9 +494,13 @@ pub async fn routing_retrieve_linked_config(
routing::retrieve_linked_routing_config(state, auth.merchant_account, query_params)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -454,9 +518,13 @@ pub async fn routing_retrieve_linked_config(
routing::retrieve_linked_routing_config(state, auth.merchant_account)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
api_locking::LockAction::NotApplicable,
))
.await
@@ -478,9 +546,17 @@ pub async fn routing_retrieve_default_config_for_profiles(
routing::retrieve_default_routing_config_for_profiles(state, auth.merchant_account)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
@@ -512,9 +588,13 @@ pub async fn routing_update_default_config_for_profile(
)
},
#[cfg(not(feature = "release"))]
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
+ req.headers(),
+ ),
#[cfg(feature = "release")]
- &auth::JWTAuth,
+ &auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs
index d0525bb272e8..4bcbacdf9912 100644
--- a/crates/router/src/routes/verification.rs
+++ b/crates/router/src/routes/verification.rs
@@ -5,7 +5,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::{api_locking, verification},
- services::{api, authentication as auth},
+ services::{api, authentication as auth, authorization::permissions::Permission},
};
#[instrument(skip_all, fields(flow = ?Flow::Verification))]
@@ -32,7 +32,11 @@ pub async fn apple_pay_merchant_registration(
merchant_id.clone(),
)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::MerchantAccountWrite),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
))
.await
@@ -60,7 +64,11 @@ pub async fn retrieve_apple_pay_verified_domains(
mca_id.to_string(),
)
},
- auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()),
+ auth::auth_type(
+ &auth::ApiKeyAuth,
+ &auth::JWTAuth(Permission::MerchantAccountRead),
+ req.headers(),
+ ),
api_locking::LockAction::NotApplicable,
)
.await
diff --git a/crates/router/src/services.rs b/crates/router/src/services.rs
index 21f33f0fa0b8..2d5552b59d17 100644
--- a/crates/router/src/services.rs
+++ b/crates/router/src/services.rs
@@ -1,5 +1,6 @@
pub mod api;
pub mod authentication;
+pub mod authorization;
pub mod encryption;
#[cfg(feature = "olap")]
pub mod jwt;
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index 4277205b0231..876804b7bb93 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -9,6 +9,7 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use masking::{PeekInterface, StrongSecret};
use serde::Serialize;
+use super::authorization::{self, permissions::Permission};
#[cfg(feature = "olap")]
use super::jwt;
#[cfg(feature = "olap")]
@@ -387,7 +388,7 @@ where
}
#[derive(Debug)]
-pub(crate) struct JWTAuth;
+pub(crate) struct JWTAuth(pub Permission);
#[derive(serde::Deserialize)]
struct JwtAuthPayloadFetchUnit {
@@ -406,6 +407,10 @@ where
state: &A,
) -> RouterResult<((), AuthenticationType)> {
let payload = parse_jwt_payload::(request_headers, state).await?;
+
+ let permissions = authorization::get_permissions(&payload.role_id)?;
+ authorization::check_authorization(&self.0, permissions)?;
+
Ok((
(),
AuthenticationType::MerchantJWT {
@@ -418,6 +423,7 @@ where
pub struct JWTAuthMerchantFromRoute {
pub merchant_id: String,
+ pub required_permission: Permission,
}
#[async_trait]
@@ -432,6 +438,9 @@ where
) -> RouterResult<((), AuthenticationType)> {
let payload = parse_jwt_payload::(request_headers, state).await?;
+ let permissions = authorization::get_permissions(&payload.role_id)?;
+ authorization::check_authorization(&self.required_permission, permissions)?;
+
// Check if token has access to merchantID that has been requested through query param
if payload.merchant_id != self.merchant_id {
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
@@ -460,6 +469,7 @@ where
#[derive(serde::Deserialize)]
struct JwtAuthPayloadFetchMerchantAccount {
merchant_id: String,
+ role_id: String,
}
#[async_trait]
@@ -475,6 +485,10 @@ where
let payload =
parse_jwt_payload::(request_headers, state)
.await?;
+
+ let permissions = authorization::get_permissions(&payload.role_id)?;
+ authorization::check_authorization(&self.0, permissions)?;
+
let key_store = state
.store()
.get_merchant_key_store_by_merchant_id(
diff --git a/crates/router/src/services/authorization.rs b/crates/router/src/services/authorization.rs
new file mode 100644
index 000000000000..cad9b1ece62e
--- /dev/null
+++ b/crates/router/src/services/authorization.rs
@@ -0,0 +1,27 @@
+use crate::core::errors::{ApiErrorResponse, RouterResult};
+
+pub mod info;
+pub mod permissions;
+pub mod predefined_permissions;
+
+pub fn get_permissions(role: &str) -> RouterResult<&Vec> {
+ predefined_permissions::PREDEFINED_PERMISSIONS
+ .get(role)
+ .map(|role_info| role_info.get_permissions())
+ .ok_or(ApiErrorResponse::InvalidJwtToken.into())
+}
+
+pub fn check_authorization(
+ required_permission: &permissions::Permission,
+ permissions: &[permissions::Permission],
+) -> RouterResult<()> {
+ permissions
+ .contains(required_permission)
+ .then_some(())
+ .ok_or(
+ ApiErrorResponse::AccessForbidden {
+ resource: required_permission.to_string(),
+ }
+ .into(),
+ )
+}
diff --git a/crates/router/src/services/authorization/info.rs b/crates/router/src/services/authorization/info.rs
new file mode 100644
index 000000000000..c6b649f3de5c
--- /dev/null
+++ b/crates/router/src/services/authorization/info.rs
@@ -0,0 +1,168 @@
+use strum::{EnumIter, IntoEnumIterator};
+
+use super::permissions::Permission;
+
+pub fn get_authorization_info() -> Vec {
+ PermissionModule::iter()
+ .map(|module| ModuleInfo::new(&module))
+ .collect()
+}
+
+pub struct PermissionInfo {
+ pub enum_name: Permission,
+ pub description: &'static str,
+}
+
+impl PermissionInfo {
+ pub fn new(permissions: &[Permission]) -> Vec {
+ let mut permission_infos = Vec::with_capacity(permissions.len());
+ for permission in permissions {
+ if let Some(description) = Permission::get_permission_description(permission) {
+ permission_infos.push(Self {
+ enum_name: permission.clone(),
+ description,
+ })
+ }
+ }
+ permission_infos
+ }
+}
+
+#[derive(PartialEq, EnumIter, Clone)]
+pub enum PermissionModule {
+ Payments,
+ Refunds,
+ MerchantAccount,
+ Connectors,
+ Forex,
+ Routing,
+ Analytics,
+ Mandates,
+ Disputes,
+ Files,
+ ThreeDsDecisionManager,
+ SurchargeDecisionManager,
+}
+
+impl PermissionModule {
+ pub fn get_module_description(&self) -> &'static str {
+ match self {
+ Self::Payments => "Everything related to payments - like creating and viewing payment related information are within this module",
+ Self::Refunds => "Refunds module encompasses everything related to refunds - like creating and viewing payment related information",
+ Self::MerchantAccount => "Accounts module permissions allow the user to view and update account details, configure webhooks and much more",
+ Self::Connectors => "All connector related actions - like configuring new connectors, viewing and updating connector configuration lies with this module",
+ Self::Routing => "All actions related to new, active, and past routing stacks take place here",
+ Self::Forex => "Forex module permissions allow the user to view and query the forex rates",
+ Self::Analytics => "Permission to view and analyse the data relating to payments, refunds, sdk etc.",
+ Self::Mandates => "Everything related to mandates - like creating and viewing mandate related information are within this module",
+ Self::Disputes => "Everything related to disputes - like creating and viewing dispute related information are within this module",
+ Self::Files => "Permissions for uploading, deleting and viewing files for disputes",
+ Self::ThreeDsDecisionManager => "View and configure 3DS decision rules configured for a merchant",
+ Self::SurchargeDecisionManager =>"View and configure surcharge decision rules configured for a merchant"
+ }
+ }
+}
+
+pub struct ModuleInfo {
+ pub module: PermissionModule,
+ pub description: &'static str,
+ pub permissions: Vec,
+}
+
+impl ModuleInfo {
+ pub fn new(module: &PermissionModule) -> Self {
+ let module_name = module.clone();
+ let description = module.get_module_description();
+
+ match module {
+ PermissionModule::Payments => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::PaymentRead,
+ Permission::PaymentWrite,
+ ]),
+ },
+ PermissionModule::Refunds => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::RefundRead,
+ Permission::RefundWrite,
+ ]),
+ },
+ PermissionModule::MerchantAccount => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::MerchantAccountRead,
+ Permission::MerchantAccountWrite,
+ ]),
+ },
+ PermissionModule::Connectors => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::MerchantConnectorAccountRead,
+ Permission::MerchantConnectorAccountWrite,
+ ]),
+ },
+ PermissionModule::Forex => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[Permission::ForexRead]),
+ },
+ PermissionModule::Routing => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::RoutingRead,
+ Permission::RoutingWrite,
+ ]),
+ },
+ PermissionModule::Analytics => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[Permission::Analytics]),
+ },
+ PermissionModule::Mandates => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::MandateRead,
+ Permission::MandateWrite,
+ ]),
+ },
+ PermissionModule::Disputes => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::DisputeRead,
+ Permission::DisputeWrite,
+ ]),
+ },
+ PermissionModule::Files => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[Permission::FileRead, Permission::FileWrite]),
+ },
+ PermissionModule::ThreeDsDecisionManager => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::ThreeDsDecisionManagerWrite,
+ Permission::ThreeDsDecisionManagerRead,
+ ]),
+ },
+
+ PermissionModule::SurchargeDecisionManager => Self {
+ module: module_name,
+ description,
+ permissions: PermissionInfo::new(&[
+ Permission::SurchargeDecisionManagerWrite,
+ Permission::SurchargeDecisionManagerRead,
+ ]),
+ },
+ }
+ }
+}
diff --git a/crates/router/src/services/authorization/permissions.rs b/crates/router/src/services/authorization/permissions.rs
new file mode 100644
index 000000000000..708da97e1e39
--- /dev/null
+++ b/crates/router/src/services/authorization/permissions.rs
@@ -0,0 +1,74 @@
+use strum::Display;
+
+#[derive(PartialEq, Display, Clone, Debug)]
+pub enum Permission {
+ PaymentRead,
+ PaymentWrite,
+ RefundRead,
+ RefundWrite,
+ ApiKeyRead,
+ ApiKeyWrite,
+ MerchantAccountRead,
+ MerchantAccountWrite,
+ MerchantConnectorAccountRead,
+ MerchantConnectorAccountWrite,
+ ForexRead,
+ RoutingRead,
+ RoutingWrite,
+ DisputeRead,
+ DisputeWrite,
+ MandateRead,
+ MandateWrite,
+ FileRead,
+ FileWrite,
+ Analytics,
+ ThreeDsDecisionManagerWrite,
+ ThreeDsDecisionManagerRead,
+ SurchargeDecisionManagerWrite,
+ SurchargeDecisionManagerRead,
+ UsersRead,
+ UsersWrite,
+ MerchantAccountCreate,
+}
+
+impl Permission {
+ pub fn get_permission_description(&self) -> Option<&'static str> {
+ match self {
+ Self::PaymentRead => Some("View all payments"),
+ Self::PaymentWrite => Some("Create payment, download payments data"),
+ Self::RefundRead => Some("View all refunds"),
+ Self::RefundWrite => Some("Create refund, download refunds data"),
+ Self::ApiKeyRead => Some("View API keys (masked generated for the system"),
+ Self::ApiKeyWrite => Some("Create and update API keys"),
+ Self::MerchantAccountRead => Some("View merchant account details"),
+ Self::MerchantAccountWrite => {
+ Some("Update merchant account details, configure webhooks, manage api keys")
+ }
+ Self::MerchantConnectorAccountRead => Some("View connectors configured"),
+ Self::MerchantConnectorAccountWrite => {
+ Some("Create, update, verify and delete connector configurations")
+ }
+ Self::ForexRead => Some("Query Forex data"),
+ Self::RoutingRead => Some("View routing configuration"),
+ Self::RoutingWrite => Some("Create and activate routing configurations"),
+ Self::DisputeRead => Some("View disputes"),
+ Self::DisputeWrite => Some("Create and update disputes"),
+ Self::MandateRead => Some("View mandates"),
+ Self::MandateWrite => Some("Create and update mandates"),
+ Self::FileRead => Some("View files"),
+ Self::FileWrite => Some("Create, update and delete files"),
+ Self::Analytics => Some("Access to analytics module"),
+ Self::ThreeDsDecisionManagerWrite => Some("Create and update 3DS decision rules"),
+ Self::ThreeDsDecisionManagerRead => {
+ Some("View all 3DS decision rules configured for a merchant")
+ }
+ Self::SurchargeDecisionManagerWrite => {
+ Some("Create and update the surcharge decision rules")
+ }
+ Self::SurchargeDecisionManagerRead => Some("View all the surcharge decision rules"),
+ Self::UsersRead => Some("View all the users for a merchant"),
+ Self::UsersWrite => Some("Invite users, assign and update roles"),
+ Self::MerchantAccountCreate => None,
+ }
+ }
+}
diff --git a/crates/router/src/services/authorization/predefined_permissions.rs b/crates/router/src/services/authorization/predefined_permissions.rs
new file mode 100644
index 000000000000..89fa2c8f739c
--- /dev/null
+++ b/crates/router/src/services/authorization/predefined_permissions.rs
@@ -0,0 +1,79 @@
+use std::collections::HashMap;
+
+use once_cell::sync::Lazy;
+
+use super::permissions::Permission;
+use crate::consts;
+
+pub struct RoleInfo {
+ permissions: Vec,
+ name: Option<&'static str>,
+ is_invitable: bool,
+}
+
+impl RoleInfo {
+ pub fn get_permissions(&self) -> &Vec {
+ &self.permissions
+ }
+
+ pub fn get_name(&self) -> Option<&'static str> {
+ self.name
+ }
+
+ pub fn is_invitable(&self) -> bool {
+ self.is_invitable
+ }
+}
+
+pub static PREDEFINED_PERMISSIONS: Lazy> = Lazy::new(|| {
+ let mut roles = HashMap::new();
+ roles.insert(
+ consts::ROLE_ID_ORGANIZATION_ADMIN,
+ RoleInfo {
+ permissions: vec![
+ Permission::PaymentRead,
+ Permission::PaymentWrite,
+ Permission::RefundRead,
+ Permission::RefundWrite,
+ Permission::ApiKeyRead,
+ Permission::ApiKeyWrite,
+ Permission::MerchantAccountRead,
+ Permission::MerchantAccountWrite,
+ Permission::MerchantConnectorAccountRead,
+ Permission::MerchantConnectorAccountWrite,
+ Permission::RoutingRead,
+ Permission::RoutingWrite,
+ Permission::ForexRead,
+ Permission::ThreeDsDecisionManagerWrite,
+ Permission::ThreeDsDecisionManagerRead,
+ Permission::SurchargeDecisionManagerWrite,
+ Permission::SurchargeDecisionManagerRead,
+ Permission::DisputeRead,
+ Permission::DisputeWrite,
+ Permission::MandateRead,
+ Permission::MandateWrite,
+ Permission::FileRead,
+ Permission::FileWrite,
+ Permission::Analytics,
+ Permission::UsersRead,
+ Permission::UsersWrite,
+ Permission::MerchantAccountCreate,
+ ],
+ name: Some("Organization Admin"),
+ is_invitable: false,
+ },
+ );
+ roles
+});
+
+pub fn get_role_name_from_id(role_id: &str) -> Option<&'static str> {
+ PREDEFINED_PERMISSIONS
+ .get(role_id)
+ .and_then(|role_info| role_info.name)
+}
+
+pub fn is_role_invitable(role_id: &str) -> bool {
+ PREDEFINED_PERMISSIONS
+ .get(role_id)
+ .map_or(false, |role_info| role_info.is_invitable)
+}