From eab0878ce2bf49a499f032a13c47f58a4b346cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Wed, 1 Feb 2023 15:04:40 +0100 Subject: [PATCH] feat: implement simplify tokens (#30) --- Cargo.lock | 124 +++++++++------------ server/Cargo.toml | 3 +- server/src/data_sources/memory_provider.rs | 16 --- server/src/lib.rs | 2 + server/src/tokens.rs | 93 ++++++++++++++++ server/src/types.rs | 68 +++++++++++ server/src/unleash_client.rs | 2 +- 7 files changed, 219 insertions(+), 89 deletions(-) create mode 100644 server/src/tokens.rs diff --git a/Cargo.lock b/Cargo.lock index 9e19efda..45647375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" dependencies = [ "actix-macros", "futures-core", @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da34f8e659ea1b077bb4637948b815cd3768ad5a188fdcd74ff4d84240cd824" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" dependencies = [ "actix-rt", "actix-service", @@ -342,9 +342,9 @@ checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "async-trait" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -470,9 +470,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bytestring" @@ -485,9 +485,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -693,9 +693,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8" dependencies = [ "cc", "cxxbridge-flags", @@ -705,9 +705,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8" dependencies = [ "cc", "codespan-reporting", @@ -720,15 +720,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" +checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971" [[package]] name = "cxxbridge-macro" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e" dependencies = [ "proc-macro2", "quote", @@ -952,9 +952,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -967,9 +967,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -977,15 +977,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -994,15 +994,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -1011,21 +1011,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -1588,9 +1588,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -1598,9 +1598,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -1608,9 +1608,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", @@ -1621,9 +1621,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", @@ -2348,20 +2348,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de" -dependencies = [ - "once_cell", - "opentelemetry", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - [[package]] name = "tracing-serde" version = "0.1.3" @@ -2435,9 +2421,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046be40136ef78dc325e0edefccf84ccddacd0afcc1ca54103fa3c61bbdab1d" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -2456,9 +2442,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -2481,7 +2467,6 @@ dependencies = [ "actix-http-test", "actix-middleware-etag", "actix-service", - "actix-tls", "actix-web", "actix-web-opentelemetry", "anyhow", @@ -2504,7 +2489,6 @@ dependencies = [ "testcontainers", "tokio", "tracing", - "tracing-opentelemetry", "tracing-subscriber", "ulid", "unleash-types", @@ -2803,18 +2787,18 @@ checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70" [[package]] name = "zstd" -version = "0.12.2+zstd.1.5.2" +version = "0.12.3+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9262a83dc741c0b0ffec209881b45dbc232c21b02a2b9cb1adb93266e41303d" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.2+zstd.1.5.2" +version = "6.0.3+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cf39f730b440bab43da8fb5faf5f254574462f73f260f85f7987f32154ff17" +checksum = "68e4a3f57d13d0ab7e478665c60f35e2a613dcd527851c2c7287ce5c787e134a" dependencies = [ "libc", "zstd-sys", @@ -2822,9 +2806,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.5+zstd.1.5.2" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", diff --git a/server/Cargo.toml b/server/Cargo.toml index d13d3d3b..feb59fde 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -11,9 +11,9 @@ homepage = "https://github.com/Unleash/unleash-edge" [dependencies] actix-cors = "0.6.4" actix-middleware-etag = "0.2.0" -actix-tls = { version = "3.0.3", features = ["rustls"] } actix-web = { version = "4.3.0", features = ["rustls"] } actix-web-opentelemetry = { version = "0.13.0", features = ["metrics", "metrics-prometheus"] } + anyhow = "1.0.68" awc = { version = "3.1.0", features = ["rustls"] } chrono = { version = "0.4.23", features = ["serde"] } @@ -31,7 +31,6 @@ serde_json = "1.0.91" shadow-rs = "0.20.0" tokio = { version = "1.25.0", features = ["macros", "rt-multi-thread", "tracing"] } tracing = { version = "0.1.37", features = ["log"] } -tracing-opentelemetry = "0.18.0" tracing-subscriber = { version = "0.3.16", features = ["json", "env-filter"] } ulid = "1.0.0" unleash-types = { version = "0.7.1", features = ["openapi", "hashes"] } diff --git a/server/src/data_sources/memory_provider.rs b/server/src/data_sources/memory_provider.rs index 26edf497..5accebfc 100644 --- a/server/src/data_sources/memory_provider.rs +++ b/server/src/data_sources/memory_provider.rs @@ -62,22 +62,6 @@ mod test { } } - //Yes, Clippy is correct but right now, this is test code and I don't want add a derive that's broken for prod code - #[allow(clippy::derivable_impls)] - impl Default for EdgeToken { - fn default() -> Self { - Self { - secret: Default::default(), - token_type: Default::default(), - environment: Default::default(), - projects: Default::default(), - expires_at: Default::default(), - seen_at: Default::default(), - alias: Default::default(), - } - } - } - #[test] fn memory_provider_correctly_deduplicates_tokens() { let mut provider = MemoryProvider::default(); diff --git a/server/src/lib.rs b/server/src/lib.rs index b9b85a82..7e1d4787 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -10,4 +10,6 @@ pub mod types; pub mod unleash_client; pub mod urls; +pub mod tokens; + pub mod internal_backstage; diff --git a/server/src/tokens.rs b/server/src/tokens.rs new file mode 100644 index 00000000..29d7585c --- /dev/null +++ b/server/src/tokens.rs @@ -0,0 +1,93 @@ +use crate::types::EdgeToken; + +#[allow(dead_code)] // until used +pub(crate) fn simplify(tokens: &[EdgeToken]) -> Vec { + tokens + .iter() + .filter_map(|token| { + tokens.iter().fold(Some(token.clone()), |acc, current| { + acc.and_then(|lead| { + if current != &lead && current.subsumes(&lead) { + None + } else { + Some(lead) + } + }) + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use crate::{tokens::simplify, types::EdgeToken}; + + fn test_token(env: Option<&str>, projects: Vec<&str>) -> EdgeToken { + EdgeToken { + environment: env.map(|env| env.into()), + projects: projects.into_iter().map(|p| p.into()).collect(), + ..EdgeToken::default() + } + } + + #[test] + fn test_case_1_token_with_two_projects_subsumes_tokens_having_individually_each_token() { + let tokens = vec![ + test_token(None, vec!["p1", "p2"]), + test_token(None, vec!["p1"]), + test_token(None, vec!["p1"]), + ]; + + let expected = vec![test_token(None, vec!["p1", "p2"])]; + + assert_eq!(simplify(&tokens), expected); + } + + #[test] + fn test_case_2_when_two_environments_are_different_we_have_at_least_two_tokens() { + let tokens = vec![ + test_token(Some("env1"), vec!["p1", "p2"]), + test_token(Some("env1"), vec!["p1"]), + test_token(None, vec!["p1"]), + ]; + let expected = vec![ + test_token(Some("env1"), vec!["p1", "p2"]), + test_token(None, vec!["p1"]), + ]; + + assert_eq!(simplify(&tokens), expected); + } + + #[test] + fn test_case_3_star_token_subsumes_all_tokens() { + let tokens = vec![ + test_token(None, vec!["p1"]), + test_token(None, vec!["*"]), + test_token(None, vec!["p1", "p2"]), + ]; + let expected = vec![test_token(None, vec!["*"])]; + + assert_eq!(simplify(&tokens), expected); + } + + #[test] + fn test_case_4_when_a_project_is_shared_between_two_tokens_we_simplify_as_much_as_we_can() { + let tokens = vec![ + test_token(None, vec!["p1", "p2"]), + test_token(Some("env"), vec!["p1", "p2"]), + test_token(None, vec!["p1"]), + test_token(Some("env"), vec!["p2", "p3"]), + test_token(None, vec!["*"]), + test_token(Some("env"), vec!["p1"]), + test_token(None, vec!["p3"]), + test_token(Some("env"), vec!["p2"]), + ]; + let expected = vec![ + test_token(Some("env"), vec!["p1", "p2"]), + test_token(Some("env"), vec!["p2", "p3"]), + test_token(None, vec!["*"]), + ]; + + assert_eq!(simplify(&tokens), expected); + } +} diff --git a/server/src/types.rs b/server/src/types.rs index 66f09b1b..9547727c 100644 --- a/server/src/types.rs +++ b/server/src/types.rs @@ -48,6 +48,7 @@ impl ClientFeaturesRequest { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(test, derive(Default))] #[serde(rename_all = "camelCase")] pub struct EdgeToken { pub secret: String, @@ -72,6 +73,14 @@ impl EdgeToken { alias: None, } } + + pub fn subsumes(&self, other: &EdgeToken) -> bool { + return self.token_type == other.token_type + && self.environment == other.environment + && (self.projects.contains(&"*".into()) + || (self.projects.len() >= other.projects.len() + && other.projects.iter().all(|p| self.projects.contains(p)))); + } } impl FromRequest for EdgeToken { @@ -244,6 +253,21 @@ mod tests { use crate::types::EdgeToken; + fn test_str(token: &str) -> EdgeToken { + EdgeToken::from_str( + &(token.to_owned() + ".614a75cf68bef8703aa1bd8304938a81ec871f86ea40c975468eabd6"), + ) + .unwrap() + } + + fn test_token(env: Option<&str>, projects: Vec<&str>) -> EdgeToken { + EdgeToken { + environment: env.map(|env| env.into()), + projects: projects.into_iter().map(|p| p.into()).collect(), + ..EdgeToken::default() + } + } + #[test_case("943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0"; "old java client token")] #[test_case("demo-app:production.614a75cf68bef8703aa1bd8304938a81ec871f86ea40c975468eabd6"; "demo token with project and environment")] #[test_case("secret-123"; "old example proxy token")] @@ -260,4 +284,48 @@ mod tests { } } } + + #[test_case( + "demo-app:production", + "demo-app:production" + => true + ; "idepmotency")] + #[test_case( + "aproject:production", + "another:production" + => false + ; "project mismatch")] + #[test_case( + "demo-app:development", + "demo-app:production" + => false + ; "environment mismatch")] + #[test_case( + "*:production", + "demo-app:production" + => true + ; "* subsumes a project token")] + fn edge_token_subsumes_edge_token(token1: &str, token2: &str) -> bool { + let t1 = test_str(token1); + let t2 = test_str(token2); + t1.subsumes(&t2) + } + + #[test] + fn edge_token_unrleated_by_subsume() { + let t1 = test_str("demo-app:production"); + let t2 = test_str("another:production"); + assert!(!t1.subsumes(&t2)); + assert!(!t2.subsumes(&t1)); + } + + #[test] + fn edge_token_does_not_subsume_if_projects_is_subset_of_other_tokens_project() { + let token1 = test_token(None, vec!["p1", "p2"]); + + let token2 = test_token(None, vec!["p1"]); + + assert!(token1.subsumes(&token2)); + assert!(!token2.subsumes(&token1)); + } } diff --git a/server/src/unleash_client.rs b/server/src/unleash_client.rs index 889f32cd..b70a10bd 100644 --- a/server/src/unleash_client.rs +++ b/server/src/unleash_client.rs @@ -44,7 +44,7 @@ impl UnleashClient { } } pub fn new(server_url: &str, instance_id_opt: Option) -> Result { - let instance_id = instance_id_opt.unwrap_or(Ulid::new().to_string()); + let instance_id = instance_id_opt.unwrap_or_else(|| Ulid::new().to_string()); Ok(Self { urls: UnleashUrls::from_str(server_url)?, backing_client: new_awc_client(instance_id),