From 6d6fd9da1db988f5b0de8c2cf48f2ccc4d4094fc Mon Sep 17 00:00:00 2001 From: xtrm Date: Wed, 31 Jan 2024 05:15:23 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20wip(client/user):=20added=20oaut?= =?UTF-8?q?h2=20example,=20working=20on=20user=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: xtrm --- Cargo.lock | 819 +++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 +- README.md | 16 + examples/oauth2.rs | 78 +++++ src/client.rs | 172 ++++++++-- src/error.rs | 10 +- src/models/mod.rs | 3 +- src/models/token.rs | 22 +- src/models/user.rs | 37 ++ 9 files changed, 1113 insertions(+), 50 deletions(-) create mode 100644 examples/oauth2.rs create mode 100644 src/models/user.rs diff --git a/Cargo.lock b/Cargo.lock index 3d34d40..2650516 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -32,6 +41,54 @@ dependencies = [ "libc", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -59,6 +116,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + [[package]] name = "bitflags" version = "1.3.2" @@ -77,6 +140,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytemuck" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" + [[package]] name = "bytes" version = "1.5.0" @@ -112,6 +181,17 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "cookie" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -128,6 +208,54 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "devise" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" +dependencies = [ + "bitflags 2.4.2", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -159,6 +287,20 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "figment" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6e5bc7bd59d60d0d45a6ccab6cf0f4ce28698fb4e81e750ddf229c9b824026" +dependencies = [ + "atomic 0.6.0", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + [[package]] name = "fnv" version = "1.0.7" @@ -195,11 +337,29 @@ version = "0.0.3" dependencies = [ "chrono", "log", + "once_cell", + "render", "reqwest", + "rocket", "serde", "serde_json", "thiserror", "tokio", + "urlencoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] @@ -209,6 +369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -217,6 +378,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + [[package]] name = "futures-sink" version = "0.3.30" @@ -235,10 +402,39 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -247,6 +443,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "h2" version = "0.3.24" @@ -390,14 +592,32 @@ checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" dependencies = [ "equivalent", "hashbrown", + "serde", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "itoa" version = "1.0.10" @@ -447,6 +667,30 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.1" @@ -479,6 +723,26 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -497,6 +761,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -554,7 +828,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -575,6 +849,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -598,6 +878,29 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "pear" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e22670e8eb757cff11d6c199ca7b987f352f0346e0be4dd23869ec72cb53c77" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.48", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -622,6 +925,18 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.78" @@ -631,6 +946,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "version_check", + "yansi", +] + [[package]] name = "quote" version = "1.0.35" @@ -640,6 +968,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -649,6 +1007,90 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "ref-cast" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "render" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07528d0483b1b4727b1419c82ed4be46926971b08af764d9d2ef0ac04a47e202" +dependencies = [ + "render_macros", +] + +[[package]] +name = "render_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eec48d491a1f66f67c50545566b74a8c8435fe4b7dbf163d15a1e391aa4a2a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "reqwest" version = "0.11.23" @@ -687,6 +1129,88 @@ dependencies = [ "winreg", ] +[[package]] +name = "rocket" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150" +dependencies = [ + "async-stream", + "async-trait", + "atomic 0.5.3", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap", + "log", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "serde_json", + "state", + "tempfile", + "time", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c" +dependencies = [ + "devise", + "glob", + "indexmap", + "proc-macro2", + "quote", + "rocket_http", + "syn 2.0.48", + "unicode-xid", + "version_check", +] + +[[package]] +name = "rocket_http" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e" +dependencies = [ + "cookie", + "either", + "futures", + "http", + "hyper", + "indexmap", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time", + "tokio", + "uncased", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -706,6 +1230,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.16" @@ -721,6 +1251,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -767,7 +1303,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -781,6 +1317,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -793,6 +1338,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -827,6 +1381,41 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.48" @@ -889,7 +1478,46 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", ] [[package]] @@ -934,7 +1562,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -947,6 +1575,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -961,6 +1600,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -974,9 +1647,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -984,6 +1669,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -992,6 +1707,25 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ubyte" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "serde", + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -1013,6 +1747,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "url" version = "2.5.0" @@ -1024,12 +1764,30 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "valuable" +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 = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "want" version = "0.3.1" @@ -1066,7 +1824,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -1100,7 +1858,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1121,6 +1879,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1262,6 +2051,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -1271,3 +2069,12 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] + +[[package]] +name = "yansi" +version = "1.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +dependencies = [ + "is-terminal", +] diff --git a/Cargo.toml b/Cargo.toml index af2bda5..5da7019 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,13 @@ rust-version = "1.56" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" log = "0.4" -reqwest = "0.11" +reqwest = { version = "0.11", features = ["json"] } thiserror = "1.0" chrono = "0.4.33" +urlencoding = "2.1.3" [dev-dependencies] +once_cell = "1.19.0" +render = "0.3" +rocket = { version = "0.5.0", features = ["serde_json"] } tokio = { version = "1.13", features = ["full"] } diff --git a/README.md b/README.md index 63ccd50..d40e083 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ # ft-rs > *Because of course it starts with `ft`* + +A practical wrapper for the [42 API](https://api.intra.42.fr), written in Rust. + +> [!WARNING] +> This is WIP and early in development, expect errors, dumb code, and breaking changes every 5 minutes. +> I'm currently in my testing phase, this isn't even *test ready*, so don't think about *production* yet... + +## Todolist + +- [ ] Proper filtering options +- [ ] Oauth2 handholding? +- [ ] Better project structure + +## License + +This project is released under the [BSD Zero Clause](./LICENSE), go wild. \ No newline at end of file diff --git a/examples/oauth2.rs b/examples/oauth2.rs new file mode 100644 index 0000000..eca6d94 --- /dev/null +++ b/examples/oauth2.rs @@ -0,0 +1,78 @@ +#![allow(unused_braces)] + +use std::time::Duration; + +use ft_rs::{models::token::AccessToken, FtClient}; +use once_cell::sync::Lazy; +use render::html; +use rocket::{get, http::{Cookie, CookieJar}, response::{content::RawHtml, Redirect}, routes, time::OffsetDateTime, uri}; + +const CALLBACK_URL: &str = "http://localhost:1337/oauth/callback"; +const SCOPES: &[&str] = &["public", "projects", "profile"]; +static CLIENT: Lazy = Lazy::new(|| { + FtClient::from_app( + std::env::var("FT_RS_TEST_UID").expect("FT_RS_TEST_UID not set"), + std::env::var("FT_RS_TEST_SECRET").expect("FT_RS_TEST_SECRET not set") + ).expect("Couldn't build the client") +}); + + +#[derive(rocket::response::Responder)] +#[response(status = 303)] +struct CustomRedirect<'a> { + inner: Redirect, + access_token: Cookie<'a>, +} + +#[get("/")] +async fn index(cookies: &CookieJar<'_>) -> RawHtml { + if let Some(token) = cookies.get("access_token") { + let token = token.value(); + let token = serde_json::from_str::(token).unwrap(); + let user = CLIENT.fetch_user_data(&token).await.unwrap(); + + RawHtml(html! { +
+

{"Hello, world!"}

+

{"You are logged in as "}{user.login}{"."}

+ {"Profile +
+ }) + } else { + let auth_url = CLIENT.get_authorization_url(CALLBACK_URL, SCOPES); + RawHtml(html! { +
+

{"Hello, world!"}

+ {"Login"} +
+ }) + } +} + +#[get("/oauth/callback?")] +async fn callback<'a>(code: String) -> CustomRedirect<'a> { + let token = CLIENT.fetch_access_token(&code, CALLBACK_URL).await.unwrap(); + let token = serde_json::to_string(&token).unwrap(); + + CustomRedirect { + inner: Redirect::to(uri!(index)), + access_token: Cookie::build(("access_token", token)) + .path("/") + .expires(OffsetDateTime::now_utc() + Duration::from_secs(15 * 60)) + .http_only(true) + .build() + } +} + +#[tokio::main] +async fn main() { + let config = rocket::Config { + port: 1337, + ..Default::default() + }; + + let _ = rocket::custom(config) + .mount("/", routes![index, callback]) + .launch() + .await; +} \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index a6395c4..10a2f7c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ use reqwest::Client; +use rocket::time::ext; use std::time::Duration; use crate::{models::{self, token::AccessToken}, FtError, Result}; @@ -12,15 +13,24 @@ macro_rules! endpoint { ($path:expr) => { format!("{}{}", API_URL, $path) }; } +enum AuthType { + App { + uid: String, + secret: String, + + last_token: Option + }, + User { + token: AccessToken + } +} + /// The client struct, used to make requests to the API. /// /// You can create a client with the [`from_app`](#method.from_app) method. pub struct FtClient { - app_uid: String, - app_secret: String, - + auth_type: AuthType, client: Client, - last_valid_token: Option } impl FtClient { @@ -38,7 +48,7 @@ impl FtClient { pub fn from_app, S: Into>( app_uid: U, app_secret: S, - ) -> crate::Result { + ) -> Result { let app_uid = app_uid.into(); let app_secret = app_secret.into(); @@ -51,16 +61,60 @@ impl FtClient { Err(FtError::ReqwestBuilderError(err)) } else { Ok(Self { - app_uid, - app_secret, - + auth_type: AuthType::App { + uid: app_uid, + secret: app_secret, + last_token: None + }, + client: client.unwrap(), + }) + } + } + + pub fn from_user(token: AccessToken) -> Result { + let client = reqwest::ClientBuilder::new() + .user_agent(format!("{}/{}", PKG_NAME, PKG_VERSION)) + .connect_timeout(Duration::from_secs(30)) + .build(); + + if let Err(err) = client { + Err(FtError::ReqwestBuilderError(err)) + } else { + Ok(Self { + auth_type: AuthType::User { + token + }, client: client.unwrap(), - last_valid_token: None }) } } - /// Fetches a new access token from the API. + async fn handle_error(&self, res: reqwest::Response) -> Result<()> { + let status = res.status().as_u16(); + let text = res.text().await?; + let json_object = serde_json::from_str::(&text)?; + + let error = json_object.get("error") + .unwrap_or(&serde_json::Value::Null) + .as_str() + .unwrap_or("unknown"); + let error_description = json_object.get("error_description") + .unwrap_or(&serde_json::Value::Null) + .as_str() + .unwrap_or("No description provided."); + let status = json_object.get("status") + .unwrap_or(&serde_json::Value::Null) + .as_u64() + .unwrap_or(status as _); + + Err(FtError::from_api_error( + error.parse()?, + status, + error_description.to_string() + )) + } + + /// Fetches a new app access token from the API. /// /// This method will return the last valid token if it is still valid, as per the API's documentation. /// @@ -74,51 +128,93 @@ impl FtClient { /// #[tokio::main] /// async fn main() -> ft_rs::Result<()> { /// let client = FtClient::from_app("my_uid", "my_super_secret_secret")?; - /// let token = client.fetch_token().await?; + /// let token = client.fetch_app_token().await?; /// println!("Token: {:?}", token); /// Ok(()) /// } /// ``` - pub async fn fetch_token(&self) -> Result { + pub async fn fetch_app_token(&self, extra_data: bool) -> Result { + if let AuthType::App { uid, secret, .. } = &self.auth_type { + let res = self.client + .post(endpoint!("/oauth/token")) + .form(&[ + ("grant_type", "client_credentials"), + ("client_id", &uid), + ("client_secret", &secret) + ]) + .send() + .await?; + + if res.status().is_success() { + let token = res.json::().await?; + if extra_data { + unimplemented!("Extra data is not implemented yet."); + } + Ok(token) + } else { + self.handle_error(res).await?; + unreachable!() + } + } else { + Err(FtError::InvalidAuthType) + } + } + + /// Ensures that the current token is still valid, and fetches a new one if it is not. + /// + /// This method is called automatically by the API Client when making a request, so there is no need to call it manually. + pub async fn ensure_app_token(&mut self) -> Result<()> { + self.auth_type.try_refresh_token(self).await?; + Ok(()) + } + + pub fn get_authorization_url(&self, callback_url: &str, scopes: &[&str]) -> String { + format!( + "{}/oauth/authorize?client_id={}&redirect_uri={}&scope={}&response_type=code", + API_URL, + self.app_uid, + urlencoding::encode(callback_url), + scopes.join(" ") + ) + } + + pub async fn fetch_access_token(&self, code: &str, callback_url: &str) -> Result { let res = self.client .post(endpoint!("/oauth/token")) .form(&[ - ("grant_type", "client_credentials"), + ("grant_type", "authorization_code"), ("client_id", &self.app_uid), - ("client_secret", &self.app_secret) + ("client_secret", &self.app_secret), + ("code", code), + ("redirect_uri", callback_url), ]) .send() .await?; - let text = res.text().await?; - let json_object = serde_json::from_str::(&text)?; - if let Some(error) = json_object.get("error") { - let error = error.as_str().unwrap(); - let error_description = json_object.get("error_description") - .unwrap_or(&serde_json::Value::Null) - .as_str() - .unwrap_or("No description provided."); - Err(FtError::from_api_error( - error.parse()?, - error_description.to_string() - )) - } else { - let token = serde_json::from_str::(&text)?; + if res.status().is_success() { + let token = res.json::().await?; Ok(token) + } else { + self.handle_error(res).await?; + unreachable!() } } - /// Ensures that the last valid token is still valid, and fetches a new one if it is not. - /// - /// This method is called automatically by the API Client when making a request, so there is no need to call it manually. - pub async fn ensure_valid_token(&mut self) -> Result<()> { - if let Some(token) = &self.last_valid_token { - if token.is_expired() { - self.last_valid_token = Some(self.fetch_token().await?); - } + pub async fn fetch_user_data(&self, token: &AccessToken) -> Result { + let res = self.client + .get(endpoint!("/v2/me")) + .bearer_auth(token.access_token.clone()) + .send() + .await?; + + if res.status().is_success() { + let text = res.text().await?; + println!("{}", text); + let user = serde_json::from_str::(&text)?; + Ok(user) } else { - self.last_valid_token = Some(self.fetch_token().await?); + self.handle_error(res).await?; + unreachable!() } - Ok(()) } } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 063b84f..6906423 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,17 +40,23 @@ pub enum FtError { #[from] source: serde_json::Error }, - #[error("API error: {error:?}: {error_description:?}")] + + #[error("Invalid auth type, tried to use a user token with an app client or vice-versa")] + InvalidAuthType, + + #[error("API error {error_status:?}: {error:?}: {error_description:?}")] ApiError { error: ErrorType, + error_status: u64, error_description: String }, } impl FtError { - pub fn from_api_error(error: ErrorType, error_description: String) -> Self { + pub fn from_api_error(error: ErrorType, status: u64, error_description: String) -> Self { Self::ApiError { error, + error_status: status, error_description } } diff --git a/src/models/mod.rs b/src/models/mod.rs index 2108f68..05e678b 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1 +1,2 @@ -pub mod token; \ No newline at end of file +pub mod token; +pub mod user; \ No newline at end of file diff --git a/src/models/token.rs b/src/models/token.rs index 0782a3c..5189e5e 100644 --- a/src/models/token.rs +++ b/src/models/token.rs @@ -2,13 +2,31 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug)] pub struct AccessToken { - access_token: String, + pub(crate) access_token: String, token_type: String, expires_in: u64, scope: String, + created_at: u64, + + token_info: Option +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TokenInfo { + resource_owner_id: u32, + scopes: Vec, + expires_in_seconds: u64, + application: ApplicationInfo, created_at: u64 } +#[derive(Deserialize, Serialize, Debug)] +pub struct ApplicationInfo { + uid: String, + name: String, + redirect_uri: String +} + impl AccessToken { /// Checks if the token is expired. /// @@ -18,6 +36,6 @@ impl AccessToken { /// /// This method will panic if the system's time is not set correctly. pub fn is_expired(&self) -> bool { - self.created_at + self.expires_in <= (chrono::Utc::now().timestamp() + 5).try_into().unwrap() + self.created_at + self.expires_in <= (chrono::Utc::now().timestamp() as u64 + 5) } } \ No newline at end of file diff --git a/src/models/user.rs b/src/models/user.rs new file mode 100644 index 0000000..3616501 --- /dev/null +++ b/src/models/user.rs @@ -0,0 +1,37 @@ +#![allow(non_snake_case)] + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct UserImageData { + pub link: String, + pub versions: UserImageVersions +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct UserImageVersions { + pub large: String, + pub medium: String, + pub small: String, + pub micro: String +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct User { + pub id: u32, + pub email: String, + pub login: String, + pub first_name: String, + pub last_name: String, + pub usual_full_name: Option, + pub usual_first_name: Option, + pub url: String, + pub phone: String, + pub displayname: String, + pub kind: String, //TODO: enum + pub image: UserImageData, + #[serde(rename = "staff?")] // what the actual fuck + pub staff: bool, + pub correction_point: i32, + pub wallet: i32, +} \ No newline at end of file