From b48604c13c6733a1ce38ff29843391b4866a5506 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Thu, 19 Sep 2024 11:09:45 +0200 Subject: [PATCH] feat: added support for client and frontend tokens to offline mode. (#465) * feat: added support for client and frontend tokens to offline mode. Assume tokens passed in --tokens are client tokens * clean up tests to use the new interface * fix: Added client and frontend tokens to constructor * Updated CLI docs --- CLI.md | 5 ++-- server/src/builder.rs | 49 +++++++++++++++++++++++++++++++++++++- server/src/cli.rs | 7 ++++++ server/src/client_api.rs | 2 ++ server/src/frontend_api.rs | 39 ++++++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 5 deletions(-) diff --git a/CLI.md b/CLI.md index f3dcb1ee..0c1ddbd5 100644 --- a/CLI.md +++ b/CLI.md @@ -151,7 +151,9 @@ Run in offline mode ###### **Options:** * `-b`, `--bootstrap-file ` — The file to load our features from. This data will be loaded at startup -* `-t`, `--tokens ` — Tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--tokens` argument +* `-t`, `--tokens ` — Tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--tokens` argument (v19.4.0) deprecated "Please use --client-tokens | CLIENT_TOKENS instead" +* `-c`, `--client-tokens ` — Client tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--client-tokens` argument +* `-f`, `--frontend-tokens ` — Frontend tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--frontend-tokens` argument * `-r`, `--reload-interval ` — The interval in seconds between reloading the bootstrap file. Disabled if unset or 0 Default value: `0` @@ -194,4 +196,3 @@ Perform a ready check against a running edge instance This document was generated automatically by clap-markdown. - diff --git a/server/src/builder.rs b/server/src/builder.rs index 6a7fe1c8..984080f2 100644 --- a/server/src/builder.rs +++ b/server/src/builder.rs @@ -78,14 +78,36 @@ async fn hydrate_from_persistent_storage(cache: CacheContainer, storage: Arc, + client_tokens: Vec, + frontend_tokens: Vec, ) -> EdgeResult { let (token_cache, features_cache, engine_cache) = build_caches(); let edge_tokens: Vec = tokens .iter() .map(|token| EdgeToken::from_str(token).unwrap_or_else(|_| EdgeToken::offline_token(token))) + .map(|mut token| { + token.token_type = Some(TokenType::Client); + token + }) .collect(); + let edge_client_tokens: Vec = client_tokens + .iter() + .map(|token| EdgeToken::from_str(token).unwrap_or_else(|_| EdgeToken::offline_token(token))) + .map(|mut token| { + token.token_type = Some(TokenType::Client); + token + }) + .collect(); + let edge_frontend_tokens: Vec = frontend_tokens + .iter() + .map(|token| EdgeToken::from_str(token).unwrap_or_else(|_| EdgeToken::offline_token(token))) + .map(|mut token| { + token.token_type = Some(TokenType::Frontend); + token + }) + .collect(); for edge_token in edge_tokens { token_cache.insert(edge_token.token.clone(), edge_token.clone()); @@ -96,6 +118,24 @@ pub(crate) fn build_offline_mode( client_features.clone(), ); } + for client_token in edge_client_tokens { + token_cache.insert(client_token.token.clone(), client_token.clone()); + load_offline_engine_cache( + &client_token, + features_cache.clone(), + engine_cache.clone(), + client_features.clone(), + ); + } + for frontend_token in edge_frontend_tokens { + token_cache.insert(frontend_token.token.clone(), frontend_token.clone()); + load_offline_engine_cache( + &frontend_token, + features_cache.clone(), + engine_cache.clone(), + client_features.clone(), + ) + } Ok((token_cache, features_cache, engine_cache)) } @@ -118,7 +158,12 @@ fn build_offline(offline_args: OfflineArgs) -> EdgeResult { let client_features = load_bootstrap(&bootstrap)?; - build_offline_mode(client_features, offline_args.tokens) + build_offline_mode( + client_features, + offline_args.tokens, + offline_args.client_tokens, + offline_args.frontend_tokens, + ) } else { Err(EdgeError::NoFeaturesFile) } @@ -267,6 +312,8 @@ mod tests { bootstrap_file: None, tokens: vec![], reload_interval: Default::default(), + client_tokens: vec![], + frontend_tokens: vec![], }; let result = build_offline(args); diff --git a/server/src/cli.rs b/server/src/cli.rs index 5dbcbe75..8689c2b3 100644 --- a/server/src/cli.rs +++ b/server/src/cli.rs @@ -219,8 +219,15 @@ pub struct OfflineArgs { #[clap(short, long, env)] pub bootstrap_file: Option, /// Tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--tokens` argument + /// (v19.4.0) deprecated "Please use --client-tokens | CLIENT_TOKENS instead" #[clap(short, long, env, value_delimiter = ',')] pub tokens: Vec, + /// Client tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--client-tokens` argument + #[clap(short, long, env, value_delimiter = ',')] + pub client_tokens: Vec, + /// Frontend tokens that should be allowed to connect to Edge. Supports a comma separated list or multiple instances of the `--frontend-tokens` argument + #[clap(short, long, env, value_delimiter = ',')] + pub frontend_tokens: Vec, /// The interval in seconds between reloading the bootstrap file. Disabled if unset or 0 #[clap(short, long, env, default_value_t = 0)] pub reload_interval: u64, diff --git a/server/src/client_api.rs b/server/src/client_api.rs index 39871222..18e60133 100644 --- a/server/src/client_api.rs +++ b/server/src/client_api.rs @@ -928,6 +928,8 @@ mod tests { .app_data(Data::new(crate::cli::EdgeMode::Offline(OfflineArgs { bootstrap_file: Some(PathBuf::from("../examples/features.json")), tokens: vec!["secret_123".into()], + client_tokens: vec![], + frontend_tokens: vec![], reload_interval: 0, }))) .service(web::scope("/api/client").service(get_features)), diff --git a/server/src/frontend_api.rs b/server/src/frontend_api.rs index dcd8c2e4..e71676c3 100644 --- a/server/src/frontend_api.rs +++ b/server/src/frontend_api.rs @@ -925,6 +925,8 @@ mod tests { "*:development.03fa5f506428fe80ed5640c351c7232e38940814d2923b08f5c05fa7" .to_string(), ], + vec![], + vec![], ) .unwrap(); @@ -975,6 +977,8 @@ mod tests { "*:development.03fa5f506428fe80ed5640c351c7232e38940814d2923b08f5c05fa7" .to_string(), ], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1023,6 +1027,8 @@ mod tests { "*:development.03fa5f506428fe80ed5640c351c7232e38940814d2923b08f5c05fa7" .to_string(), ], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1081,6 +1087,8 @@ mod tests { "*:development.03fa5f506428fe80ed5640c351c7232e38940814d2923b08f5c05fa7" .to_string(), ], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1137,6 +1145,8 @@ mod tests { "*:development.03fa5f506428fe80ed5640c351c7232e38940814d2923b08f5c05fa7" .to_string(), ], + vec![], + vec![], ) .unwrap(); @@ -1248,8 +1258,13 @@ mod tests { #[tokio::test] async fn when_running_in_offline_mode_with_proxy_key_should_not_filter_features() { let client_features = client_features_with_constraint_requiring_user_id_of_seven(); - let (token_cache, feature_cache, engine_cache) = - build_offline_mode(client_features.clone(), vec!["secret-123".to_string()]).unwrap(); + let (token_cache, feature_cache, engine_cache) = build_offline_mode( + client_features.clone(), + vec!["secret-123".to_string()], + vec![], + vec![], + ) + .unwrap(); let app = test::init_service( App::new() .app_data(Data::from(token_cache)) @@ -1259,6 +1274,8 @@ mod tests { bootstrap_file: None, tokens: vec!["secret-123".into()], reload_interval: 0, + client_tokens: vec![], + frontend_tokens: vec![], }))) .service(web::scope("/api/frontend").service(super::get_frontend_all_features)), ) @@ -1280,6 +1297,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features.clone(), vec!["dx:development.secret123".to_string()], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1368,6 +1387,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features, vec!["dx:development.secret123".to_string()], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1397,6 +1418,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features.clone(), vec!["dx:development.secret123".to_string()], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1425,6 +1448,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features_with_constraint_requiring_test_property_to_be_42(), vec!["*:development.secret123".to_string()], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1453,6 +1478,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features.clone(), vec!["dx:development.secret123".to_string()], + vec![], + vec![], ) .unwrap(); let app = test::init_service( @@ -1484,6 +1511,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features_with_custom_context_field.clone(), vec![auth_key.clone()], + vec![], + vec![], ) .unwrap(); let config = @@ -1524,6 +1553,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features_with_custom_context_field.clone(), vec![auth_key.clone()], + vec![], + vec![], ) .unwrap(); let trust_proxy = TrustProxy { @@ -1561,6 +1592,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features_with_custom_context_field.clone(), vec![auth_key.clone()], + vec![], + vec![], ) .unwrap(); let trust_proxy = TrustProxy { @@ -1599,6 +1632,8 @@ mod tests { let (token_cache, feature_cache, engine_cache) = build_offline_mode( client_features_with_custom_context_field.clone(), vec![auth_key.clone()], + vec![], + vec![], ) .unwrap(); let app = test::init_service(