diff --git a/server/src/http/feature_refresher.rs b/server/src/http/feature_refresher.rs index b35fc1a6..7aaf6d64 100644 --- a/server/src/http/feature_refresher.rs +++ b/server/src/http/feature_refresher.rs @@ -22,6 +22,17 @@ use crate::{ types::{ClientFeaturesRequest, ClientFeaturesResponse, EdgeToken, TokenRefresh}, }; +fn frontend_token_is_covered_by_tokens( + frontend_token: &EdgeToken, + tokens_to_refresh: Arc>, +) -> bool { + tokens_to_refresh.iter().any(|client_token| { + client_token + .token + .same_environment_and_broader_or_equal_project_access(frontend_token) + }) +} + #[derive(Clone)] pub struct FeatureRefresher { pub unleash_client: Arc, @@ -119,9 +130,7 @@ impl FeatureRefresher { &self, frontend_token: &EdgeToken, ) -> bool { - self.tokens_to_refresh.iter().any(|client_token| { - frontend_token.same_environment_and_broader_or_equal_project_access(&client_token.token) - }) + frontend_token_is_covered_by_tokens(frontend_token, self.tokens_to_refresh.clone()) } pub(crate) async fn register_and_hydrate_token( @@ -364,7 +373,7 @@ mod tests { types::{EdgeToken, TokenRefresh}, }; - use super::FeatureRefresher; + use super::{frontend_token_is_covered_by_tokens, FeatureRefresher}; impl PartialEq for TokenRefresh { fn eq(&self, other: &Self) -> bool { @@ -998,4 +1007,35 @@ mod tests { 7 ); } + + #[test] + fn front_end_token_is_properly_covered_by_current_tokens() { + let fe_token = EdgeToken { + projects: vec!["a".into(), "b".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let wildcard_token = EdgeToken { + projects: vec!["*".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let current_tokens = DashMap::new(); + let token_refresh = TokenRefresh { + token: wildcard_token.clone(), + etag: None, + last_refreshed: None, + last_check: None, + }; + + current_tokens.insert(wildcard_token.token, token_refresh); + + let current_tokens_arc = Arc::new(current_tokens); + assert!(frontend_token_is_covered_by_tokens( + &fe_token, + current_tokens_arc + )); + } } diff --git a/server/src/tokens.rs b/server/src/tokens.rs index 41ab9fef..30d00f90 100644 --- a/server/src/tokens.rs +++ b/server/src/tokens.rs @@ -194,7 +194,7 @@ impl EdgeToken { mod tests { use crate::{ tokens::simplify, - types::{EdgeToken, TokenRefresh}, + types::{EdgeToken, TokenRefresh, TokenType}, }; use ulid::Ulid; @@ -311,4 +311,80 @@ mod tests { assert_eq!(actual, expected); } + + #[test] + fn test_single_project_token_is_covered_by_wildcard() { + let self_token = EdgeToken { + projects: vec!["*".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let other_token = EdgeToken { + projects: vec!["A".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let is_covered = + self_token.same_environment_and_broader_or_equal_project_access(&other_token); + assert!(is_covered); + } + + #[test] + fn test_multi_project_token_is_covered_by_wildcard() { + let self_token = EdgeToken { + projects: vec!["*".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let other_token = EdgeToken { + projects: vec!["A".into(), "B".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let is_covered = + self_token.same_environment_and_broader_or_equal_project_access(&other_token); + assert!(is_covered); + } + + #[test] + fn test_multi_project_tokens_cover_each_other() { + let self_token = EdgeToken { + projects: vec!["A".into(), "B".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let fe_token = EdgeToken { + projects: vec!["A".into()], + environment: Some("development".into()), + token_type: Some(TokenType::Frontend), + ..Default::default() + }; + + let is_covered = self_token.same_environment_and_broader_or_equal_project_access(&fe_token); + assert!(is_covered); + } + + #[test] + fn test_multi_project_tokens_do_not_cover_each_other_when_they_do_not_overlap() { + let self_token = EdgeToken { + projects: vec!["A".into(), "B".into()], + environment: Some("development".into()), + ..Default::default() + }; + + let fe_token = EdgeToken { + projects: vec!["A".into(), "C".into()], + environment: Some("development".into()), + token_type: Some(TokenType::Frontend), + ..Default::default() + }; + + let is_covered = self_token.same_environment_and_broader_or_equal_project_access(&fe_token); + assert!(!is_covered); + } }