From 5482bbf3a65d2599b89036ce33d42c622d9350bc Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 14:26:40 +0530 Subject: [PATCH 1/7] feat: add authentication and signup apis --- Cargo.lock | 56 + config/development.toml | 11 +- crates/api_models/src/events.rs | 1 + crates/api_models/src/events/user.rs | 13 + crates/api_models/src/lib.rs | 1 + crates/api_models/src/user.rs | 20 + crates/router/Cargo.toml | 3 + crates/router/src/consts.rs | 5 + crates/router/src/consts/user.rs | 8 + crates/router/src/core.rs | 1 + crates/router/src/core/errors.rs | 2 + crates/router/src/core/errors/user.rs | 78 + crates/router/src/core/user.rs | 65 + crates/router/src/lib.rs | 1 + crates/router/src/routes.rs | 3 +- crates/router/src/routes/app.rs | 16 +- crates/router/src/routes/lock_utils.rs | 3 + crates/router/src/routes/user.rs | 31 + crates/router/src/services.rs | 1 + crates/router/src/services/authentication.rs | 95 +- crates/router/src/services/jwt.rs | 42 + crates/router/src/types/domain.rs | 2 + crates/router/src/types/domain/user.rs | 480 ++++ crates/router/src/utils.rs | 1 + crates/router/src/utils/user.rs | 1 + .../router/src/utils/user/blocker_emails.txt | 2349 +++++++++++++++++ crates/router/src/utils/user/password.rs | 43 + crates/router_env/src/logger/types.rs | 2 + 28 files changed, 3321 insertions(+), 13 deletions(-) create mode 100644 crates/api_models/src/events/user.rs create mode 100644 crates/api_models/src/user.rs create mode 100644 crates/router/src/consts/user.rs create mode 100644 crates/router/src/core/errors/user.rs create mode 100644 crates/router/src/core/user.rs create mode 100644 crates/router/src/routes/user.rs create mode 100644 crates/router/src/services/jwt.rs create mode 100644 crates/router/src/types/domain/user.rs create mode 100644 crates/router/src/utils/user.rs create mode 100644 crates/router/src/utils/user/blocker_emails.txt create mode 100644 crates/router/src/utils/user/password.rs diff --git a/Cargo.lock b/Cargo.lock index ac7fde55d8e3..c3be35b8b2b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,6 +430,18 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f907281554a3d0312bb7aab855a8e0ef6cbf1614d06de54105039ca8b34460e" +[[package]] +name = "argon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -1130,6 +1142,12 @@ dependencies = [ "vsimd", ] +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bb8" version = "0.8.1" @@ -1179,6 +1197,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "blake3" version = "1.4.0" @@ -3730,6 +3757,17 @@ dependencies = [ "regex", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.14" @@ -4438,6 +4476,7 @@ dependencies = [ "actix-rt", "actix-web", "api_models", + "argon2", "async-bb8-diesel", "async-trait", "awc", @@ -4510,10 +4549,12 @@ dependencies = [ "time", "tokio", "toml 0.7.4", + "unicode-segmentation", "url", "utoipa", "utoipa-swagger-ui", "uuid", + "validator", "wiremock", "x509-parser", ] @@ -6127,6 +6168,21 @@ dependencies = [ "serde", ] +[[package]] +name = "validator" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/config/development.toml b/config/development.toml index 63c1f045d94f..3fa42ef20dcf 100644 --- a/config/development.toml +++ b/config/development.toml @@ -1,3 +1,8 @@ +[server] +port = 8000 +host = "0.0.0.0" +request_body_limit = 16_384 # Post request body is limited to 16k. + [log.file] enabled = false @@ -30,9 +35,9 @@ dbname = "hyperswitch_db" pool_size = 5 connection_timeout = 10 -[server] -# HTTP Request body limit. Defaults to 32kB -request_body_limit = 32768 +# [server] +# # HTTP Request body limit. Defaults to 32kB +# request_body_limit = 32768 [secrets] admin_api_key = "test_admin" diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 23e7c9dc706a..ad07340615b4 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -5,6 +5,7 @@ pub mod payment; pub mod payouts; pub mod refund; pub mod routing; +pub mod user; use common_utils::{ events::{ApiEventMetric, ApiEventsType}, diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs new file mode 100644 index 000000000000..25aba4631b1a --- /dev/null +++ b/crates/api_models/src/events/user.rs @@ -0,0 +1,13 @@ +use crate::user::{SignInRequest, SignInResponse}; +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +impl ApiEventMetric for SignInResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::User { + merchant_id: self.merchant_id.clone(), + user_id: self.user_id.clone(), + }) + } +} + +impl ApiEventMetric for SignInRequest {} diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index 5da916b14817..b8dc506ff38d 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -20,5 +20,6 @@ pub mod payments; pub mod payouts; pub mod refunds; pub mod routing; +pub mod user; pub mod verifications; pub mod webhooks; diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs new file mode 100644 index 000000000000..6f391137627c --- /dev/null +++ b/crates/api_models/src/user.rs @@ -0,0 +1,20 @@ +use common_utils::pii; +use masking::Secret; + +#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] +pub struct SignInRequest { + pub email: pii::Email, + pub password: Secret, +} + +#[derive(serde::Serialize, Debug, Clone)] +pub struct SignInResponse { + pub merchant_id: String, + pub name: Secret, + pub email: pii::Email, + pub verification_days_left: Option, + pub user_role: String, + //this field is added for audit/debug reasons + #[serde(skip_serializing)] + pub user_id: String, +} diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 9ab955813336..0d7b89dd8d4f 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -38,6 +38,7 @@ actix-cors = "0.6.4" actix-multipart = "0.6.0" actix-rt = "2.8.0" actix-web = "4.3.1" +argon2 = { version = "0.5.0", features = ["std"] } async-bb8-diesel = "0.1.0" async-trait = "0.1.68" aws-config = { version = "0.55.3", optional = true } @@ -87,10 +88,12 @@ thiserror = "1.0.40" time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } tera = "1.19.1" +unicode-segmentation = "1.10.1" url = { version = "2.4.0", features = ["serde"] } utoipa = { version = "3.3.0", features = ["preserve_order", "time"] } utoipa-swagger-ui = { version = "3.1.3", features = ["actix-web"] } uuid = { version = "1.3.3", features = ["serde", "v4"] } +validator = "0.16.0" openssl = "0.10.55" x509-parser = "0.15.0" sha-1 = { version = "0.9"} diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 7b20c3865d15..4e3a1fd050fe 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -1,3 +1,5 @@ +pub mod user; + // ID generation pub(crate) const ID_LENGTH: usize = 20; pub(crate) const MAX_ID_LENGTH: usize = 64; @@ -52,3 +54,6 @@ pub const ROUTING_CONFIG_ID_LENGTH: usize = 10; pub const LOCKER_REDIS_PREFIX: &str = "LOCKER_PM_TOKEN"; 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 diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs new file mode 100644 index 000000000000..3a71fed01a12 --- /dev/null +++ b/crates/router/src/consts/user.rs @@ -0,0 +1,8 @@ +#[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.rs b/crates/router/src/core.rs index 817fafdae520..72dfc28746ce 100644 --- a/crates/router/src/core.rs +++ b/crates/router/src/core.rs @@ -18,6 +18,7 @@ pub mod payments; pub mod payouts; pub mod refunds; pub mod routing; +pub mod user; pub mod utils; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index dc1d56721e88..438f55ce8ca0 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -2,6 +2,7 @@ pub mod api_error_response; pub mod customers_error_response; pub mod error_handlers; pub mod transformers; +pub mod user; pub mod utils; use std::fmt::Display; @@ -13,6 +14,7 @@ use diesel_models::errors as storage_errors; pub use redis_interface::errors::RedisError; use scheduler::errors as sch_errors; use storage_impl::errors as storage_impl_errors; +pub use user::*; pub use self::{ api_error_response::ApiErrorResponse, diff --git a/crates/router/src/core/errors/user.rs b/crates/router/src/core/errors/user.rs new file mode 100644 index 000000000000..b4d48365dc84 --- /dev/null +++ b/crates/router/src/core/errors/user.rs @@ -0,0 +1,78 @@ +use common_utils::errors::CustomResult; + +use crate::services::ApplicationResponse; + +pub type UserResult = CustomResult; +pub type UserResponse = CustomResult, UserErrors>; + +#[derive(Debug, thiserror::Error)] +pub enum UserErrors { + #[error("User InternalServerError")] + InternalServerError, + #[error("InvalidCredentials")] + InvalidCredentials, + #[error("UserExists")] + UserExists, + #[error("EmailParsingError")] + EmailParsingError, + #[error("NameParsingError")] + NameParsingError, + #[error("PasswordParsingError")] + PasswordParsingError, + #[error("CompanyNameParsingError")] + CompanyNameParsingError, + #[error("MerchantAccountCreationError: {0}")] + MerchantAccountCreationError(String), + #[error("InvalidEmailError")] + InvalidEmailError, + #[error("DuplicateOrganizationId")] + DuplicateOrganizationId, +} + +impl common_utils::errors::ErrorSwitch for UserErrors { + fn switch(&self) -> api_models::errors::types::ApiErrorResponse { + use api_models::errors::types::{ApiError, ApiErrorResponse as AER}; + let sub_code = "UR"; + match self { + Self::InternalServerError => { + AER::InternalServerError(ApiError::new("HE", 0, "Something Went Wrong", None)) + } + Self::InvalidCredentials => AER::Unauthorized(ApiError::new( + sub_code, + 1, + "Incorrect email or password", + None, + )), + Self::UserExists => AER::BadRequest(ApiError::new( + sub_code, + 3, + "An account already exists with this email", + None, + )), + Self::EmailParsingError => { + AER::BadRequest(ApiError::new(sub_code, 7, "Invalid Email", None)) + } + Self::NameParsingError => { + AER::BadRequest(ApiError::new(sub_code, 8, "Invalid Name", None)) + } + Self::PasswordParsingError => { + AER::BadRequest(ApiError::new(sub_code, 9, "Invalid Password", None)) + } + Self::CompanyNameParsingError => { + AER::BadRequest(ApiError::new(sub_code, 14, "Invalid Company Name", None)) + } + Self::MerchantAccountCreationError(error_message) => { + AER::InternalServerError(ApiError::new(sub_code, 15, error_message, None)) + } + Self::InvalidEmailError => { + AER::BadRequest(ApiError::new(sub_code, 16, "Invalid Email", None)) + } + Self::DuplicateOrganizationId => AER::InternalServerError(ApiError::new( + sub_code, + 21, + "An Organization with the id already exists", + None, + )), + } + } +} diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs new file mode 100644 index 000000000000..b4d0f5b2755b --- /dev/null +++ b/crates/router/src/core/user.rs @@ -0,0 +1,65 @@ +use api_models::user as api; +use diesel_models::enums::UserStatus; +use error_stack::IntoReport; +use masking::ExposeInterface; +use router_env::env; + +use super::errors::{UserErrors, UserResponse}; +use crate::{ + consts::user as consts, routes::AppState, services::ApplicationResponse, types::domain, +}; + +pub async fn connect_account( + state: AppState, + request: api::SignInRequest, +) -> UserResponse { + let find_user = state + .store + .find_user_by_email(request.email.clone().expose().expose().as_str()) + .await; + + if let Ok(found_user) = find_user { + let user_from_db: domain::UserFromStorage = found_user.into(); + + user_from_db.compare_password(request.password)?; + return Ok(ApplicationResponse::Json( + user_from_db.get_signin_response_from_db(state).await?, + )); + } else if find_user + .map_err(|e| e.current_context().is_db_not_found()) + .err() + .unwrap_or(false) + { + if matches!(env::which(), env::Env::Production) { + return Err(UserErrors::InvalidCredentials).into_report(); + } + + let new_user = domain::NewUser::try_from(request)?; + let _ = new_user + .get_new_merchant() + .get_new_organization() + .insert_org_in_db(state.clone()) + .await?; + let user_from_db = new_user + .insert_user_and_merchant_in_db(state.clone()) + .await?; + let user_role = new_user + .insert_user_role_in_db( + state.clone(), + consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), + UserStatus::Active, + ) + .await?; + + return Ok(ApplicationResponse::Json(api::SignInResponse { + merchant_id: user_role.merchant_id, + name: user_from_db.get_name(), + email: user_from_db.get_email(), + verification_days_left: None, + user_role: user_role.role_id, + user_id: user_from_db.get_user_id().to_string(), + })); + } else { + Err(UserErrors::InternalServerError.into()) + } +} diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 38efe8b75134..14076ef103f5 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -143,6 +143,7 @@ pub fn mk_app( .service(routes::Disputes::server(state.clone())) .service(routes::Routing::server(state.clone())) .service(routes::Gsm::server(state.clone())) + .service(routes::User::server(state.clone())) } #[cfg(all(feature = "olap", feature = "kms"))] diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 47b9f23cf8cb..f33156ab76fe 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -23,6 +23,7 @@ pub mod payouts; pub mod refunds; #[cfg(feature = "olap")] pub mod routing; +pub mod user; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; pub mod webhooks; @@ -38,7 +39,7 @@ pub use self::app::Verify; pub use self::app::{ ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, Files, Gsm, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, - PaymentMethods, Payments, Refunds, Webhooks, + PaymentMethods, Payments, Refunds, User, Webhooks, }; #[cfg(feature = "stripe")] pub use super::compatibility::stripe::StripeApis; diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index ec87fcdc3900..b82119a735d8 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -20,7 +20,7 @@ use super::routing as cloud_routing; use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(feature = "olap")] use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*}; -use super::{cache::*, health::*, payment_link::*}; +use super::{cache::*, health::*, payment_link::*, user::*}; #[cfg(any(feature = "olap", feature = "oltp"))] use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; #[cfg(feature = "oltp")] @@ -698,3 +698,17 @@ impl Verify { ) } } + +pub struct User; + +#[cfg(feature = "olap")] +impl User { + pub fn server(state: AppState) -> Scope { + web::scope("/user") + .app_data(web::Data::new(state)) + .service(web::resource("/signin").route(web::post().to(user_connect_account))) + .service(web::resource("/signup").route(web::post().to(user_connect_account))) + .service(web::resource("/v2/signin").route(web::post().to(user_connect_account))) + .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) + } +} diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 4e6fc1870f56..ae573e871627 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -24,6 +24,7 @@ pub enum ApiIdentifier { PaymentLink, Routing, Gsm, + User, } impl From for ApiIdentifier { @@ -134,6 +135,8 @@ impl From for ApiIdentifier { | Flow::GsmRuleRetrieve | Flow::GsmRuleUpdate | Flow::GsmRuleDelete => Self::Gsm, + + Flow::UserConnectAccount => Self::User, } } } diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs new file mode 100644 index 000000000000..cb8311de85d1 --- /dev/null +++ b/crates/router/src/routes/user.rs @@ -0,0 +1,31 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use api_models::user as user_api; +use router_env::Flow; + +use super::AppState; +use crate::{ + core::{api_locking, user}, + services::{ + api, + authentication::{self as auth}, + }, +}; + +pub async fn user_connect_account( + state: web::Data, + http_req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::UserConnectAccount; + let req_payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow.clone(), + state, + &http_req, + req_payload.clone(), + |state, _, req_body| user::connect_account(state, req_body), + &auth::NoAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/services.rs b/crates/router/src/services.rs index 631e9a5c189d..ca4bf67e0aff 100644 --- a/crates/router/src/services.rs +++ b/crates/router/src/services.rs @@ -1,6 +1,7 @@ pub mod api; pub mod authentication; pub mod encryption; +pub mod jwt; pub mod logger; #[cfg(feature = "kms")] diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index faa7864aff5b..2033bbb6366f 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,6 +11,7 @@ use serde::Serialize; use crate::{ configs::settings, + consts, core::{ api_keys, errors::{self, utils::StorageErrorExt, RouterResult}, @@ -22,6 +23,8 @@ use crate::{ utils::OptionExt, }; +use super::jwt; + #[derive(Clone, Debug)] pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, @@ -67,6 +70,37 @@ impl AuthenticationType { } } +#[derive(serde::Serialize, serde::Deserialize)] +pub struct AuthToken { + pub user_id: String, + pub merchant_id: String, + pub role_id: String, + pub exp: u64, + pub org_id: String, +} + +#[cfg(feature = "olap")] +impl AuthToken { + pub async fn new_token( + user_id: String, + merchant_id: String, + role_id: String, + settings: &settings::Settings, + org_id: String, + ) -> errors::UserResult { + let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS); + let exp = jwt::generate_exp(exp_duration)?.as_secs(); + let token_payload = Self { + user_id, + merchant_id, + role_id, + exp, + org_id, + }; + jwt::generate_jwt(&token_payload, settings).await + } +} + pub trait AuthInfo { fn get_merchant_id(&self) -> Option<&str>; } @@ -362,14 +396,58 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<((), AuthenticationType)> { - let mut token = get_jwt(request_headers)?; - token = strip_jwt_token(token)?; - decode_jwt::(token, state) - .await - .map(|_| ((), AuthenticationType::NoAuth)) + let payload = parse_jwt_payload::(request_headers, state).await?; + Ok(( + (), + AuthenticationType::MerchantJWT { + merchant_id: payload.merchant_id, + user_id: Some(payload.user_id), + }, + )) } } +pub struct JWTAuthMerchantFromRoute { + pub merchant_id: String, +} + +#[async_trait] +impl AuthenticateAndFetch<(), A> for JWTAuthMerchantFromRoute +where + A: AppStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<((), AuthenticationType)> { + let payload = parse_jwt_payload::(request_headers, state).await?; + + // 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)); + } + Ok(( + (), + AuthenticationType::MerchantJWT { + merchant_id: payload.merchant_id, + user_id: Some(payload.user_id), + }, + )) + } +} + +pub async fn parse_jwt_payload(headers: &HeaderMap, state: &A) -> RouterResult +where + T: serde::de::DeserializeOwned, + A: AppStateInfo + Sync, +{ + let token = get_jwt(headers)?; + let payload = decode_jwt(token, state).await?; + + Ok(payload) +} + #[derive(serde::Deserialize)] struct JwtAuthPayloadFetchMerchantAccount { merchant_id: String, @@ -385,8 +463,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - let mut token = get_jwt(request_headers)?; - token = strip_jwt_token(token)?; + let token = get_jwt(request_headers)?; let payload = decode_jwt::(token, state).await?; let key_store = state .store() @@ -598,7 +675,9 @@ pub fn get_jwt(headers: &HeaderMap) -> RouterResult<&str> { .to_str() .into_report() .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to convert JWT token to string") + .attach_printable("Failed to convert JWT token to string")? + .strip_prefix("Bearer ") + .ok_or(errors::ApiErrorResponse::InvalidJwtToken.into()) } pub fn strip_jwt_token(token: &str) -> RouterResult<&str> { diff --git a/crates/router/src/services/jwt.rs b/crates/router/src/services/jwt.rs new file mode 100644 index 000000000000..f27d5246113d --- /dev/null +++ b/crates/router/src/services/jwt.rs @@ -0,0 +1,42 @@ +use common_utils::errors::CustomResult; +use error_stack::{IntoReport, ResultExt}; +use jsonwebtoken::{encode, EncodingKey, Header}; +use masking::PeekInterface; + +use super::authentication; +use crate::{configs::settings::Settings, core::errors::UserErrors}; + +pub fn generate_exp( + exp_duration: std::time::Duration, +) -> CustomResult { + std::time::SystemTime::now() + .checked_add(exp_duration) + .ok_or(UserErrors::InternalServerError)? + .duration_since(std::time::UNIX_EPOCH) + .into_report() + .change_context(UserErrors::InternalServerError) +} + +pub async fn generate_jwt( + claims_data: &T, + settings: &Settings, +) -> CustomResult +where + T: serde::ser::Serialize, +{ + let jwt_secret = authentication::get_jwt_secret( + &settings.secrets, + #[cfg(feature = "kms")] + external_services_oss::kms::get_kms_client(&settings.kms).await, + ) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to obtain JWT secret")?; + encode( + &Header::default(), + claims_data, + &EncodingKey::from_secret(jwt_secret.peek().as_bytes()), + ) + .into_report() + .change_context(UserErrors::InternalServerError) +} diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index 44123850d468..d1d7500cc897 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -5,9 +5,11 @@ mod merchant_account; mod merchant_connector_account; mod merchant_key_store; pub mod types; +pub mod user; pub use address::*; pub use customer::*; pub use merchant_account::*; pub use merchant_connector_account::*; pub use merchant_key_store::*; +pub use user::*; diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs new file mode 100644 index 000000000000..8898a3a20d03 --- /dev/null +++ b/crates/router/src/types/domain/user.rs @@ -0,0 +1,480 @@ +use std::{collections::HashSet, ops, str::FromStr}; + +use api_models::{admin as admin_api, organization as api_org, user as user_api}; +use common_utils::pii; +use diesel_models::{ + enums::UserStatus, + organization as diesel_org, + organization::Organization, + user as storage_user, + user_role::{UserRole, UserRoleNew}, +}; +use error_stack::{IntoReport, ResultExt}; +use masking::{ExposeInterface, PeekInterface, Secret}; +use once_cell::sync::Lazy; +use unicode_segmentation::UnicodeSegmentation; + +use crate::{ + consts::user as consts, + core::{ + admin, + errors::{UserErrors, UserResult}, + }, + db::StorageInterface, + routes::AppState, + types::transformers::ForeignFrom, + utils::user::password, +}; + +#[derive(Clone)] +pub struct UserName(Secret); + +impl UserName { + pub fn new(name: Secret) -> UserResult { + let name = name.expose(); + let is_empty_or_whitespace = name.trim().is_empty(); + let is_too_long = name.graphemes(true).count() > consts::MAX_NAME_LENGTH; + + let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}']; + let contains_forbidden_characters = name.chars().any(|g| forbidden_characters.contains(&g)); + + if is_empty_or_whitespace || is_too_long || contains_forbidden_characters { + Err(UserErrors::NameParsingError.into()) + } else { + Ok(Self(name.into())) + } + } + + pub fn get_secret(self) -> Secret { + self.0 + } +} + +impl TryFrom for UserName { + type Error = error_stack::Report; + + fn try_from(value: pii::Email) -> UserResult { + Self::new(Secret::new( + value + .peek() + .split_once('@') + .ok_or(UserErrors::InvalidEmailError)? + .0 + .to_string(), + )) + } +} + +#[derive(Clone, Debug)] +pub struct UserEmail(pii::Email); + +static BLOCKED_EMAIL: Lazy> = Lazy::new(|| { + let blocked_emails_content = include_str!("../../utils/user/blocker_emails.txt"); + let blocked_emails: HashSet = blocked_emails_content + .lines() + .map(|s| s.trim().to_owned()) + .collect(); + blocked_emails +}); + +impl UserEmail { + pub fn new(email: Secret) -> UserResult { + let email_string = email.expose(); + let email = + pii::Email::from_str(&email_string).change_context(UserErrors::EmailParsingError)?; + + if validator::validate_email(&email_string) { + let (_username, domain) = match email_string.as_str().split_once('@') { + Some((u, d)) => (u, d), + None => return Err(UserErrors::EmailParsingError.into()), + }; + + if BLOCKED_EMAIL.contains(domain) { + return Err(UserErrors::InvalidEmailError.into()); + } + Ok(Self(email)) + } else { + Err(UserErrors::EmailParsingError.into()) + } + } + + pub fn from_pii_email(email: pii::Email) -> UserResult { + let email_string = email.peek(); + if validator::validate_email(email_string) { + let (_username, domain) = match email_string.split_once('@') { + Some((u, d)) => (u, d), + None => return Err(UserErrors::EmailParsingError.into()), + }; + if BLOCKED_EMAIL.contains(domain) { + return Err(UserErrors::InvalidEmailError.into()); + } + Ok(Self(email)) + } else { + Err(UserErrors::EmailParsingError.into()) + } + } + + pub fn into_inner(self) -> pii::Email { + self.0 + } + + pub fn get_secret(self) -> Secret { + (*self.0).clone() + } +} + +impl TryFrom for UserEmail { + type Error = error_stack::Report; + + fn try_from(value: pii::Email) -> Result { + Self::from_pii_email(value) + } +} + +impl ops::Deref for UserEmail { + type Target = Secret; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone)] +pub struct UserPassword(Secret); + +impl UserPassword { + pub fn new(password: Secret) -> UserResult { + let password = password.expose(); + if password.is_empty() { + Err(UserErrors::PasswordParsingError.into()) + } else { + Ok(Self(password.into())) + } + } + + pub fn get_secret(&self) -> Secret { + self.0.clone() + } +} + +#[derive(Clone)] +pub struct UserCompanyName(String); + +impl UserCompanyName { + pub fn new(company_name: String) -> UserResult { + let company_name = company_name.trim(); + let is_empty_or_whitespace = company_name.is_empty(); + let is_too_long = company_name.graphemes(true).count() > consts::MAX_COMPANY_NAME_LENGTH; + + let is_all_valid_characters = company_name + .chars() + .all(|x| x.is_alphanumeric() || x.is_ascii_whitespace() || x == '_'); + if is_empty_or_whitespace || is_too_long || !is_all_valid_characters { + Err(UserErrors::CompanyNameParsingError.into()) + } else { + Ok(Self(company_name.to_string())) + } + } + + pub fn get_secret(self) -> String { + self.0 + } +} + +#[derive(Clone)] +pub struct NewUserOrganization(diesel_org::OrganizationNew); + +impl NewUserOrganization { + pub async fn insert_org_in_db(self, state: AppState) -> UserResult { + state + .store + .insert_organization(self.0) + .await + .map_err(|e| { + if e.current_context().is_db_unique_violation() { + e.change_context(UserErrors::DuplicateOrganizationId) + } else { + e.change_context(UserErrors::InternalServerError) + } + }) + .attach_printable("Error while inserting organization") + } + + pub fn get_organization_id(&self) -> String { + self.0.org_id.clone() + } +} + +impl From for NewUserOrganization { + fn from(_value: user_api::SignInRequest) -> Self { + let new_organization = api_org::OrganizationNew::new(None); + let db_organization = ForeignFrom::foreign_from(new_organization); + Self(db_organization) + } +} + +#[derive(Clone)] +pub struct NewUserMerchant { + merchant_id: String, + company_name: Option, + new_organization: NewUserOrganization, +} + +impl NewUserMerchant { + pub fn get_company_name(&self) -> Option { + self.company_name.clone().map(UserCompanyName::get_secret) + } + + pub fn get_merchant_id(&self) -> String { + self.merchant_id.clone() + } + + pub fn get_new_organization(&self) -> NewUserOrganization { + self.new_organization.clone() + } + + pub async fn check_if_already_exists_in_db(&self, state: AppState) -> UserResult<()> { + if state + .store + .get_merchant_key_store_by_merchant_id( + self.get_merchant_id().as_str(), + &state.store.get_master_key().to_vec().into(), + ) + .await + .is_ok() + { + return Err(UserErrors::MerchantAccountCreationError(format!( + "Merchant with {} already exists", + self.get_merchant_id() + ))) + .into_report(); + } + Ok(()) + } + + pub async fn create_new_merchant_and_insert_in_db(&self, state: AppState) -> UserResult<()> { + self.check_if_already_exists_in_db(state.clone()).await?; + Box::pin(admin::create_merchant_account( + state.clone(), + admin_api::MerchantAccountCreate { + merchant_id: self.get_merchant_id(), + metadata: None, + locker_id: None, + return_url: None, + merchant_name: self.get_company_name().map(Secret::new), + webhook_details: None, + publishable_key: None, + organization_id: Some(self.new_organization.get_organization_id()), + merchant_details: None, + routing_algorithm: None, + parent_merchant_id: None, + payment_link_config: None, + sub_merchants_enabled: None, + frm_routing_algorithm: None, + intent_fulfillment_time: None, + payout_routing_algorithm: None, + primary_business_details: None, + payment_response_hash_key: None, + enable_payment_response_hash: None, + redirect_to_merchant_with_http_post: None, + }, + )) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable("Error while creating a merchant")?; + Ok(()) + } +} + +impl TryFrom for NewUserMerchant { + type Error = error_stack::Report; + + fn try_from(value: user_api::SignInRequest) -> UserResult { + let merchant_id = format!("merchant_{}", common_utils::date_time::now_unix_timestamp()); + let new_organization = NewUserOrganization::from(value); + + Ok(Self { + company_name: None, + merchant_id, + new_organization, + }) + } +} + +#[derive(Clone)] +pub struct NewUser { + user_id: String, + name: UserName, + email: UserEmail, + password: UserPassword, + new_merchant: NewUserMerchant, +} + +impl NewUser { + pub fn get_user_id(&self) -> String { + self.user_id.clone() + } + + pub fn get_email(&self) -> UserEmail { + self.email.clone() + } + + pub fn get_name(&self) -> Secret { + self.name.clone().get_secret() + } + + pub fn get_new_merchant(&self) -> NewUserMerchant { + self.new_merchant.clone() + } + + pub async fn insert_user_in_db( + &self, + db: &dyn StorageInterface, + ) -> UserResult { + match db.insert_user(self.clone().try_into()?).await { + Ok(user) => Ok(user.into()), + Err(e) => { + if e.current_context().is_db_unique_violation() { + return Err(e.change_context(UserErrors::UserExists)); + } else { + return Err(e.change_context(UserErrors::InternalServerError)); + } + } + } + .attach_printable("Error while inserting user") + } + + pub async fn insert_user_and_merchant_in_db( + &self, + state: AppState, + ) -> UserResult { + let db = state.store.as_ref(); + let merchant_id = self.get_new_merchant().get_merchant_id(); + self.new_merchant + .create_new_merchant_and_insert_in_db(state.clone()) + .await?; + let created_user = self.insert_user_in_db(db).await; + if created_user.is_err() { + let _ = admin::merchant_account_delete(state, merchant_id).await; + }; + created_user + } + + pub async fn insert_user_role_in_db( + self, + state: AppState, + role_id: String, + user_status: UserStatus, + ) -> UserResult { + let now = common_utils::date_time::now(); + let user_id = self.get_user_id(); + + state + .store + .insert_user_role(UserRoleNew { + merchant_id: self.get_new_merchant().get_merchant_id(), + status: user_status, + created_by: user_id.clone(), + last_modified_by: user_id.clone(), + user_id, + role_id, + created_at: now, + last_modified_at: now, + org_id: self + .get_new_merchant() + .get_new_organization() + .get_organization_id(), + }) + .await + .change_context(UserErrors::InternalServerError) + } +} + +impl TryFrom for storage_user::UserNew { + type Error = error_stack::Report; + + fn try_from(value: NewUser) -> UserResult { + let hashed_password = password::generate_password_hash(value.password.get_secret())?; + Ok(Self { + user_id: value.get_user_id(), + name: value.get_name(), + email: value.get_email().into_inner(), + password: hashed_password, + ..Default::default() + }) + } +} + +impl TryFrom for NewUser { + type Error = error_stack::Report; + + fn try_from(value: user_api::SignInRequest) -> UserResult { + let user_id = uuid::Uuid::new_v4().to_string(); + let email = value.email.clone().try_into()?; + let name = UserName::try_from(value.email.clone())?; + let password = UserPassword::new(value.password.clone())?; + let new_merchant = NewUserMerchant::try_from(value)?; + + Ok(Self { + user_id, + name, + email, + password, + new_merchant, + }) + } +} + +pub struct UserFromStorage(pub storage_user::User); + +impl From for UserFromStorage { + fn from(value: storage_user::User) -> Self { + Self(value) + } +} + +impl UserFromStorage { + pub fn get_user_id(&self) -> &str { + self.0.user_id.as_str() + } + + pub fn compare_password(&self, candidate: Secret) -> UserResult<()> { + match password::is_correct_password(candidate, self.0.password.clone()) { + Ok(true) => Ok(()), + Ok(false) => Err(UserErrors::InvalidCredentials.into()), + Err(e) => Err(e), + } + } + + pub fn get_name(&self) -> Secret { + self.0.name.clone() + } + + pub fn get_email(&self) -> pii::Email { + self.0.email.clone() + } + + pub async fn get_signin_response_from_db( + &self, + state: AppState, + ) -> UserResult { + let user_role = self.get_role_from_db(state.clone()).await?; + + Ok(user_api::SignInResponse { + merchant_id: user_role.merchant_id, + name: self.get_name(), + email: self.get_email(), + verification_days_left: None, + user_role: user_role.role_id, + user_id: self.get_user_id().to_string(), + }) + } + + pub async fn get_role_from_db(&self, state: AppState) -> UserResult { + state + .store + .find_user_role_by_user_id(self.get_user_id()) + .await + .change_context(UserErrors::InternalServerError) + } +} diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 558044028f7a..ba792e317006 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -1,6 +1,7 @@ pub mod custom_serde; pub mod db_utils; pub mod ext_traits; +pub mod user; #[cfg(feature = "kv_store")] pub mod storage_partitioning; diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs new file mode 100644 index 000000000000..c72e4b9feb3c --- /dev/null +++ b/crates/router/src/utils/user.rs @@ -0,0 +1 @@ +pub mod password; diff --git a/crates/router/src/utils/user/blocker_emails.txt b/crates/router/src/utils/user/blocker_emails.txt new file mode 100644 index 000000000000..e29e1b2d86f4 --- /dev/null +++ b/crates/router/src/utils/user/blocker_emails.txt @@ -0,0 +1,2349 @@ +020.co.uk +123.com +123box.net +123india.com +123mail.cl +123mail.org +123qwe.co.uk +138mail.com +141.ro +150mail.com +150ml.com +16mail.com +1963chevrolet.com +1963pontiac.com +1netdrive.com +1st-website.com +1stpd.net +2-mail.com +20after4.com +21cn.com +24h.co.jp +24horas.com +271soundview.com +2die4.com +2mydns.com +2net.us +3000.it +3ammagazine.com +3email.com +3xl.net +444.net +4email.com +4email.net +4newyork.com +50mail.com +55mail.cc +5fm.za.com +6210.hu +6sens.com +702mail.co.za +7110.hu +8848.net +8m.com +8m.net +8x.com.br +8u8.com +8u8.hk +8u8.tw +a-topmail.at +about.com +abv.bg +acceso.or.cr +access4less.net +accessgcc.com +acmemail.net +adiga.com +adinet.com.uy +adres.nl +advalvas.be +aeiou.pt +aeneasmail.com +afrik.com +afropoets.com +aggies.com +ahaa.dk +aichi.com +aim.com +airpost.net +aiutamici.com +aklan.com +aknet.kg +alabama.usa.com +alaska.usa.com +alavatotal.com +albafind.com +albawaba.com +alburaq.net +aldeax.com +aldeax.com.ar +alex4all.com +aliyun.com +alexandria.cc +algeria.com +alice.it +allmail.net +alskens.dk +altavista.se +altbox.org +alternativagratis.com +alum.com +alunos.unipar.br +alvilag.hu +amenworld.com +america.hm +americamail.com +amnetsal.com +amorous.com +ananzi.co.za +anet.ne.jp +anfmail.com +angelfire.com +animail.net +aniverse.com +anjungcafe.com +another.com +antedoonsub.com +antwerpen.com +anunciador.net +anytimenow.com +aon.at +apexmail.com +apollo.lv +approvers.net +aprava.com +apropo.ro +arcor.de +argentina.com +arizona.usa.com +arkansas.usa.com +armmail.com +army.com +arnet.com.ar +aroma.com +arrl.net +aruba.it +asheville.com +asia-links.com +asiamail.com +assala.com +assamesemail.com +asurfer.com +atl.lv +atlas.cz +atlas.sk +atozasia.com +atreillou.com +att.net +au.ru +aubenin.com +aus-city.com +aussiemail.com.au +avasmail.com.mv +axarnet.com +ayna.com +azet.sk +babbalu.com +badgers.com +bakpaka.com +bakpaka.net +balochistan.org +baluch.com +bama-fan.com +bancora.net +bankersmail.com +barlick.net +beeebank.com +beehive.org +been-there.com +beirut.com +belizehome.com +belizemail.net +belizeweb.com +bellsouth.net +berlin.de +bestmail.us +bflomail.com +bgnmail.com +bharatmail.com +big-orange.com +bigboss.cz +bigfoot.com +bigger.com +bigmailbox.com +bigmir.net +bigstring.com +bip.net +bigpond.com +bitwiser.com +biz.by +bizhosting.com +black-sea.ro +blackburnmail.com +blackglobalnetwork.net +blink182.net +blue.devils.com +bluebottle.com +bluemail.ch +blumail.org +blvds.com +bol.com.br +bolando.com +bollywood2000.com +bollywoodz.com +bombka.dyn.pl +bonbon.net +boom.com +bootmail.com +bostonoffice.com +box.az +boxbg.com +boxemail.com +brain.com.pk +brasilia.net +bravanese.com +brazilmail.com.br +breathe.com +brestonline.com +brfree.com.br +brujula.net +btcc.org +buffaloes.com +bulgaria.com +bulldogs.com +bumerang.ro +burntmail.com +butch-femme.net +buzy.com +buzzjakkerz.com +c-box.cz +c3.hu +c4.com +cadinfo.net +calcfacil.com.br +calcware.org +california.usa.com +callnetuk.com +camaroclubsweden.com +canada-11.com +canada.com +canal21.com +canoemail.com +caramail.com +cardblvd.com +care-mail.com +care2.com +caress.com +carioca.net +cashette.com +casino.com +casinomail.com +cataloniamail.com +catalunyamail.com +cataz.com +catcha.com +catholic.org +caths.co.uk +caxess.net +cbrmail.com +cc.lv +cemelli.com +centoper.it +centralpets.com +centrum.cz +centrum.sk +centurylink.net +cercaziende.it +cgac.es +chaiyo.com +chaiyomail.com +chance2mail.com +channelonetv.com +charter.net +chattown.com +checkitmail.at +chelny.com +cheshiremail.com +chil-e.com +chillimail.com +china.com +christianmail.org +ciaoweb.it +cine.com +ciphercom.net +circlemail.com +cititrustbank1.cjb.net +citromail.hu +citynetusa.com +ciudad.com.ar +claramail.com +classicmail.co.za +cliffhanger.com +clix.pt +close2you.net +cluemail.com +clujnapoca.ro +collegeclub.com +colombia.com +colorado.usa.com +comcast.net +comfortable.com +compaqnet.fr +compuserve.com +computer.net +computermail.net +computhouse.com +conevyt.org.mx +connect4free.net +connecticut.usa.com +coolgoose.com +coolkiwi.com +coollist.com +coxinet.net +coolmail.com +coolmail.net +coolsend.com +cooltoad.com +cooperation.net +copacabana.com +copticmail.com +corporateattorneys.com +corporation.net +correios.net.br +correomagico.com +cosmo.com +cosmosurf.net +cougars.com +count.com +countrybass.com +couple.com +criticalpath.net +critterpost.com +crosspaths.net +crosswinds.net +cryingmail.com +cs.com +csucsposta.hu +cumbriamail.com +curio-city.com +custmail.com +cwazy.co.uk +cwazy.net +cww.de +cyberaccess.com.pk +cybergirls.dk +cyberguys.dk +cybernet.it +cymail.net +dabsol.net +dada.net +dadanet.it +dailypioneer.com +damuc.org.br +dansegulvet.com +darkhorsefan.net +data54.com +davegracey.com +dayzers.com +daum.net +dbmail.com +dcemail.com +dcsi.net +deacons.com +deadlymob.org +deal-maker.com +dearriba.com +degoo.com +delajaonline.org +delaware.usa.com +delfi.lv +delhimail.com +demon.deacons.com +desertonline.com +desidrivers.com +deskpilot.com +despammed.com +detik.com +devils.com +dexara.net +dhmail.net +di-ve.com +didamail.com +digitaltrue.com +direccion.com +director-general.com +diri.com +discardmail.com +discoverymail.net +disinfo.net +djmillenium.com +dmailman.com +dnsmadeeasy.com +do.net.ar +dodgeit.com +dogmail.co.uk +doityourself.com +domaindiscover.com +domainmanager.com +doneasy.com +dontexist.org +dores.com +dostmail.com +dot5hosting.com +dotcom.fr +dotnow.com +dott.it +doubt.com +dplanet.ch +dragoncon.net +dragonfans.com +dropzone.com +dserver.org +dubaiwebcity.com +dublin.ie +dustdevil.com +dynamitemail.com +dyndns.org +e-apollo.lv +e-hkma.com +e-mail.cz +e-mail.ph +e-mailanywhere.com +e-milio.com +e-tapaal.com +e-webtec.com +earthalliance.com +earthling.net +eastmail.com +eastrolog.com +easy-pages.com +easy.com +easyinfomail.co.za +easypeasy.com +echina.com +ecn.org +ecplaza.net +eircom.net +edsamail.com.ph +educacao.te.pt +edumail.co.za +eeism.com +ego.co.th +ekolay.net +elforotv.com.ar +elitemail.org +elsitio.com +eltimon.com +elvis.com +email.com.br +email.cz +email.bg +email.it +email.lu +email.lviv.ua +email.nu +email.ro +email.si +email2me.com +emailacc.com +emailaccount.com +emailaddresses.com +emailchoice.com +emailcorner.net +emailn.de +emailengine.net +emailengine.org +emailgaul.com +emailgroups.net +emailhut.net +emailpinoy.com +emailplanet.com +emailplus.org +emailuser.net +ematic.com +embarqmail.com +embroideryforums.com +eml.cc +emoka.ro +emptymail.com +enel.net +enelpunto.net +england.com +enterate.com.ar +entryweb.it +entusiastisk.com +enusmail.com +epatra.com +epix.net +epomail.com +epost.de +eprompter.com +eqqu.com +eramail.co.za +eresmas.com +eriga.lv +ertelecom.ru +esde-s.org +esfera.cl +estadao.com.br +etllao.com +euromail.net +euroseek.com +euskalmail.com +evafan.com +everyday.com.kh +everymail.net +everyone.net +execs2k.com +executivemail.co.za +expn.com +ezilon.com +ezrs.com +f-m.fm +facilmail.com +fadrasha.net +fadrasha.org +faithhighway.com +faithmail.com +familymailbox.com +familyroll.com +familysafeweb.net +fan.com +fan.net +faroweb.com +fast-email.com +fast-mail.org +fastem.com +fastemail.us +fastemailer.com +fastermail.com +fastest.cc +fastimap.com +fastmailbox.net +fastmessaging.com +fastwebmail.it +fawz.net +fea.st +federalcontractors.com +fedxmail.com +feelings.com +female.ru +fepg.net +ffanet.com +fiberia.com +filipinolinks.com +financesource.com +findmail.com +fiscal.net +flashmail.com +flipcode.com +florida.usa.com +floridagators.com +fmail.co.uk +fmailbox.com +fmgirl.com +fmguy.com +fnmail.com +footballer.com +foxmail.com +forfree.at +forsythmissouri.org +fortuncity.com +forum.dk +free.com.pe +free.fr +free.net.nz +freeaccess.nl +freegates.be +freeghana.com +freehosting.nl +freei.co.th +freeler.nl +freemail.globalsite.com.br +freemuslim.net +freenet.de +freenet.kg +freeola.net +freepgs.com +freesbee.fr +freeservers.com +freestart.hu +freesurf.ch +freesurf.fr +freesurf.nl +freeuk.com +freeuk.net +freeweb.it +freewebemail.com +freeyellow.com +frisurf.no +frontiernet.net +fsmail.net +fsnet.co.uk +ftml.net +fuelie.org +fun-greetings-jokes.com +fun.21cn.com +fusemail.com +fut.es +gala.net +galmail.co.za +gamebox.net +gamecocks.com +gawab.com +gay.com +gaymailbox.com +gaza.net +gazeta.pl +gci.net +gdi.net +geeklife.com +gemari.or.id +genxemail.com +geopia.com +georgia.usa.com +getmail.no +ggaweb.ch +giga4u.de +gjk.dk +glay.org +glendale.net +globalfree.it +globomail.com +globalpinoy.com +globalsite.com.br +globalum.com +globetrotter.net +go-bama.com +go-cavs.com +go-chargers.com +go-dawgs.com +go-gators.com +go-hogs.com +go-irish.com +go-spartans.com +go-tigers.com +go.aggies.com +go.air-force.com +go.badgers.com +go.big-orange.com +go.blue.devils.com +go.buffaloes.com +go.bulldogs.com +go.com +go.cougars.com +go.dores.com +go.gamecocks.com +go.huskies.com +go.longhorns.com +go.mustangs.com +go.rebels.com +go.ro +go.ru +go.terrapins.com +go.wildcats.com +go.wolverines.com +go.yellow-jackets.com +go2net.com +go4.it +gofree.co.uk +golfemail.com +goliadtexas.com +gomail.com.ua +gonowmail.com +gonuts4free.com +googlemail.com +goplay.com +gorontalo.net +gotmail.com +gotomy.com +govzone.com +grad.com +graffiti.net +gratisweb.com +gtechnics.com +guate.net +guessmail.com +gwalla.com +h-mail.us +haberx.com +hailmail.net +halejob.com +hamptonroads.com +handbag.com +hanmail.net +happemail.com +happycounsel.com +hawaii.com +hawaii.usa.com +hayahaya.tg +hedgeai.com +heesun.net +heremail.com +hetnet.nl +highveldmail.co.za +hildebrands.de +hingis.org +hispavista.com +hitmanrecords.com +hockeyghiaccio.com +hockeymail.com +holapuravida.com +home.no.net +home.ro +home.se +homelocator.com +homemail.co.za +homenetmail.com +homestead.com +homosexual.net +hongkong.com +hong-kong-1.com +hopthu.com +hosanna.net +hot.ee +hotbot.com +hotbox.ru +hotcoolmail.com +hotdak.com +hotfire.net +hotinbox.com +hotpop.com +hotvoice.com +hour.com +howling.com +huhmail.com +humour.com +hurra.de +hush.ai +hush.com +hushmail.com +huskies.com +hutchcity.com +i-france.com +i-p.com +i12.com +i2828.com +ibatam.com +ibest.com.br +ibizdns.com +icafe.com +ice.is +icestorm.com +icq.com +icqmail.com +icrazy.com +id.ru +idaho.usa.com +idirect.com +idncafe.com +ieg.com.br +iespalomeras.net +iespana.es +ifrance.com +ig.com.br +ignazio.it +illinois.usa.com +ilse.net +ilse.nl +imail.ru +imailbox.com +imap-mail.com +imap.cc +imapmail.org +imel.org +in-box.net +inbox.com +inbox.ge +inbox.lv +inbox.net +inbox.ru +in.com +incamail.com +indexa.fr +india.com +indiamail.com +indiana.usa.com +indiatimes.com +induquimica.org +inet.com.ua +infinito.it +infoapex.com +infohq.com +infomail.es +infomart.or.jp +infosat.net +infovia.com.ar +inicia.es +inmail.sk +inmail24.com +inoutbox.com +intelnet.net.gt +intelnett.com +interblod.com +interfree.it +interia.pl +interlap.com.ar +intermail.hu +internet-e-mail.com +internet-mail.org +internet.lu +internetegypt.com +internetemails.net +internetkeno.com +internetmailing.net +inwind.it +iobox.com +iobox.fi +iol.it +iol.pt +iowa.usa.com +ip3.com +ipermitmail.com +iqemail.com +iquebec.com +iran.com +irangate.net +iscool.net +islandmama.com +ismart.net +isonews2.com +isonfire.com +isp9.net +ispey.com +itelgua.com +itloox.com +itmom.com +ivenus.com +iwan-fals.com +iwon.com +ixp.net +japan.com +jaydemail.com +jedrzejow.pl +jetemail.net +jingjo.net +jippii.fi +jmail.co.za +jojomail.com +jovem.te.pt +joymail.com +jubii.dk +jubiipost.dk +jumpy.it +juno.com +justemail.net +justmailz.com +k.ro +kaazoo.com +kabissa.org +kaixo.com +kalluritimes.com +kalpoint.com +kansas.usa.com +katamail.com +kataweb.it +kayafmmail.co.za +keko.com.ar +kentucky.usa.com +keptprivate.com +kimo.com +kiwitown.com +klik.it +klikni.cz +kmtn.ru +koko.com +kolozsvar.ro +kombud.com +koreanmail.com +kotaksuratku.info +krunis.com +kukamail.com +kuronowish.com +kyokodate.com +kyokofukada.net +ladymail.cz +lagoon.nc +lahaonline.com +lamalla.net +lancsmail.com +land.ru +laposte.net +latinmail.com +lawyer.com +lawyersmail.com +lawyerzone.com +lebanonatlas.com +leehom.net +leonardo.it +leonlai.net +letsjam.com +letterbox.org +letterboxes.org +levele.com +lexpress.net +libero.it +liberomail.com +libertysurf.net +libre.net +lightwines.org +linkmaster.com +linuxfreemail.com +lionsfan.com.au +livedoor.com +llandudno.com +llangollen.com +lmxmail.sk +loggain.net +loggain.nu +lolnetwork.net +london.com +longhorns.com +look.com +looksmart.co.uk +looksmart.com +looksmart.com.au +loteria.net +lotonazo.com +louisiana.usa.com +louiskoo.com +loveable.com +lovemail.com +lovingjesus.com +lpemail.com +luckymail.com +luso.pt +lusoweb.pt +luukku.com +lycosmail.com +mac.com +machinecandy.com +macmail.com +mad.scientist.com +madcrazy.com +madonno.com +madrid.com +mag2.com +magicmail.co.za +magik-net.com +mail-atlas.net +mail-awu.de +mail-box.cz +mail.by +mail-center.com +mail-central.com +mail-jp.org +mail-online.dk +mail-page.com +mail-x-change.com +mail.austria.com +mail.az +mail.de +mail.be +mail.bg +mail.bulgaria.com +mail.co.za +mail.dk +mail.ee +mail.goo.ne.jp +mail.gr +mail.lawguru.com +mail.md +mail.mn +mail.org +mail.pf +mail.pt +mail.ru +mail.yahoo.co.jp +mail15.com +mail3000.com +mail333.com +mail8.com +mailandftp.com +mailandnews.com +mailas.com +mailasia.com +mailbg.com +mailblocks.com +mailbolt.com +mailbox.as +mailbox.co.za +mailbox.gr +mailbox.hu +mailbox.sk +mailc.net +mailcan.com +mailcircuit.com +mailclub.fr +mailclub.net +maildozy.com +mailfly.com +mailforce.net +mailftp.com +mailglobal.net +mailhaven.com +mailinator.com +mailingaddress.org +mailingweb.com +mailisent.com +mailite.com +mailme.dk +mailmight.com +mailmij.nl +mailnew.com +mailops.com +mailpanda.com +mailpersonal.com +mailroom.com +mailru.com +mails.de +mailsent.net +mailserver.dk +mailservice.ms +mailsnare.net +mailsurf.com +mailup.net +mailvault.com +mailworks.org +maine.usa.com +majorana.martina-franca.ta.it +maktoob.com +malayalamtelevision.net +malayalapathram.com +male.ru +manager.de +manlymail.net +mantrafreenet.com +mantramail.com +mantraonline.com +marihuana.ro +marijuana.nl +marketweighton.com +maryland.usa.com +masrawy.com +massachusetts.usa.com +mauimail.com +mbox.com.au +mcrmail.com +me.by +me.com +medicinatv.com +meetingmall.com +megamail.pt +menara.ma +merseymail.com +mesra.net +messagez.com +metacrawler.com +mexico.com +miaoweb.net +michigan.usa.com +micro2media.com +miesto.sk +mighty.co.za +milacamn.net +milmail.com +mindless.com +mindviz.com +minnesota.usa.com +mississippi.usa.com +missouri.usa.com +mixmail.com +ml1.net +ml2clan.com +mlanime.com +mm.st +mmail.com +mobimail.mn +mobsters.com +mobstop.com +modemnet.net +modomail.com +moldova.com +moldovacc.com +monarchy.com +montana.usa.com +montevideo.com.uy +moomia.com +moose-mail.com +mosaicfx.com +motormania.com +movemail.com +mr.outblaze.com +mrspender.com +mscold.com +msnzone.cn +mundo-r.com +muslimsonline.com +mustangs.com +mxs.de +myblue.cc +mycabin.com +mycity.com +mycommail.com +mycool.com +mydomain.com +myeweb.com +myfastmail.com +myfunnymail.com +mygrande.net +mykolab.com +mygamingconsoles.com +myiris.com +myjazzmail.com +mymacmail.com +mymail.dk +mymail.ph.inter.net +mymail.ro +mynet.com +mynet.com.tr +myotw.net +myopera.com +myownemail.com +mypersonalemail.com +myplace.com +myrealbox.com +myspace.com +myt.mu +myway.com +mzgchaos.de +n2.com +n2business.com +n2mail.com +n2software.com +nabble.com +name.com +nameplanet.com +nanamail.co.il +nanaseaikawa.com +nandomail.com +naseej.com +nastything.com +national-champs.com +nativeweb.net +narod.ru +nate.com +naveganas.com +naver.com +nebraska.usa.com +nemra1.com +nenter.com +nerdshack.com +nervhq.org +net.hr +net4b.pt +net4jesus.com +net4you.at +netbounce.com +netcabo.pt +netcape.net +netcourrier.com +netexecutive.com +netfirms.com +netkushi.com +netmongol.com +netpiper.com +netposta.net +netscape.com +netscape.net +netscapeonline.co.uk +netsquare.com +nettaxi.com +netti.fi +networld.com +netzero.com +netzero.net +neustreet.com +nevada.usa.com +newhampshire.usa.com +newjersey.usa.com +newmail.com +newmail.net +newmail.ok.com +newmail.ru +newmexico.usa.com +newspaperemail.com +newyork.com +newyork.usa.com +newyorkcity.com +nfmail.com +nicegal.com +nightimeuk.com +nightly.com +nightmail.com +nightmail.ru +noavar.com +noemail.com +nonomail.com +nokiamail.com +noolhar.com +northcarolina.usa.com +northdakota.usa.com +nospammail.net +nowzer.com +ny.com +nyc.com +nz11.com +nzoomail.com +o2.pl +oceanfree.net +ocsnet.net +oddpost.com +odeon.pl +odmail.com +offshorewebmail.com +ofir.dk +ohio.usa.com +oicexchange.com +ok.ru +oklahoma.usa.com +ole.com +oleco.net +olympist.net +omaninfo.com +onatoo.com +ondikoi.com +onebox.com +onenet.com.ar +onet.pl +ongc.net +oninet.pt +online.ie +online.ru +onlinewiz.com +onobox.com +open.by +openbg.com +openforyou.com +opentransfer.com +operamail.com +oplusnet.com +orange.fr +orangehome.co.uk +orange.es +orange.jo +orange.pl +orbitel.bg +orcon.net.nz +oregon.usa.com +oreka.com +organizer.net +orgio.net +orthodox.com +osite.com.br +oso.com +ourbrisbane.com +ournet.md +ourprofile.net +ourwest.com +outgun.com +ownmail.net +oxfoot.com +ozu.es +pacer.com +paginasamarillas.com +pakistanmail.com +pandawa.com +pando.com +pandora.be +paris.com +parsimail.com +parspage.com +patmail.com +pattayacitythailand.com +pc4me.us +pcpostal.com +penguinmaster.com +pennsylvania.usa.com +peoplepc.com +peopleweb.com +personal.ro +personales.com +peru.com +petml.com +phreaker.net +pigeonportal.com +pilu.com +pimagop.com +pinoymail.com +pipni.cz +pisem.net +planet-school.de +planetaccess.com +planetout.com +plasa.com +playersodds.com +playful.com +pluno.com +plusmail.com.br +pmail.net +pnetmail.co.za +pobox.ru +pobox.sk +pochtamt.ru +pochta.ru +poczta.fm +poetic.com +pogowave.com +polbox.com +pop3.ru +pop.co.th +popmail.com +poppymail.com +popsmail.com +popstar.com +portafree.com +portaldosalunos.com +portugalmail.com +portugalmail.pt +post.cz +post.expart.ne.jp +post.pl +post.sk +posta.ge +postaccesslite.com +postiloota.net +postinbox.com +postino.ch +postino.it +postmaster.co.uk +postpro.net +praize.com +press.co.jp +primposta.com +printesamargareta.ro +private.21cn.com +probemail.com +profesional.com +profession.freemail.com.br +proinbox.com +promessage.com +prontomail.com +provincial.net +publicaccounting.com +punkass.com +puppy.com.my +q.com +qatar.io +qlmail.com +qq.com +qrio.com +qsl.net +qudsmail.com +queerplaces.com +quepasa.com +quick.cz +quickwebmail.com +r-o-o-t.com +r320.hu +raakim.com +rbcmail.ru +racingseat.com +radicalz.com +radiojobbank.com +ragingbull.com +raisingadaughter.com +rallye-webmail.com +rambler.ru +ranmamail.com +ravearena.com +ravemail.co.za +razormail.com +real.ro +realemail.net +reallyfast.biz +reallyfast.info +rebels.com +recife.net +recme.net +rediffmailpro.com +redseven.de +redwhitearmy.com +relia.com +revenue.com +rexian.com +rhodeisland.usa.com +ritmes.net +rn.com +roanokemail.com +rochester-mail.com +rock.com +rocketmail.com +rockfan.com +rockinghamgateway.com +rojname.com +rol.ro +rollin.com +rome.com +romymichele.com +royal.net +rpharmacist.com +rt.nl +ru.ru +rushpost.com +russiamail.com +rxpost.net +s-mail.com +saabnet.com +sacbeemail.com +sacmail.com +safe-mail.net +safe-mailbox.com +saigonnet.vn +saint-mike.org +samilan.net +sandiego.com +sanook.com +sanriotown.com +sapibon.com +sapo.pt +saturnfans.com +sayhi.net +sbcglobal.com +scfn.net +schweiz.org +sci.fi +sciaga.pl +scrapbookscrapbook.com +seapole.com +search417.com +seark.com +sebil.com +secretservices.net +secure-jlnet.com +seductive.com +sendmail.ru +sendme.cz +sent.as +sent.at +sent.com +serga.com.ar +sermix.com +server4free.de +serverwench.com +sesmail.com +sexmagnet.com +seznam.cz +shadango.com +she.com +shuf.com +siamlocalhost.com +siamnow.net +sify.com +sinamail.com +singapore.com +singmail.com +singnet.com.sg +siraj.org +sirindia.com +sirunet.com +sister.com +sina.com +sina.cn +sinanail.com +sistersbrothers.com +sizzling.com +slamdunkfan.com +slickriffs.co.uk +slingshot.com +slo.net +slomusic.net +smartemail.co.uk +smtp.ru +snail-mail.net +sndt.net +sneakemail.com +snoopymail.com +snowboarding.com +so-simple.org +socamail.com +softhome.net +sohu.com +sol.dk +solidmail.com +soon.com +sos.lv +soundvillage.org +southcarolina.usa.com +southdakota.usa.com +space.com +spacetowns.com +spamex.com +spartapiet.com +speed-racer.com +speedpost.net +speedymail.org +spils.com +spinfinder.com +sportemail.com +spray.net +spray.no +spray.se +spymac.com +srbbs.com +srilankan.net +ssan.com +ssl-mail.com +stade.fr +stalag13.com +stampmail.com +starbuzz.com +starline.ee +starmail.com +starmail.org +starmedia.com +starspath.com +start.com.au +start.no +stribmail.com +student.com +student.ednet.ns.ca +studmail.com +sudanmail.net +suisse.org +sunbella.net +sunmail1.com +sunpoint.net +sunrise.ch +sunumail.sn +sunuweb.net +suomi24.fi +superdada.it +supereva.com +supereva.it +supermailbox.com +superposta.com +surf3.net +surfassistant.com +surfsupnet.net +surfy.net +surimail.com +surnet.cl +sverige.nu +svizzera.org +sweb.cz +swift-mail.com +swissinfo.org +swissmail.net +switzerland.org +syom.com +syriamail.com +t-mail.com +t-net.net.ve +t2mail.com +tabasheer.com +talk21.com +talkcity.com +tangmonkey.com +tatanova.com +taxcutadvice.com +techemail.com +technisamail.co.za +teenmail.co.uk +teenmail.co.za +tejary.com +telebot.com +telefonica.net +telegraf.by +teleline.es +telinco.net +telkom.net +telpage.net +telstra.com +telenet.be +telusplanet.net +tempting.com +tenchiclub.com +tennessee.usa.com +terrapins.com +texas.usa.com +texascrossroads.com +tfz.net +thai.com +thaimail.com +thaimail.net +the-fastest.net +the-quickest.com +thegame.com +theinternetemail.com +theoffice.net +thepostmaster.net +theracetrack.com +theserverbiz.com +thewatercooler.com +thewebpros.co.uk +thinkpost.net +thirdage.com +thundermail.com +tim.it +timemail.com +tin.it +tinati.net +tiscalinet.it +tjohoo.se +tkcity.com +tlcfan.com +tlen.pl +tmicha.net +todito.com +todoperros.com +tokyo.com +topchat.com +topmail.com.ar +topmail.dk +topmail.co.ie +topmail.co.in +topmail.co.nz +topmail.co.uk +topmail.co.za +topsurf.com +toquedequeda.com +torba.com +torchmail.com +totalmail.com +totalsurf.com +totonline.net +tough.com +toughguy.net +trav.se +trevas.net +tripod-mail.com +triton.net +trmailbox.com +tsamail.co.za +turbonett.com +turkey.com +tvnet.lv +twc.com +typemail.com +u2club.com +uae.ac +ubbi.com +ubbi.com.br +uboot.com +ugeek.com +uk2.net +uk2net.com +ukr.net +ukrpost.net +ukrpost.ua +uku.co.uk +ulimit.com +ummah.org +unbounded.com +unicum.de +unimail.mn +unitedemailsystems.com +universal.pt +universia.cl +universia.edu.ve +universia.es +universia.net.co +universia.net.mx +universia.pr +universia.pt +universiabrasil.net +unofree.it +uol.com.ar +uol.com.br +uole.com +uolmail.com +uomail.com +uraniomail.com +urbi.com.br +ureach.com +usanetmail.com +userbeam.com +utah.usa.com +uyuyuy.com +v-sexi.com +v3mail.com +valanides.com +vegetarisme.be +velnet.com +velocall.com +vercorreo.com +verizonmail.com +vermont.usa.com +verticalheaven.com +veryfast.biz +veryspeedy.net +vfemail.net +vietmedia.com +vip.gr +virgilio.it +virgin.net +virginia.usa.com +virtual-mail.com +visitmail.com +visto.com +vivelared.com +vjtimail.com +vnn.vn +vsnl.com +vsnl.net +vodamail.co.za +voila.fr +volkermord.com +vosforums.com +w.cn +walla.com +walla.co.il +wallet.com +wam.co.za +wanex.ge +wap.hu +wapda.com +wapicode.com +wappi.com +warpmail.net +washington.usa.com +wassup.com +waterloo.com +waumail.com +wazmail.com +wearab.net +web-mail.com.ar +web.de +web.nl +web2mail.com +webaddressbook.com +webbworks.com +webcity.ca +webdream.com +webemaillist.com +webindia123.com +webinfo.fi +webjump.com +webl-3.br.inter.net +webmail.co.yu +webmail.co.za +webmails.com +webmailv.com +webpim.cc +webspawner.com +webstation.com +websurfer.co.za +webtopmail.com +webtribe.net +webtv.net +weedmail.com +weekonline.com +weirdness.com +westvirginia.usa.com +whale-mail.com +whipmail.com +who.net +whoever.com +wildcats.com +wildmail.com +williams.net.ar +winning.com +winningteam.com +winwinhosting.com +wisconsin.usa.com +witelcom.com +witty.com +wolverines.com +wooow.it +workmail.co.za +worldcrossing.com +worldemail.com +worldmedic.com +worldonline.de +wowmail.com +wp.pl +wprost.pl +wrongmail.com +wtonetwork.com +wurtele.net +www.com +www.consulcredit.it +wyoming.usa.com +x-mail.net +xasa.com +xfreehosting.com +xmail.net +xmsg.com +xnmsn.cn +xoom.com +xtra.co.nz +xuite.net +xpectmore.com +xrea.com +xsmail.com +xzapmail.com +y7mail.com +yahala.co.il +yaho.com +yalla.com.lb +ya.com +yeah.net +ya.ru +yahoomail.com +yam.com +yamal.info +yapost.com +yawmail.com +yebox.com +yehey.com +yellow-jackets.com +yellowstone.net +yenimail.com +yepmail.net +yifan.net +yopmail.com +your-mail.com +yours.com +yourwap.com +yyhmail.com +z11.com +z6.com +zednet.co.uk +zeeman.nl +ziplip.com +zipmail.com.br +zipmax.com +zmail.pt +zmail.ru +zona-andina.net +zonai.com +zoneview.net +zonnet.nl +zoho.com +zoomshare.com +zoznam.sk +zubee.com +zuvio.com +zwallet.com +zworg.com +zybermail.com +zzn.com +126.com +139.com +163.com +188.com +189.cn +263.net +9.cn +vip.126.com +vip.163.com +vip.188.com +vip.sina.com +vip.sohu.com +vip.sohu.net +vip.tom.com +vip.qq.com +vipsohu.net +clovermail.net +mail-on.us +chewiemail.com +offcolormail.com +powdermail.com +tightmail.com +toothandmail.com +tushmail.com +openmail.cc +expressmail.dk +4xn.de +5x2.de +5x2.me +aufdrogen.de +auf-steroide.de +besser-als-du.de +brainsurfer.de +chillaxer.de +cyberkriminell.de +danneben.so +freemailen.de +freemailn.de +ist-der-mann.de +ist-der-wahnsinn.de +ist-echt.so +istecht.so +ist-genialer.de +ist-schlauer.de +ist-supersexy.de +kann.so +mag-spam.net +mega-schlau.de +muss.so +nerd4life.de +ohne-drogen-gehts.net +on-steroids.de +scheint.so +staatsterrorist.de +super-gerissen.de +unendlich-schlau.de +vip-client.de +will-keinen-spam.de +zu-geil.de +rbox.me +rbox.co +tunome.com +acatperson.com +adogperson.com +all4theskins.com +allsportsrock.com +alwaysgrilling.com +alwaysinthekitchen.com +alwayswatchingmovies.com +alwayswatchingtv.com +asylum.com +basketball-email.com +beabookworm.com +beagolfer.com +beahealthnut.com +believeinliberty.com +bestcoolcars.com +bestjobcandidate.com +besure2vote.com +bigtimecatperson.com +bigtimedogperson.com +bigtimereader.com +bigtimesportsfan.com +blackvoices.com +capsfanatic.com +capshockeyfan.com +capsred.com +car-nut.net +cat-person.com +catpeoplerule.com +chat-with-me.com +cheatasrule.com +crazy4baseball.com +crazy4homeimprovement.com +crazy4mail.com +crazyaboutfilms.net +crazycarfan.com +crazyforemail.com +crazymoviefan.com +descriptivemail.com +differentmail.com +dog-person.com +dogpeoplerule.com +easydoesit.com +expertrenovator.com +expressivemail.com +fanaticos.com +fanofbooks.com +fanofcomputers.com +fanofcooking.com +fanoftheweb.com +fieldmail.com +fleetmail.com +focusedonprofits.com +focusedonreturns.com +futboladdict.com +games.com +getintobooks.com +hail2theskins.com +hitthepuck.com +i-dig-movies.com +i-love-restaurants.com +idigcomputers.com +idigelectronics.com +idigvideos.com +ilike2helpothers.com +ilike2invest.com +ilike2workout.com +ilikeelectronics.com +ilikeworkingout.com +ilovehomeprojects.com +iloveourteam.com +iloveworkingout.com +in2autos.net +interestedinthejob.com +intomotors.com +iwatchrealitytv.com +lemondrop.com +love2exercise.com +love2workout.com +lovefantasysports.com +lovetoexercise.com +luvfishing.com +luvgolfing.com +luvsoccer.com +mail4me.com +majorgolfer.com +majorshopaholic.com +majortechie.com +mcom.com +motor-nut.com +moviefan.com +mycapitalsmail.com +mycatiscool.com +myfantasyteamrules.com +myteamisbest.com +netbusiness.com +news-fanatic.com +newspaperfan.com +onlinevideosrock.com +realbookfan.com +realhealthnut.com +realitytvaddict.net +realitytvnut.com +reallyintomusic.com +realtravelfan.com +redskinscheer.com +redskinsfamily.com +redskinsfancentral.com +redskinshog.com +redskinsrule.com +redskinsspecialteams.com +redskinsultimatefan.com +scoutmail.com +skins4life.com +stargate2.com +stargateatlantis.com +stargatefanclub.com +stargatesg1.com +stargateu.com +switched.com +t-online.de +thegamefanatic.com +total-techie.com +totalfoodnut.com +totally-into-cooking.com +totallyintobaseball.com +totallyintobasketball.com +totallyintocooking.com +totallyintofootball.com +totallyintogolf.com +totallyintohockey.com +totallyintomusic.com +totallyintoreading.com +totallyintosports.com +totallyintotravel.com +totalmoviefan.com +travel2newplaces.com +tvchannelsurfer.com +ultimateredskinsfan.com +videogamesrock.com +volunteeringisawesome.com +wayintocomputers.com +whatmail.com +when.com +wild4music.com +wildaboutelectronics.com +workingaroundthehouse.com +workingonthehouse.com +writesoon.com +xmasmail.com +arab.ir +denmark.ir +egypt.ir +icq.ir +ir.ae +iraq.ir +ire.ir +ireland.ir +irr.ir +jpg.ir +ksa.ir +kuwait.ir +london.ir +paltalk.ir +spain.ir +sweden.ir +tokyo.ir +111mail.com +123iran.com +37.com +420email.com +4degreez.com +4-music-today.com +actingbiz.com +allhiphop.com +anatomicrock.com +animeone.com +asiancutes.com +a-teens.net +ausi.com +autoindia.com +autopm.com +barriolife.com +b-boy.com +beautifulboy.com +bgay.com +bicycledata.com +bicycling.com +bigheavyworld.com +bigmailbox.net +bikerheaven.net +bikermail.com +billssite.com +blackandchristian.com +blackcity.net +blackvault.com +bmxtrix.com +boarderzone.com +boatnerd.com +bolbox.com +bongmail.com +bowl.com +butch-femme.org +byke.com +calle22.com +cannabismail.com +catlovers.com +certifiedbitches.com +championboxing.com +chatway.com +chillymail.com +classprod.com +classycouples.com +congiu.net +coolshit.com +corpusmail.com +cyberunlimited.org +cycledata.com +darkfear.com +darkforces.com +dirtythird.com +dopefiends.com +draac.com +drakmail.net +dr-dre.com +dreamstop.com +egypt.net +emailfast.com +envirocitizen.com +escapeartist.com +ezsweeps.com +famous.as +farts.com +feelingnaughty.com +firemyst.com +freeonline.com +fudge.com +funkytimes.com +gamerssolution.com +gazabo.net +glittergrrrls.com +goatrance.com +goddess.com +gohip.com +gospelcity.com +gothicgirl.com +grapemail.net +greatautos.org +guy.com +haitisurf.com +happyhippo.com +hateinthebox.com +houseofhorrors.com +hugkiss.com +hullnumber.com +idunno4recipes.com +ihatenetscape.com +intimatefire.com +irow.com +jazzemail.com +juanitabynum.com +kanoodle.com +kickboxing.com +kidrock.com +kinkyemail.com +kool-things.com +latinabarbie.com +latinogreeks.com +leesville.com +loveemail.com +lowrider.com +lucky7lotto.net +madeniggaz.net +mailbomb.com +marillion.net +megarave.com +mofa.com +motley.com +music.com +musician.net +musicsites.com +netbroadcaster.com +netfingers.com +net-surf.com +nocharge.com +operationivy.com +paidoffers.net +pcbee.com +persian.com +petrofind.com +phunkybitches.com +pikaguam.com +pinkcity.net +pitbullmail.com +planetsmeg.com +poop.com +poormail.com +potsmokersnet.com +primetap.com +project420.com +prolife.net +puertoricowow.com +puppetweb.com +rapstar.com +rapworld.com +rastamall.com +ratedx.net +ravermail.com +relapsecult.com +remixer.com +rockeros.com +romance106fm.com +singalongcenter.com +sketchyfriends.com +slayerized.com +smartstocks.com +soulja-beatz.org +specialoperations.com +speedymail.net +spells.com +superbikeclub.com +superintendents.net +surfguiden.com +sweetwishes.com +tattoodesign.com +teamster.net +teenchatnow.com +the5thquarter.com +theblackmarket.com +tombstone.ws +troamail.org +u2tours.com +vitalogy.org +whatisthis.com +wrestlezone.com +abha.cc +agadir.cc +ahsa.ws +ajman.cc +ajman.us +ajman.ws +albaha.cc +algerie.cc +alriyadh.cc +amman.cc +aqaba.cc +arar.ws +aswan.cc +baalbeck.cc +bahraini.cc +banha.cc +bizerte.cc +blida.info +buraydah.cc +cameroon.cc +dhahran.cc +dhofar.cc +djibouti.cc +dominican.cc +eritrea.cc +falasteen.cc +fujairah.cc +fujairah.us +fujairah.ws +gabes.cc +gafsa.cc +giza.cc +guinea.cc +hamra.cc +hasakah.com +hebron.tv +homs.cc +ibra.cc +irbid.ws +ismailia.cc +jadida.cc +jadida.org +jerash.cc +jizan.cc +jouf.cc +kairouan.cc +karak.cc +khaimah.cc +khartoum.cc +khobar.cc +kuwaiti.tv +kyrgyzstan.cc +latakia.cc +lebanese.cc +lubnan.cc +lubnan.ws +madinah.cc +maghreb.cc +manama.cc +mansoura.tv +marrakesh.cc +mascara.ws +meknes.cc +muscat.tv +muscat.ws +nabeul.cc +nabeul.info +nablus.cc +nador.cc +najaf.cc +omani.ws +omdurman.cc +oran.cc +oued.info +oued.org +oujda.biz +oujda.cc +pakistani.ws +palmyra.cc +palmyra.ws +portsaid.cc +qassem.cc +quds.cc +rabat.cc +rafah.cc +ramallah.cc +safat.biz +safat.info +safat.us +safat.ws +salalah.cc +salmiya.biz +sanaa.cc +seeb.cc +sfax.ws +sharm.cc +sinai.cc +siria.cc +sousse.cc +sudanese.cc +suez.cc +tabouk.cc +tajikistan.cc +tangiers.cc +tanta.cc +tayef.cc +tetouan.cc +timor.cc +tunisian.cc +urdun.cc +yanbo.cc +yemeni.cc +yunus.cc +zagazig.cc +zambia.cc +5005.lv +a.org.ua +bmx.lv +company.org.ua +coolmail.ru +dino.lv +eclub.lv +e-mail.am +fit.lv +hacker.am +human.lv +iphon.biz +latchess.com +loveis.lv +lv-inter.net +pookmail.com +sexriga.lv diff --git a/crates/router/src/utils/user/password.rs b/crates/router/src/utils/user/password.rs new file mode 100644 index 000000000000..cff17863c32d --- /dev/null +++ b/crates/router/src/utils/user/password.rs @@ -0,0 +1,43 @@ +use argon2::{ + password_hash::{ + rand_core::OsRng, Error as argon2Err, PasswordHash, PasswordHasher, PasswordVerifier, + SaltString, + }, + Argon2, +}; +use common_utils::errors::CustomResult; +use error_stack::{IntoReport, ResultExt}; +use masking::{ExposeInterface, Secret}; + +use crate::core::errors::UserErrors; + +pub fn generate_password_hash( + password: Secret, +) -> CustomResult, UserErrors> { + let salt = SaltString::generate(&mut OsRng); + + let argon2 = Argon2::default(); + let password_hash = argon2 + .hash_password(password.expose().as_bytes(), &salt) + .into_report() + .change_context(UserErrors::InternalServerError)?; + Ok(Secret::new(password_hash.to_string())) +} + +pub fn is_correct_password( + candidate: Secret, + password: Secret, +) -> CustomResult { + let password = password.expose(); + let parsed_hash = PasswordHash::new(&password) + .into_report() + .change_context(UserErrors::InternalServerError)?; + let result = Argon2::default().verify_password(candidate.expose().as_bytes(), &parsed_hash); + match result { + Ok(_) => Ok(true), + Err(argon2Err::Password) => Ok(false), + Err(e) => Err(e), + } + .into_report() + .change_context(UserErrors::InternalServerError) +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 0c9751aee440..9cd678083959 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -243,6 +243,8 @@ pub enum Flow { GsmRuleUpdate, /// Gsm Rule Delete flow GsmRuleDelete, + /// User connect account + UserConnectAccount, } /// From d84b9530470cb1f32357a8c500baff6bfcdb54b3 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 16:52:37 +0530 Subject: [PATCH 2/7] feat: add jwt to dashboard routes --- config/development.toml | 11 +-- crates/api_models/src/events/user.rs | 3 +- crates/api_models/src/user.rs | 1 + crates/router/src/core/user.rs | 24 +++++- crates/router/src/routes/admin.rs | 91 ++++++++++++++++---- crates/router/src/routes/api_keys.rs | 32 +++++-- crates/router/src/routes/payments.rs | 19 ++-- crates/router/src/routes/refunds.rs | 8 +- crates/router/src/routes/routing.rs | 44 +++++----- crates/router/src/services/authentication.rs | 3 +- crates/router/src/types/domain/user.rs | 31 ++++--- 11 files changed, 184 insertions(+), 83 deletions(-) diff --git a/config/development.toml b/config/development.toml index 3fa42ef20dcf..63c1f045d94f 100644 --- a/config/development.toml +++ b/config/development.toml @@ -1,8 +1,3 @@ -[server] -port = 8000 -host = "0.0.0.0" -request_body_limit = 16_384 # Post request body is limited to 16k. - [log.file] enabled = false @@ -35,9 +30,9 @@ dbname = "hyperswitch_db" pool_size = 5 connection_timeout = 10 -# [server] -# # HTTP Request body limit. Defaults to 32kB -# request_body_limit = 32768 +[server] +# HTTP Request body limit. Defaults to 32kB +request_body_limit = 32768 [secrets] admin_api_key = "test_admin" diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 25aba4631b1a..634ec82788f9 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -1,6 +1,7 @@ -use crate::user::{SignInRequest, SignInResponse}; use common_utils::events::{ApiEventMetric, ApiEventsType}; +use crate::user::{SignInRequest, SignInResponse}; + impl ApiEventMetric for SignInResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::User { diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 6f391137627c..076ee585023d 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -9,6 +9,7 @@ pub struct SignInRequest { #[derive(serde::Serialize, Debug, Clone)] pub struct SignInResponse { + pub token: Secret, pub merchant_id: String, pub name: Secret, pub email: pii::Email, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index b4d0f5b2755b..bddee51c1413 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,7 +1,7 @@ use api_models::user as api; use diesel_models::enums::UserStatus; use error_stack::IntoReport; -use masking::ExposeInterface; +use masking::{ExposeInterface, Secret}; use router_env::env; use super::errors::{UserErrors, UserResponse}; @@ -22,9 +22,21 @@ pub async fn connect_account( let user_from_db: domain::UserFromStorage = found_user.into(); user_from_db.compare_password(request.password)?; - return Ok(ApplicationResponse::Json( - user_from_db.get_signin_response_from_db(state).await?, - )); + + let user_role = user_from_db.get_role_from_db(state.clone()).await?; + let jwt_token = user_from_db + .get_jwt_auth_token(state.clone(), user_role.org_id) + .await?; + + return Ok(ApplicationResponse::Json(api::SignInResponse { + token: Secret::new(jwt_token), + merchant_id: user_role.merchant_id, + name: user_from_db.get_name(), + email: user_from_db.get_email(), + verification_days_left: None, + user_role: user_role.role_id, + user_id: user_from_db.get_user_id().to_string(), + })); } else if find_user .map_err(|e| e.current_context().is_db_not_found()) .err() @@ -50,8 +62,12 @@ pub async fn connect_account( UserStatus::Active, ) .await?; + let jwt_token = user_from_db + .get_jwt_auth_token(state.clone(), user_role.org_id) + .await?; return Ok(ApplicationResponse::Json(api::SignInResponse { + token: Secret::new(jwt_token), merchant_id: user_role.merchant_id, name: user_from_db.get_name(), email: user_from_db.get_email(), diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 9153e9e747f6..a8eda22402c3 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -64,7 +64,10 @@ pub async fn retrieve_merchant_account( ) -> HttpResponse { let flow = Flow::MerchantsAccountRetrieve; let merchant_id = mid.into_inner(); - let payload = web::Json(admin::MerchantId { merchant_id }).into_inner(); + let payload = web::Json(admin::MerchantId { + merchant_id: merchant_id.to_owned(), + }) + .into_inner(); api::server_wrap( flow, @@ -72,7 +75,11 @@ pub async fn retrieve_merchant_account( &req, payload, |state, _, req| get_merchant_account(state, req), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -130,7 +137,13 @@ pub async fn update_merchant_account( &req, json_payload.into_inner(), |state, _, req| merchant_account_update(state, &merchant_id, req), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -203,7 +216,13 @@ pub async fn payment_connector_create( &req, json_payload.into_inner(), |state, _, req| create_payment_connector(state, req, &merchant_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -236,7 +255,7 @@ pub async fn payment_connector_retrieve( let flow = Flow::MerchantConnectorsRetrieve; let (merchant_id, merchant_connector_id) = path.into_inner(); let payload = web::Json(admin::MerchantConnectorId { - merchant_id, + merchant_id: merchant_id.clone(), merchant_connector_id, }) .into_inner(); @@ -249,7 +268,11 @@ pub async fn payment_connector_retrieve( |state, _, req| { retrieve_payment_connector(state, req.merchant_id, req.merchant_connector_id) }, - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -285,9 +308,13 @@ pub async fn payment_connector_list( flow, state, &req, - merchant_id, + merchant_id.to_owned(), |state, _, merchant_id| list_payment_connectors(state, merchant_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -328,7 +355,13 @@ pub async fn payment_connector_update( &req, json_payload.into_inner(), |state, _, req| update_payment_connector(state, &merchant_id, &merchant_connector_id, req), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -362,7 +395,7 @@ pub async fn payment_connector_delete( let (merchant_id, merchant_connector_id) = path.into_inner(); let payload = web::Json(admin::MerchantConnectorId { - merchant_id, + merchant_id: merchant_id.clone(), merchant_connector_id, }) .into_inner(); @@ -372,7 +405,11 @@ pub async fn payment_connector_delete( &req, payload, |state, _, req| delete_payment_connector(state, req.merchant_id, req.merchant_connector_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -419,7 +456,13 @@ pub async fn business_profile_create( &req, payload, |state, _, req| create_business_profile(state, req, &merchant_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -431,7 +474,7 @@ pub async fn business_profile_retrieve( path: web::Path<(String, String)>, ) -> HttpResponse { let flow = Flow::BusinessProfileRetrieve; - let (_, profile_id) = path.into_inner(); + let (merchant_id, profile_id) = path.into_inner(); api::server_wrap( flow, @@ -439,7 +482,11 @@ pub async fn business_profile_retrieve( &req, profile_id, |state, _, profile_id| retrieve_business_profile(state, profile_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -460,7 +507,13 @@ pub async fn business_profile_update( &req, json_payload.into_inner(), |state, _, req| update_business_profile(state, &profile_id, &merchant_id, req), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -498,9 +551,13 @@ pub async fn business_profiles_list( flow, state, &req, - merchant_id, + merchant_id.clone(), |state, _, merchant_id| list_business_profile(state, merchant_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index c2e289cd0f7e..1f71f1dc2800 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -53,7 +53,13 @@ pub async fn api_key_create( ) .await }, - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -91,7 +97,13 @@ pub async fn api_key_retrieve( &req, (&merchant_id, &key_id), |state, _, (merchant_id, key_id)| api_keys::retrieve_api_key(state, merchant_id, key_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -173,7 +185,13 @@ pub async fn api_key_revoke( &req, (&merchant_id, &key_id), |state, _, (merchant_id, key_id)| api_keys::revoke_api_key(state, merchant_id, key_id), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await @@ -213,11 +231,15 @@ pub async fn api_key_list( flow, state, &req, - (limit, offset, merchant_id), + (limit, offset, merchant_id.clone()), |state, _, (limit, offset, merchant_id)| async move { api_keys::list_api_keys(state, merchant_id, limit, offset).await }, - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromRoute { merchant_id }, + req.headers(), + ), api_locking::LockAction::NotApplicable, ) .await diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 5ed73df1c175..ed36721da445 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -4,7 +4,7 @@ pub mod helpers; use actix_web::{web, Responder}; use api_models::payments::HeaderPayload; use error_stack::report; -use router_env::{instrument, tracing, types, Flow}; +use router_env::{env, instrument, tracing, types, Flow}; use crate::{ self as app, @@ -118,7 +118,10 @@ pub async fn payments_create( api::AuthFlow::Merchant, ) }, - &auth::ApiKeyAuth, + match env::which() { + env::Env::Production => &auth::ApiKeyAuth, + _ => auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + }, locking_action, ) .await @@ -249,7 +252,11 @@ pub async fn payments_retrieve( HeaderPayload::default(), ) }, - &*auth_type, + auth::auth_type( + &*auth_type, + &auth::JWTAuth, + req.headers(), + ), locking_action, ) .await @@ -828,7 +835,7 @@ pub async fn payments_list( &req, payload, |state, auth, req| payments::list_payments(state, auth.merchant_account, req), - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await @@ -848,7 +855,7 @@ pub async fn payments_list_by_filter( &req, payload, |state, auth, req| payments::apply_filters_on_payments(state, auth.merchant_account, req), - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await @@ -868,7 +875,7 @@ pub async fn get_filters_for_payments( &req, payload, |state, auth, req| payments::get_filters_for_payments(state, auth.merchant_account, req), - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index c20f3fbf975d..d1f5cb56fe23 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -37,7 +37,7 @@ 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::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await @@ -88,7 +88,7 @@ pub async fn refunds_retrieve( refund_retrieve_core, ) }, - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await @@ -202,7 +202,7 @@ pub async fn refunds_list( &req, payload.into_inner(), |state, auth, req| refund_list(state, auth.merchant_account, req), - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await @@ -235,7 +235,7 @@ pub async fn refunds_filter_list( &req, payload.into_inner(), |state, auth, req| refund_filter_list(state, auth.merchant_account, req), - &auth::ApiKeyAuth, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 9252c360a9ce..b87116f47fc5 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, routing}, routes::AppState, - services::{api as oss_api, authentication as oss_auth, authentication as auth}, + services::{api as oss_api, authentication as auth}, }; #[cfg(feature = "olap")] @@ -30,11 +30,11 @@ pub async fn routing_create_config( state, &req, json_payload.into_inner(), - |state, auth: oss_auth::AuthenticationData, payload| { + |state, auth: auth::AuthenticationData, payload| { routing::create_routing_config(state, auth.merchant_account, auth.key_store, payload) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -55,7 +55,7 @@ pub async fn routing_link_config( state, &req, path.into_inner(), - |state, auth: oss_auth::AuthenticationData, algorithm_id| { + |state, auth: auth::AuthenticationData, algorithm_id| { routing::link_routing_config( state, auth.merchant_account, @@ -65,7 +65,7 @@ pub async fn routing_link_config( ) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -87,11 +87,11 @@ pub async fn routing_retrieve_config( state, &req, algorithm_id, - |state, auth: oss_auth::AuthenticationData, algorithm_id| { + |state, auth: auth::AuthenticationData, algorithm_id| { routing::retrieve_routing_config(state, auth.merchant_account, algorithm_id) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -114,7 +114,7 @@ pub async fn routing_retrieve_dictionary( state, &req, query.into_inner(), - |state, auth: oss_auth::AuthenticationData, query_params| { + |state, auth: auth::AuthenticationData, query_params| { routing::retrieve_merchant_routing_dictionary( state, auth.merchant_account, @@ -122,7 +122,7 @@ pub async fn routing_retrieve_dictionary( ) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -138,11 +138,11 @@ pub async fn routing_retrieve_dictionary( state, &req, (), - |state, auth: oss_auth::AuthenticationData, _| { + |state, auth: auth::AuthenticationData, _| { routing::retrieve_merchant_routing_dictionary(state, auth.merchant_account) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -168,11 +168,11 @@ pub async fn routing_unlink_config( state, &req, payload.into_inner(), - |state, auth: oss_auth::AuthenticationData, payload_req| { + |state, auth: auth::AuthenticationData, payload_req| { routing::unlink_routing_config(state, auth.merchant_account, payload_req) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -188,11 +188,11 @@ pub async fn routing_unlink_config( state, &req, (), - |state, auth: oss_auth::AuthenticationData, _| { + |state, auth: auth::AuthenticationData, _| { routing::unlink_routing_config(state, auth.merchant_account, auth.key_store) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -213,11 +213,11 @@ pub async fn routing_update_default_config( state, &req, json_payload.into_inner(), - |state, auth: oss_auth::AuthenticationData, updated_config| { + |state, auth: auth::AuthenticationData, updated_config| { routing::update_default_routing_config(state, auth.merchant_account, updated_config) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -236,11 +236,11 @@ pub async fn routing_retrieve_default_config( state, &req, (), - |state, auth: oss_auth::AuthenticationData, _| { + |state, auth: auth::AuthenticationData, _| { routing::retrieve_default_routing_config(state, auth.merchant_account) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -268,7 +268,7 @@ 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(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, @@ -284,11 +284,11 @@ pub async fn routing_retrieve_linked_config( state, &req, (), - |state, auth: oss_auth::AuthenticationData, _| { + |state, auth: auth::AuthenticationData, _| { routing::retrieve_linked_routing_config(state, auth.merchant_account) }, #[cfg(not(feature = "release"))] - auth::auth_type(&oss_auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), #[cfg(feature = "release")] &auth::JWTAuth, api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 2033bbb6366f..a7e54e9afdd6 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::jwt; use crate::{ configs::settings, consts, @@ -23,8 +24,6 @@ use crate::{ utils::OptionExt, }; -use super::jwt; - #[derive(Clone, Debug)] pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 8898a3a20d03..063eb80bd08f 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -22,6 +22,7 @@ use crate::{ }, db::StorageInterface, routes::AppState, + services::authentication::AuthToken, types::transformers::ForeignFrom, utils::user::password, }; @@ -454,20 +455,22 @@ impl UserFromStorage { self.0.email.clone() } - pub async fn get_signin_response_from_db( - &self, - state: AppState, - ) -> UserResult { - let user_role = self.get_role_from_db(state.clone()).await?; - - Ok(user_api::SignInResponse { - merchant_id: user_role.merchant_id, - name: self.get_name(), - email: self.get_email(), - verification_days_left: None, - user_role: user_role.role_id, - user_id: self.get_user_id().to_string(), - }) + pub async fn get_jwt_auth_token(&self, state: AppState, org_id: String) -> UserResult { + let role_id = self.get_role_from_db(state.clone()).await?.role_id; + let merchant_id = state + .store + .find_user_role_by_user_id(self.get_user_id()) + .await + .change_context(UserErrors::InternalServerError)? + .merchant_id; + AuthToken::new_token( + self.0.user_id.clone(), + merchant_id, + role_id, + &state.conf, + org_id, + ) + .await } pub async fn get_role_from_db(&self, state: AppState) -> UserResult { From a4004245a0bc6fc43405ab4e62b28779eb851230 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 17:26:16 +0530 Subject: [PATCH 3/7] refactor: rename user api types and fix clippy --- crates/api_models/src/events/user.rs | 6 +++--- crates/api_models/src/user.rs | 4 ++-- crates/cards/Cargo.toml | 3 --- crates/common_enums/Cargo.toml | 3 --- crates/common_utils/Cargo.toml | 5 ----- crates/data_models/Cargo.toml | 2 +- crates/euclid/Cargo.toml | 3 --- crates/kgraph_utils/Cargo.toml | 3 --- crates/masking/Cargo.toml | 3 --- crates/redis_interface/Cargo.toml | 3 --- crates/router/Cargo.toml | 15 --------------- crates/router/src/core/user.rs | 8 ++++---- crates/router/src/routes/user.rs | 2 +- crates/router/src/services/authentication.rs | 9 +++++---- crates/router/src/services/jwt.rs | 2 +- crates/router/src/types/domain/user.rs | 12 ++++++------ crates/router_derive/Cargo.toml | 6 ------ crates/router_env/Cargo.toml | 3 --- 18 files changed, 23 insertions(+), 69 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 634ec82788f9..2a896cc38776 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -1,8 +1,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -use crate::user::{SignInRequest, SignInResponse}; +use crate::user::{ConnectAccountRequest, ConnectAccountResponse}; -impl ApiEventMetric for SignInResponse { +impl ApiEventMetric for ConnectAccountResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::User { merchant_id: self.merchant_id.clone(), @@ -11,4 +11,4 @@ impl ApiEventMetric for SignInResponse { } } -impl ApiEventMetric for SignInRequest {} +impl ApiEventMetric for ConnectAccountRequest {} diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 076ee585023d..91f7702c654e 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -2,13 +2,13 @@ use common_utils::pii; use masking::Secret; #[derive(serde::Deserialize, Debug, Clone, serde::Serialize)] -pub struct SignInRequest { +pub struct ConnectAccountRequest { pub email: pii::Email, pub password: Secret, } #[derive(serde::Serialize, Debug, Clone)] -pub struct SignInResponse { +pub struct ConnectAccountResponse { pub token: Secret, pub merchant_id: String, pub name: Secret, diff --git a/crates/cards/Cargo.toml b/crates/cards/Cargo.toml index 00445936e3d2..4d38d3118f82 100644 --- a/crates/cards/Cargo.toml +++ b/crates/cards/Cargo.toml @@ -19,6 +19,3 @@ time = "0.3.21" # First party crates common_utils = { version = "0.1.0", path = "../common_utils" } masking = { version = "0.1.0", path = "../masking" } - -[dev-dependencies] -serde_json = "1.0.96" diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index e9f2dffcc050..9b748a16782a 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -20,6 +20,3 @@ utoipa = { version = "3.3.0", features = ["preserve_order"] } # First party crates router_derive = { version = "0.1.0", path = "../router_derive" } - -[dev-dependencies] -serde_json = "1.0.96" diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index 62bd747da1b0..4438350a44e2 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -46,8 +46,3 @@ common_enums = { version = "0.1.0", path = "../common_enums" } [target.'cfg(not(target_os = "windows"))'.dependencies] signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true } - -[dev-dependencies] -fake = "2.6.1" -proptest = "1.2.0" -test-case = "3.1.0" diff --git a/crates/data_models/Cargo.toml b/crates/data_models/Cargo.toml index 254c194182f3..c7c872771689 100644 --- a/crates/data_models/Cargo.toml +++ b/crates/data_models/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "1.0.163", features = ["derive"] } serde_json = "1.0.96" strum = { version = "0.25", features = [ "derive" ] } thiserror = "1.0.40" -time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } \ No newline at end of file +time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index f0e24b1ff63c..6cd28115f5dc 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -29,9 +29,6 @@ connector_choice_mca_id = [] dummy_connector = [] backwards_compatibility = ["connector_choice_bcompat"] -[dev-dependencies] -criterion = "0.5" - [[bench]] name = "backends" harness = false diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index fa90b3974c20..a1cb25e935b5 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -19,9 +19,6 @@ serde = "1.0.163" serde_json = "1.0.96" thiserror = "1.0.43" -[dev-dependencies] -criterion = "0.5" - [[bench]] name = "evaluation" harness = false diff --git a/crates/masking/Cargo.toml b/crates/masking/Cargo.toml index 21d791642895..67ae3448ac1d 100644 --- a/crates/masking/Cargo.toml +++ b/crates/masking/Cargo.toml @@ -22,6 +22,3 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = "1.0.96" subtle = "=2.4.1" zeroize = { version = "1.6", default-features = false } - -[dev-dependencies] -serde_json = "1.0.96" diff --git a/crates/redis_interface/Cargo.toml b/crates/redis_interface/Cargo.toml index 8066787dcae2..633233e21829 100644 --- a/crates/redis_interface/Cargo.toml +++ b/crates/redis_interface/Cargo.toml @@ -18,6 +18,3 @@ tokio = "1.28.2" # First party crates common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext"] } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } - -[dev-dependencies] -tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 0d7b89dd8d4f..253062f9e8f2 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -121,21 +121,6 @@ signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } [build-dependencies] router_env = { version = "0.1.0", path = "../router_env", default-features = false } -[dev-dependencies] -actix-http = "3.3.1" -awc = { version = "3.1.1", features = ["rustls"] } -derive_deref = "1.1.1" -rand = "0.8.5" -serial_test = "2.0.0" -thirtyfour = "0.31.0" -time = { version = "0.3.21", features = ["macros"] } -tokio = "1.28.2" -toml = "0.7.4" -wiremock = "0.5" - -# First party dev-dependencies -test_utils = { version = "0.1.0", path = "../test_utils" } - [[bin]] name = "router" path = "src/bin/router.rs" diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index bddee51c1413..710dc9281bfa 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -11,8 +11,8 @@ use crate::{ pub async fn connect_account( state: AppState, - request: api::SignInRequest, -) -> UserResponse { + request: api::ConnectAccountRequest, +) -> UserResponse { let find_user = state .store .find_user_by_email(request.email.clone().expose().expose().as_str()) @@ -28,7 +28,7 @@ pub async fn connect_account( .get_jwt_auth_token(state.clone(), user_role.org_id) .await?; - return Ok(ApplicationResponse::Json(api::SignInResponse { + return Ok(ApplicationResponse::Json(api::ConnectAccountResponse { token: Secret::new(jwt_token), merchant_id: user_role.merchant_id, name: user_from_db.get_name(), @@ -66,7 +66,7 @@ pub async fn connect_account( .get_jwt_auth_token(state.clone(), user_role.org_id) .await?; - return Ok(ApplicationResponse::Json(api::SignInResponse { + return Ok(ApplicationResponse::Json(api::ConnectAccountResponse { token: Secret::new(jwt_token), merchant_id: user_role.merchant_id, name: user_from_db.get_name(), diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index cb8311de85d1..0ff11ce087b5 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -14,7 +14,7 @@ use crate::{ pub async fn user_connect_account( state: web::Data, http_req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::UserConnectAccount; let req_payload = json_payload.into_inner(); diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index a7e54e9afdd6..4104ea972fb7 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -441,7 +441,7 @@ where T: serde::de::DeserializeOwned, A: AppStateInfo + Sync, { - let token = get_jwt(headers)?; + let token = get_jwt_from_authorization_header(headers)?; let payload = decode_jwt(token, state).await?; Ok(payload) @@ -462,8 +462,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - let token = get_jwt(request_headers)?; - let payload = decode_jwt::(token, state).await?; + let payload = + parse_jwt_payload::(request_headers, state) + .await?; let key_store = state .store() .get_merchant_key_store_by_merchant_id( @@ -667,7 +668,7 @@ pub fn get_header_value_by_key(key: String, headers: &HeaderMap) -> RouterResult .transpose() } -pub fn get_jwt(headers: &HeaderMap) -> RouterResult<&str> { +pub fn get_jwt_from_authorization_header(headers: &HeaderMap) -> RouterResult<&str> { headers .get(crate::headers::AUTHORIZATION) .get_required_value(crate::headers::AUTHORIZATION)? diff --git a/crates/router/src/services/jwt.rs b/crates/router/src/services/jwt.rs index f27d5246113d..b69a21583919 100644 --- a/crates/router/src/services/jwt.rs +++ b/crates/router/src/services/jwt.rs @@ -27,7 +27,7 @@ where let jwt_secret = authentication::get_jwt_secret( &settings.secrets, #[cfg(feature = "kms")] - external_services_oss::kms::get_kms_client(&settings.kms).await, + external_services::kms::get_kms_client(&settings.kms).await, ) .await .change_context(UserErrors::InternalServerError) diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 063eb80bd08f..c053b0f15448 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -206,8 +206,8 @@ impl NewUserOrganization { } } -impl From for NewUserOrganization { - fn from(_value: user_api::SignInRequest) -> Self { +impl From for NewUserOrganization { + fn from(_value: user_api::ConnectAccountRequest) -> Self { let new_organization = api_org::OrganizationNew::new(None); let db_organization = ForeignFrom::foreign_from(new_organization); Self(db_organization) @@ -287,10 +287,10 @@ impl NewUserMerchant { } } -impl TryFrom for NewUserMerchant { +impl TryFrom for NewUserMerchant { type Error = error_stack::Report; - fn try_from(value: user_api::SignInRequest) -> UserResult { + fn try_from(value: user_api::ConnectAccountRequest) -> UserResult { let merchant_id = format!("merchant_{}", common_utils::date_time::now_unix_timestamp()); let new_organization = NewUserOrganization::from(value); @@ -406,10 +406,10 @@ impl TryFrom for storage_user::UserNew { } } -impl TryFrom for NewUser { +impl TryFrom for NewUser { type Error = error_stack::Report; - fn try_from(value: user_api::SignInRequest) -> UserResult { + fn try_from(value: user_api::ConnectAccountRequest) -> UserResult { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.email.clone().try_into()?; let name = UserName::try_from(value.email.clone())?; diff --git a/crates/router_derive/Cargo.toml b/crates/router_derive/Cargo.toml index b4e60a8c2a33..4d82afc38eb6 100644 --- a/crates/router_derive/Cargo.toml +++ b/crates/router_derive/Cargo.toml @@ -17,9 +17,3 @@ indexmap = "2.0.0" proc-macro2 = "1.0.56" quote = "1.0.26" syn = { version = "1.0.109", features = ["full", "extra-traits"] } # the full feature does not seem to encompass all the features - -[dev-dependencies] -diesel = { version = "2.1.0", features = ["postgres"] } -serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" -strum = { version = "0.24.1", features = ["derive"] } diff --git a/crates/router_env/Cargo.toml b/crates/router_env/Cargo.toml index 266baf0e3863..dea11e4ef7fe 100644 --- a/crates/router_env/Cargo.toml +++ b/crates/router_env/Cargo.toml @@ -30,9 +30,6 @@ tracing-opentelemetry = { version = "0.19.0" } tracing-subscriber = { version = "0.3.17", default-features = true, features = ["env-filter", "json", "registry"] } vergen = { version = "8.2.1", optional = true, features = ["cargo", "git", "git2", "rustc"] } -[dev-dependencies] -tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } - [build-dependencies] cargo_metadata = "0.15.4" vergen = { version = "8.2.1", features = ["cargo", "git", "git2", "rustc"], optional = true } From a2514aa587072fec0000916d1e0f3c8615b9954e Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 17:32:29 +0530 Subject: [PATCH 4/7] fix: typos --- .typos.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.typos.toml b/.typos.toml index 0d6e6fd8e38c..1ac38a005c9e 100644 --- a/.typos.toml +++ b/.typos.toml @@ -40,4 +40,5 @@ afe = "afe" # Commit id extend-exclude = [ "config/redis.conf", # `typos` also checked "AKE" in the file, which is present as a quoted string "openapi/open_api_spec.yaml", # no longer updated + "crates/router/src/utils/user/blocker_emails.txt", # this file contains various email domains ] From 5d8d0fa97ec4c199c7dc5f4108d3a026e7fae24c Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 17:47:37 +0530 Subject: [PATCH 5/7] fix: toml dependencies --- crates/cards/Cargo.toml | 3 +++ crates/common_enums/Cargo.toml | 3 +++ crates/common_utils/Cargo.toml | 5 +++++ crates/euclid/Cargo.toml | 3 +++ crates/kgraph_utils/Cargo.toml | 3 +++ crates/masking/Cargo.toml | 3 +++ crates/redis_interface/Cargo.toml | 3 +++ crates/router/Cargo.toml | 15 +++++++++++++++ crates/router_derive/Cargo.toml | 6 ++++++ crates/router_env/Cargo.toml | 3 +++ 10 files changed, 47 insertions(+) diff --git a/crates/cards/Cargo.toml b/crates/cards/Cargo.toml index 4d38d3118f82..00445936e3d2 100644 --- a/crates/cards/Cargo.toml +++ b/crates/cards/Cargo.toml @@ -19,3 +19,6 @@ time = "0.3.21" # First party crates common_utils = { version = "0.1.0", path = "../common_utils" } masking = { version = "0.1.0", path = "../masking" } + +[dev-dependencies] +serde_json = "1.0.96" diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index b33d9d61f56d..db37d27ab0f1 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -20,3 +20,6 @@ utoipa = { version = "3.3.0", features = ["preserve_order"] } # First party crates router_derive = { version = "0.1.0", path = "../router_derive" } + +[dev-dependencies] +serde_json = "1.0.96" diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index 4438350a44e2..62bd747da1b0 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -46,3 +46,8 @@ common_enums = { version = "0.1.0", path = "../common_enums" } [target.'cfg(not(target_os = "windows"))'.dependencies] signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true } + +[dev-dependencies] +fake = "2.6.1" +proptest = "1.2.0" +test-case = "3.1.0" diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index 6cd28115f5dc..f0e24b1ff63c 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -29,6 +29,9 @@ connector_choice_mca_id = [] dummy_connector = [] backwards_compatibility = ["connector_choice_bcompat"] +[dev-dependencies] +criterion = "0.5" + [[bench]] name = "backends" harness = false diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index a1cb25e935b5..fa90b3974c20 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -19,6 +19,9 @@ serde = "1.0.163" serde_json = "1.0.96" thiserror = "1.0.43" +[dev-dependencies] +criterion = "0.5" + [[bench]] name = "evaluation" harness = false diff --git a/crates/masking/Cargo.toml b/crates/masking/Cargo.toml index 67ae3448ac1d..21d791642895 100644 --- a/crates/masking/Cargo.toml +++ b/crates/masking/Cargo.toml @@ -22,3 +22,6 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = "1.0.96" subtle = "=2.4.1" zeroize = { version = "1.6", default-features = false } + +[dev-dependencies] +serde_json = "1.0.96" diff --git a/crates/redis_interface/Cargo.toml b/crates/redis_interface/Cargo.toml index 633233e21829..8066787dcae2 100644 --- a/crates/redis_interface/Cargo.toml +++ b/crates/redis_interface/Cargo.toml @@ -18,3 +18,6 @@ tokio = "1.28.2" # First party crates common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext"] } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } + +[dev-dependencies] +tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 253062f9e8f2..0d7b89dd8d4f 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -121,6 +121,21 @@ signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } [build-dependencies] router_env = { version = "0.1.0", path = "../router_env", default-features = false } +[dev-dependencies] +actix-http = "3.3.1" +awc = { version = "3.1.1", features = ["rustls"] } +derive_deref = "1.1.1" +rand = "0.8.5" +serial_test = "2.0.0" +thirtyfour = "0.31.0" +time = { version = "0.3.21", features = ["macros"] } +tokio = "1.28.2" +toml = "0.7.4" +wiremock = "0.5" + +# First party dev-dependencies +test_utils = { version = "0.1.0", path = "../test_utils" } + [[bin]] name = "router" path = "src/bin/router.rs" diff --git a/crates/router_derive/Cargo.toml b/crates/router_derive/Cargo.toml index 4d82afc38eb6..b4e60a8c2a33 100644 --- a/crates/router_derive/Cargo.toml +++ b/crates/router_derive/Cargo.toml @@ -17,3 +17,9 @@ indexmap = "2.0.0" proc-macro2 = "1.0.56" quote = "1.0.26" syn = { version = "1.0.109", features = ["full", "extra-traits"] } # the full feature does not seem to encompass all the features + +[dev-dependencies] +diesel = { version = "2.1.0", features = ["postgres"] } +serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.96" +strum = { version = "0.24.1", features = ["derive"] } diff --git a/crates/router_env/Cargo.toml b/crates/router_env/Cargo.toml index dea11e4ef7fe..266baf0e3863 100644 --- a/crates/router_env/Cargo.toml +++ b/crates/router_env/Cargo.toml @@ -30,6 +30,9 @@ tracing-opentelemetry = { version = "0.19.0" } tracing-subscriber = { version = "0.3.17", default-features = true, features = ["env-filter", "json", "registry"] } vergen = { version = "8.2.1", optional = true, features = ["cargo", "git", "git2", "rustc"] } +[dev-dependencies] +tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } + [build-dependencies] cargo_metadata = "0.15.4" vergen = { version = "8.2.1", features = ["cargo", "git", "git2", "rustc"], optional = true } From 4cff8eab566986df5b2ce8724f36d180ac2952b9 Mon Sep 17 00:00:00 2001 From: Mani Chandra Dulam Date: Fri, 10 Nov 2023 19:38:14 +0530 Subject: [PATCH 6/7] fix: hack --- crates/router/src/consts.rs | 1 + crates/router/src/core.rs | 1 + crates/router/src/core/errors.rs | 2 ++ crates/router/src/routes.rs | 1 + crates/router/src/routes/app.rs | 4 ++-- crates/router/src/services.rs | 1 + crates/router/src/services/authentication.rs | 6 ++++-- crates/router/src/types/domain.rs | 2 ++ crates/router/src/utils.rs | 1 + 9 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 4e3a1fd050fe..410e3c1113b1 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "olap")] pub mod user; // ID generation diff --git a/crates/router/src/core.rs b/crates/router/src/core.rs index 72dfc28746ce..b7023fe5ae46 100644 --- a/crates/router/src/core.rs +++ b/crates/router/src/core.rs @@ -18,6 +18,7 @@ pub mod payments; pub mod payouts; pub mod refunds; pub mod routing; +#[cfg(feature = "olap")] pub mod user; pub mod utils; #[cfg(all(feature = "olap", feature = "kms"))] diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index 438f55ce8ca0..810c079987eb 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -2,6 +2,7 @@ pub mod api_error_response; pub mod customers_error_response; pub mod error_handlers; pub mod transformers; +#[cfg(feature = "olap")] pub mod user; pub mod utils; @@ -14,6 +15,7 @@ use diesel_models::errors as storage_errors; pub use redis_interface::errors::RedisError; use scheduler::errors as sch_errors; use storage_impl::errors as storage_impl_errors; +#[cfg(feature = "olap")] pub use user::*; pub use self::{ diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 659467965cb4..745433c2074b 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -23,6 +23,7 @@ pub mod payouts; pub mod refunds; #[cfg(feature = "olap")] pub mod routing; +#[cfg(feature = "olap")] pub mod user; #[cfg(all(feature = "olap", feature = "kms"))] pub mod verification; diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 8929984c3728..c34c542d1b6c 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -19,8 +19,8 @@ use super::routing as cloud_routing; #[cfg(all(feature = "olap", feature = "kms"))] use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(feature = "olap")] -use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*}; -use super::{cache::*, health::*, payment_link::*, user::*}; +use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*, user::*}; +use super::{cache::*, health::*, payment_link::*}; #[cfg(any(feature = "olap", feature = "oltp"))] use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; #[cfg(feature = "oltp")] diff --git a/crates/router/src/services.rs b/crates/router/src/services.rs index ca4bf67e0aff..21f33f0fa0b8 100644 --- a/crates/router/src/services.rs +++ b/crates/router/src/services.rs @@ -1,6 +1,7 @@ pub mod api; pub mod authentication; pub mod encryption; +#[cfg(feature = "olap")] pub mod jwt; pub mod logger; diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 32ac8b793158..835f2a0fadbb 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "olap")] +use super::jwt; +#[cfg(feature = "olap")] +use crate::consts; use actix_web::http::header::HeaderMap; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; @@ -9,10 +13,8 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::{PeekInterface, StrongSecret}; use serde::Serialize; -use super::jwt; use crate::{ configs::settings, - consts, core::{ api_keys, errors::{self, utils::StorageErrorExt, RouterResult}, diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index d1d7500cc897..c93f96eaf09e 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -5,6 +5,7 @@ mod merchant_account; mod merchant_connector_account; mod merchant_key_store; pub mod types; +#[cfg(feature = "olap")] pub mod user; pub use address::*; @@ -12,4 +13,5 @@ pub use customer::*; pub use merchant_account::*; pub use merchant_connector_account::*; pub use merchant_key_store::*; +#[cfg(feature = "olap")] pub use user::*; diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index ba792e317006..aadb714e8ce2 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -1,6 +1,7 @@ pub mod custom_serde; pub mod db_utils; pub mod ext_traits; +#[cfg(feature = "olap")] pub mod user; #[cfg(feature = "kv_store")] From fe12374ea22e76999d66ba54a9fd4947ce364fb1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 14:11:05 +0000 Subject: [PATCH 7/7] chore: run formatter --- crates/router/src/services/authentication.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 835f2a0fadbb..da4dec2eec8a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1,7 +1,3 @@ -#[cfg(feature = "olap")] -use super::jwt; -#[cfg(feature = "olap")] -use crate::consts; use actix_web::http::header::HeaderMap; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; @@ -13,6 +9,10 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::{PeekInterface, StrongSecret}; use serde::Serialize; +#[cfg(feature = "olap")] +use super::jwt; +#[cfg(feature = "olap")] +use crate::consts; use crate::{ configs::settings, core::{