From d885b55cf5916a76cb9a66f596558a042f04eee9 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Fri, 19 Jul 2024 13:33:52 +0200 Subject: [PATCH 1/3] fix: trim tokens when parsing --- server/src/tokens.rs | 71 +++++++++++++++++++------------------------- server/src/types.rs | 46 ++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/server/src/tokens.rs b/server/src/tokens.rs index a84a264e..eb829edc 100644 --- a/server/src/tokens.rs +++ b/server/src/tokens.rs @@ -1,12 +1,13 @@ -use actix_web::dev::Payload; -use actix_web::http::header::HeaderValue; -use actix_web::web::Data; -use actix_web::FromRequest; -use actix_web::HttpRequest; use std::collections::HashSet; use std::future::{ready, Ready}; use std::str::FromStr; +use actix_web::dev::Payload; +use actix_web::FromRequest; +use actix_web::http::header::HeaderValue; +use actix_web::HttpRequest; +use actix_web::web::Data; + use crate::cli::EdgeMode; use crate::cli::TokenHeader; use crate::error::EdgeError; @@ -167,45 +168,13 @@ impl FromStr for EdgeToken { type Err = EdgeError; fn from_str(s: &str) -> Result { - if s.contains(':') && s.contains('.') { - let token_parts: Vec = s.split(':').take(2).map(|s| s.to_string()).collect(); - let token_projects = if let Some(projects) = token_parts.first() { - if projects == "[]" { - vec![] - } else { - vec![projects.clone()] - } - } else { - return Err(EdgeError::TokenParseError(s.into())); - }; - if let Some(env_and_key) = token_parts.get(1) { - let e_a_k: Vec = env_and_key - .split('.') - .take(2) - .map(|s| s.to_string()) - .collect(); - if e_a_k.len() != 2 { - return Err(EdgeError::TokenParseError(s.into())); - } - Ok(EdgeToken { - environment: e_a_k.first().cloned(), - projects: token_projects, - token_type: None, - token: s.into(), - status: TokenValidationStatus::Unknown, - }) - } else { - Err(EdgeError::TokenParseError(s.into())) - } - } else { - Err(EdgeError::TokenParseError(s.into())) - } + EdgeToken::from_trimmed_str(s.trim()) } } impl EdgeToken { pub fn offline_token(s: &str) -> Self { - let mut token = EdgeToken::try_from(s.to_string()) + let mut token = EdgeToken::try_from(s.trim().to_string()) .ok() .unwrap_or_else(|| EdgeToken::no_project_or_environment(s)); token.status = TokenValidationStatus::Validated; @@ -214,11 +183,14 @@ impl EdgeToken { } #[cfg(test)] mod tests { + use std::str::FromStr; + + use ulid::Ulid; + use crate::{ tokens::simplify, types::{EdgeToken, TokenRefresh, TokenType}, }; - use ulid::Ulid; fn test_token(token: Option<&str>, env: Option<&str>, projects: Vec<&str>) -> EdgeToken { EdgeToken { @@ -428,4 +400,23 @@ mod tests { let is_covered = self_token.same_environment_and_broader_or_equal_project_access(&fe_token); assert!(!is_covered); } + + #[test] + fn leading_or_trailing_whitespace_gets_trimmed_away_when_constructing_token() { + let some_token = "*:development.somesecretstring"; + let some_token_with_leading_whitespace = " *:development.somesecretstring"; + let some_token_with_trailing_whitespace = "*:development.somesecretstring "; + let some_token_with_leading_and_trailing_whitespace = + " *:development.somesecretstring "; + let token = EdgeToken::from_str(some_token).expect("Could not parse token"); + let token1 = + EdgeToken::from_str(some_token_with_leading_whitespace).expect("Could not parse token"); + let token2 = EdgeToken::from_str(some_token_with_trailing_whitespace) + .expect("Could not parse token"); + let token3 = EdgeToken::from_str(some_token_with_leading_and_trailing_whitespace) + .expect("Could not parse token"); + assert_eq!(token, token1); + assert_eq!(token1, token2); + assert_eq!(token2, token3); + } } diff --git a/server/src/types.rs b/server/src/types.rs index e68c5fda..be74ab9e 100644 --- a/server/src/types.rs +++ b/server/src/types.rs @@ -1,13 +1,13 @@ -use std::cmp::min; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; -use std::net::IpAddr; -use std::sync::Arc; use std::{ collections::HashMap, hash::{Hash, Hasher}, str::FromStr, }; +use std::cmp::min; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::net::IpAddr; +use std::sync::Arc; use actix_web::{http::header::EntityTag, web::Json}; use async_trait::async_trait; @@ -215,6 +215,42 @@ impl EdgeToken { }) .unwrap() } + + pub fn from_trimmed_str(s: &str) -> Result { + if s.contains(':') && s.contains('.') { + let token_parts: Vec = s.split(':').take(2).map(|s| s.to_string()).collect(); + let token_projects = if let Some(projects) = token_parts.first() { + if projects == "[]" { + vec![] + } else { + vec![projects.clone()] + } + } else { + return Err(EdgeError::TokenParseError(s.into())); + }; + if let Some(env_and_key) = token_parts.get(1) { + let e_a_k: Vec = env_and_key + .split('.') + .take(2) + .map(|s| s.to_string()) + .collect(); + if e_a_k.len() != 2 { + return Err(EdgeError::TokenParseError(s.into())); + } + Ok(EdgeToken { + environment: e_a_k.first().cloned(), + projects: token_projects, + token_type: None, + token: s.into(), + status: TokenValidationStatus::Unknown, + }) + } else { + Err(EdgeError::TokenParseError(s.into())) + } + } else { + Err(EdgeError::TokenParseError(s.into())) + } + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, utoipa::ToSchema)] From c2da8f131d1eaf5aea01269d667eb93510f68c2e Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Fri, 19 Jul 2024 13:39:33 +0200 Subject: [PATCH 2/3] Removed unnecessary call to trim. FromStr now calls from_trimmed_str --- server/src/tokens.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/tokens.rs b/server/src/tokens.rs index eb829edc..be92dab1 100644 --- a/server/src/tokens.rs +++ b/server/src/tokens.rs @@ -174,7 +174,7 @@ impl FromStr for EdgeToken { impl EdgeToken { pub fn offline_token(s: &str) -> Self { - let mut token = EdgeToken::try_from(s.trim().to_string()) + let mut token = EdgeToken::try_from(s.to_string()) .ok() .unwrap_or_else(|| EdgeToken::no_project_or_environment(s)); token.status = TokenValidationStatus::Validated; From 72b0aef896f2f3237e9d397b848dc7b348adc1cd Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Fri, 19 Jul 2024 14:16:09 +0200 Subject: [PATCH 3/3] Move from_trimmed_str into tokens module --- server/src/client_api.rs | 4 +-- server/src/frontend_api.rs | 10 +++--- server/src/http/feature_refresher.rs | 4 +-- server/src/tokens.rs | 39 +++++++++++++++++++++-- server/src/types.rs | 46 +++------------------------- 5 files changed, 51 insertions(+), 52 deletions(-) diff --git a/server/src/client_api.rs b/server/src/client_api.rs index 72896ded..1f88dd6a 100644 --- a/server/src/client_api.rs +++ b/server/src/client_api.rs @@ -428,8 +428,8 @@ mod tests { platform_name: None, platform_version: None, sdk_version: None, - yggdrasil_version: None - } + yggdrasil_version: None, + }, }; assert_eq!(found_metric.yes, expected.yes); diff --git a/server/src/frontend_api.rs b/server/src/frontend_api.rs index 63fe6ec2..6c9abb1e 100644 --- a/server/src/frontend_api.rs +++ b/server/src/frontend_api.rs @@ -487,7 +487,7 @@ pub fn evaluate_feature( payload: r.variant.payload, }, impression_data: r.impression_data, - impressionData: r.impression_data + impressionData: r.impression_data, }) .ok_or_else(|| EdgeError::FeatureNotFound(feature_name.clone())) } @@ -736,7 +736,7 @@ pub fn frontend_from_yggdrasil( payload: resolved.variant.payload.clone(), }, impression_data: resolved.impression_data, - impressionData: resolved.impression_data + impressionData: resolved.impression_data, }) .collect::>(); FrontendResult { toggles } @@ -1007,7 +1007,7 @@ mod tests { payload: None, }, impression_data: false, - impressionData: false + impressionData: false, }], }; @@ -1065,7 +1065,7 @@ mod tests { payload: None, }, impression_data: false, - impressionData: false + impressionData: false, }], }; @@ -1121,7 +1121,7 @@ mod tests { payload: None, }, impression_data: false, - impressionData: false + impressionData: false, }], }; diff --git a/server/src/http/feature_refresher.rs b/server/src/http/feature_refresher.rs index b2a1bffe..7dc45378 100644 --- a/server/src/http/feature_refresher.rs +++ b/server/src/http/feature_refresher.rs @@ -130,8 +130,8 @@ fn client_application_from_token(token: EdgeToken, refresh_interval: i64) -> Cli platform_name: None, platform_version: None, sdk_version: Some(format!("unleash-edge:{}", build::PKG_VERSION)), - yggdrasil_version: None - } + yggdrasil_version: None, + }, } } diff --git a/server/src/tokens.rs b/server/src/tokens.rs index be92dab1..4816471f 100644 --- a/server/src/tokens.rs +++ b/server/src/tokens.rs @@ -3,10 +3,10 @@ use std::future::{ready, Ready}; use std::str::FromStr; use actix_web::dev::Payload; -use actix_web::FromRequest; use actix_web::http::header::HeaderValue; -use actix_web::HttpRequest; use actix_web::web::Data; +use actix_web::FromRequest; +use actix_web::HttpRequest; use crate::cli::EdgeMode; use crate::cli::TokenHeader; @@ -180,6 +180,41 @@ impl EdgeToken { token.status = TokenValidationStatus::Validated; token } + pub fn from_trimmed_str(s: &str) -> Result { + if s.contains(':') && s.contains('.') { + let token_parts: Vec = s.split(':').take(2).map(|s| s.to_string()).collect(); + let token_projects = if let Some(projects) = token_parts.first() { + if projects == "[]" { + vec![] + } else { + vec![projects.clone()] + } + } else { + return Err(EdgeError::TokenParseError(s.into())); + }; + if let Some(env_and_key) = token_parts.get(1) { + let e_a_k: Vec = env_and_key + .split('.') + .take(2) + .map(|s| s.to_string()) + .collect(); + if e_a_k.len() != 2 { + return Err(EdgeError::TokenParseError(s.into())); + } + Ok(EdgeToken { + environment: e_a_k.first().cloned(), + projects: token_projects, + token_type: None, + token: s.into(), + status: TokenValidationStatus::Unknown, + }) + } else { + Err(EdgeError::TokenParseError(s.into())) + } + } else { + Err(EdgeError::TokenParseError(s.into())) + } + } } #[cfg(test)] mod tests { diff --git a/server/src/types.rs b/server/src/types.rs index be74ab9e..e68c5fda 100644 --- a/server/src/types.rs +++ b/server/src/types.rs @@ -1,13 +1,13 @@ -use std::{ - collections::HashMap, - hash::{Hash, Hasher}, - str::FromStr, -}; use std::cmp::min; use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::net::IpAddr; use std::sync::Arc; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, + str::FromStr, +}; use actix_web::{http::header::EntityTag, web::Json}; use async_trait::async_trait; @@ -215,42 +215,6 @@ impl EdgeToken { }) .unwrap() } - - pub fn from_trimmed_str(s: &str) -> Result { - if s.contains(':') && s.contains('.') { - let token_parts: Vec = s.split(':').take(2).map(|s| s.to_string()).collect(); - let token_projects = if let Some(projects) = token_parts.first() { - if projects == "[]" { - vec![] - } else { - vec![projects.clone()] - } - } else { - return Err(EdgeError::TokenParseError(s.into())); - }; - if let Some(env_and_key) = token_parts.get(1) { - let e_a_k: Vec = env_and_key - .split('.') - .take(2) - .map(|s| s.to_string()) - .collect(); - if e_a_k.len() != 2 { - return Err(EdgeError::TokenParseError(s.into())); - } - Ok(EdgeToken { - environment: e_a_k.first().cloned(), - projects: token_projects, - token_type: None, - token: s.into(), - status: TokenValidationStatus::Unknown, - }) - } else { - Err(EdgeError::TokenParseError(s.into())) - } - } else { - Err(EdgeError::TokenParseError(s.into())) - } - } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, utoipa::ToSchema)]