Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): Add new JWT authentication variants and use them #2835

Merged
merged 9 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
]
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/api_models/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
14 changes: 14 additions & 0 deletions crates/api_models/src/events/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::user::{ConnectAccountRequest, ConnectAccountResponse};

impl ApiEventMetric for ConnectAccountResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::User {
merchant_id: self.merchant_id.clone(),
user_id: self.user_id.clone(),
})
}
}

impl ApiEventMetric for ConnectAccountRequest {}
1 change: 1 addition & 0 deletions crates/api_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ pub mod payments;
pub mod payouts;
pub mod refunds;
pub mod routing;
pub mod user;
pub mod verifications;
pub mod webhooks;
21 changes: 21 additions & 0 deletions crates/api_models/src/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use common_utils::pii;
use masking::Secret;

#[derive(serde::Deserialize, Debug, Clone, serde::Serialize)]
pub struct ConnectAccountRequest {
pub email: pii::Email,
pub password: Secret<String>,
}

#[derive(serde::Serialize, Debug, Clone)]
pub struct ConnectAccountResponse {
pub token: Secret<String>,
pub merchant_id: String,
pub name: Secret<String>,
pub email: pii::Email,
pub verification_days_left: Option<i64>,
pub user_role: String,
//this field is added for audit/debug reasons
#[serde(skip_serializing)]
pub user_id: String,
}
2 changes: 1 addition & 1 deletion crates/data_models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] }
3 changes: 3 additions & 0 deletions crates/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -89,10 +90,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"}
Expand Down
6 changes: 6 additions & 0 deletions crates/router/src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "olap")]
pub mod user;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is going to be small enough (only a few lines), you might as well have the module defined inline instead of keeping it in a separate file.

pub mod user {
    #[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";
}

Not a necessary change however.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we add new APIs and new roles, this file is going to become big.


// ID generation
pub(crate) const ID_LENGTH: usize = 20;
pub(crate) const MAX_ID_LENGTH: usize = 64;
Expand Down Expand Up @@ -52,3 +55,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
8 changes: 8 additions & 0 deletions crates/router/src/consts/user.rs
Original file line number Diff line number Diff line change
@@ -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";
2 changes: 2 additions & 0 deletions crates/router/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ 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"))]
pub mod verification;
Expand Down
4 changes: 4 additions & 0 deletions crates/router/src/core/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ 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;

use std::fmt::Display;
Expand All @@ -13,6 +15,8 @@ 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::{
api_error_response::ApiErrorResponse,
Expand Down
78 changes: 78 additions & 0 deletions crates/router/src/core/errors/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use common_utils::errors::CustomResult;

use crate::services::ApplicationResponse;

pub type UserResult<T> = CustomResult<T, UserErrors>;
pub type UserResponse<T> = CustomResult<ApplicationResponse<T>, 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<api_models::errors::types::ApiErrorResponse> 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,
)),
}
}
}
Loading
Loading