mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(roles): Add blacklist for roles (#3794)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -70,6 +70,8 @@ pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days
|
||||
|
||||
pub const USER_BLACKLIST_PREFIX: &str = "BU_";
|
||||
|
||||
pub const ROLE_BLACKLIST_PREFIX: &str = "BR_";
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub const EMAIL_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24; // 1 day
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ use crate::{
|
||||
core::errors::{StorageErrorExt, UserErrors, UserResponse},
|
||||
routes::AppState,
|
||||
services::{
|
||||
authentication::UserFromToken,
|
||||
authentication::{blacklist, UserFromToken},
|
||||
authorization::roles::{self, predefined_roles::PREDEFINED_ROLES},
|
||||
ApplicationResponse,
|
||||
},
|
||||
@ -219,5 +219,7 @@ pub async fn update_role(
|
||||
.await
|
||||
.to_duplicate_response(UserErrors::RoleNameAlreadyExists)?;
|
||||
|
||||
blacklist::insert_role_in_blacklist(&state, role_id).await?;
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ use masking::ExposeInterface;
|
||||
use masking::{PeekInterface, StrongSecret};
|
||||
use serde::Serialize;
|
||||
|
||||
use self::blacklist::BlackList;
|
||||
use super::authorization::{self, permissions::Permission};
|
||||
#[cfg(feature = "olap")]
|
||||
use super::jwt;
|
||||
@ -334,7 +335,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(UserWithoutMerchantFromToken, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, UserAuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -499,7 +500,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<((), AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -528,7 +529,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(UserFromToken, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -566,7 +567,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<((), AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -609,7 +610,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(AuthenticationData, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -659,7 +660,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(AuthenticationDataWithUserId, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -710,7 +711,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<(UserFromToken, AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
@ -741,7 +742,7 @@ where
|
||||
state: &A,
|
||||
) -> RouterResult<((), AuthenticationType)> {
|
||||
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||
if blacklist::check_user_in_blacklist(state, &payload.user_id, payload.exp).await? {
|
||||
if payload.check_in_blacklist(state).await? {
|
||||
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,11 @@ use common_utils::date_time;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use redis_interface::RedisConnectionPool;
|
||||
|
||||
use super::{AuthToken, UserAuthToken};
|
||||
#[cfg(feature = "email")]
|
||||
use crate::consts::{EMAIL_TOKEN_BLACKLIST_PREFIX, EMAIL_TOKEN_TIME_IN_SECS};
|
||||
use crate::{
|
||||
consts::{JWT_TOKEN_TIME_IN_SECS, USER_BLACKLIST_PREFIX},
|
||||
consts::{JWT_TOKEN_TIME_IN_SECS, ROLE_BLACKLIST_PREFIX, USER_BLACKLIST_PREFIX},
|
||||
core::errors::{ApiErrorResponse, RouterResult},
|
||||
routes::app::AppStateInfo,
|
||||
};
|
||||
@ -34,6 +35,22 @@ pub async fn insert_user_in_blacklist(state: &AppState, user_id: &str) -> UserRe
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
pub async fn insert_role_in_blacklist(state: &AppState, role_id: &str) -> UserResult<()> {
|
||||
let role_blacklist_key = format!("{}{}", ROLE_BLACKLIST_PREFIX, role_id);
|
||||
let expiry =
|
||||
expiry_to_i64(JWT_TOKEN_TIME_IN_SECS).change_context(UserErrors::InternalServerError)?;
|
||||
let redis_conn = get_redis_connection(state).change_context(UserErrors::InternalServerError)?;
|
||||
redis_conn
|
||||
.set_key_with_expiry(
|
||||
role_blacklist_key.as_str(),
|
||||
date_time::now_unix_timestamp(),
|
||||
expiry,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
|
||||
pub async fn check_user_in_blacklist<A: AppStateInfo>(
|
||||
state: &A,
|
||||
user_id: &str,
|
||||
@ -49,6 +66,21 @@ pub async fn check_user_in_blacklist<A: AppStateInfo>(
|
||||
.map(|timestamp| timestamp.map_or(false, |timestamp| timestamp > token_issued_at))
|
||||
}
|
||||
|
||||
pub async fn check_role_in_blacklist<A: AppStateInfo>(
|
||||
state: &A,
|
||||
role_id: &str,
|
||||
token_expiry: u64,
|
||||
) -> RouterResult<bool> {
|
||||
let token = format!("{}{}", ROLE_BLACKLIST_PREFIX, role_id);
|
||||
let token_issued_at = expiry_to_i64(token_expiry - JWT_TOKEN_TIME_IN_SECS)?;
|
||||
let redis_conn = get_redis_connection(state)?;
|
||||
redis_conn
|
||||
.get_key::<Option<i64>>(token.as_str())
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.map(|timestamp| timestamp.map_or(false, |timestamp| timestamp > token_issued_at))
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn insert_email_token_in_blacklist(state: &AppState, token: &str) -> UserResult<()> {
|
||||
let redis_conn = get_redis_connection(state).change_context(UserErrors::InternalServerError)?;
|
||||
@ -90,3 +122,33 @@ fn expiry_to_i64(expiry: u64) -> RouterResult<i64> {
|
||||
.into_report()
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait BlackList {
|
||||
async fn check_in_blacklist<A>(&self, state: &A) -> RouterResult<bool>
|
||||
where
|
||||
A: AppStateInfo + Sync;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BlackList for AuthToken {
|
||||
async fn check_in_blacklist<A>(&self, state: &A) -> RouterResult<bool>
|
||||
where
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
Ok(
|
||||
check_user_in_blacklist(state, &self.user_id, self.exp).await?
|
||||
|| check_role_in_blacklist(state, &self.role_id, self.exp).await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BlackList for UserAuthToken {
|
||||
async fn check_in_blacklist<A>(&self, state: &A) -> RouterResult<bool>
|
||||
where
|
||||
A: AppStateInfo + Sync,
|
||||
{
|
||||
check_user_in_blacklist(state, &self.user_id, self.exp).await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user