From 219fab288bd8df6b4b020dd5e0d52890c2eddab1 Mon Sep 17 00:00:00 2001 From: Panagiotis Date: Wed, 18 Sep 2024 10:02:55 +0300 Subject: [PATCH] feat: add Github oauth provider --- Cargo.lock | 551 ++++++++++++++++++++++++++-- Cargo.toml | 24 +- proto/empty.proto | 5 + proto/tailcall.proto | 28 ++ src/lib.rs | 13 + src/main.rs | 45 ++- src/services/github_auth_service.rs | 150 ++++++++ src/services/github_service.rs | 5 + src/services/mod.rs | 1 + 9 files changed, 777 insertions(+), 45 deletions(-) create mode 100644 proto/empty.proto create mode 100644 src/services/github_auth_service.rs diff --git a/Cargo.lock b/Cargo.lock index c8364be..06a9925 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,8 +90,6 @@ dependencies = [ "http", "http-body", "http-body-util", - "hyper", - "hyper-util", "itoa", "matchit", "memchr", @@ -100,15 +98,10 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", "sync_wrapper 1.0.1", - "tokio", "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -129,7 +122,6 @@ dependencies = [ "sync_wrapper 0.1.2", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -159,6 +151,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -186,6 +184,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "deranged" version = "0.3.11" @@ -207,6 +221,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -241,6 +264,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -416,6 +454,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", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.5.1" @@ -429,6 +484,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.7" @@ -449,6 +520,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -469,6 +550,12 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + [[package]] name = "itertools" version = "0.13.0" @@ -484,6 +571,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -562,6 +658,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -593,6 +706,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -647,6 +804,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "powerfmt" version = "0.2.0" @@ -817,6 +980,64 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -836,6 +1057,46 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -848,6 +1109,38 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.209" @@ -870,9 +1163,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -880,16 +1173,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -951,6 +1234,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[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.77" @@ -973,15 +1268,41 @@ 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", + "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 = "tailcall-launchpad" version = "0.1.0" dependencies = [ - "axum", "dotenvy", "prost", "regex", + "reqwest", + "serde", + "serde_json", "thiserror", "time", "tokio", @@ -1056,6 +1377,21 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -1084,6 +1420,27 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "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-stream" version = "0.1.16" @@ -1202,7 +1559,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1264,12 +1620,44 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.10.0" @@ -1286,6 +1674,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "want" version = "0.3.1" @@ -1301,6 +1695,83 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1323,6 +1794,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[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", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[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", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1425,3 +1926,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 6189653..6d17e58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,26 @@ tokio = { version = "1.0", features = [ "sync", ] } tokio-stream = { version = "0.1.15" } -tracing = { version = "0.1.40" } +tracing = { workspace = true } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -time = { version = "0.3.36", features = [] } -thiserror = { version = "1.0.63", features = [] } dotenvy = { version = "0.15.7" } -uuid = { version = "1.10.0", features = ["v4", "serde"] } -regex = { version = "1.10.6" } -axum = { version = "0.7.5" } +thiserror = { workspace = true } +time = { workspace = true } +uuid = { workspace = true } +regex = { workspace = true } +reqwest = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true } [build-dependencies] tonic-build = "0.12" + +[workspace.dependencies] +tracing = { version = "0.1.40" } +thiserror = { version = "1.0.63" } +time = { version = "0.3.36" } +uuid = { version = "1.10.0", features = ["v4", "serde"] } +regex = { version = "1.10.6" } +reqwest = { version = "0.12.7", features = ["json"] } +serde_json = { version = "1.0.128" } +serde = { version = "1.0", features = ["derive"] } diff --git a/proto/empty.proto b/proto/empty.proto new file mode 100644 index 0000000..906a5dc --- /dev/null +++ b/proto/empty.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package tailcall; + +message Empty { } \ No newline at end of file diff --git a/proto/tailcall.proto b/proto/tailcall.proto index c0a40aa..dd8a87d 100644 --- a/proto/tailcall.proto +++ b/proto/tailcall.proto @@ -1,4 +1,5 @@ syntax = "proto3"; +import "empty.proto"; package tailcall; @@ -32,3 +33,30 @@ enum GithubStatusEnum { Error = 1; Deployed = 2; } + +service GithubAuthService { + rpc Start(LoginRequest) returns (LoginLinkResponse); + rpc GetAccessToken(GetAccessTokenRequest) returns (GetAccessTokenResponse); + rpc UserInfo(Empty) returns (UserInfoResponse); +} + +message LoginRequest { + string state = 1; +} + +message LoginLinkResponse { + string url = 1; +} + +message GetAccessTokenRequest { + string access_code = 1; +} + +message GetAccessTokenResponse { + string access_token = 1; +} + +message UserInfoResponse { + int64 id = 1; + string username = 2; +} diff --git a/src/lib.rs b/src/lib.rs index 8daa86b..f6a452f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,19 @@ enum AppError { Simple(String), #[error("IO Error: {0}")] IoError(#[from] std::io::Error), + #[error("Remote Error: {0}")] + RemoteRequestError(#[from] reqwest::Error), } type AppResult = Result; + +// TODO: add logging to make debugging easier +impl From for tonic::Status { + fn from(value: AppError) -> Self { + match value { + AppError::Simple(error) => tonic::Status::aborted(error), + AppError::IoError(_error) => tonic::Status::internal("IO Error"), + AppError::RemoteRequestError(_error) => tonic::Status::internal("Remote Request Error"), + } + } +} diff --git a/src/main.rs b/src/main.rs index 3fc81bf..a1fd736 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,13 @@ +use std::env; use tailcall_launchpad::{ - proto::github_service_server::GithubServiceServer, - services::github_service::GithubDeploymentService, + proto::{ + github_auth_service_server::GithubAuthServiceServer, + github_service_server::GithubServiceServer, + }, + services::{ + github_auth_service::{auth_interceptor, GithubAuthService}, + github_service::GithubDeploymentService, + }, }; use tonic::transport::Server; use tracing_subscriber::prelude::*; @@ -24,7 +31,16 @@ async fn main() { } // initialize services - let github_deployment_service = GithubServiceServer::new(GithubDeploymentService::default()); + let github_deployment_service = + GithubServiceServer::with_interceptor(GithubDeploymentService::default(), auth_interceptor); + + let client_id = env::var("OAUTH_CLIENT_ID").expect("OAUTH_CLIENT_ID is not set in .env file"); + let client_secret = + env::var("OAUTH_CLIENT_SECRET").expect("OAUTH_CLIENT_SECRET is not set in .env file"); + let github_auth_service = GithubAuthServiceServer::with_interceptor( + GithubAuthService::new(&client_id, &client_secret), + auth_interceptor, + ); // reflection service let reflection_service = tonic_reflection::server::Builder::configure() @@ -32,23 +48,18 @@ async fn main() { .build_v1() .unwrap(); - // start server - let grpc_service = Server::builder() - .add_service(reflection_service) - .add_service(github_deployment_service) - .into_service() - .into_axum_router(); - - run(grpc_service).await; -} - -async fn run(router: axum::Router) { // extract important config variables - use std::env; let host = env::var("SERVER_HOST").expect("SERVER_HOST is not set in .env file"); let port = env::var("SERVER_PORT").expect("SERVER_PORT is not set in .env file"); let addr = format!("{host}:{port}"); - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - axum::serve(listener, router).await.unwrap(); + println!("server running {}", addr); + // start server + Server::builder() + .add_service(reflection_service) + .add_service(github_deployment_service) + .add_service(github_auth_service) + .serve(addr.parse().unwrap()) + .await + .unwrap(); } diff --git a/src/services/github_auth_service.rs b/src/services/github_auth_service.rs new file mode 100644 index 0000000..b720e5e --- /dev/null +++ b/src/services/github_auth_service.rs @@ -0,0 +1,150 @@ +use regex::Regex; +use reqwest::StatusCode; +use serde::Deserialize; +use tonic::{Request, Response, Status}; + +use crate::{ + proto::{ + github_auth_service_server, Empty, GetAccessTokenRequest, GetAccessTokenResponse, + LoginLinkResponse, LoginRequest, UserInfoResponse, + }, + AppError, +}; + +#[derive(Debug)] +pub struct GithubAuthService { + client_id: String, + client_secret: String, +} + +impl GithubAuthService { + pub fn new(client_id: &str, client_secret: &str) -> Self { + Self { + client_id: client_id.to_string(), + client_secret: client_secret.to_string(), + } + } +} + +#[tonic::async_trait] +impl github_auth_service_server::GithubAuthService for GithubAuthService { + async fn start( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new(LoginLinkResponse { + url: format!( + "https://github.com/login/oauth/authorize?client_id={}&state={}", + self.client_id, + request.into_inner().state + ), + })) + } + + async fn get_access_token( + &self, + request: Request, + ) -> Result, Status> { + let access_token = get_access_token( + &self.client_id, + &self.client_secret, + &request.into_inner().access_code, + ) + .await?; + Ok(Response::new(GetAccessTokenResponse { access_token })) + } + + async fn user_info( + &self, + request: Request, + ) -> Result, Status> { + let extension = request.extensions().get::().unwrap(); + + match &extension.bearer { + Some(access_token) => { + let user = get_user(&self.client_id, &self.client_secret, access_token).await?; + Ok(Response::new(user)) + } + None => Err(Status::unauthenticated("The request is not authorized")), + } + } +} + +async fn get_access_token( + client_id: &str, + client_secret: &str, + access_code: &str, +) -> Result { + let url = format!( + "https://github.com/login/oauth/access_token?client_id={}&client_secret={}&code={}", + client_id, client_secret, access_code + ); + let res = reqwest::get(url).await?; + let body = res.text().await?; + let re = Regex::new("access_token=([a-z_A-Z0-9]+)").unwrap(); + let captures = re.captures(&body).unwrap(); + Ok(captures[1].to_string()) +} + +pub fn auth_interceptor(mut req: Request<()>) -> Result, Status> { + let bearer = match req.metadata().get("authorization") { + Some(bearer) => bearer + .to_str() + .map_err(|_| Status::invalid_argument("`authorization` header is bad formatted"))? + .split(" ") + .last() + .map(|bearer| bearer.to_string()), + None => None, + }; + + req.extensions_mut().insert(AuthExtension { bearer }); + + Ok(req) +} + +#[derive(Clone)] +struct AuthExtension { + pub bearer: Option, +} + +async fn get_user( + client_id: &str, + client_secret: &str, + access_token: &str, +) -> Result { + let url = format!("https://api.github.com/applications/{}/token", client_id); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .body(format!("{{\"access_token\":\"{}\"}}", access_token)) + .basic_auth(client_id, Some(client_secret)) + .header("Content-Type", "application/json") + .header("User-Agent", "Tailcall Launchpad") + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .send() + .await?; + if res.status() == StatusCode::OK { + let json: AuthInfoJson = res.json().await.unwrap(); + + Ok(UserInfoResponse { + id: json.user.id, + username: json.user.login, + }) + } else { + Err(AppError::Simple("Could not fetch user data.".to_string())) + } +} + +#[derive(Deserialize)] +pub struct AuthInfoJson { + pub id: i64, + pub user: UserInfoJson, +} + +#[derive(Deserialize)] +pub struct UserInfoJson { + pub id: i64, + pub login: String, +} diff --git a/src/services/github_service.rs b/src/services/github_service.rs index 545eb92..77b9d3c 100644 --- a/src/services/github_service.rs +++ b/src/services/github_service.rs @@ -72,5 +72,10 @@ async fn send_error(message_channel: &MessageChannel, err: AppError) { .send_status(Status::cancelled("IO Error")) .await } + AppError::RemoteRequestError(_) => { + message_channel + .send_status(Status::cancelled("Remote Error")) + .await + } }; } diff --git a/src/services/mod.rs b/src/services/mod.rs index be7e04b..1022971 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1 +1,2 @@ +pub mod github_auth_service; pub mod github_service;