mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(users): Implemented Set-Cookie (#3865)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
21
Cargo.lock
generated
21
Cargo.lock
generated
@ -224,7 +224,7 @@ dependencies = [
|
|||||||
"bytes 1.5.0",
|
"bytes 1.5.0",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"cookie",
|
"cookie 0.16.2",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -616,7 +616,7 @@ dependencies = [
|
|||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
"bytes 1.5.0",
|
"bytes 1.5.0",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"cookie",
|
"cookie 0.16.2",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1763,6 +1763,16 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
|
||||||
|
dependencies = [
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie-factory"
|
name = "cookie-factory"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -2524,7 +2534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "65f0fbe245d714b596ba5802b46f937f5ce68dcae0f32f9a70b5c3b04d3c6f64"
|
checksum = "65f0fbe245d714b596ba5802b46f937f5ce68dcae0f32f9a70b5c3b04d3c6f64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"cookie",
|
"cookie 0.16.2",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
@ -5219,6 +5229,7 @@ dependencies = [
|
|||||||
"common_enums",
|
"common_enums",
|
||||||
"common_utils",
|
"common_utils",
|
||||||
"config",
|
"config",
|
||||||
|
"cookie 0.18.0",
|
||||||
"currency_conversion",
|
"currency_conversion",
|
||||||
"data_models",
|
"data_models",
|
||||||
"derive_deref",
|
"derive_deref",
|
||||||
@ -6414,7 +6425,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cookie",
|
"cookie 0.16.2",
|
||||||
"fantoccini",
|
"fantoccini",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"http",
|
"http",
|
||||||
@ -7461,7 +7472,7 @@ checksum = "9973cb72c8587d5ad5efdb91e663d36177dc37725e6c90ca86c626b0cc45c93f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"bytes 1.5.0",
|
"bytes 1.5.0",
|
||||||
"cookie",
|
"cookie 0.16.2",
|
||||||
"http",
|
"http",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@ -58,6 +58,24 @@ impl<T: Eq + PartialEq + Clone> Maskable<T> {
|
|||||||
pub fn new_normal(item: T) -> Self {
|
pub fn new_normal(item: T) -> Self {
|
||||||
Self::Normal(item)
|
Self::Normal(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks whether the data is masked.
|
||||||
|
/// Returns `true` if the data is wrapped in the `Masked` variant,
|
||||||
|
/// returns `false` otherwise.
|
||||||
|
///
|
||||||
|
pub fn is_masked(&self) -> bool {
|
||||||
|
matches!(self, Self::Masked(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks whether the data is normal (not masked).
|
||||||
|
/// Returns `true` if the data is wrapped in the `Normal` variant,
|
||||||
|
/// returns `false` otherwise.
|
||||||
|
///
|
||||||
|
pub fn is_normal(&self) -> bool {
|
||||||
|
matches!(self, Self::Normal(_))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for providing a method on custom types for constructing `Maskable`
|
/// Trait for providing a method on custom types for constructing `Maskable`
|
||||||
|
|||||||
@ -46,6 +46,7 @@ blake3 = "1.3.3"
|
|||||||
bytes = "1.4.0"
|
bytes = "1.4.0"
|
||||||
clap = { version = "4.3.2", default-features = false, features = ["std", "derive", "help", "usage"] }
|
clap = { version = "4.3.2", default-features = false, features = ["std", "derive", "help", "usage"] }
|
||||||
config = { version = "0.13.3", features = ["toml"] }
|
config = { version = "0.13.3", features = ["toml"] }
|
||||||
|
cookie = "0.18.0"
|
||||||
diesel = { version = "2.1.0", features = ["postgres"] }
|
diesel = { version = "2.1.0", features = ["postgres"] }
|
||||||
digest = "0.9"
|
digest = "0.9"
|
||||||
dyn-clone = "1.0.11"
|
dyn-clone = "1.0.11"
|
||||||
|
|||||||
@ -68,6 +68,8 @@ pub const LOCKER_REDIS_EXPIRY_SECONDS: u32 = 60 * 15; // 15 minutes
|
|||||||
|
|
||||||
pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days
|
pub const JWT_TOKEN_TIME_IN_SECS: u64 = 60 * 60 * 24 * 2; // 2 days
|
||||||
|
|
||||||
|
pub const JWT_TOKEN_COOKIE_NAME: &str = "login_token";
|
||||||
|
|
||||||
pub const USER_BLACKLIST_PREFIX: &str = "BU_";
|
pub const USER_BLACKLIST_PREFIX: &str = "BU_";
|
||||||
|
|
||||||
pub const ROLE_BLACKLIST_PREFIX: &str = "BR_";
|
pub const ROLE_BLACKLIST_PREFIX: &str = "BR_";
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use common_enums::RequestIncrementalAuthorization;
|
|||||||
use common_utils::{consts::X_HS_LATENCY, fp_utils};
|
use common_utils::{consts::X_HS_LATENCY, fp_utils};
|
||||||
use diesel_models::ephemeral_key;
|
use diesel_models::ephemeral_key;
|
||||||
use error_stack::{report, IntoReport, ResultExt};
|
use error_stack::{report, IntoReport, ResultExt};
|
||||||
|
use masking::Maskable;
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
use super::{flows::Feature, PaymentData};
|
use super::{flows::Feature, PaymentData};
|
||||||
@ -466,20 +467,25 @@ where
|
|||||||
.map(|status_code| {
|
.map(|status_code| {
|
||||||
vec![(
|
vec![(
|
||||||
"connector_http_status_code".to_string(),
|
"connector_http_status_code".to_string(),
|
||||||
status_code.to_string(),
|
Maskable::new_normal(status_code.to_string()),
|
||||||
)]
|
)]
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if let Some(payment_confirm_source) = payment_intent.payment_confirm_source {
|
if let Some(payment_confirm_source) = payment_intent.payment_confirm_source {
|
||||||
headers.push((
|
headers.push((
|
||||||
"payment_confirm_source".to_string(),
|
"payment_confirm_source".to_string(),
|
||||||
payment_confirm_source.to_string(),
|
Maskable::new_normal(payment_confirm_source.to_string()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.extend(
|
headers.extend(
|
||||||
external_latency
|
external_latency
|
||||||
.map(|latency| vec![(X_HS_LATENCY.to_string(), latency.to_string())])
|
.map(|latency| {
|
||||||
|
vec![(
|
||||||
|
X_HS_LATENCY.to_string(),
|
||||||
|
Maskable::new_normal(latency.to_string()),
|
||||||
|
)]
|
||||||
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -93,12 +93,13 @@ pub async fn signup(
|
|||||||
UserStatus::Active,
|
UserStatus::Active,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
let response =
|
||||||
))
|
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
||||||
|
|
||||||
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn signin_without_invite_checks(
|
pub async fn signin_without_invite_checks(
|
||||||
@ -121,12 +122,12 @@ pub async fn signin_without_invite_checks(
|
|||||||
user_from_db.compare_password(request.password)?;
|
user_from_db.compare_password(request.password)?;
|
||||||
|
|
||||||
let user_role = user_from_db.get_role_from_db(state.clone()).await?;
|
let user_role = user_from_db.get_role_from_db(state.clone()).await?;
|
||||||
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
let response =
|
||||||
))
|
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
||||||
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn signin(
|
pub async fn signin(
|
||||||
@ -168,9 +169,9 @@ pub async fn signin(
|
|||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let response = signin_strategy.get_signin_response(&state).await?;
|
||||||
signin_strategy.get_signin_response(&state).await?,
|
let token = utils::user::get_token_from_signin_response(&response);
|
||||||
))
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
@ -271,7 +272,7 @@ pub async fn connect_account(
|
|||||||
|
|
||||||
pub async fn signout(state: AppState, user_from_token: auth::UserFromToken) -> UserResponse<()> {
|
pub async fn signout(state: AppState, user_from_token: auth::UserFromToken) -> UserResponse<()> {
|
||||||
auth::blacklist::insert_user_in_blacklist(&state, &user_from_token.user_id).await?;
|
auth::blacklist::insert_user_in_blacklist(&state, &user_from_token.user_id).await?;
|
||||||
Ok(ApplicationResponse::StatusOk)
|
auth::cookies::remove_cookie_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn change_password(
|
pub async fn change_password(
|
||||||
@ -971,14 +972,14 @@ pub async fn accept_invite_from_email(
|
|||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &update_status_result)
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &update_status_result)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let response = utils::user::get_dashboard_entry_response(
|
||||||
utils::user::get_dashboard_entry_response(
|
&state,
|
||||||
&state,
|
user_from_db,
|
||||||
user_from_db,
|
update_status_result,
|
||||||
update_status_result,
|
token.clone(),
|
||||||
token,
|
)?;
|
||||||
)?,
|
|
||||||
))
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_internal_user(
|
pub async fn create_internal_user(
|
||||||
@ -1130,17 +1131,17 @@ pub async fn switch_merchant_id(
|
|||||||
(token, user_role.role_id.clone())
|
(token, user_role.role_id.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let response = user_api::DashboardEntryResponse {
|
||||||
user_api::DashboardEntryResponse {
|
token: token.clone(),
|
||||||
token,
|
name: user.get_name(),
|
||||||
name: user.get_name(),
|
email: user.get_email(),
|
||||||
email: user.get_email(),
|
user_id: user.get_user_id().to_string(),
|
||||||
user_id: user.get_user_id().to_string(),
|
verification_days_left: None,
|
||||||
verification_days_left: None,
|
user_role: role_id,
|
||||||
user_role: role_id,
|
merchant_id: request.merchant_id,
|
||||||
merchant_id: request.merchant_id,
|
};
|
||||||
},
|
|
||||||
))
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_merchant_account(
|
pub async fn create_merchant_account(
|
||||||
@ -1318,9 +1319,10 @@ pub async fn verify_email_without_invite_checks(
|
|||||||
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let response =
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
||||||
))
|
|
||||||
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
@ -1373,9 +1375,9 @@ pub async fn verify_email(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| logger::error!(?e));
|
.map_err(|e| logger::error!(?e));
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let response = signin_strategy.get_signin_response(&state).await?;
|
||||||
signin_strategy.get_signin_response(&state).await?,
|
let token = utils::user::get_token_from_signin_response(&response);
|
||||||
))
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
|
|||||||
@ -158,12 +158,13 @@ pub async fn transfer_org_ownership(
|
|||||||
.await
|
.await
|
||||||
.to_not_found_response(UserErrors::InvalidRoleOperation)?;
|
.to_not_found_response(UserErrors::InvalidRoleOperation)?;
|
||||||
|
|
||||||
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
let response =
|
||||||
))
|
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
||||||
|
|
||||||
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn accept_invitation(
|
pub async fn accept_invitation(
|
||||||
@ -202,12 +203,17 @@ pub async fn accept_invitation(
|
|||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
return Ok(ApplicationResponse::Json(
|
let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?;
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?,
|
|
||||||
));
|
let response = utils::user::get_dashboard_entry_response(
|
||||||
|
&state,
|
||||||
|
user_from_db,
|
||||||
|
user_role,
|
||||||
|
token.clone(),
|
||||||
|
)?;
|
||||||
|
return auth::cookies::set_cookie_response(response, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ApplicationResponse::StatusOk)
|
Ok(ApplicationResponse::StatusOk)
|
||||||
|
|||||||
@ -9,7 +9,10 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_web::{body, web, FromRequest, HttpRequest, HttpResponse, Responder, ResponseError};
|
use actix_web::{
|
||||||
|
body, http::header::HeaderValue, web, FromRequest, HttpRequest, HttpResponse, Responder,
|
||||||
|
ResponseError,
|
||||||
|
};
|
||||||
use api_models::enums::{CaptureMethod, PaymentMethodType};
|
use api_models::enums::{CaptureMethod, PaymentMethodType};
|
||||||
pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient};
|
pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient};
|
||||||
use common_enums::Currency;
|
use common_enums::Currency;
|
||||||
@ -20,7 +23,7 @@ use common_utils::{
|
|||||||
request::RequestContent,
|
request::RequestContent,
|
||||||
};
|
};
|
||||||
use error_stack::{report, IntoReport, Report, ResultExt};
|
use error_stack::{report, IntoReport, Report, ResultExt};
|
||||||
use masking::{PeekInterface, Secret};
|
use masking::{Maskable, PeekInterface, Secret};
|
||||||
use router_env::{instrument, tracing, tracing_actix_web::RequestId, Tag};
|
use router_env::{instrument, tracing, tracing_actix_web::RequestId, Tag};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -110,7 +113,7 @@ pub trait ConnectorIntegration<T, Req, Resp>: ConnectorIntegrationAny<T, Req, Re
|
|||||||
&self,
|
&self,
|
||||||
_req: &types::RouterData<T, Req, Resp>,
|
_req: &types::RouterData<T, Req, Resp>,
|
||||||
_connectors: &Connectors,
|
_connectors: &Connectors,
|
||||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,7 +850,7 @@ pub enum ApplicationResponse<R> {
|
|||||||
Form(Box<RedirectionFormData>),
|
Form(Box<RedirectionFormData>),
|
||||||
PaymentLinkForm(Box<PaymentLinkAction>),
|
PaymentLinkForm(Box<PaymentLinkAction>),
|
||||||
FileData((Vec<u8>, mime::Mime)),
|
FileData((Vec<u8>, mime::Mime)),
|
||||||
JsonWithHeaders((R, Vec<(String, String)>)),
|
JsonWithHeaders((R, Vec<(String, Maskable<String>)>)),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -1046,7 +1049,7 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some((_, value)) = headers.iter().find(|(key, _)| key == X_HS_LATENCY) {
|
if let Some((_, value)) = headers.iter().find(|(key, _)| key == X_HS_LATENCY) {
|
||||||
if let Ok(external_latency) = value.parse::<u128>() {
|
if let Ok(external_latency) = value.clone().into_inner().parse::<u128>() {
|
||||||
overhead_latency.replace(external_latency);
|
overhead_latency.replace(external_latency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1126,7 +1129,7 @@ where
|
|||||||
|
|
||||||
let incoming_request_header = request.headers();
|
let incoming_request_header = request.headers();
|
||||||
|
|
||||||
let incoming_header_to_log: HashMap<String, http::header::HeaderValue> =
|
let incoming_header_to_log: HashMap<String, HeaderValue> =
|
||||||
incoming_request_header
|
incoming_request_header
|
||||||
.iter()
|
.iter()
|
||||||
.fold(HashMap::new(), |mut acc, (key, value)| {
|
.fold(HashMap::new(), |mut acc, (key, value)| {
|
||||||
@ -1333,21 +1336,33 @@ pub fn http_server_error_json_response<T: body::MessageBody + 'static>(
|
|||||||
|
|
||||||
pub fn http_response_json_with_headers<T: body::MessageBody + 'static>(
|
pub fn http_response_json_with_headers<T: body::MessageBody + 'static>(
|
||||||
response: T,
|
response: T,
|
||||||
mut headers: Vec<(String, String)>,
|
headers: Vec<(String, Maskable<String>)>,
|
||||||
request_duration: Option<Duration>,
|
request_duration: Option<Duration>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
let mut response_builder = HttpResponse::Ok();
|
let mut response_builder = HttpResponse::Ok();
|
||||||
|
for (header_name, header_value) in headers {
|
||||||
for (name, value) in headers.iter_mut() {
|
let is_sensitive_header = header_value.is_masked();
|
||||||
if name == X_HS_LATENCY {
|
let mut header_value = header_value.into_inner();
|
||||||
|
if header_name == X_HS_LATENCY {
|
||||||
if let Some(request_duration) = request_duration {
|
if let Some(request_duration) = request_duration {
|
||||||
if let Ok(external_latency) = value.parse::<u128>() {
|
if let Ok(external_latency) = header_value.parse::<u128>() {
|
||||||
let updated_duration = request_duration.as_millis() - external_latency;
|
let updated_duration = request_duration.as_millis() - external_latency;
|
||||||
*value = updated_duration.to_string();
|
header_value = updated_duration.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response_builder.append_header((name.clone(), value.clone()));
|
let mut header_value = match HeaderValue::from_str(header_value.as_str()) {
|
||||||
|
Ok(header_value) => header_value,
|
||||||
|
Err(e) => {
|
||||||
|
logger::error!(?e);
|
||||||
|
return http_server_error_json_response("Something Went Wrong");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_sensitive_header {
|
||||||
|
header_value.set_sensitive(true);
|
||||||
|
}
|
||||||
|
response_builder.append_header((header_name, header_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
response_builder
|
response_builder
|
||||||
|
|||||||
@ -33,6 +33,8 @@ use crate::{
|
|||||||
utils::OptionExt,
|
utils::OptionExt,
|
||||||
};
|
};
|
||||||
pub mod blacklist;
|
pub mod blacklist;
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
pub mod cookies;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AuthenticationData {
|
pub struct AuthenticationData {
|
||||||
|
|||||||
62
crates/router/src/services/authentication/cookies.rs
Normal file
62
crates/router/src/services/authentication/cookies.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use cookie::{
|
||||||
|
time::{Duration, OffsetDateTime},
|
||||||
|
Cookie, SameSite,
|
||||||
|
};
|
||||||
|
use masking::{ExposeInterface, Mask, Secret};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
consts::{JWT_TOKEN_COOKIE_NAME, JWT_TOKEN_TIME_IN_SECS},
|
||||||
|
core::errors::{UserErrors, UserResponse},
|
||||||
|
services::ApplicationResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserResponse<R> {
|
||||||
|
let jwt_expiry_in_seconds = JWT_TOKEN_TIME_IN_SECS
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| UserErrors::InternalServerError)?;
|
||||||
|
let (expiry, max_age) = get_expiry_and_max_age_from_seconds(jwt_expiry_in_seconds);
|
||||||
|
|
||||||
|
let header_value = create_cookie(token, expiry, max_age)
|
||||||
|
.to_string()
|
||||||
|
.into_masked();
|
||||||
|
let header_key = get_cookie_header();
|
||||||
|
let header = vec![(header_key, header_value)];
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::JsonWithHeaders((response, header)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_cookie_response() -> UserResponse<()> {
|
||||||
|
let (expiry, max_age) = get_expiry_and_max_age_from_seconds(0);
|
||||||
|
|
||||||
|
let header_key = get_cookie_header();
|
||||||
|
let header_value = create_cookie("".to_string().into(), expiry, max_age)
|
||||||
|
.to_string()
|
||||||
|
.into_masked();
|
||||||
|
let header = vec![(header_key, header_value)];
|
||||||
|
Ok(ApplicationResponse::JsonWithHeaders(((), header)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_cookie<'c>(
|
||||||
|
token: Secret<String>,
|
||||||
|
expires: OffsetDateTime,
|
||||||
|
max_age: Duration,
|
||||||
|
) -> Cookie<'c> {
|
||||||
|
Cookie::build((JWT_TOKEN_COOKIE_NAME, token.expose()))
|
||||||
|
.http_only(true)
|
||||||
|
.secure(true)
|
||||||
|
.same_site(SameSite::Strict)
|
||||||
|
.path("/")
|
||||||
|
.expires(expires)
|
||||||
|
.max_age(max_age)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_expiry_and_max_age_from_seconds(seconds: i64) -> (OffsetDateTime, Duration) {
|
||||||
|
let max_age = Duration::seconds(seconds);
|
||||||
|
let expiry = OffsetDateTime::now_utc().saturating_add(max_age);
|
||||||
|
(expiry, max_age)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cookie_header() -> String {
|
||||||
|
actix_http::header::SET_COOKIE.to_string()
|
||||||
|
}
|
||||||
@ -170,3 +170,10 @@ pub async fn get_user_from_db_by_email(
|
|||||||
.await
|
.await
|
||||||
.map(UserFromStorage::from)
|
.map(UserFromStorage::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_token_from_signin_response(resp: &user_api::SignInResponse) -> Secret<String> {
|
||||||
|
match resp {
|
||||||
|
user_api::SignInResponse::DashboardEntry(data) => data.token.clone(),
|
||||||
|
user_api::SignInResponse::MerchantSelect(data) => data.token.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user