diff --git a/websocket/Cargo.lock b/websocket/Cargo.lock index fe126d34e..ea982a6ac 100644 --- a/websocket/Cargo.lock +++ b/websocket/Cargo.lock @@ -53,6 +53,16 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -257,10 +267,10 @@ dependencies = [ "base64 0.21.7", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "itoa", "matchit", @@ -292,8 +302,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -315,8 +325,8 @@ dependencies = [ "bytes", "futures-util", "headers", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -354,6 +364,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -448,7 +464,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -480,6 +496,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adcf94f05e094fca3005698822ec791cb4433ced416afda1c5ca3b8dfc05a2f" + [[package]] name = "core-foundation" version = "0.9.4" @@ -521,20 +543,61 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b8ce8218c97789f16356e7896b3714f26c2ee1079b79c0b7ae7064bb9089fa" +dependencies = [ + "getrandom 0.2.15", + "hybrid-array", + "rand_core 0.6.4", +] + [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "const-oid", - "pem-rfc7468", + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82db698b33305f0134faf590b9d1259dc171b5481ac41d5c8146c3b3ee7d4319" +dependencies = [ + "const-oid 0.10.0-rc.0", + "pem-rfc7468 1.0.0-rc.1", "zeroize", ] @@ -555,7 +618,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "crypto-common", + "crypto-common 0.1.6", +] + +[[package]] +name = "digest" +version = "0.11.0-pre.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379" +dependencies = [ + "const-oid 0.10.0-rc.0", + "crypto-common 0.2.0-rc.1", ] [[package]] @@ -567,6 +640,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.9" @@ -619,7 +698,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -761,6 +840,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" @@ -789,6 +874,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -798,7 +894,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -871,7 +967,7 @@ dependencies = [ "hex", "once_cell", "percent-encoding", - "pkcs8", + "pkcs8 0.10.2", "regex", "reqwest", "reqwest-middleware", @@ -895,6 +991,50 @@ dependencies = [ "async-trait", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "headers" version = "0.4.0" @@ -904,7 +1044,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -916,7 +1056,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.1.0", ] [[package]] @@ -946,6 +1086,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -957,6 +1108,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -964,7 +1126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -975,8 +1137,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -986,6 +1148,27 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel 1.9.0", + "base64 0.13.1", + "futures-lite 1.13.0", + "http 0.2.12", + "infer", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + [[package]] name = "httparse" version = "1.9.4" @@ -998,6 +1181,39 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.2.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d306b679262030ad8813a82d4915fc04efff97776e4db7f8eb5137039d56400" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.3.1" @@ -1007,8 +1223,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -1018,6 +1235,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -1026,7 +1260,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -1043,9 +1277,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -1087,6 +1321,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "infra" version = "0.1.0" @@ -1095,19 +1345,26 @@ dependencies = [ "axum", "axum-extra", "axum-macros", + "base64 0.13.1", "chrono", "flow-websocket-domain", "google-cloud-storage", + "jsonwebtoken", + "rand 0.8.5", "redis 0.25.4", + "reqwest", + "rsa", "rslock", "serde", "serde_json", + "thiserror", "tokio", "tower", "tower-http", "tracing", "tracing-subscriber", "uuid", + "wiremock", "yrs", ] @@ -1181,6 +1438,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1188,6 +1448,12 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -1272,7 +1538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -1313,6 +1579,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1328,6 +1611,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1335,6 +1629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1438,7 +1733,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1460,6 +1755,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c1cde4770761bf6bd336f947b9ac1fe700b0a4ec5867cf66cf08597fe89e8c" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1509,14 +1813,35 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.8.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2f4c73d459a85331915baebd5082dce5ee8ef16fd9a1ca75559ac91e66a9ee" +dependencies = [ + "der 0.8.0-rc.1", + "pkcs8 0.11.0-rc.0", + "spki 0.8.0-rc.0", +] + [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.9", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66180445f1dce533620a7743467ef85fe1c5e80cdaf7c7053609d7a2fbcdae20" +dependencies = [ + "der 0.8.0-rc.1", + "spki 0.8.0-rc.0", ] [[package]] @@ -1586,6 +1911,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -1593,8 +1931,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -1604,7 +1952,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -1613,7 +1970,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -1714,19 +2080,21 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "http", - "http-body", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.3.1", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -1743,6 +2111,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", + "system-configuration", "tokio", "tokio-native-tls", "tokio-util", @@ -1752,7 +2121,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg", + "windows-registry", ] [[package]] @@ -1763,13 +2132,19 @@ checksum = "a45d100244a467870f6cb763c4484d010a6bed6bd610b3676e3825d93fb4cfbd" dependencies = [ "anyhow", "async-trait", - "http", + "http 1.1.0", "reqwest", "serde", "thiserror", "tower-service", ] +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + [[package]] name = "ring" version = "0.17.8" @@ -1778,13 +2153,33 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.10.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e864e43f5d003321ab452feea6450f9611d7be6726489b4ec051da34774c62" +dependencies = [ + "const-oid 0.10.0-rc.0", + "digest 0.11.0-pre.9", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8 0.11.0-rc.0", + "rand_core 0.6.4", + "signature", + "spki 0.8.0-rc.0", + "subtle", + "zeroize", +] + [[package]] name = "rslock" version = "0.3.0" @@ -1792,7 +2187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069fed2ed397dcba536df6105c2b11ded413297746b451876e60297a87939900" dependencies = [ "futures", - "rand", + "rand 0.8.5", "redis 0.24.0", "tokio", ] @@ -1830,6 +2225,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "2.1.2" @@ -1846,6 +2254,17 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustls-webpki" +version = "0.102.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -1937,6 +2356,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1957,7 +2387,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -1974,7 +2404,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -1995,6 +2425,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.3.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054d71959c7051b9042c26af337f05cc930575ed2604d7d3ced3158383e59734" +dependencies = [ + "digest 0.11.0-pre.9", + "rand_core 0.6.4", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -2064,9 +2504,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.9", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3fb1c675852398475928637b3ebbdd7e1d0cc24d27b3bbc81788b4eb51e310" +dependencies = [ + "base64ct", + "der 0.8.0-rc.1", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.66" @@ -2089,6 +2545,30 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] [[package]] name = "tempfile" @@ -2104,18 +2584,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -2218,6 +2698,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.21.0" @@ -2268,8 +2759,8 @@ dependencies = [ "bitflags 2.5.0", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "http-range-header", "httpdate", @@ -2373,10 +2864,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.1.0", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", "thiserror", "url", @@ -2434,6 +2925,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2454,8 +2946,8 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom", - "rand", + "getrandom 0.2.15", + "rand 0.8.5", "serde", "uuid-macro-internal", ] @@ -2510,6 +3002,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2633,7 +3131,37 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -2651,7 +3179,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2671,18 +3199,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2693,9 +3221,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2705,9 +3233,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2717,15 +3245,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2735,9 +3263,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2747,9 +3275,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2759,9 +3287,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2771,18 +3299,30 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winreg" -version = "0.52.0" +name = "wiremock" +version = "0.5.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "13a3a53eaf34f390dd30d7b1b078287dd05df2aa2e21a589ccb80f5c7253c2e9" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "assert-json-diff", + "async-trait", + "base64 0.21.7", + "deadpool", + "futures", + "futures-timer", + "http-types", + "hyper 0.14.30", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", ] [[package]] diff --git a/websocket/Cargo.toml b/websocket/Cargo.toml index 8f7e06421..6229cc0a2 100644 --- a/websocket/Cargo.toml +++ b/websocket/Cargo.toml @@ -1,7 +1,5 @@ [workspace] -members = [ - "crates/*", -] +members = ["crates/*"] resolver = "2" @@ -35,27 +33,34 @@ panic = "abort" strip = true [workspace.dependencies] -flow-websocket-domain = {path = "crates/domain"} +flow-websocket-domain = { path = "crates/domain" } async-trait = "0.1.80" -axum = {version = "0.7", features = ["ws"]} -axum-extra = {version = "0.9", features = ["typed-header"]} +axum = { version = "0.7", features = ["ws"] } +axum-extra = { version = "0.9", features = ["typed-header"] } axum-macros = "0.4" -chrono = {version = "0.4", features = ["serde"]} +chrono = { version = "0.4", features = ["serde"] } google-cloud-storage = "0.18" -redis = {version = "0.25.4", features = ["aio", "tokio-comp"]} +redis = { version = "0.25.4", features = ["aio", "tokio-comp"] } rslock = "0.3.0" -serde = {version = "1.0", features = ["derive"]} -serde_json = {version = "1.0.117", features = ["arbitrary_precision"]} -tokio = {version = "1.38.0", features = ["full", "time"]} -tower = {version = "0.4", features = ["timeout"]} -tower-http = {version = "0.5", features = ["fs", "trace"]} +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0.117", features = ["arbitrary_precision"] } +tokio = { version = "1.38.0", features = ["full", "time"] } +tower = { version = "0.4", features = ["timeout"] } +tower-http = { version = "0.5", features = ["fs", "trace"] } tracing = "0.1" -tracing-subscriber = {version = "0.3", features = ["env-filter"]} -uuid = {version = "1.8.0", features = [ +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +uuid = { version = "1.8.0", features = [ "v4", "fast-rng", "macro-diagnostics", "serde", -]} +] } yrs = "0.18" +reqwest = { version = "0.12.7", features = ["json"] } +thiserror = "1.0.63" +jsonwebtoken = "9.3.0" +wiremock = "0.5" +rsa = { version = "0.10.0-pre.2", features = ["pem"] } +rand = "0.8" +base64 = "0.13" diff --git a/websocket/crates/domain/src/project.rs b/websocket/crates/domain/src/project.rs index d2fb40b87..076ca8c36 100644 --- a/websocket/crates/domain/src/project.rs +++ b/websocket/crates/domain/src/project.rs @@ -50,7 +50,7 @@ impl ProjectEditingSession { let session_id = generate_id(14, "editor-session"); self.session_id = Some(session_id.clone()); if !self.session_setup_complete { - let _latest_snapshot_state = snapshot_repo + let latest_snapshot_state = snapshot_repo .get_latest_snapshot_state(&self.project_id) .await?; // Initialize Redis with latest snapshot state @@ -110,7 +110,7 @@ impl ProjectEditingSession { snapshot_repo: &impl ProjectSnapshotRepository, data: SnapshotData, ) -> Result<(), Box> { - self.merge_updates().await?; + let merged_state = self.merge_updates().await?; let snapshot = ProjectSnapshot { id: generate_id(14, "snap"), project_id: self.project_id.clone(), diff --git a/websocket/crates/domain/src/utils.rs b/websocket/crates/domain/src/utils.rs index b24aab493..41cd0c6b6 100644 --- a/websocket/crates/domain/src/utils.rs +++ b/websocket/crates/domain/src/utils.rs @@ -3,4 +3,4 @@ use uuid::Uuid; pub fn generate_id(length: usize, prefix: &str) -> String { let _ = length; format!("{}{}", prefix, Uuid::new_v4().to_string()) -} \ No newline at end of file +} diff --git a/websocket/crates/infra/Cargo.toml b/websocket/crates/infra/Cargo.toml index 3080009b9..61a1768ed 100644 --- a/websocket/crates/infra/Cargo.toml +++ b/websocket/crates/infra/Cargo.toml @@ -30,3 +30,10 @@ tracing-subscriber.workspace = true tracing.workspace = true uuid.workspace = true yrs.workspace = true +thiserror.workspace = true +reqwest.workspace = true +jsonwebtoken.workspace = true +wiremock.workspace = true +rsa.workspace = true +rand.workspace = true +base64.workspace = true diff --git a/websocket/crates/infra/src/auth/error.rs b/websocket/crates/infra/src/auth/error.rs new file mode 100644 index 000000000..9d4a29702 --- /dev/null +++ b/websocket/crates/infra/src/auth/error.rs @@ -0,0 +1,31 @@ +use reqwest::StatusCode; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum AuthError { + #[error("Invalid token header: {0}")] + InvalidTokenHeader(String), + + #[error("No `kid` found in token header")] + NoKidInTokenHeader, + + #[error("No matching JWK found for kid: {0}")] + NoMatchingJwk(String), + + #[error("Invalid JWK key: {0}")] + InvalidJwkKey(String), + + #[error("Token validation failed: {0}")] + TokenValidationFailed(String), + + #[error("Failed to fetch JWKS: {0}")] + FailedToFetchJwks(String), + + #[error("HTTP request failed with status: {0}")] + HttpRequestFailed(StatusCode), + + #[error("Unexpected error: {0}")] + Unexpected(String), +} + +pub type Result = std::result::Result; diff --git a/websocket/crates/infra/src/auth/jwt.rs b/websocket/crates/infra/src/auth/jwt.rs new file mode 100644 index 000000000..c6d64f608 --- /dev/null +++ b/websocket/crates/infra/src/auth/jwt.rs @@ -0,0 +1,243 @@ +use super::error::{AuthError, Result}; +use super::jwt_validator::JWTValidator; +use super::types::{Jwk, Jwks}; +use jsonwebtoken::Algorithm; +use reqwest::Client; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::RwLock; + +pub struct JWTProvider { + iss: String, + aud: Vec, + alg: Algorithm, + jwks_uri: String, + ttl: Duration, + jwks_cache: Arc>, +} + +struct JwksCache { + keys: HashMap, + last_updated: Instant, // Last time the keys were updated +} + +impl JWTProvider { + // Create a new JWTProvider + pub fn new(iss: String, aud: Vec, alg: Algorithm, jwks_uri: String, ttl: u64) -> Self { + JWTProvider { + iss, + aud, + alg, + jwks_uri, + ttl: Duration::from_secs(ttl), + jwks_cache: Arc::new(RwLock::new(JwksCache { + keys: HashMap::new(), + last_updated: Instant::now() - Duration::from_secs(ttl), + })), + } + } + + // Fetch the JWKS and cache it, respecting TTL + pub async fn fetch_jwks(&self) -> Result<()> { + let mut cache = self.jwks_cache.write().await; + + // Check if cache is still valid + if !cache.is_expired(self.ttl) { + return Ok(()); + } + + let response = Client::new() + .get(&self.jwks_uri) + .send() + .await + .map_err(|e| AuthError::FailedToFetchJwks(e.to_string()))?; + + if !response.status().is_success() { + return Err(AuthError::HttpRequestFailed(response.status())); + } + + let jwks: Jwks = response + .json() + .await + .map_err(|e| AuthError::FailedToFetchJwks(e.to_string()))?; + + cache.keys.clear(); // Clear old cache + for jwk in jwks.keys { + cache.keys.insert(jwk.kid.clone(), jwk); + } + cache.last_updated = Instant::now(); // Update last_updated time + + Ok(()) + } + + pub async fn get_validator(&self) -> JWTValidator { + let keys = { + let cache = self.jwks_cache.read().await; + Arc::new(RwLock::new(cache.keys.clone())) // Clone only the keys + }; + + JWTValidator::new(self.iss.clone(), self.aud.clone(), self.alg, keys) + } +} + +impl JwksCache { + #[inline] + pub fn is_expired(&self, ttl: Duration) -> bool { + self.last_updated.elapsed() >= ttl + } +} + +#[cfg(test)] +mod tests { + use crate::auth::jwt_validator::JWTValidatorTrait; + use crate::auth::types::Claims; + + use super::super::types::CustomClaims; + use super::*; + use base64::{encode_config, URL_SAFE_NO_PAD}; + use chrono::{Duration, Utc}; + use jsonwebtoken::{encode, EncodingKey, Header}; + use rand::rngs::OsRng; + use rsa::traits::PublicKeyParts; + use rsa::{pkcs1::LineEnding, pkcs8::EncodePrivateKey, RsaPrivateKey, RsaPublicKey}; + use serde::{Deserialize, Serialize}; + use wiremock::matchers::{method, path}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + #[derive(Debug, Serialize, Deserialize)] + pub struct TestClaims { + pub sub: String, + pub exp: usize, + pub iss: String, + pub aud: Vec, + pub iat: usize, + pub custom_claims: CustomClaims, + } + + #[tokio::test] + async fn test_fetch_jwks() { + let mock_server = MockServer::start().await; + + let jwks_response = r#" + { + "keys": [ + { + "kty": "RSA", + "kid": "m3eP7rPCsf3hgtWU1seMZ", + "use": "sig", + "n": "xokzqjjolOLqzFYs7bNVyTafNmfWT-9_i8dgV4OcnBaKP8Es5O5u4dHxx6pZoZYxUyZS0-IOc5w3Em3g2PYUS4TmwDD65nrNZEJwr-CbxwbUwEGkYtTNcQML1LHhkRnGvxdwl_3st5HFBPRHnU5y8hMgke_nMTqrUTLaS7Px7v5lpt_nNH60FnfPJqcgD6pG4TG510HhnLV0ELDCqD8F79omuuqwqgntQLr-XR7mw_2PfMV8QdMx-kcwtVVhBeM5hr-KdKAQ-56MbU5GAke7kZJt94_2DHv8wpmQtlmKuIOEBFJNoS3prisdXmlmP6qKDSGufRNg3x5wJ-di0IlIeQ", + "e": "AQAB", + "alg": "RS256" + } + ] + }"#; + + Mock::given(method("GET")) + .and(path("/.well-known/jwks.json")) + .respond_with(ResponseTemplate::new(200).set_body_string(jwks_response)) + .mount(&mock_server) + .await; + + let provider = JWTProvider::new( + "https://issuer.com".to_string(), + vec!["my_audience".to_string()], + Algorithm::RS256, + format!("{}/.well-known/jwks.json", &mock_server.uri()), + 3600, + ); + + provider.fetch_jwks().await.unwrap(); // Now we call fetch_jwks() directly + + let cache = provider.jwks_cache.read().await; + assert_eq!(cache.keys.len(), 1); + assert!(cache.keys.contains_key("m3eP7rPCsf3hgtWU1seMZ")); + } + + #[tokio::test] + async fn test_validate_jwt() { + let mut rng = OsRng; + let private_key = RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate a key"); + let public_key = RsaPublicKey::from(&private_key); + + let n_base64 = encode_config(public_key.n().to_bytes_be(), URL_SAFE_NO_PAD); + let e_base64 = encode_config(public_key.e().to_bytes_be(), URL_SAFE_NO_PAD); + + let mock_server = MockServer::start().await; + let jwks_response = format!( + r#" + {{ + "keys": [ + {{ + "kty": "RSA", + "kid": "m3eP7rPCsf3hgtWU1seMZ", + "use": "sig", + "n": "{}", + "e": "{}", + "alg": "RS256" + }} + ] + }}"#, + n_base64, e_base64 + ); + + Mock::given(method("GET")) + .and(path("/.well-known/jwks.json")) + .respond_with(ResponseTemplate::new(200).set_body_string(jwks_response)) + .mount(&mock_server) + .await; + + let now = Utc::now(); + let my_claims = Claims { + sub: "test_sub".to_string(), + exp: (now + Duration::hours(1)).timestamp() as usize, + iss: "https://issuer.com".to_string(), + aud: vec!["my_audience".to_string()], + iat: now.timestamp() as usize, + custom_claims: CustomClaims { + name: Some("test_user".to_string()), + nickname: Some("test_nickname".to_string()), + email: Some("123@gmail.com".to_string()), + email_verified: Some(true), + }, + }; + let encoding_key = + EncodingKey::from_rsa_pem(private_key.to_pkcs8_pem(LineEnding::LF).unwrap().as_bytes()) + .expect("Invalid private key"); + let mut header = Header::new(Algorithm::RS256); + header.kid = Some("m3eP7rPCsf3hgtWU1seMZ".to_string()); + let token = encode(&header, &my_claims, &encoding_key).expect("Failed to encode token"); + + let provider = JWTProvider::new( + "https://issuer.com".to_string(), + vec!["my_audience".to_string()], + Algorithm::RS256, + format!("{}/.well-known/jwks.json", &mock_server.uri()), + 3600, + ); + + provider.fetch_jwks().await.unwrap(); // Fetch keys first + + let validator = provider.get_validator(); // Get the validator instance + + let token_data = validator + .await + .validate_token(&token) + .await + .expect("Token validation failed"); + + assert_eq!( + token_data.claims.custom_claims.name, + Some("test_user".to_string()) + ); + assert_eq!( + token_data.claims.custom_claims.nickname, + Some("test_nickname".to_string()) + ); + assert_eq!( + token_data.claims.custom_claims.email, + Some("123@gmail.com".to_string()) + ); + assert_eq!(token_data.claims.custom_claims.email_verified, Some(true)); + } +} diff --git a/websocket/crates/infra/src/auth/jwt_validator.rs b/websocket/crates/infra/src/auth/jwt_validator.rs new file mode 100644 index 000000000..e11e99070 --- /dev/null +++ b/websocket/crates/infra/src/auth/jwt_validator.rs @@ -0,0 +1,63 @@ +use super::error::{AuthError, Result}; +use super::types::{Claims, Jwk}; +use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, TokenData, Validation}; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; + +#[async_trait::async_trait] +pub trait JWTValidatorTrait { + async fn validate_token(&self, token: &str) -> Result>; +} + +pub struct JWTValidator { + iss: String, + aud: Vec, + alg: Algorithm, + jwks: Arc>>, +} + +impl JWTValidator { + pub fn new( + iss: String, + aud: Vec, + alg: Algorithm, + jwks: Arc>>, + ) -> Self { + JWTValidator { + iss, + aud, + alg, + jwks, + } + } +} + +#[async_trait::async_trait] +impl JWTValidatorTrait for JWTValidator { + // Validate the JWT token + async fn validate_token(&self, token: &str) -> Result> { + let header = + decode_header(token).map_err(|e| AuthError::InvalidTokenHeader(e.to_string()))?; + let kid = header.kid.ok_or(AuthError::NoKidInTokenHeader)?; + + // Get JWK from the cache + let jwks = self.jwks.read().await; + let jwk = jwks + .get(&kid) + .ok_or_else(|| AuthError::NoMatchingJwk(kid.clone()))?; + + // Create the decoding key + let decoding_key = DecodingKey::from_rsa_components(&jwk.n, &jwk.e) + .map_err(|e| AuthError::InvalidJwkKey(e.to_string()))?; + + // Create validation object + let mut validation = Validation::new(self.alg); + validation.set_audience(&self.aud); + validation.set_issuer(&[&self.iss]); + + // Validate the token + decode::(token, &decoding_key, &validation) + .map_err(|err| AuthError::TokenValidationFailed(err.to_string())) + } +} diff --git a/websocket/crates/infra/src/auth/mod.rs b/websocket/crates/infra/src/auth/mod.rs new file mode 100644 index 000000000..d1709e052 --- /dev/null +++ b/websocket/crates/infra/src/auth/mod.rs @@ -0,0 +1,5 @@ +pub mod error; +pub mod jwt; +pub mod jwt_validator; +pub mod types; +//todo: add auth module to ws connection diff --git a/websocket/crates/infra/src/auth/types.rs b/websocket/crates/infra/src/auth/types.rs new file mode 100644 index 000000000..0ba32696b --- /dev/null +++ b/websocket/crates/infra/src/auth/types.rs @@ -0,0 +1,35 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub sub: String, + pub exp: usize, + pub iss: String, + pub aud: Vec, + pub iat: usize, + pub custom_claims: CustomClaims, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct CustomClaims { + pub name: Option, + pub nickname: Option, + pub email: Option, + pub email_verified: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Jwk { + pub kid: String, + pub kty: String, + pub use_: Option, + pub alg: Option, + pub n: String, + pub e: String, + pub x5t: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Jwks { + pub keys: Vec, +} diff --git a/websocket/crates/infra/src/lib.rs b/websocket/crates/infra/src/lib.rs index d55e273fc..e1c45e077 100644 --- a/websocket/crates/infra/src/lib.rs +++ b/websocket/crates/infra/src/lib.rs @@ -1,2 +1,3 @@ +pub mod auth; pub mod persistence; pub mod socket;