mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
refactor(api_keys): use merchant_id and key_id to query the table (#939)
Signed-off-by: Chethan <chethan.rao@juspay.in>
This commit is contained in:
@ -134,10 +134,13 @@ pub struct UpdateApiKeyRequest {
|
|||||||
/// The response body for revoking an API Key.
|
/// The response body for revoking an API Key.
|
||||||
#[derive(Debug, Serialize, ToSchema)]
|
#[derive(Debug, Serialize, ToSchema)]
|
||||||
pub struct RevokeApiKeyResponse {
|
pub struct RevokeApiKeyResponse {
|
||||||
|
/// The identifier for the Merchant Account.
|
||||||
|
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44")]
|
||||||
|
pub merchant_id: String,
|
||||||
|
|
||||||
/// The identifier for the API Key.
|
/// The identifier for the API Key.
|
||||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX")]
|
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX")]
|
||||||
pub key_id: String,
|
pub key_id: String,
|
||||||
|
|
||||||
/// Indicates whether the API key was revoked or not.
|
/// Indicates whether the API key was revoked or not.
|
||||||
#[schema(example = "true")]
|
#[schema(example = "true")]
|
||||||
pub revoked: bool,
|
pub revoked: bool,
|
||||||
|
|||||||
@ -158,10 +158,11 @@ pub async fn create_api_key(
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn retrieve_api_key(
|
pub async fn retrieve_api_key(
|
||||||
store: &dyn StorageInterface,
|
store: &dyn StorageInterface,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
||||||
let api_key = store
|
let api_key = store
|
||||||
.find_api_key_by_key_id_optional(key_id)
|
.find_api_key_by_merchant_id_key_id_optional(merchant_id, key_id)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError) // If retrieve failed
|
.change_context(errors::ApiErrorResponse::InternalServerError) // If retrieve failed
|
||||||
.attach_printable("Failed to retrieve new API key")?
|
.attach_printable("Failed to retrieve new API key")?
|
||||||
@ -173,11 +174,16 @@ pub async fn retrieve_api_key(
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn update_api_key(
|
pub async fn update_api_key(
|
||||||
store: &dyn StorageInterface,
|
store: &dyn StorageInterface,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
api_key: api::UpdateApiKeyRequest,
|
api_key: api::UpdateApiKeyRequest,
|
||||||
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
||||||
let api_key = store
|
let api_key = store
|
||||||
.update_api_key(key_id.to_owned(), api_key.foreign_into())
|
.update_api_key(
|
||||||
|
merchant_id.to_owned(),
|
||||||
|
key_id.to_owned(),
|
||||||
|
api_key.foreign_into(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;
|
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;
|
||||||
|
|
||||||
@ -187,16 +193,18 @@ pub async fn update_api_key(
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn revoke_api_key(
|
pub async fn revoke_api_key(
|
||||||
store: &dyn StorageInterface,
|
store: &dyn StorageInterface,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
) -> RouterResponse<api::RevokeApiKeyResponse> {
|
) -> RouterResponse<api::RevokeApiKeyResponse> {
|
||||||
let revoked = store
|
let revoked = store
|
||||||
.revoke_api_key(key_id)
|
.revoke_api_key(merchant_id, key_id)
|
||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;
|
.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound)?;
|
||||||
|
|
||||||
metrics::API_KEY_REVOKED.add(&metrics::CONTEXT, 1, &[]);
|
metrics::API_KEY_REVOKED.add(&metrics::CONTEXT, 1, &[]);
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(api::RevokeApiKeyResponse {
|
Ok(ApplicationResponse::Json(api::RevokeApiKeyResponse {
|
||||||
|
merchant_id: merchant_id.to_owned(),
|
||||||
key_id: key_id.to_owned(),
|
key_id: key_id.to_owned(),
|
||||||
revoked,
|
revoked,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -16,14 +16,20 @@ pub trait ApiKeyInterface {
|
|||||||
|
|
||||||
async fn update_api_key(
|
async fn update_api_key(
|
||||||
&self,
|
&self,
|
||||||
|
merchant_id: String,
|
||||||
key_id: String,
|
key_id: String,
|
||||||
api_key: storage::ApiKeyUpdate,
|
api_key: storage::ApiKeyUpdate,
|
||||||
) -> CustomResult<storage::ApiKey, errors::StorageError>;
|
) -> CustomResult<storage::ApiKey, errors::StorageError>;
|
||||||
|
|
||||||
async fn revoke_api_key(&self, key_id: &str) -> CustomResult<bool, errors::StorageError>;
|
async fn revoke_api_key(
|
||||||
|
|
||||||
async fn find_api_key_by_key_id_optional(
|
|
||||||
&self,
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
key_id: &str,
|
||||||
|
) -> CustomResult<bool, errors::StorageError>;
|
||||||
|
|
||||||
|
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;
|
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;
|
||||||
|
|
||||||
@ -56,30 +62,36 @@ impl ApiKeyInterface for Store {
|
|||||||
|
|
||||||
async fn update_api_key(
|
async fn update_api_key(
|
||||||
&self,
|
&self,
|
||||||
|
merchant_id: String,
|
||||||
key_id: String,
|
key_id: String,
|
||||||
api_key: storage::ApiKeyUpdate,
|
api_key: storage::ApiKeyUpdate,
|
||||||
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
let conn = connection::pg_connection_write(self).await?;
|
||||||
storage::ApiKey::update_by_key_id(&conn, key_id, api_key)
|
storage::ApiKey::update_by_merchant_id_key_id(&conn, merchant_id, key_id, api_key)
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.into_report()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revoke_api_key(&self, key_id: &str) -> CustomResult<bool, errors::StorageError> {
|
async fn revoke_api_key(
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
|
||||||
storage::ApiKey::revoke_by_key_id(&conn, key_id)
|
|
||||||
.await
|
|
||||||
.map_err(Into::into)
|
|
||||||
.into_report()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_api_key_by_key_id_optional(
|
|
||||||
&self,
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
key_id: &str,
|
||||||
|
) -> CustomResult<bool, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_write(self).await?;
|
||||||
|
storage::ApiKey::revoke_by_merchant_id_key_id(&conn, merchant_id, key_id)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
||||||
let conn = connection::pg_connection_read(self).await?;
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
storage::ApiKey::find_optional_by_key_id(&conn, key_id)
|
storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.into_report()
|
||||||
@ -122,6 +134,7 @@ impl ApiKeyInterface for MockDb {
|
|||||||
|
|
||||||
async fn update_api_key(
|
async fn update_api_key(
|
||||||
&self,
|
&self,
|
||||||
|
_merchant_id: String,
|
||||||
_key_id: String,
|
_key_id: String,
|
||||||
_api_key: storage::ApiKeyUpdate,
|
_api_key: storage::ApiKeyUpdate,
|
||||||
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
||||||
@ -129,13 +142,18 @@ impl ApiKeyInterface for MockDb {
|
|||||||
Err(errors::StorageError::MockDbError)?
|
Err(errors::StorageError::MockDbError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revoke_api_key(&self, _key_id: &str) -> CustomResult<bool, errors::StorageError> {
|
async fn revoke_api_key(
|
||||||
|
&self,
|
||||||
|
_merchant_id: &str,
|
||||||
|
_key_id: &str,
|
||||||
|
) -> CustomResult<bool, errors::StorageError> {
|
||||||
// [#172]: Implement function for `MockDb`
|
// [#172]: Implement function for `MockDb`
|
||||||
Err(errors::StorageError::MockDbError)?
|
Err(errors::StorageError::MockDbError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_api_key_by_key_id_optional(
|
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||||
&self,
|
&self,
|
||||||
|
_merchant_id: &str,
|
||||||
_key_id: &str,
|
_key_id: &str,
|
||||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
||||||
// [#172]: Implement function for `MockDb`
|
// [#172]: Implement function for `MockDb`
|
||||||
|
|||||||
@ -82,14 +82,16 @@ pub async fn api_key_retrieve(
|
|||||||
path: web::Path<(String, String)>,
|
path: web::Path<(String, String)>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let flow = Flow::ApiKeyRetrieve;
|
let flow = Flow::ApiKeyRetrieve;
|
||||||
let (_merchant_id, key_id) = path.into_inner();
|
let (merchant_id, key_id) = path.into_inner();
|
||||||
|
|
||||||
api::server_wrap(
|
api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
state.get_ref(),
|
state.get_ref(),
|
||||||
&req,
|
&req,
|
||||||
&key_id,
|
(&merchant_id, &key_id),
|
||||||
|state, _, key_id| api_keys::retrieve_api_key(&*state.store, key_id),
|
|state, _, (merchant_id, key_id)| {
|
||||||
|
api_keys::retrieve_api_key(&*state.store, merchant_id, key_id)
|
||||||
|
},
|
||||||
&auth::AdminApiAuth,
|
&auth::AdminApiAuth,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -122,15 +124,17 @@ pub async fn api_key_update(
|
|||||||
json_payload: web::Json<api_types::UpdateApiKeyRequest>,
|
json_payload: web::Json<api_types::UpdateApiKeyRequest>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let flow = Flow::ApiKeyUpdate;
|
let flow = Flow::ApiKeyUpdate;
|
||||||
let (_merchant_id, key_id) = path.into_inner();
|
let (merchant_id, key_id) = path.into_inner();
|
||||||
let payload = json_payload.into_inner();
|
let payload = json_payload.into_inner();
|
||||||
|
|
||||||
api::server_wrap(
|
api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
state.get_ref(),
|
state.get_ref(),
|
||||||
&req,
|
&req,
|
||||||
(&key_id, payload),
|
(&merchant_id, &key_id, payload),
|
||||||
|state, _, (key_id, payload)| api_keys::update_api_key(&*state.store, key_id, payload),
|
|state, _, (merchant_id, key_id, payload)| {
|
||||||
|
api_keys::update_api_key(&*state.store, merchant_id, key_id, payload)
|
||||||
|
},
|
||||||
&auth::AdminApiAuth,
|
&auth::AdminApiAuth,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -162,14 +166,16 @@ pub async fn api_key_revoke(
|
|||||||
path: web::Path<(String, String)>,
|
path: web::Path<(String, String)>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let flow = Flow::ApiKeyRevoke;
|
let flow = Flow::ApiKeyRevoke;
|
||||||
let (_merchant_id, key_id) = path.into_inner();
|
let (merchant_id, key_id) = path.into_inner();
|
||||||
|
|
||||||
api::server_wrap(
|
api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
state.get_ref(),
|
state.get_ref(),
|
||||||
&req,
|
&req,
|
||||||
&key_id,
|
(&merchant_id, &key_id),
|
||||||
|state, _, key_id| api_keys::revoke_api_key(&*state.store, key_id),
|
|state, _, (merchant_id, key_id)| {
|
||||||
|
api_keys::revoke_api_key(&*state.store, merchant_id, key_id)
|
||||||
|
},
|
||||||
&auth::AdminApiAuth,
|
&auth::AdminApiAuth,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use diesel::{associations::HasTable, ExpressionMethods};
|
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
use super::generics;
|
use super::generics;
|
||||||
@ -18,14 +18,22 @@ impl ApiKeyNew {
|
|||||||
|
|
||||||
impl ApiKey {
|
impl ApiKey {
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn update_by_key_id(
|
pub async fn update_by_merchant_id_key_id(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
|
merchant_id: String,
|
||||||
key_id: String,
|
key_id: String,
|
||||||
api_key_update: ApiKeyUpdate,
|
api_key_update: ApiKeyUpdate,
|
||||||
) -> StorageResult<Self> {
|
) -> StorageResult<Self> {
|
||||||
match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
|
match generics::generic_update_with_unique_predicate_get_result::<
|
||||||
|
<Self as HasTable>::Table,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
conn,
|
conn,
|
||||||
key_id.clone(),
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.to_owned())
|
||||||
|
.and(dsl::key_id.eq(key_id.to_owned())),
|
||||||
ApiKeyUpdateInternal::from(api_key_update),
|
ApiKeyUpdateInternal::from(api_key_update),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -35,8 +43,13 @@ impl ApiKey {
|
|||||||
Err(error.attach_printable("API key with the given key ID does not exist"))
|
Err(error.attach_printable("API key with the given key ID does not exist"))
|
||||||
}
|
}
|
||||||
errors::DatabaseError::NoFieldsToUpdate => {
|
errors::DatabaseError::NoFieldsToUpdate => {
|
||||||
generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(conn, key_id)
|
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||||
.await
|
conn,
|
||||||
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.to_owned())
|
||||||
|
.and(dsl::key_id.eq(key_id.to_owned())),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
_ => Err(error),
|
_ => Err(error),
|
||||||
},
|
},
|
||||||
@ -45,22 +58,31 @@ impl ApiKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn revoke_by_key_id(conn: &PgPooledConn, key_id: &str) -> StorageResult<bool> {
|
pub async fn revoke_by_merchant_id_key_id(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
merchant_id: &str,
|
||||||
|
key_id: &str,
|
||||||
|
) -> StorageResult<bool> {
|
||||||
generics::generic_delete::<<Self as HasTable>::Table, _>(
|
generics::generic_delete::<<Self as HasTable>::Table, _>(
|
||||||
conn,
|
conn,
|
||||||
dsl::key_id.eq(key_id.to_owned()),
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.to_owned())
|
||||||
|
.and(dsl::key_id.eq(key_id.to_owned())),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(conn))]
|
#[instrument(skip(conn))]
|
||||||
pub async fn find_optional_by_key_id(
|
pub async fn find_optional_by_merchant_id_key_id(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
|
merchant_id: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
) -> StorageResult<Option<Self>> {
|
) -> StorageResult<Option<Self>> {
|
||||||
generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
|
generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
|
||||||
conn,
|
conn,
|
||||||
key_id.to_owned(),
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.to_owned())
|
||||||
|
.and(dsl::key_id.eq(key_id.to_owned())),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
DROP INDEX api_keys_merchant_id_key_id_index;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
CREATE UNIQUE INDEX api_keys_merchant_id_key_id_index ON api_keys (merchant_id, key_id);
|
||||||
Reference in New Issue
Block a user