Skip to content

Commit

Permalink
feat(decision): add support to register api keys to proxy (#5168)
Browse files Browse the repository at this point in the history
  • Loading branch information
NishantJoshi00 authored Jul 9, 2024
1 parent ffc7967 commit 071d534
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 2 deletions.
1 change: 1 addition & 0 deletions crates/router/src/configs/secrets_transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,5 +394,6 @@ pub(crate) async fn fetch_raw_secrets(
saved_payment_methods: conf.saved_payment_methods,
multitenancy: conf.multitenancy,
user_auth_methods,
decision: conf.decision,
}
}
6 changes: 6 additions & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub struct Settings<S: SecretState> {
pub multitenancy: Multitenancy,
pub saved_payment_methods: EligiblePaymentMethods,
pub user_auth_methods: SecretStateContainer<UserAuthMethodSettings, S>,
pub decision: Option<DecisionConfig>,
}

#[derive(Debug, Deserialize, Clone, Default)]
Expand All @@ -146,6 +147,11 @@ impl Multitenancy {
}
}

#[derive(Debug, Deserialize, Clone, Default)]
pub struct DecisionConfig {
pub base_url: String,
}

#[derive(Debug, Deserialize, Clone, Default)]
#[serde(transparent)]
pub struct TenantConfig(pub HashMap<String, Tenant>);
Expand Down
43 changes: 42 additions & 1 deletion crates/router/src/core/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::{
},
db::StorageInterface,
routes::{metrics, SessionState},
services::{self, api as service_api},
services::{self, api as service_api, authentication},
types::{
self, api,
domain::{
Expand Down Expand Up @@ -281,6 +281,25 @@ pub async fn create_merchant_account(
.await
.to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?;

if let Some(api_key) = merchant_account.publishable_key.as_ref() {
let state = state.clone();
let api_key = api_key.clone();
let merchant_id = merchant_account.merchant_id.clone();

authentication::decision::spawn_tracked_job(
async move {
authentication::decision::add_publishable_key(
&state,
api_key.into(),
merchant_id,
None,
)
.await
},
authentication::decision::ADD,
);
}

db.insert_config(configs::ConfigNew {
key: format!("{}_requires_cvv", merchant_account.merchant_id),
config: "true".to_string(),
Expand Down Expand Up @@ -650,6 +669,20 @@ pub async fn merchant_account_delete(
) -> RouterResponse<api::MerchantAccountDeleteResponse> {
let mut is_deleted = false;
let db = state.store.as_ref();

let merchant_key_store = db
.get_merchant_key_store_by_merchant_id(
&merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

let merchant_account = db
.find_merchant_account_by_merchant_id(&merchant_id, &merchant_key_store)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

let is_merchant_account_deleted = db
.delete_merchant_account_by_merchant_id(&merchant_id)
.await
Expand All @@ -662,6 +695,14 @@ pub async fn merchant_account_delete(
is_deleted = is_merchant_account_deleted && is_merchant_key_store_deleted;
}

if let Some(api_key) = merchant_account.publishable_key {
let state = state.clone();
authentication::decision::spawn_tracked_job(
async move { authentication::decision::revoke_api_key(&state, api_key.into()).await },
authentication::decision::REVOKE,
)
}

match db
.delete_config_by_key(format!("{}_requires_cvv", merchant_id).as_str())
.await
Expand Down
60 changes: 59 additions & 1 deletion crates/router/src/core/api_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
consts,
core::errors::{self, RouterResponse, StorageErrorExt},
routes::{metrics, SessionState},
services::ApplicationResponse,
services::{authentication, ApplicationResponse},
types::{api, storage, transformers::ForeignInto},
utils,
};
Expand Down Expand Up @@ -148,6 +148,26 @@ pub async fn create_api_key(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to insert new API key")?;

let state_inner = state.clone();
let hashed_api_key = api_key.hashed_api_key.clone();
let merchant_id_inner = merchant_id.clone();
let key_id = api_key.key_id.clone();
let expires_at = api_key.expires_at;

authentication::decision::spawn_tracked_job(
async move {
authentication::decision::add_api_key(
&state_inner,
hashed_api_key.into_inner().into(),
merchant_id_inner,
key_id,
expires_at.map(authentication::decision::convert_expiry),
)
.await
},
authentication::decision::ADD,
);

metrics::API_KEY_CREATED.add(
&metrics::CONTEXT,
1,
Expand Down Expand Up @@ -277,6 +297,25 @@ pub async fn update_api_key(
.await
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;

let state_inner = state.clone();
let hashed_api_key = api_key.hashed_api_key.clone();
let key_id_inner = api_key.key_id.clone();
let expires_at = api_key.expires_at;

authentication::decision::spawn_tracked_job(
async move {
authentication::decision::add_api_key(
&state_inner,
hashed_api_key.into_inner().into(),
merchant_id.clone(),
key_id_inner,
expires_at.map(authentication::decision::convert_expiry),
)
.await
},
authentication::decision::ADD,
);

#[cfg(feature = "email")]
{
let expiry_reminder_days = state.conf.api_keys.get_inner().expiry_reminder_days.clone();
Expand Down Expand Up @@ -402,11 +441,30 @@ pub async fn revoke_api_key(
key_id: &str,
) -> RouterResponse<api::RevokeApiKeyResponse> {
let store = state.store.as_ref();

let api_key = store
.find_api_key_by_merchant_id_key_id_optional(merchant_id, key_id)
.await
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;

let revoked = store
.revoke_api_key(merchant_id, key_id)
.await
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;

if let Some(api_key) = api_key {
let hashed_api_key = api_key.hashed_api_key;
let state = state.clone();

authentication::decision::spawn_tracked_job(
async move {
authentication::decision::revoke_api_key(&state, hashed_api_key.into_inner().into())
.await
},
authentication::decision::REVOKE,
);
}

metrics::API_KEY_REVOKED.add(&metrics::CONTEXT, 1, &[]);

#[cfg(feature = "email")]
Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/core/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,6 @@ counter_metric!(
ROUTING_RETRIEVE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE,
GLOBAL_METER
);

counter_metric!(API_KEY_REQUEST_INITIATED, GLOBAL_METER);
counter_metric!(API_KEY_REQUEST_COMPLETED, GLOBAL_METER);
1 change: 1 addition & 0 deletions crates/router/src/services/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::{
};
pub mod blacklist;
pub mod cookies;
pub mod decision;

#[derive(Clone, Debug)]
pub struct AuthenticationData {
Expand Down
Loading

0 comments on commit 071d534

Please sign in to comment.