From 9f012016edc2319a36f58f6e132d069f14ceef18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Thu, 4 Jul 2024 09:13:52 +0100 Subject: [PATCH] feat: strict behavior (#474) --- server/src/builder.rs | 58 +++++++ server/src/cli.rs | 4 + server/src/client_api.rs | 79 ++++++++- server/src/error.rs | 6 + server/src/http/feature_refresher.rs | 161 +++++++++--------- server/src/internal_backstage.rs | 69 +++++++- .../client_token_from_frontend_token.rs | 14 +- 7 files changed, 294 insertions(+), 97 deletions(-) diff --git a/server/src/builder.rs b/server/src/builder.rs index 95fef7ed..d1986a94 100644 --- a/server/src/builder.rs +++ b/server/src/builder.rs @@ -99,6 +99,10 @@ pub(crate) fn build_offline_mode( } fn build_offline(offline_args: OfflineArgs) -> EdgeResult { + if offline_args.tokens.is_empty() { + return Err(EdgeError::NoTokens("No tokens provided. Tokens must be specified when running in offline mode".into())); + } + if let Some(bootstrap) = offline_args.bootstrap_file { let file = File::open(bootstrap.clone()).map_err(|_| EdgeError::NoFeaturesFile)?; @@ -151,6 +155,10 @@ async fn get_data_source(args: &EdgeArgs) -> Option> { } async fn build_edge(args: &EdgeArgs) -> EdgeResult { + if args.strict && args.tokens.is_empty() { + return Err(EdgeError::NoTokens("No tokens provided. Tokens must be specified when running with strict behavior".into())); + } + let (token_cache, feature_cache, engine_cache) = build_caches(); let persistence = get_data_source(args).await; @@ -183,6 +191,7 @@ async fn build_edge(args: &EdgeArgs) -> EdgeResult { engine_cache.clone(), Duration::seconds(args.features_refresh_interval_seconds.try_into().unwrap()), persistence.clone(), + args.strict, )); let _ = token_validator.register_tokens(args.tokens.clone()).await; @@ -223,3 +232,52 @@ pub async fn build_caches_and_refreshers(args: CliArgs) -> EdgeResult _ => unreachable!(), } } + +#[cfg(test)] +mod tests { + use crate::{builder::{build_edge, build_offline}, cli::{EdgeArgs, OfflineArgs, TokenHeader}}; + + #[test] + fn should_fail_with_empty_tokens_when_offline_mode() { + let args = OfflineArgs { + bootstrap_file: None, + tokens: vec![], + reload_interval: Default::default() + }; + + let result = build_offline(args); + assert!(result.is_err()); + assert_eq!(result + .err() + .unwrap() + .to_string(), "No tokens provided. Tokens must be specified when running in offline mode"); + } + + #[tokio::test] + async fn should_fail_with_empty_tokens_when_strict() { + let args = EdgeArgs { + upstream_url: Default::default(), + backup_folder: None, + metrics_interval_seconds: Default::default(), + features_refresh_interval_seconds: Default::default(), + strict: true, + tokens: vec![], + redis: None, + client_identity: Default::default(), + skip_ssl_verification: false, + upstream_request_timeout: Default::default(), + upstream_socket_timeout: Default::default(), + custom_client_headers: Default::default(), + token_header: TokenHeader { token_header: "Authorization".into() }, + upstream_certificate_file: Default::default(), + token_revalidation_interval_seconds: Default::default(), + }; + + let result = build_edge(&args).await; + assert!(result.is_err()); + assert_eq!(result + .err() + .unwrap() + .to_string(), "No tokens provided. Tokens must be specified when running with strict behavior"); + } + } \ No newline at end of file diff --git a/server/src/cli.rs b/server/src/cli.rs index b89dd94e..48ec42d4 100644 --- a/server/src/cli.rs +++ b/server/src/cli.rs @@ -174,6 +174,10 @@ pub struct EdgeArgs { /// Token header to use for both edge authorization and communication with the upstream server. #[clap(long, env, global = true, default_value = "Authorization")] pub token_header: TokenHeader, + + /// If set to true, Edge starts with strict behavior. Strict behavior means that Edge will refuse tokens outside of the scope of the startup tokens + #[clap(long, env, default_value_t = false)] + pub strict: bool, } pub fn string_to_header_tuple(s: &str) -> Result<(String, String), String> { diff --git a/server/src/client_api.rs b/server/src/client_api.rs index 9c91107f..747c9946 100644 --- a/server/src/client_api.rs +++ b/server/src/client_api.rs @@ -918,7 +918,7 @@ mod tests { } #[tokio::test] - async fn calling_client_features_endpoint_with_new_token_hydrates_from_upstream() { + async fn calling_client_features_endpoint_with_new_token_hydrates_from_upstream_when_dynamic() { let upstream_features_cache: Arc> = Arc::new(DashMap::default()); let upstream_token_cache: Arc> = Arc::new(DashMap::default()); @@ -949,6 +949,7 @@ mod tests { engine_cache: engine_cache.clone(), refresh_interval: Duration::seconds(6000), persistence: None, + strict: false, }); let token_validator = Arc::new(TokenValidator { unleash_client: unleash_client.clone(), @@ -977,6 +978,65 @@ mod tests { assert_eq!(res.status(), StatusCode::OK); } + #[tokio::test] + async fn calling_client_features_endpoint_with_new_token_does_not_hydrate_when_strict() { + let upstream_features_cache: Arc> = + Arc::new(DashMap::default()); + let upstream_token_cache: Arc> = Arc::new(DashMap::default()); + let upstream_engine_cache: Arc> = Arc::new(DashMap::default()); + let server = upstream_server( + upstream_token_cache.clone(), + upstream_features_cache.clone(), + upstream_engine_cache.clone(), + ) + .await; + let upstream_features = features_from_disk("../examples/hostedexample.json"); + let mut upstream_known_token = EdgeToken::from_str("dx:development.secret123").unwrap(); + upstream_known_token.status = TokenValidationStatus::Validated; + upstream_known_token.token_type = Some(TokenType::Client); + upstream_token_cache.insert( + upstream_known_token.token.clone(), + upstream_known_token.clone(), + ); + upstream_features_cache.insert(cache_key(&upstream_known_token), upstream_features.clone()); + let unleash_client = Arc::new(UnleashClient::new(server.url("/").as_str(), None).unwrap()); + let features_cache: Arc> = Arc::new(DashMap::default()); + let token_cache: Arc> = Arc::new(DashMap::default()); + let engine_cache: Arc> = Arc::new(DashMap::default()); + let feature_refresher = Arc::new(FeatureRefresher { + unleash_client: unleash_client.clone(), + features_cache: features_cache.clone(), + engine_cache: engine_cache.clone(), + refresh_interval: Duration::seconds(6000), + ..Default::default() + }); + let token_validator = Arc::new(TokenValidator { + unleash_client: unleash_client.clone(), + token_cache: token_cache.clone(), + persistence: None, + }); + let local_app = test::init_service( + App::new() + .app_data(Data::from(token_validator.clone())) + .app_data(Data::from(features_cache.clone())) + .app_data(Data::from(engine_cache.clone())) + .app_data(Data::from(token_cache.clone())) + .app_data(Data::from(feature_refresher.clone())) + .wrap(middleware::as_async_middleware::as_async_middleware( + middleware::validate_token::validate_token, + )) + .service(web::scope("/api").configure(configure_client_api)), + ) + .await; + let req = test::TestRequest::get() + .uri("/api/client/features") + .insert_header(ContentType::json()) + .insert_header(("Authorization", upstream_known_token.token.clone())) + .to_request(); + let res = test::call_service(&local_app, req).await; + assert_eq!(res.status(), StatusCode::FORBIDDEN); + } + #[tokio::test] pub async fn gets_feature_by_name() { let features_cache: Arc> = Arc::new(DashMap::default()); @@ -1041,7 +1101,7 @@ mod tests { assert_eq!(result.status(), StatusCode::NOT_FOUND); } #[tokio::test] - pub async fn still_subsumes_tokens_after_moving_registration_to_initial_hydration() { + pub async fn still_subsumes_tokens_after_moving_registration_to_initial_hydration_when_dynamic() { let upstream_features_cache: Arc> = Arc::new(DashMap::default()); let upstream_token_cache: Arc> = Arc::new(DashMap::default()); @@ -1066,13 +1126,14 @@ mod tests { let features_cache: Arc> = Arc::new(DashMap::default()); let token_cache: Arc> = Arc::new(DashMap::default()); let engine_cache: Arc> = Arc::new(DashMap::default()); - let feature_refresher = Arc::new(FeatureRefresher::new( - unleash_client.clone(), - features_cache.clone(), - engine_cache.clone(), - Duration::seconds(6000), - None, - )); + let feature_refresher = Arc::new(FeatureRefresher { + unleash_client: unleash_client.clone(), + features_cache: features_cache.clone(), + engine_cache: engine_cache.clone(), + refresh_interval: Duration::seconds(6000), + strict: false, + ..Default::default() + }); let token_validator = Arc::new(TokenValidator { unleash_client: unleash_client.clone(), token_cache: token_cache.clone(), diff --git a/server/src/error.rs b/server/src/error.rs index ccc16ff4..d1202013 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -105,10 +105,12 @@ pub enum EdgeError { EdgeTokenParseError, InvalidBackupFile(String, String), InvalidServerUrl(String), + InvalidTokenWithStrictBehavior, HealthCheckError(String), JsonParseError(String), NoFeaturesFile, NoTokenProvider, + NoTokens(String), NotReady, ReadyCheckError(String), TlsError, @@ -129,6 +131,7 @@ impl Display for EdgeError { EdgeError::NoFeaturesFile => write!(f, "No features file located"), EdgeError::AuthorizationDenied => write!(f, "Not allowed to access"), EdgeError::NoTokenProvider => write!(f, "Could not get a TokenProvider"), + EdgeError::NoTokens(msg) => write!(f, "{msg}"), EdgeError::TokenParseError(token) => write!(f, "Could not parse edge token: {token}"), EdgeError::PersistenceError(msg) => write!(f, "{msg}"), EdgeError::JsonParseError(msg) => write!(f, "{msg}"), @@ -204,6 +207,7 @@ impl Display for EdgeError { EdgeError::NotReady => { write!(f, "Edge is not ready to serve requests") } + EdgeError::InvalidTokenWithStrictBehavior => write!(f, "Edge is running with strict behavior and the token is not subsumed by any registered tokens"), } } } @@ -216,6 +220,7 @@ impl ResponseError for EdgeError { EdgeError::NoFeaturesFile => StatusCode::INTERNAL_SERVER_ERROR, EdgeError::AuthorizationDenied => StatusCode::FORBIDDEN, EdgeError::NoTokenProvider => StatusCode::INTERNAL_SERVER_ERROR, + EdgeError::NoTokens(_) => StatusCode::INTERNAL_SERVER_ERROR, EdgeError::TokenParseError(_) => StatusCode::FORBIDDEN, EdgeError::ClientBuildError(_) => StatusCode::INTERNAL_SERVER_ERROR, EdgeError::ClientFeaturesParseError(_) => StatusCode::INTERNAL_SERVER_ERROR, @@ -240,6 +245,7 @@ impl ResponseError for EdgeError { EdgeError::ClientCacheError => StatusCode::INTERNAL_SERVER_ERROR, EdgeError::FrontendExpectedToBeHydrated(_) => StatusCode::INTERNAL_SERVER_ERROR, EdgeError::NotReady => StatusCode::SERVICE_UNAVAILABLE, + EdgeError::InvalidTokenWithStrictBehavior => StatusCode::FORBIDDEN, } } diff --git a/server/src/http/feature_refresher.rs b/server/src/http/feature_refresher.rs index 2759b575..f1a98a89 100644 --- a/server/src/http/feature_refresher.rs +++ b/server/src/http/feature_refresher.rs @@ -100,6 +100,7 @@ pub struct FeatureRefresher { pub engine_cache: Arc>, pub refresh_interval: chrono::Duration, pub persistence: Option>, + pub strict: bool, } impl Default for FeatureRefresher { @@ -111,6 +112,7 @@ impl Default for FeatureRefresher { features_cache: Default::default(), engine_cache: Default::default(), persistence: None, + strict: true, } } } @@ -135,6 +137,7 @@ impl FeatureRefresher { engines: Arc>, features_refresh_interval: chrono::Duration, persistence: Option>, + strict: bool, ) -> Self { FeatureRefresher { unleash_client, @@ -143,17 +146,14 @@ impl FeatureRefresher { engine_cache: engines, refresh_interval: features_refresh_interval, persistence, + strict, } } pub fn with_client(client: Arc) -> Self { Self { unleash_client: client, - tokens_to_refresh: Arc::new(Default::default()), - features_cache: Arc::new(Default::default()), - engine_cache: Arc::new(Default::default()), - refresh_interval: chrono::Duration::seconds(10), - persistence: None, + ..Default::default() } } @@ -228,7 +228,11 @@ impl FeatureRefresher { match self.get_features_by_filter(&token, filters) { Some(features) if self.token_is_subsumed(&token) => Ok(features), _ => { - debug!("Had never seen this environment. Configuring fetcher"); + if self.strict { + debug!("Strict behavior: Token is not subsumed by any registered tokens. Returning error"); + Err(EdgeError::InvalidTokenWithStrictBehavior) + } else { + debug!("Dynamic behavior: Had never seen this environment. Configuring fetcher"); self.register_and_hydrate_token(&token).await; self.get_features_by_filter(&token, filters).ok_or_else(|| { EdgeError::ClientHydrationFailed( @@ -236,6 +240,7 @@ impl FeatureRefresher { .into(), ) }) + } } } } @@ -474,16 +479,16 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let token = EdgeToken::try_from("*:development.abcdefghijklmnopqrstuvwxyz".to_string()).unwrap(); feature_refresher @@ -506,16 +511,16 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let token1 = EdgeToken::try_from("*:development.abcdefghijklmnopqrstuvwxyz".to_string()).unwrap(); let token2 = @@ -542,15 +547,15 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let project_a_token = EdgeToken::try_from("projecta:development.abcdefghijklmnopqrstuvwxyz".to_string()) .unwrap(); @@ -585,15 +590,15 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let project_a_token = EdgeToken::try_from("projecta:development.abcdefghijklmnopqrstuvwxyz".to_string()) .unwrap(); @@ -637,15 +642,15 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let project_a_token = EdgeToken::try_from("projecta:development.abcdefghijklmnopqrstuvwxyz".to_string()) .unwrap(); @@ -693,16 +698,16 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let star_token = EdgeToken::try_from("*:development.abcdefghijklmnopqrstuvwxyz".to_string()).unwrap(); let project_a_token = @@ -734,16 +739,16 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let project_a_token = EdgeToken::try_from("projecta:development.abcdefghijklmnopqrstuvwxyz".to_string()) .unwrap(); @@ -770,16 +775,16 @@ mod tests { "Authorization".to_string(), ); let features_cache = Arc::new(DashMap::default()); - let engines_cache = Arc::new(DashMap::default()); + let engine_cache = Arc::new(DashMap::default()); let duration = Duration::seconds(5); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, - engines_cache, - duration, - None, - ); + engine_cache, + refresh_interval: duration, + ..Default::default() + }; let no_etag_due_for_refresh_token = EdgeToken::try_from("projecta:development.no_etag_due_for_refresh_token".to_string()) .unwrap(); @@ -871,13 +876,13 @@ mod tests { let unleash_client = UnleashClient::new(server.url("/").as_str(), None).unwrap(); let features_cache: Arc> = Arc::new(DashMap::default()); let engine_cache: Arc> = Arc::new(DashMap::default()); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, engine_cache, - Duration::seconds(60), - None, - ); + refresh_interval: Duration::seconds(60), + ..Default::default() + }; let mut token = EdgeToken::try_from("*:development.secret123".to_string()).unwrap(); token.status = Validated; token.token_type = Some(TokenType::Client); @@ -917,13 +922,13 @@ mod tests { let unleash_client = UnleashClient::new(server.url("/").as_str(), None).unwrap(); let features_cache: Arc> = Arc::new(DashMap::default()); let engine_cache: Arc> = Arc::new(DashMap::default()); - let feature_refresher = FeatureRefresher::new( - Arc::new(unleash_client), + let feature_refresher = FeatureRefresher { + unleash_client: Arc::new(unleash_client), features_cache, engine_cache, - Duration::milliseconds(1), - None, - ); + refresh_interval: Duration::milliseconds(1), + ..Default::default() + }; feature_refresher .register_token_for_refresh(token, None) .await; @@ -1041,7 +1046,7 @@ mod tests { } #[tokio::test] - pub async fn fetching_two_projects_from_same_environment_should_get_features_for_both() { + pub async fn fetching_two_projects_from_same_environment_should_get_features_for_both_when_dynamic() { let upstream_features_cache: Arc> = Arc::new(DashMap::default()); let upstream_engine_cache: Arc> = Arc::new(DashMap::default()); @@ -1068,6 +1073,7 @@ mod tests { .await; let unleash_client = UnleashClient::new(server.url("/").as_str(), None).unwrap(); let mut feature_refresher = FeatureRefresher::with_client(Arc::new(unleash_client)); + feature_refresher.strict = false; feature_refresher.refresh_interval = Duration::seconds(0); let dx_features = feature_refresher .features_for_filter( @@ -1097,7 +1103,7 @@ mod tests { } #[tokio::test] - pub async fn should_get_data_for_multi_project_token_even_if_we_have_data_for_one_of_the_projects( + pub async fn should_get_data_for_multi_project_token_even_if_we_have_data_for_one_of_the_projects_when_dynamic( ) { let upstream_features_cache: Arc> = Arc::new(DashMap::default()); @@ -1130,6 +1136,7 @@ mod tests { .await; let unleash_client = UnleashClient::new(server.url("/").as_str(), None).unwrap(); let mut feature_refresher = FeatureRefresher::with_client(Arc::new(unleash_client)); + feature_refresher.strict = false; feature_refresher.refresh_interval = Duration::seconds(0); let dx_features = feature_refresher .features_for_filter( @@ -1240,11 +1247,9 @@ mod tests { let unleash_client = UnleashClient::new(server.url("/").as_str(), None).unwrap(); let feature_refresher = FeatureRefresher { unleash_client: Arc::new(unleash_client), - tokens_to_refresh: Arc::new(Default::default()), features_cache: features_cache.clone(), - engine_cache: Arc::new(Default::default()), - refresh_interval: chrono::Duration::seconds(0), - persistence: None, + refresh_interval: Duration::seconds(0), + ..Default::default() }; let _ = feature_refresher diff --git a/server/src/internal_backstage.rs b/server/src/internal_backstage.rs index 39564f13..f87bf2e1 100644 --- a/server/src/internal_backstage.rs +++ b/server/src/internal_backstage.rs @@ -321,7 +321,7 @@ mod tests { } #[actix_web::test] - async fn returns_validated_tokens() { + async fn returns_validated_tokens_when_dynamic() { let upstream_features_cache: Arc> = Arc::new(DashMap::default()); let upstream_token_cache: Arc> = Arc::new(DashMap::default()); @@ -347,11 +347,11 @@ mod tests { let engine_cache: Arc> = Arc::new(DashMap::default()); let feature_refresher = Arc::new(FeatureRefresher { unleash_client: unleash_client.clone(), - tokens_to_refresh: Arc::new(Default::default()), features_cache: features_cache.clone(), engine_cache: engine_cache.clone(), refresh_interval: Duration::seconds(6000), - persistence: None, + strict: false, + ..Default::default() }); let token_validator = Arc::new(TokenValidator { unleash_client: unleash_client.clone(), @@ -391,4 +391,67 @@ mod tests { assert_eq!(status.token_refreshes.len(), 1); assert_eq!(status.token_validation_status.len(), 1); } + + #[actix_web::test] + async fn returns_validated_tokens_when_strict() { + let upstream_features_cache: Arc> = + Arc::new(DashMap::default()); + let upstream_token_cache: Arc> = Arc::new(DashMap::default()); + let upstream_engine_cache: Arc> = Arc::new(DashMap::default()); + let server = upstream_server( + upstream_token_cache.clone(), + upstream_features_cache.clone(), + upstream_engine_cache.clone(), + ) + .await; + let upstream_features = crate::tests::features_from_disk("../examples/hostedexample.json"); + let mut upstream_known_token = EdgeToken::from_str("dx:development.secret123").unwrap(); + upstream_known_token.status = TokenValidationStatus::Validated; + upstream_known_token.token_type = Some(TokenType::Client); + upstream_token_cache.insert( + upstream_known_token.token.clone(), + upstream_known_token.clone(), + ); + upstream_features_cache.insert(cache_key(&upstream_known_token), upstream_features.clone()); + let unleash_client = Arc::new(UnleashClient::new(server.url("/").as_str(), None).unwrap()); + let features_cache: Arc> = Arc::new(DashMap::default()); + let token_cache: Arc> = Arc::new(DashMap::default()); + let engine_cache: Arc> = Arc::new(DashMap::default()); + let feature_refresher = Arc::new(FeatureRefresher { + unleash_client: unleash_client.clone(), + features_cache: features_cache.clone(), + engine_cache: engine_cache.clone(), + refresh_interval: Duration::seconds(6000), + ..Default::default() + }); + let token_validator = Arc::new(TokenValidator { + unleash_client: unleash_client.clone(), + token_cache: token_cache.clone(), + persistence: None, + }); + let local_app = test::init_service( + App::new() + .app_data(web::Data::from(token_validator.clone())) + .app_data(web::Data::from(features_cache.clone())) + .app_data(web::Data::from(engine_cache.clone())) + .app_data(web::Data::from(token_cache.clone())) + .app_data(web::Data::from(feature_refresher.clone())) + .service(web::scope("/internal-backstage").service(super::tokens)) + .service( + web::scope("/api") + .wrap(middleware::as_async_middleware::as_async_middleware( + middleware::validate_token::validate_token, + )) + .configure(crate::client_api::configure_client_api), + ), + ) + .await; + let client_request = test::TestRequest::get() + .uri("/api/client/features") + .insert_header(ContentType::json()) + .insert_header(("Authorization", upstream_known_token.token.clone())) + .to_request(); + let res = test::call_service(&local_app, client_request).await; + assert_eq!(res.status(), actix_http::StatusCode::FORBIDDEN); + } } diff --git a/server/src/middleware/client_token_from_frontend_token.rs b/server/src/middleware/client_token_from_frontend_token.rs index d7b8ad6f..8688496a 100644 --- a/server/src/middleware/client_token_from_frontend_token.rs +++ b/server/src/middleware/client_token_from_frontend_token.rs @@ -81,13 +81,13 @@ mod tests { token_cache: local_token_cache.clone(), persistence: None, }); - let feature_refresher = Arc::new(FeatureRefresher::new( - unleash_client.clone(), - local_features_cache.clone(), - local_engine_cache.clone(), - Duration::seconds(5), - None, - )); + let feature_refresher = Arc::new(FeatureRefresher { + unleash_client: unleash_client.clone(), + features_cache: local_features_cache.clone(), + engine_cache: local_engine_cache.clone(), + refresh_interval: Duration::seconds(5), + ..Default::default() + }); test_server(move || { let config = serde_qs::actix::QsQueryConfig::default() .qs_config(serde_qs::Config::new(5, false));