From 56e4e22b4cb0af7868106bf4f89bc0efe6317864 Mon Sep 17 00:00:00 2001 From: shockrah Date: Tue, 30 Mar 2021 12:24:10 -0700 Subject: [PATCH] + JWT Authentication * Server JWT's and Client JWT's built with seperate signature types --- rtc-server/Cargo.lock | 441 +++++++++++++++++++++++++++++++++++-- rtc-server/Cargo.toml | 14 +- rtc-server/src/auth.rs | 87 ++++++++ rtc-server/src/channels.rs | 6 - rtc-server/src/main.rs | 110 +++++---- rtc-server/src/peers.rs | 51 +++++ 6 files changed, 643 insertions(+), 66 deletions(-) create mode 100644 rtc-server/src/auth.rs delete mode 100644 rtc-server/src/channels.rs create mode 100644 rtc-server/src/peers.rs diff --git a/rtc-server/Cargo.lock b/rtc-server/Cargo.lock index b7913cd..f448cbf 100644 --- a/rtc-server/Cargo.lock +++ b/rtc-server/Cargo.lock @@ -26,6 +26,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.0" @@ -47,6 +53,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + [[package]] name = "byteorder" version = "1.4.3" @@ -59,12 +71,31 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clap" version = "2.33.3" @@ -224,6 +255,31 @@ dependencies = [ "wasi", ] +[[package]] +name = "h2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + [[package]] name = "hermit-abi" version = "0.1.18" @@ -244,12 +300,53 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "hyper" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "idna" version = "0.2.2" @@ -261,6 +358,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "input_buffer" version = "0.4.0" @@ -277,10 +384,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] -name = "libc" -version = "0.2.90" +name = "js-sys" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" +checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +dependencies = [ + "base64 0.12.3", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "log" @@ -305,9 +441,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "mio" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", @@ -318,11 +454,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi", ] @@ -335,6 +470,36 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -345,12 +510,29 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.0", + "once_cell", + "regex", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -359,18 +541,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5" dependencies = [ "proc-macro2", "quote", @@ -465,16 +647,87 @@ dependencies = [ "rand_core", ] +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rtc-server" version = "0.1.0" dependencies = [ "clap", "futures", + "hyper", + "jsonwebtoken", + "lazy_static", + "serde", "tokio", "tokio-tungstenite", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.9.4" @@ -488,6 +741,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", +] + [[package]] name = "slab" version = "0.4.2" @@ -496,15 +760,20 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if", "libc", "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "strsim" version = "0.8.0" @@ -513,9 +782,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" dependencies = [ "proc-macro2", "quote", @@ -551,6 +820,16 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "tinyvec" version = "1.1.1" @@ -606,13 +885,59 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "tokio-util" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "tungstenite" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" dependencies = [ - "base64", + "base64 0.13.0", "byteorder", "bytes", "http", @@ -662,6 +987,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.2.1" @@ -692,12 +1023,86 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" + +[[package]] +name = "web-sys" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/rtc-server/Cargo.toml b/rtc-server/Cargo.toml index 930a440..192fea2 100644 --- a/rtc-server/Cargo.toml +++ b/rtc-server/Cargo.toml @@ -7,8 +7,20 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Async infra stuff here futures = "0.3" -tokio = { version = "1", default-features = false, features = ["rt", "rt-multi-thread", "net", "macros"] } +tokio = { version = "1", default-features = false, features = ["rt", "rt-multi-thread", "net", "macros", "fs"] } tokio-tungstenite = "0.14.0" +# Required for the private server interface +hyper = { version = "0.14", features = ["full"] } + +jsonwebtoken = "7" + +# For caching mysql pools +lazy_static = "1.4.0" + +# For serialization of data in events +serde = { version = "1.0", features = ["derive"] } + clap = "2.33.3" diff --git a/rtc-server/src/auth.rs b/rtc-server/src/auth.rs new file mode 100644 index 0000000..3d52a34 --- /dev/null +++ b/rtc-server/src/auth.rs @@ -0,0 +1,87 @@ +use std::time::{SystemTime, UNIX_EPOCH}; +use serde::{Serialize, Deserialize, de::DeserializeOwned}; +use jsonwebtoken::{decode, DecodingKey}; +use jsonwebtoken::{Validation, Algorithm}; +use lazy_static::lazy_static; + +use crate::peers::ConnectionType; + +lazy_static! { + // This hmac is the key we use to sign ONLY API <-> WSS communications + static ref API_HMAC_SECRET: Vec = { + std::fs::read("wss-hmac.secret").expect("[WSS FATAL] Couldn't get WSS HMAC secret") + }; + + static ref USER_HMAC: Vec = { + std::fs::read("hmac.secret").expect("[WSS FATAL] no user hmac.secret found") + }; +} + + +trait Claims { + /// Returns the unix timestamp in ms 0 if this field does not exist + fn time(&self) -> i64; + /// Returns user id of if one is present + fn sub(&self) -> Option; +} + +#[derive(Deserialize, Serialize)] +struct APIClaim; + +impl Claims for APIClaim { + fn time(&self) -> i64 { 0 } + fn sub(&self) -> Option { None } +} + + +#[derive(Debug, Serialize, Deserialize)] +struct UserClaim { + sub: u64, // user id + exp: i64, // expiry date + cookie: String, // unique cookie value +} + +impl Claims for UserClaim { + fn time(&self) -> i64 { self.exp } + fn sub(&self) -> Option { Some(self.sub) } +} + + + +fn verify_token(token: &str) -> Option where + T: DeserializeOwned + Claims +{ + let dk = DecodingKey::from_secret(&USER_HMAC); + let algo = Algorithm::HS512; + + if let Ok(decoded) = decode::(token, &dk, &Validation::new(algo)) { + let time = decoded.claims.time(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("[WSS UMMM] Couldn't get unix time") + .as_millis() as i64; + let active = now < time; + match active { + true => Some(ConnectionType::User), + false => None + } + } + // Only fallback to checking for a server connection since this basically never happens + // compared to the user connection branch + else { + let dk = DecodingKey::from_secret(&API_HMAC_SECRET); + if let Ok(_decoded) = decode::(token, &dk, &Validation::new(algo)) { + Some(ConnectionType::Server) + } else { + None + } + } +} + +pub fn verify(token: &str) -> Option { + match verify_token::(token) { + Some(user) => Some(user), + None => verify_token::(token) + } +} + diff --git a/rtc-server/src/channels.rs b/rtc-server/src/channels.rs deleted file mode 100644 index 49c13a9..0000000 --- a/rtc-server/src/channels.rs +++ /dev/null @@ -1,6 +0,0 @@ -// The concept of channels here is a bit different from - -struct VoiceCollection { -} -struct TextCollection { -} diff --git a/rtc-server/src/main.rs b/rtc-server/src/main.rs index 4f1249a..488117d 100644 --- a/rtc-server/src/main.rs +++ b/rtc-server/src/main.rs @@ -1,24 +1,29 @@ +mod auth; +mod peers; + use std::sync::{Arc, Mutex}; -use std::net::SocketAddr; use std::collections::HashMap; use futures::StreamExt; -use futures::channel::mpsc::UnboundedSender; use futures::channel::mpsc::unbounded; use futures::future; use futures::pin_mut; use futures::stream::TryStreamExt; use tokio_tungstenite as tokio_ws; -use tokio_ws::tungstenite::Message; use tokio_ws::tungstenite::handshake::server::ErrorResponse; use tokio_ws::tungstenite::http::{Response, Request}; use tokio::net::{TcpListener, TcpStream}; use clap::{Arg, App}; -type Tx = UnboundedSender; -type Peers = Arc>>; +use peers::{Peer, Channel, PeerMap, ConnectionType}; + +macro_rules! header_err { + ($s:literal) => { + Err(ErrorResponse::new(Some($s.to_string()))) + } +} #[tokio::main] async fn main() -> Result<(), std::io::Error> { @@ -33,73 +38,96 @@ async fn main() -> Result<(), std::io::Error> { .takes_value(true)) .get_matches(); - let addr = match matches.value_of("port") { + let connections: PeerMap = Arc::new(Mutex::new(HashMap::new())); + + // Websocket server initialization + let wsaddr = match matches.value_of("port") { Some(pval) => { let port = pval.parse::().unwrap(); format!("127.0.0.1:{}", port) }, None => format!("127.0.0.1:5648") }; + let wssocket = TcpListener::bind(&wsaddr).await?; + println!("[INFO] WSS Listening on {}", wsaddr); - let socket = TcpListener::bind(&addr).await?; - - println!("[INFO] Listening on {}", addr); - - let peers = Peers::new(Mutex::new(HashMap::new())); - - while let Ok((stream, _)) = socket.accept().await { - tokio::spawn(handle_connections(stream, peers.clone())); + while let Ok((stream, _)) = wssocket.accept().await { + tokio::spawn(handle_connections(stream, connections.clone())); } + Ok(()) } -fn header_validation(request: &Request<()>, response: Response<()>) -> Result, ErrorResponse> { - // validate that the required headers are presetn - // Required headers: Subscribe-Channel & Jwt-Token - let valid_channels = ["/text", "/voice"]; - let path = request.uri(); - - for (hdr, val) in request.headers().iter() { - println!("{:?} -> {:?}", hdr, val); - } - Ok(response) -} -async fn handle_connections(stream: TcpStream, peers: Peers) { +async fn handle_connections(stream: TcpStream, peermap: PeerMap) { let addr = stream.peer_addr().expect("[ERROR] Peer address not found"); // NOTE: this call underneath actually blocks which blows but it will do for now // NOTE: find some kind of way of doing async callbacks here so that we can scale - let ws_stream = tokio_ws::accept_hdr_async(stream, header_validation) - .await.expect("[ERROR] Could not finish handshake"); - println!("[INFO] New websocket connection: {}", addr.ip()); + + let mut domain: Option = None; + let ws_stream = tokio_ws::accept_hdr_async(stream, + |request:&Request<()>, response:Response<()>| -> Result, ErrorResponse> { + + domain = match request.uri().path() { + "/voice" => Some(Channel::Voice), + "/text" => Some(Channel::Text), + _ => None + }; + + println!("{:?}", request.headers()); + let entry = request.headers() + .iter().find(|(name, _)| name.as_str() == "jwt"); + + if let Some((_, jwt)) = entry { + match auth::verify(jwt.to_str().expect("Unable to convert header to str")) { + Some(_conn_type) => Ok(response), + None => panic!("[WSS] Unable to verify connection") + } + } else { + header_err!("JWT not found in header") + } + }).await; + + let ws_stream = match ws_stream { + Ok(stream) => { + println!("[WSS INFO] New connection established"); + stream + } + Err(e) => panic!(format!("[WSS ERROR] Could not finish handshake: {}", e)) + }; + let (tx, rx) = unbounded(); // split the peer's write and read streams - peers.lock().unwrap().insert(addr, tx); + let peer = Peer::new(tx, domain); // safe as the handshake fails with None domain + println!("{:?}", peer); + + // Add the new peer + peermap.lock().unwrap().insert(addr, peer); let (write, read) = ws_stream.split(); let broadcast_incoming = read.try_for_each(|msg| { - println!("Got message from {}: {}", addr, msg.to_text().unwrap()); - // hold a ref to the peers map so that we can iterate through them - // OPTI NOTE: because this runs next to the REST API it makes sense to avoid - // doing anything if the user-level peers try do anything but listen - let peers = peers.lock().unwrap(); + let peers = peermap.lock().unwrap(); + // TODO: restructure this so that the server connection + // never gets rans over to avoid this meme of a .collect + // collect everyone except the server connection let recipients = peers - .iter().filter(|(p_addr, _)| p_addr != &&addr ) // avoid echo back to sender - .map(|(_, ws_sink)| ws_sink); + .iter().filter(|(p_addr, meta)| p_addr != &&addr || meta.conn == ConnectionType::Server) + .map(|(_, sink)| sink); - for rec in recipients { - rec.unbounded_send(msg.clone()).expect("[WARN] Unable to send message"); + for recv in recipients { + println!("{:?}", recv); + recv.try_send_text(msg.clone()); } future::ok(()) }); - // magic + // magic let forward = rx.map(Ok).forward(write); pin_mut!(broadcast_incoming, forward); future::select(broadcast_incoming, forward).await; println!("{} dc'd", &addr); - peers.lock().unwrap().remove(&addr); + peermap.lock().unwrap().remove(&addr); } diff --git a/rtc-server/src/peers.rs b/rtc-server/src/peers.rs new file mode 100644 index 0000000..bf260cf --- /dev/null +++ b/rtc-server/src/peers.rs @@ -0,0 +1,51 @@ +use std::sync::{Arc, Mutex}; +use std::collections::HashMap; +use std::net::SocketAddr; +use futures::channel::mpsc::UnboundedSender; + +use tokio_tungstenite as tokio_ws; +use tokio_ws::tungstenite::Message; + + +pub type PeerMap = Arc>>; +type Tx = UnboundedSender; + +#[derive(Debug, PartialEq)] +pub enum ConnectionType { + User, + Server +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Channel { + Text, + Voice, +} + +#[derive(Debug)] +pub struct Peer { + transfer: Tx, + channel: Option, + pub conn: ConnectionType +} + +impl Peer { + pub fn new(transfer: Tx, channel: Option) -> Self { + Self { + transfer, + channel: channel.clone(), + conn: match channel { + Some(_) => ConnectionType::User, + _ => ConnectionType::Server + } + } + } + + pub fn try_send_text(&self, msg: Message) { + if self.channel == Some(Channel::Text) { + self.transfer.unbounded_send(msg) + .expect("[WSS-COMM-ERROR] Unable to notify peer"); + } + } +} +