From 2aa4449e3289892beba44d626e56ea83e1d638cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bardon?= Date: Sun, 25 Aug 2024 12:19:55 -0600 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Migrate=20from=20Rocket?= =?UTF-8?q?=20to=20Axum=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This new version is 100% compatible with the Rocket-based one - The project can now be compiled using the stable Rust toolchain --- src/Cargo.lock | 1130 ++++++----------- src/Cargo.toml | 10 +- src/helpers/Cargo.toml | 7 +- src/helpers/src/config.rs | 14 +- src/helpers/src/generate.rs | 8 +- src/helpers/src/lib.rs | 173 +-- src/helpers/src/readers/mod.rs | 1 - src/helpers/src/readers/object_reader.rs | 106 -- src/orangutan-server/Cargo.toml | 11 +- src/orangutan-server/src/main.rs | 219 ++-- src/orangutan-server/src/request_guards.rs | 251 +++- .../src/routes/auth_routes.rs | 177 --- .../src/routes/debug_routes.rs | 112 +- src/orangutan-server/src/routes/main_route.rs | 260 ++-- src/orangutan-server/src/routes/mod.rs | 18 +- .../src/routes/update_content_routes.rs | 19 +- src/orangutan-server/src/util/mod.rs | 73 +- src/orangutan-server/src/util/templating.rs | 9 +- src/orangutan-server/src/util/website_root.rs | 25 +- 19 files changed, 1104 insertions(+), 1519 deletions(-) delete mode 100644 src/helpers/src/readers/object_reader.rs delete mode 100644 src/orangutan-server/src/routes/auth_routes.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index f56bf31..ee9927b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -48,58 +48,112 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] -name = "async-stream" -version = "0.3.5" +name = "async-trait" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", + "proc-macro2", + "quote", + "syn 2.0.75", ] [[package]] -name = "async-stream-impl" -version = "0.3.5" +name = "autocfg" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] -name = "async-trait" -version = "0.1.81" +name = "axum" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", + "async-trait", + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "atomic" -version = "0.5.3" +name = "axum-core" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] [[package]] -name = "atomic" -version = "0.6.0" +name = "axum-extra" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" dependencies = [ - "bytemuck", + "axum", + "axum-core", + "bytes", + "cookie", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "autocfg" -version = "1.3.0" +name = "axum-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.75", +] [[package]] name = "backtrace" @@ -134,12 +188,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - [[package]] name = "biscuit-auth" version = "5.0.0" @@ -231,12 +279,6 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "bytemuck" -version = "1.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" - [[package]] name = "byteorder" version = "1.5.0" @@ -251,9 +293,12 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.8" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -271,8 +316,9 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -316,15 +362,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -388,66 +434,69 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] -name = "der" -version = "0.7.9" +name = "darling" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "const-oid", - "zeroize", + "darling_core", + "darling_macro", ] [[package]] -name = "deranged" -version = "0.3.11" +name = "darling_core" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ - "powerfmt", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.75", ] [[package]] -name = "deunicode" -version = "1.6.0" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.75", +] [[package]] -name = "devise" -version = "0.4.1" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "devise_codegen", - "devise_core", + "const-oid", + "zeroize", ] [[package]] -name = "devise_codegen" -version = "0.4.1" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "devise_core", - "quote", + "powerfmt", + "serde", ] [[package]] -name = "devise_core" -version = "0.4.1" +name = "deunicode" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.72", -] +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" [[package]] name = "digest" @@ -499,57 +548,18 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.0", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - [[package]] name = "fnv" version = "1.0.7" @@ -557,17 +567,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "futures" -version = "0.3.30" +name = "form_urlencoded" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "percent-encoding", ] [[package]] @@ -577,7 +582,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -586,12 +590,6 @@ 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" @@ -610,28 +608,10 @@ 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]] @@ -672,12 +652,6 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "globset" version = "0.4.14" @@ -703,23 +677,10 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.26" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" @@ -727,6 +688,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -741,9 +708,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -751,27 +718,34 @@ dependencies = [ ] [[package]] -name = "http" -version = "1.1.0" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "fnv", - "itoa", + "http", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "http 0.2.12", + "futures-util", + "http", + "http-body", "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + [[package]] name = "httparse" version = "1.9.4" @@ -795,26 +769,36 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.30" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2", - "http 0.2.12", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", "tokio", - "tower-service", - "tracing", - "want", ] [[package]] @@ -840,6 +824,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "ignore" version = "0.4.22" @@ -858,30 +848,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "equivalent", - "hashbrown", + "autocfg", + "hashbrown 0.12.3", "serde", ] [[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "is-terminal" -version = "0.4.12" +name = "indexmap" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.52.0", + "equivalent", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -910,9 +894,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -925,9 +909,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -935,43 +919,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[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" @@ -981,6 +934,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -993,6 +952,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1010,9 +979,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", @@ -1020,25 +989,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin", - "tokio", - "tokio-util", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -1074,16 +1024,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.36.3" @@ -1107,14 +1047,17 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "orangutan-helpers" -version = "0.1.1" +version = "0.2.0" dependencies = [ + "axum", "biscuit-auth", "hex", "lazy_static", - "rocket", + "serde", "serde_json", + "serde_with", "thiserror", + "tower-http", "tracing", ] @@ -1146,20 +1089,27 @@ dependencies = [ [[package]] name = "orangutan-server" -version = "0.4.13" +version = "0.5.0" dependencies = [ + "axum", + "axum-extra", "base64 0.22.1", "biscuit-auth", "chrono", "hex", "lazy_static", + "mime", "orangutan-helpers", "orangutan-refresh-token", - "rocket", + "serde", "serde_json", + "serde_urlencoded", "tera", "thiserror", "time", + "tokio", + "tower 0.5.0", + "tower-http", "tracing", "tracing-subscriber", "urlencoding", @@ -1171,29 +1121,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - [[package]] name = "parse-zoneinfo" version = "0.3.1" @@ -1203,29 +1130,6 @@ dependencies = [ "regex", ] -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.72", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1263,7 +1167,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1315,6 +1219,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1385,19 +1309,6 @@ 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.72", - "version_check", - "yansi", -] - [[package]] name = "prost" version = "0.10.4" @@ -1470,35 +1381,6 @@ dependencies = [ "getrandom 0.2.15", ] -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "regex" version = "1.10.6" @@ -1543,87 +1425,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" -[[package]] -name = "rocket" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" -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", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" -dependencies = [ - "devise", - "glob", - "indexmap", - "proc-macro2", - "quote", - "rocket_http", - "syn 2.0.72", - "unicode-xid", - "version_check", -] - -[[package]] -name = "rocket_http" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" -dependencies = [ - "cookie", - "either", - "futures", - "http 0.2.12", - "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.24" @@ -1639,19 +1440,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustversion" version = "1.0.17" @@ -1673,18 +1461,6 @@ dependencies = [ "winapi-util", ] -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.23" @@ -1693,29 +1469,29 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", @@ -1724,12 +1500,55 @@ dependencies = [ ] [[package]] -name = "serde_spanned" -version = "0.6.7" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.4.0", "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.75", ] [[package]] @@ -1766,13 +1585,10 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.2" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" @@ -1789,20 +1605,11 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - [[package]] name = "slug" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" dependencies = [ "deunicode", "wasm-bindgen", @@ -1824,12 +1631,6 @@ 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 = "spki" version = "0.7.3" @@ -1841,22 +1642,10 @@ dependencies = [ ] [[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" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -1877,9 +1666,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -1887,17 +1676,16 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.12.0" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "tera" @@ -1938,7 +1726,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1984,16 +1772,15 @@ dependencies = [ [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -2007,72 +1794,84 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] -name = "tokio-stream" -version = "0.1.15" +name = "tokio-util" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ + "bytes", "futures-core", + "futures-sink", "pin-project-lite", "tokio", ] [[package]] -name = "tokio-util" -version = "0.7.11" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "bytes", "futures-core", - "futures-sink", + "futures-util", + "pin-project", "pin-project-lite", "tokio", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "toml" -version = "0.8.19" +name = "tower" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "36b837f86b25d7c0d7988f00a54e74739be6477f2aac6201b8f429a7569991b7" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "tower-layer", + "tower-service", ] [[package]] -name = "toml_datetime" -version = "0.6.8" +name = "tower-http" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "serde", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "toml_edit" -version = "0.22.20" +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2080,6 +1879,7 @@ 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", @@ -2093,7 +1893,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2135,43 +1935,18 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ubyte" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" -dependencies = [ - "serde", -] - [[package]] name = "ucd-trie" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[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 = "unic-char-property" version = "0.9.0" @@ -2223,16 +1998,19 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "unicase" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] [[package]] -name = "unicode-xid" -version = "0.2.4" +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "urlencoding" @@ -2262,15 +2040,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2285,34 +2054,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2320,22 +2090,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "winapi" @@ -2368,22 +2138,13 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2392,7 +2153,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2401,22 +2162,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2425,46 +2171,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2477,72 +2205,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" -dependencies = [ - "memchr", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -dependencies = [ - "is-terminal", -] - [[package]] name = "zerocopy" version = "0.7.35" @@ -2561,7 +2247,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] diff --git a/src/Cargo.toml b/src/Cargo.toml index 5f5ce8c..8a8657a 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -9,17 +9,25 @@ members = [ resolver = "2" [workspace.dependencies] +axum = { version = "0.7", features = ["macros"] } +axum-extra = { version = "0.9", features = ["cookie"] } base64 = "0.22" biscuit-auth = "5" chrono = "0.4" hex = "0.4" iso8601-duration = "0.2" lazy_static = "1" -rocket = "0.5" +mime = "0.3" +serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_urlencoded = "0.7" +serde_with = "3" tera = "1" thiserror = "1" time = "0.3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tower = "0.5" +tower-http = { version = "0.5", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } urlencoding = "2" diff --git a/src/helpers/Cargo.toml b/src/helpers/Cargo.toml index ce5e1d2..ebddf64 100644 --- a/src/helpers/Cargo.toml +++ b/src/helpers/Cargo.toml @@ -1,15 +1,18 @@ [package] name = "orangutan-helpers" -version = "0.1.1" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum = { workspace = true } biscuit-auth = { workspace = true } hex = { workspace = true } lazy_static = { workspace = true } -rocket = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } +serde_with = { workspace = true } thiserror = { workspace = true } +tower-http = { workspace = true } tracing = { workspace = true } diff --git a/src/helpers/src/config.rs b/src/helpers/src/config.rs index 1c45e6d..8ef65aa 100644 --- a/src/helpers/src/config.rs +++ b/src/helpers/src/config.rs @@ -4,26 +4,24 @@ use lazy_static::lazy_static; pub const THEME_NAME: &str = "Orangutan"; pub const DATA_FILE_EXTENSION: &str = "orangutan"; -pub(super) const READ_ALLOWED_FIELD: &str = "read_allowed"; -pub const PATH_FIELD: &str = "path"; pub const DEFAULT_PROFILE: &str = "_default"; pub const ROOT_KEY_NAME: &'static str = "_biscuit_root"; pub(super) const WEBSITE_DIR_NAME: &'static str = "website"; lazy_static! { - static ref WORK_DIR: PathBuf = env::current_dir().unwrap(); pub static ref WEBSITE_REPOSITORY: String = env::var("WEBSITE_REPOSITORY") .expect("Environment variable `WEBSITE_REPOSITORY` is required."); + pub static ref MODE: Result = env::var("MODE"); + pub static ref KEYS_MODE: Result = env::var("KEYS_MODE"); +} +lazy_static! { + static ref WORK_DIR: PathBuf = env::current_dir().unwrap(); pub static ref BASE_DIR: PathBuf = WORK_DIR.join(".orangutan"); pub static ref TMP_DIR: PathBuf = BASE_DIR.join("tmp"); pub static ref KEYS_DIR: PathBuf = BASE_DIR.join("keys"); - pub static ref MODE: Result = env::var("MODE"); - pub static ref KEYS_MODE: Result = env::var("KEYS_MODE"); - pub(super) static ref WEBSITE_ROOT: PathBuf = BASE_DIR.join("website"); + pub(super) static ref WEBSITE_ROOT: PathBuf = BASE_DIR.join("website-src"); pub(super) static ref HUGO_CONFIG_DIR: PathBuf = BASE_DIR.join("hugo-config"); pub static ref DEST_DIR: PathBuf = BASE_DIR.join("out"); pub static ref WEBSITE_DATA_DIR: PathBuf = DEST_DIR.join("data"); - pub(super) static ref SUFFIXED_EXTENSIONS: Vec<&'static str> = - vec!["html", "json", "xml", "css", "js", "txt"]; } diff --git a/src/helpers/src/generate.rs b/src/helpers/src/generate.rs index 315bd1b..1415de0 100644 --- a/src/helpers/src/generate.rs +++ b/src/helpers/src/generate.rs @@ -406,14 +406,14 @@ pub fn trash_outdated_websites() -> Result { hugo_config_generated: HUGO_CONFIG_GENERATED.load(Ordering::Relaxed), data_files_generated: DATA_FILES_GENERATED.load(Ordering::Relaxed), generated_websites: GENERATED_WEBSITES.lock().unwrap().to_owned(), - used_profiles: super::USED_PROFILES.lock().unwrap().to_owned(), + used_profiles: super::USED_PROFILES.read().unwrap().to_owned(), }; // Clear caches HUGO_CONFIG_GENERATED.store(false, Ordering::Relaxed); DATA_FILES_GENERATED.store(false, Ordering::Relaxed); GENERATED_WEBSITES.lock().unwrap().clear(); - *super::USED_PROFILES.lock().unwrap() = None; + *super::USED_PROFILES.write().unwrap() = None; Ok(state) } @@ -428,7 +428,7 @@ pub fn recover_trash(state: State) -> Result<(), Error> { HUGO_CONFIG_GENERATED.store(state.hugo_config_generated, Ordering::Relaxed); DATA_FILES_GENERATED.store(state.data_files_generated, Ordering::Relaxed); *GENERATED_WEBSITES.lock().unwrap() = state.generated_websites; - *super::USED_PROFILES.lock().unwrap() = state.used_profiles; + *super::USED_PROFILES.write().unwrap() = state.used_profiles; Ok(()) } @@ -456,4 +456,6 @@ pub enum Error { CannotCreateHugoConfigFile(io::Error), #[error("IO error: {0}")] IOError(#[from] io::Error), + #[error("Could not read page metadata: JSON error: {0}")] + CannotReadPageMetadata(serde_json::Error), } diff --git a/src/helpers/src/lib.rs b/src/helpers/src/lib.rs index 18bb7af..820d090 100644 --- a/src/helpers/src/lib.rs +++ b/src/helpers/src/lib.rs @@ -7,48 +7,61 @@ use std::{ collections::HashSet, fs::{self, File}, io, - path::{Path, PathBuf}, - sync::{Arc, Mutex}, + ops::Deref, + path::PathBuf, + sync::{Arc, RwLock}, }; use lazy_static::lazy_static; -use serde_json::Value; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_with::{serde_as, DefaultOnNull}; use tracing::{debug, error, trace}; use crate::config::*; lazy_static! { - static ref USED_PROFILES: Arc>>> = - Arc::new(Mutex::new(None)); + static ref USED_PROFILES: Arc>>> = Arc::default(); } pub fn used_profiles<'a>() -> &'a HashSet { - let mut used_profiles = USED_PROFILES.lock().unwrap(); - if let Some(profiles) = used_profiles.clone() { + if let Some(profiles) = *USED_PROFILES.read().unwrap() { trace!("Read used profiles from cache"); return profiles; } - trace!("Reading used profiles…"); + debug!("Reading all used profiles…"); let acc: &'static mut HashSet = Box::leak(Box::new(HashSet::new())); for data_file in find_data_files() { // trace!("Reading <{}>…", data_file.display()); - // Make sure this generator isn't broken (could be replaced by unit tests) - // let html_file = html_file(&data_file).unwrap(); - // trace!("{}", html_file.display()); - - let read_allowed = read_allowed(&data_file).unwrap(); + let metadata: PageMetadata = match deser(&data_file) { + Ok(Some(metadata)) => metadata, + Ok(None) => { + error!( + "Could not read page metadata at <{}>: File not found", + data_file.display(), + ); + continue; + }, + Err(err) => { + error!( + "Could not read page metadata at <{}>: {err}", + data_file.display(), + ); + continue; + }, + }; + let read_allowed = metadata.read_allowed; // trace!(" read_allowed: {:?}", read_allowed); // Store new profiles - read_allowed.iter().for_each(|p| { - acc.insert(p.clone()); + read_allowed.into_iter().for_each(|p| { + acc.insert(p.to_owned()); }); } - *used_profiles = Some(acc); + *USED_PROFILES.write().unwrap() = Some(acc); acc } @@ -76,43 +89,13 @@ pub fn find( } } -fn _html_file(data_file: &PathBuf) -> Option> { - let file = File::open(data_file).ok()?; - let reader = io::BufReader::new(file); - let data: Value = match serde_json::from_reader(reader) { - Ok(data) => data, - Err(_) => return None, - }; - - let path = data.get(PATH_FIELD)?; - - Some(serde_json::from_value(path.to_owned()).ok()) -} - -fn html_file(data_file: &PathBuf) -> Result { - match _html_file(data_file) { - Some(Some(path)) => Ok(path), - Some(None) => { - error!("Path not defined"); - Err(()) - }, - None => { - error!("File not found"); - Err(()) - }, - } -} - -pub fn data_file(html_file: &PathBuf) -> PathBuf { - let mut data_file_rel = html_file - .strip_prefix(WEBSITE_DATA_DIR.to_path_buf()) - .unwrap_or(html_file) - .with_extension(DATA_FILE_EXTENSION); - data_file_rel = match data_file_rel.strip_prefix("/") { +pub fn data_file_path(page_relpath: &PathBuf) -> PathBuf { + let mut data_file_relpath = page_relpath.with_extension(DATA_FILE_EXTENSION); + data_file_relpath = match data_file_relpath.strip_prefix("/") { Ok(trimmed) => trimmed.to_path_buf(), - Err(_) => data_file_rel, + Err(_) => data_file_relpath, }; - WEBSITE_DATA_DIR.join(data_file_rel) + WEBSITE_DATA_DIR.join(data_file_relpath) } fn find_data_files() -> Vec { @@ -125,35 +108,81 @@ fn find_data_files() -> Vec { data_files } -fn _read_allowed(data_file: &PathBuf) -> Option>> { - let file = File::open(data_file).ok()?; - let reader = io::BufReader::new(file); - let data: Value = match serde_json::from_reader(reader) { - Ok(data) => data, - Err(_) => return None, - }; +#[serde_as] +#[derive(Deserialize)] +pub struct PageMetadata { + // NOTE: Hugo taxonomy term pages contain `read_allowed": null`. + // TODO: Investigate and fix this, to remove the `serde` default which could be in conflict with the user's preference. + #[serde(default)] + #[serde_as(deserialize_as = "DefaultOnNull")] + pub read_allowed: ReadAllowed, + pub path: PathBuf, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ReadAllowed(Vec); - let read_allowed = data.get(READ_ALLOWED_FIELD)?; +impl Deref for ReadAllowed { + type Target = Vec; - Some(serde_json::from_value(read_allowed.to_owned()).ok()) + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for ReadAllowed { + fn default() -> Self { + Self(vec![DEFAULT_PROFILE.to_owned()]) + } +} + +impl IntoIterator for ReadAllowed { + type Item = <::Target as IntoIterator>::Item; + type IntoIter = <::Target as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } } -// `None` if file not found -pub fn read_allowed(data_file: &PathBuf) -> Option> { - _read_allowed(data_file).map(|o| o.unwrap_or(vec![DEFAULT_PROFILE.to_string()])) +fn deser(file_path: &PathBuf) -> Result, serde_json::Error> { + let Ok(file) = File::open(file_path) else { + return Ok(None); + }; + let res: T = serde_json::from_reader(file)?; + Ok(Some(res)) } -pub fn object_key>( - path: &P, - profile: &str, -) -> String { - let path = path.as_ref(); - if let Some(ext) = path.extension() { - if SUFFIXED_EXTENSIONS.contains(&ext.to_str().unwrap()) { - return format!("{}@{}", path.display(), profile); +fn deser_first_match( + file_paths: Vec +) -> Result, serde_json::Error> { + for file_path in file_paths.iter() { + trace!("Trying {}…", file_path.display()); + match deser(file_path)? { + Some(res) => { + // TODO: Check if this index is always the same. + // debug!("Found page metadata in match #{i}"); + return Ok(Some(res)); + }, + None => continue, } } - path.display().to_string() + Ok(None) +} + +// `Ok(None)` if file not found. +// `Err(_)` if file found but deserialization error. +// `Ok(Some(_))` if file found. +pub fn page_metadata(page_relpath: &PathBuf) -> Result, serde_json::Error> { + let mut file_paths = vec![ + data_file_path(page_relpath), + data_file_path(&page_relpath.join("index.html")), + ]; + // Don't try parsing the exact path if it points to a directory. + if page_relpath.is_dir() { + file_paths.remove(0); + } + deser_first_match(file_paths) } pub fn copy_directory( diff --git a/src/helpers/src/readers/mod.rs b/src/helpers/src/readers/mod.rs index 134b6d7..1878936 100644 --- a/src/helpers/src/readers/mod.rs +++ b/src/helpers/src/readers/mod.rs @@ -1,2 +1 @@ pub mod keys_reader; -pub mod object_reader; diff --git a/src/helpers/src/readers/object_reader.rs b/src/helpers/src/readers/object_reader.rs deleted file mode 100644 index 621d716..0000000 --- a/src/helpers/src/readers/object_reader.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::{fs, io, path::PathBuf}; - -use rocket::{fs::NamedFile, response::Responder}; -use tracing::trace; - -use crate::{ - generate::{self, generate_website_if_needed}, - website_id::WebsiteId, -}; - -pub type ObjectReader = LocalObjectReader; - -impl ObjectReader { - pub fn new() -> Self { - LocalObjectReader {} - } -} - -pub type ReadObjectResponse = LocalReadObjectResponse; - -pub struct LocalObjectReader; - -#[derive(Responder)] -pub enum LocalReadObjectResponse { - #[response(status = 404)] - NotFound(String), - Found(Result), -} - -impl LocalObjectReader { - async fn serve_file(file_path: PathBuf) -> io::Result { - NamedFile::open(file_path).await - } -} - -impl LocalObjectReader { - pub fn list_objects( - &self, - prefix: &str, - website_id: &WebsiteId, - ) -> Result, Error> { - trace!("Listing files with prefix '{}' for {}…", prefix, website_id); - - let website_dir = - generate_website_if_needed(website_id).map_err(Error::WebsiteGenerationError)?; - - Ok(find_all_files(&website_dir) - .iter() - .map(|path| { - format!( - "/{}", - path.strip_prefix(website_dir.as_path()) - .expect("Could not remove prefix") - .display() - ) - }) - .collect()) - } - - pub async fn read_object<'r>( - &self, - object_key: &str, - website_id: &WebsiteId, - ) -> LocalReadObjectResponse { - let website_dir = match generate_website_if_needed(website_id) { - Ok(dir) => dir, - Err(err) => return LocalReadObjectResponse::NotFound(err.to_string()), - }; - let file_path = website_dir.join(object_key.strip_prefix("/").unwrap()); - trace!( - "Reading '{}' from disk at <{}>…", - object_key, - file_path.display() - ); - - LocalReadObjectResponse::Found(Self::serve_file(file_path).await) - } -} - -fn find_all_files(dir: &PathBuf) -> Vec { - let mut files: Vec = Vec::new(); - find(dir, &mut files); - files -} - -fn find( - dir: &PathBuf, - files: &mut Vec, -) { - for entry in fs::read_dir(dir).unwrap() { - if let Ok(entry) = entry { - let path = entry.path(); - if path.is_file() { - files.push(path); - } else if path.is_dir() { - find(&path, files); - } - } - } -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Website generation error: {0}")] - WebsiteGenerationError(generate::Error), -} diff --git a/src/orangutan-server/Cargo.toml b/src/orangutan-server/Cargo.toml index af180f2..4700720 100644 --- a/src/orangutan-server/Cargo.toml +++ b/src/orangutan-server/Cargo.toml @@ -1,23 +1,30 @@ [package] name = "orangutan-server" -version = "0.4.13" +version = "0.5.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum = { workspace = true } +axum-extra = { workspace = true } base64 = { workspace = true } biscuit-auth = { workspace = true } chrono = { workspace = true } hex = { workspace = true } lazy_static = { workspace = true } +mime = { workspace = true } orangutan-helpers = { path = "../helpers" } orangutan-refresh-token = { path = "../orangutan-refresh-token" } -rocket = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } +serde_urlencoded = { workspace = true } tera = { workspace = true, optional = true } thiserror = { workspace = true } time = { workspace = true } +tokio = { workspace = true } +tower = { workspace = true } +tower-http = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } urlencoding = { workspace = true } diff --git a/src/orangutan-server/src/main.rs b/src/orangutan-server/src/main.rs index ecfefe5..8b3bbad 100644 --- a/src/orangutan-server/src/main.rs +++ b/src/orangutan-server/src/main.rs @@ -3,73 +3,96 @@ mod request_guards; mod routes; mod util; -use object_reader::ObjectReader; +use std::{fs, process::ExitCode}; + +use axum::{ + body::Body, + http::{Request, StatusCode}, + middleware, + response::{IntoResponse, Response}, + Router, +}; use orangutan_helpers::{ generate::{self, *}, - readers::object_reader, website_id::WebsiteId, }; -use rocket::{ - catch, catchers, - fairing::AdHoc, - fs::NamedFile, - http::Status, - response::{self, Responder}, - Request, +use request_guards::{handle_refresh_token, REVOKED_TOKENS}; +use tokio::runtime::Handle; +use tower::Service; +use tower_http::{ + services::{fs::ServeFileSystemResponseBody, ServeFile}, + trace::TraceLayer, }; -use routes::auth_routes::REVOKED_TOKENS; -#[cfg(feature = "templating")] -use tracing::debug; -use tracing::warn; -use tracing_subscriber::{EnvFilter, FmtSubscriber}; +use tracing::{debug, info, warn}; +use tracing_subscriber::EnvFilter; +#[cfg(feature = "token-generator")] +use util::WebsiteRoot; #[cfg(feature = "templating")] use crate::util::templating; -use crate::{ - config::NOT_FOUND_FILE, - routes::{main_route, update_content_routes}, - util::error, -}; +use crate::{config::NOT_FOUND_FILE, routes::update_content_routes, util::error}; + +#[derive(Clone)] +struct AppState { + #[cfg(feature = "token-generator")] + website_root: WebsiteRoot, + #[cfg(feature = "templating")] + tera: tera::Tera, +} + +#[tokio::main] +async fn main() -> ExitCode { + #[cfg(feature = "token-generator")] + let website_root = match WebsiteRoot::try_from_env() { + Ok(r) => r, + Err(err) => { + tracing::error!("{err}"); + return ExitCode::FAILURE; + }, + }; + + let app_state = AppState { + #[cfg(feature = "token-generator")] + website_root, + #[cfg(feature = "templating")] + tera: Default::default(), + }; -#[rocket::launch] -fn rocket() -> _ { - let rocket = rocket::build() - .mount("/", routes::routes()) - .register("/", catchers![unauthorized, forbidden, not_found]) - .manage(ObjectReader::new()) - .attach(AdHoc::on_ignite("Tracing subsciber", |rocket| async move { - let subscriber = FmtSubscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .finish(); - tracing::subscriber::set_global_default(subscriber) - .expect("Failed to set tracing subscriber."); - rocket - })) - .attach(AdHoc::on_liftoff("Website generation", |rocket| { - Box::pin(async move { - if let Err(err) = liftoff() { - // We drop the error to get a Rocket-formatted panic. - drop(err); - rocket.shutdown().notify(); - } - }) - })); + info!("Setting up tracing…"); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); // Add support for templating if needed #[cfg(feature = "templating")] - let rocket = rocket.attach(AdHoc::on_ignite( - "Initialize templating engine", - |rocket| async move { - let mut tera = tera::Tera::default(); - if let Err(err) = tera.add_raw_templates(routes::templates()) { - tracing::error!("{err}"); - std::process::exit(1) - } - rocket.manage(tera) - }, - )); + let mut app_state = app_state; + #[cfg(feature = "templating")] + { + info!("Initializing templating engine…"); + if let Err(err) = app_state.tera.add_raw_templates(routes::templates()) { + tracing::error!("{err}"); + return ExitCode::FAILURE; + } + } + + let app = Router::new() + .nest("/", routes::router()) + .layer(TraceLayer::new_for_http()) + .route_layer(middleware::from_fn(handle_refresh_token)) + .with_state(app_state); + // .register("/", catchers![unauthorized, forbidden, not_found]) + + info!("Generating default website"); + if let Err(err) = liftoff() { + tracing::error!("{err}"); + return ExitCode::FAILURE; + } - rocket + // Run our app with hyper, listening globally on port 8080. + let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); + axum::serve(listener, app).await.unwrap(); + + return ExitCode::SUCCESS; } fn liftoff() -> Result<(), Error> { @@ -81,81 +104,89 @@ fn liftoff() -> Result<(), Error> { Ok(()) } -#[catch(401)] -async fn unauthorized() -> Result { - not_found().await -} - -#[catch(403)] -async fn forbidden() -> &'static str { - "403 Forbidden. Token revoked." -} - /// TODO: Re-enable Basic authentication /// (`.raw_header("WWW-Authenticate", "Basic realm=\"This page is protected. Please log in.\"")`). -#[catch(404)] -async fn not_found() -> Result { +fn not_found() -> Result, Response> { + fn fallback() -> Response { + let mut fallback = + "This page doesn't exist or you are not allowed to see it.".into_response(); + *fallback.status_mut() = StatusCode::NOT_FOUND; + fallback + } + let website_id = WebsiteId::default(); - let website_dir = match generate_website_if_needed(&website_id) { - Ok(dir) => dir, + let website_dir = generate_website_if_needed(&website_id).map_err(|err| { + error(format!("Could not get default website directory: {err}")); + fallback() + })?; + + let file_path = website_dir.join(NOT_FOUND_FILE); + match fs::metadata(&file_path) { + Ok(_) => { + let res = tokio::task::block_in_place(move || { + Handle::current().block_on(async move { + ServeFile::new(file_path.clone()) + .call(Request::new(Body::empty())) + .await + .map_err(|err| match err {}) + .unwrap() + }) + }); + Ok(res) + }, Err(err) => { - error(format!("Could not get default website directory: {}", err)); - return Err("This page doesn't exist or you are not allowed to see it."); + error(format!( + "Could not read \"not found\" file at <{}>: {err}", + file_path.display(), + )); + Err(fallback()) }, - }; - let file_path = website_dir.join(NOT_FOUND_FILE); - NamedFile::open(file_path.clone()).await.map_err(|err| { - error(format!( - "Could not read \"not found\" file at <{}>: {}", - file_path.display(), - err - )); - "This page doesn't exist or you are not allowed to see it." - }) + } } #[derive(Debug, thiserror::Error)] enum Error { #[error(transparent)] WebsiteGenerationError(#[from] generate::Error), - #[error(transparent)] - MainRouteError(#[from] main_route::Error), #[error("Could not update content: {0}")] UpdateContentError(#[from] update_content_routes::Error), #[error("Unauthorized")] Unauthorized, #[error("Forbidden")] Forbidden, + #[error("Token revoked")] + TokenRevoked, #[cfg(feature = "templating")] #[error("Templating error: {0}")] TemplatingError(#[from] templating::Error), - #[cfg(feature = "templating")] #[error("Internal server error: {0}")] InternalServerError(String), - #[cfg(feature = "templating")] #[error("Client error: {0}")] ClientError(String), } -#[rocket::async_trait] -impl<'r> Responder<'r, 'static> for Error { - fn respond_to( - self, - _: &'r Request<'_>, - ) -> response::Result<'static> { +impl IntoResponse for Error { + fn into_response(self) -> Response { match self { Self::Unauthorized => { warn!("{self}"); - Err(Status::Unauthorized) + (StatusCode::UNAUTHORIZED, not_found()).into_response() + }, + Self::Forbidden => { + warn!("{self}"); + (StatusCode::FORBIDDEN, not_found()).into_response() + }, + Self::TokenRevoked => { + warn!("{self}"); + (StatusCode::FORBIDDEN, "403 Forbidden. Token revoked.").into_response() }, - #[cfg(feature = "templating")] Self::ClientError(_) => { debug!("{self}"); - Err(Status::BadRequest) + (StatusCode::BAD_REQUEST, not_found()).into_response() }, _ => { error(format!("{self}")); - Err(Status::InternalServerError) + (StatusCode::INTERNAL_SERVER_ERROR, "I messed up. My bad 🙊").into_response() }, } } diff --git a/src/orangutan-server/src/request_guards.rs b/src/orangutan-server/src/request_guards.rs index 2f1974c..997062b 100644 --- a/src/orangutan-server/src/request_guards.rs +++ b/src/orangutan-server/src/request_guards.rs @@ -1,7 +1,21 @@ -use std::ops::Deref; +use std::{ + collections::{HashMap, HashSet}, + ops::Deref, + str::FromStr, + sync::RwLock, + time::SystemTime, +}; -use biscuit_auth::Biscuit; -use rocket::{http::Status, outcome::Outcome, request, request::FromRequest, Request}; +use axum::{ + extract::{rejection::QueryRejection, FromRequestParts, Query, Request}, + http::{request, HeaderMap, HeaderValue, Uri}, + middleware::Next, + response::{IntoResponse, Redirect, Response}, +}; +use axum_extra::{either::Either, extract::CookieJar}; +use biscuit_auth::{macros::authorizer, Biscuit}; +use lazy_static::lazy_static; +use serde::Deserialize; use tracing::{debug, trace}; use crate::{ @@ -9,6 +23,10 @@ use crate::{ util::{add_cookie, add_padding, profiles}, }; +lazy_static! { + pub static ref REVOKED_TOKENS: RwLock>> = RwLock::default(); +} + pub struct Token { pub biscuit: Biscuit, } @@ -27,25 +45,46 @@ impl Deref for Token { } } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum TokenError { // TODO: Re-enable Basic authentication // Invalid, + #[error("Invalid query: {0}")] + InvalidQuery(#[from] QueryRejection), + #[error("Unauthorized")] + Unauthorized, +} +impl IntoResponse for TokenError { + fn into_response(self) -> Response { + match self { + Self::InvalidQuery(err) => err.into_response(), + Self::Unauthorized => crate::Error::Unauthorized.into_response(), + } + } } -#[rocket::async_trait] -impl<'r> FromRequest<'r> for Token { - type Error = TokenError; +#[axum::async_trait] +impl FromRequestParts for Token +where + S: Send + Sync, +{ + type Rejection = TokenError; - async fn from_request(req: &'r Request<'_>) -> request::Outcome { + async fn from_request_parts( + parts: &mut request::Parts, + state: &S, + ) -> Result { + // if let Some(user_agent) = parts.headers.get(USER_AGENT) { + // Ok(ExtractUserAgent(user_agent.clone())) + // } else { + // Err((StatusCode::BAD_REQUEST, "`User-Agent` header is missing")) + // } let mut biscuit: Option = None; - let mut should_save: bool = false; fn process_token( token: &str, token_source: &str, biscuit: &mut Option, - should_save: &mut bool, ) { // Because tokens can be passed as URL query params, // they might have the "=" padding characters removed. @@ -58,13 +97,11 @@ impl<'r> FromRequest<'r> for Token { (None, Ok(new_biscuit)) => { trace!("Found biscuit in {}", token_source); *biscuit = Some(new_biscuit); - *should_save = true; }, (Some(acc), Ok(new_biscuit)) => { trace!("Making bigger biscuit from {}", token_source); if let Some(b) = merge_biscuits(&acc, &new_biscuit) { *biscuit = Some(b); - *should_save = true; } }, (_, Err(err)) => { @@ -74,53 +111,191 @@ impl<'r> FromRequest<'r> for Token { } // Check cookies - if let Some(cookie) = req.cookies().get(TOKEN_COOKIE_NAME) { - debug!("Found token cookie"); + let cookies = CookieJar::from_request_parts(parts, state) + .await + .map_err(|err| match err {}) + .unwrap(); + if let Some(cookie) = cookies.get(TOKEN_COOKIE_NAME) { + trace!("Found token cookie"); let token: &str = cookie.value(); - // NOTE: We don't want to send a `Set-Cookie` header after finding a token in a cookie, - // so let's create a temporary value which prevents `process_token` from changing - // the global `should_save` value. - let mut should_save = false; - process_token(token, "token cookie", &mut biscuit, &mut should_save); + process_token(token, "token cookie", &mut biscuit); } else { - debug!("Did not find a token cookie"); + trace!("Did not find a token cookie"); } // Check authorization headers - let authorization_headers: Vec<&str> = req.headers().get("Authorization").collect(); - debug!( + let headers = HeaderMap::from_request_parts(parts, state) + .await + .map_err(|err| match err {}) + .unwrap(); + let authorization_headers: Vec<&HeaderValue> = + headers.get_all("Authorization").into_iter().collect(); + trace!( "{} 'Authorization' headers provided", authorization_headers.len() ); for authorization in authorization_headers { + let authorization = match authorization.to_str() { + Ok(str) => str, + Err(_err) => { + trace!("Skipped 1 authorization header because it contains non visible ASCII chars."); + continue; + }, + }; if authorization.starts_with("Bearer ") { - debug!("Bearer Authorization provided"); + trace!("Bearer Authorization provided"); let token: &str = authorization.trim_start_matches("Bearer "); - process_token(token, "Bearer token", &mut biscuit, &mut should_save); + process_token(token, "Bearer token", &mut biscuit); } else if authorization.starts_with("Basic ") { debug!("Basic authentication disabled"); } } // Check query params - if let Some(token) = req - .query_value::(TOKEN_QUERY_PARAM_NAME) - .and_then(Result::ok) - { - debug!("Found token query param"); - process_token(&token, "token query param", &mut biscuit, &mut should_save); + let query = Query::>::from_request_parts(parts, state).await?; + if let Some(token) = query.get(TOKEN_QUERY_PARAM_NAME) { + trace!("Found token query param"); + process_token(&token, "token query param", &mut biscuit); } - match biscuit { - Some(biscuit) => { - if should_save { - add_cookie(&biscuit, req.cookies()); - } - Outcome::Success(Token { biscuit }) - }, - None => Outcome::Forward(Status::Unauthorized), + let biscuit = biscuit.ok_or(TokenError::Unauthorized)?; + Ok(Token { biscuit }) + } +} + +#[derive(Deserialize)] +pub struct RefreshTokenQuery { + #[serde(default)] + refresh_token: Option, + #[serde(default)] + force: bool, +} + +pub async fn handle_refresh_token( + cookies: CookieJar, + Query(RefreshTokenQuery { + refresh_token, + force, + .. + }): Query, + token: Option, + req: Request, + next: Next, +) -> Result, crate::Error> { + trace!("Running refresh token middleware…"); + let Some(refresh_token) = refresh_token else { + trace!("Refresh token middleware found no refresh token, forwarding…"); + return Ok(Either::E1(next.run(req).await)); + }; + + // NOTE: We're sure there is a query since `refresh_token` is `Some`. + let query = req.uri().query().unwrap(); + let mut query: HashMap = serde_urlencoded::from_str(query) + .map_err(|err| crate::Error::ClientError(format!("Invalid query: {err}")))?; + + // URL-decode the string. + let mut refresh_token: String = urlencoding::decode(&refresh_token).unwrap().to_string(); + + // Because tokens can be passed as URL query params, + // they might have the "=" padding characters removed. + // We need to add them back. + refresh_token = add_padding(&refresh_token); + + let refresh_biscuit: Biscuit = + Biscuit::from_base64(refresh_token, ROOT_KEY.public()).map_err(|err| { + debug!("Error decoding biscuit from base64: {err}"); + crate::Error::Unauthorized + })?; + + // NOTE: This is just a hotfix. I had to quickly revoke a token. I'll improve this one day. + trace!("Checking if refresh token is revoked…"); + trace!( + "Revocation identifiers: {}", + refresh_biscuit + .revocation_identifiers() + .into_iter() + .map(hex::encode) + .collect::>() + .join(", "), + ); + let revoked_id = refresh_biscuit + .revocation_identifiers() + .into_iter() + .collect::>>() + .intersection(&REVOKED_TOKENS.read().unwrap()) + .next() + .cloned(); + if let Some(revoked_id) = revoked_id { + debug!( + "Refresh token has been revoked ({})", + String::from_utf8(revoked_id).unwrap_or("".to_string()), + ); + return Err(crate::Error::TokenRevoked); + } + + trace!("Checking if refresh token is valid or not"); + let authorizer = authorizer!( + r#" + time({now}); + allow if true; + "#, + now = SystemTime::now(), + ); + if let Err(err) = refresh_biscuit.authorize(&authorizer) { + debug!("Refresh token is invalid: {}", err); + return Err(crate::Error::Unauthorized); + } + + fn redirect_to_same_page_without_query_param( + uri: &Uri, + query: &mut HashMap, + cookies: CookieJar, + ) -> Result<(CookieJar, Redirect), crate::Error> { + query.remove(REFRESH_TOKEN_QUERY_PARAM_NAME); + // TODO: Check if we need to URL-encode keys and values or if they are still encoded. + let query_segs: Vec = query.iter().map(|(k, v)| format!("{k}={v}")).collect(); + let redirect_to = if query_segs.is_empty() { + uri.path().to_string() + } else { + format!("{}?{}", uri.path(), query_segs.join("&")) + }; + let redirect_to = Uri::from_str(&redirect_to) + .map_err(|err| crate::Error::InternalServerError(format!("{err}")))?; + debug!("Redirecting to <{redirect_to}> from <{uri}>…"); + Ok(( + cookies, + Redirect::to(redirect_to.path().to_string().as_str()), + )) + } + + if let Some(token) = token { + if token.profiles().contains(&"*".to_owned()) && !force { + // NOTE: If a super admin generates an access link and accidentally opens it, + // they loose their super admin profile. Then we must regenerate a super admin + // access link and send it to the super admin's device, which increases the potential + // for such a sensitive link to be intercepted. As a safety measure, we don't do anything + // if a super admin uses a refresh token link. + return redirect_to_same_page_without_query_param(req.uri(), &mut query, cookies) + .map(Either::E2); } } + + trace!("Baking new biscuit from refresh token"); + let block_0 = refresh_biscuit.print_block_source(0).unwrap(); + let mut builder = Biscuit::builder(); + builder.add_code(block_0).unwrap(); + let new_biscuit = builder.build(&ROOT_KEY).map_err(|err| { + crate::Error::InternalServerError(format!( + "Error: Could not append block to biscuit: {err}" + )) + })?; + debug!("Successfully created new biscuit from refresh token"); + + // Save token to a HTTP Cookie + let cookies = add_cookie(&new_biscuit, cookies)?; + + // Redirect to the same page without the refresh token query param + redirect_to_same_page_without_query_param(req.uri(), &mut query, cookies).map(Either::E2) } fn merge_biscuits( diff --git a/src/orangutan-server/src/routes/auth_routes.rs b/src/orangutan-server/src/routes/auth_routes.rs deleted file mode 100644 index 172dc61..0000000 --- a/src/orangutan-server/src/routes/auth_routes.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::{collections::HashSet, sync::RwLock, time::SystemTime}; - -use biscuit_auth::{macros::authorizer, Biscuit}; -use lazy_static::lazy_static; -use rocket::{ - get, - http::{uri::Origin, CookieJar, Status}, - response::Redirect, - routes, Route, -}; -use tracing::{debug, trace}; - -use crate::{ - config::*, - error, - request_guards::Token, - util::{add_cookie, add_padding}, -}; - -lazy_static! { - pub static ref REVOKED_TOKENS: RwLock>> = RwLock::default(); -} - -pub(super) fn routes() -> Vec { - routes![handle_refresh_token] -} - -#[get("/<_..>?&")] -fn handle_refresh_token( - origin: &Origin, - cookies: &CookieJar<'_>, - refresh_token: &str, - token: Option, - force: Option, -) -> Result { - // URL-decode the string. - let mut refresh_token: String = urlencoding::decode(refresh_token).unwrap().to_string(); - - // Because tokens can be passed as URL query params, - // they might have the "=" padding characters removed. - // We need to add them back. - refresh_token = add_padding(&refresh_token); - - let refresh_biscuit: Biscuit = match Biscuit::from_base64(refresh_token, ROOT_KEY.public()) { - Ok(biscuit) => biscuit, - Err(err) => { - debug!("Error decoding biscuit from base64: {}", err); - return Err(Status::Unauthorized); - }, - }; - - // NOTE: This is just a hotfix. I had to quickly revoke a token. I'll improve this one day. - trace!("Checking if refresh token is revoked…"); - trace!( - "Revocation identifiers: {}", - refresh_biscuit - .revocation_identifiers() - .into_iter() - .map(hex::encode) - .collect::>() - .join(", "), - ); - let revoked_id = refresh_biscuit - .revocation_identifiers() - .into_iter() - .collect::>>() - .intersection(&REVOKED_TOKENS.read().unwrap()) - .next() - .cloned(); - if let Some(revoked_id) = revoked_id { - debug!( - "Refresh token has been revoked ({})", - String::from_utf8(revoked_id).unwrap_or("".to_string()), - ); - return Err(Status::Forbidden); - } - - trace!("Checking if refresh token is valid or not"); - let authorizer = authorizer!( - r#" - time({now}); - allow if true; - "#, - now = SystemTime::now(), - ); - if let Err(err) = refresh_biscuit.authorize(&authorizer) { - debug!("Refresh token is invalid: {}", err); - return Err(Status::Unauthorized); - } - - fn redirect_to_same_page_without_query_param(origin: &Origin) -> Result { - let query_segs: Vec = origin - .query() - .unwrap() - .raw_segments() - .filter(|s| !s.starts_with(format!("{REFRESH_TOKEN_QUERY_PARAM_NAME}=").as_str())) - .map(ToString::to_string) - .collect(); - match Origin::parse_owned(format!("{}?{}", origin.path(), query_segs.join("&"))) { - Ok(redirect_to) => { - debug!("Redirecting to <{redirect_to}> from <{origin}>…"); - Ok(Redirect::found(redirect_to.path().to_string())) - }, - Err(err) => { - error(format!("{err}")); - Err(Status::InternalServerError) - }, - } - } - - if let Some(token) = token { - if token.profiles().contains(&"*".to_owned()) && !force.unwrap_or(false) { - // NOTE: If a super admin generates an access link and accidentally opens it, - // they loose their super admin profile. Then we must regenerate a super admin - // access link and send it to the super admin's device, which increases the potential - // for such a sensitive link to be intercepted. As a safety measure, we don't do anything - // if a super admin uses a refresh token link. - return redirect_to_same_page_without_query_param(origin); - } - } - - trace!("Baking new biscuit from refresh token"); - let block_0 = refresh_biscuit.print_block_source(0).unwrap(); - let mut builder = Biscuit::builder(); - builder.add_code(block_0).unwrap(); - let new_biscuit = match builder.build(&ROOT_KEY) { - Ok(biscuit) => biscuit, - Err(err) => { - error(format!("Error: Could not append block to biscuit: {err}")); - return Err(Status::InternalServerError); - }, - }; - debug!("Successfully created new biscuit from refresh token"); - - // Save token to a HTTP Cookie - add_cookie(&new_biscuit, cookies); - - // Redirect to the same page without the refresh token query param - redirect_to_same_page_without_query_param(origin) -} - -#[cfg(test)] -mod tests { - use super::add_padding; - - #[test] - fn test_base64_padding() { - assert_eq!(add_padding("a"), "a===".to_string()); - assert_eq!(add_padding("ab"), "ab==".to_string()); - assert_eq!(add_padding("abc"), "abc=".to_string()); - assert_eq!(add_padding("abcd"), "abcd".to_string()); - - assert_eq!(add_padding("a==="), "a===".to_string()); - assert_eq!(add_padding("ab=="), "ab==".to_string()); - assert_eq!(add_padding("abc="), "abc=".to_string()); - assert_eq!(add_padding("abcd"), "abcd".to_string()); - } - - // #[test] - // fn test_should_force_token_refresh() { - // assert_eq!(should_force_token_refresh(None), false); - // assert_eq!(should_force_token_refresh(Some(Ok(true))), true); - // assert_eq!(should_force_token_refresh(Some(Ok(false))), false); - // assert_eq!( - // should_force_token_refresh(Some(Err(Errors::new().with_name("yes")))), - // true - // ); - // assert_eq!( - // should_force_token_refresh(Some(Err(Errors::new().with_name("no")))), - // true - // ); - // assert_eq!( - // should_force_token_refresh(Some(Err(Errors::new().with_name("")))), - // true - // ); - // } -} diff --git a/src/orangutan-server/src/routes/debug_routes.rs b/src/orangutan-server/src/routes/debug_routes.rs index 2f558a8..a562884 100644 --- a/src/orangutan-server/src/routes/debug_routes.rs +++ b/src/orangutan-server/src/routes/debug_routes.rs @@ -1,11 +1,14 @@ use std::sync::{Arc, RwLock}; +use axum::{routing::get, Router}; +use axum_extra::extract::CookieJar; use chrono::{DateTime, Utc}; use lazy_static::lazy_static; -use rocket::{get, http::CookieJar, routes, Route}; -use super::auth_routes::REVOKED_TOKENS; -use crate::{request_guards::Token, Error}; +use crate::{ + request_guards::{Token, REVOKED_TOKENS}, + AppState, Error, +}; lazy_static! { /// A list of runtime errors, used to show error logs in an admin page @@ -19,21 +22,25 @@ lazy_static! { pub(crate) static ref ACCESS_LOGS: Arc>> = Arc::default(); } -pub(super) fn routes() -> Vec { - let routes = routes![ - clear_cookies, - get_user_info, - errors, - access_logs, - revoked_tokens, - ]; +pub(super) fn router() -> Router { + let router = Router::::new() + .route("/clear-cookies", get(clear_cookies).put(clear_cookies)) + .route("/_info", get(get_user_info)) + .route("/_errors", get(errors)) + .route("/_access-logs", get(access_logs)) + .route("/_revoked-tokens", get(revoked_tokens)); + + #[cfg(feature = "token-generator")] + let mut router = router; #[cfg(feature = "token-generator")] - let routes = vec![routes, routes![ - token_generator::token_generation_form, - token_generator::generate_token, - ]] - .concat(); - routes + { + router = router.route( + "/_generate-token", + get(token_generator::token_generation_form).post(token_generator::generate_token), + ); + } + + router } #[cfg(feature = "templating")] @@ -44,17 +51,17 @@ pub(super) fn templates() -> Vec<(&'static str, &'static str)> { )] } -#[get("/clear-cookies")] -fn clear_cookies(cookies: &CookieJar<'_>) -> &'static str { - for cookie in cookies.iter().map(Clone::clone) { - cookies.remove(cookie); +// #[axum::debug_handler] +async fn clear_cookies(cookie_jar: CookieJar) -> (CookieJar, String) { + let mut empty_jar = cookie_jar.clone(); + for cookie in cookie_jar.iter() { + empty_jar = empty_jar.remove(cookie.clone()); } - "Success" + (empty_jar, "Success".to_string()) } -#[get("/_info")] -fn get_user_info(token: Option) -> String { +async fn get_user_info(token: Option) -> String { match token { Some(Token { biscuit, .. }) => format!( "**Biscuit:**\n\n{}\n\n\ @@ -73,8 +80,7 @@ pub struct ErrorLog { pub line: String, } -#[get("/_errors")] -fn errors(token: Token) -> Result { +async fn errors(token: Token) -> Result { if !token.profiles().contains(&"*".to_owned()) { Err(Error::Unauthorized)? } @@ -100,8 +106,7 @@ pub struct AccessLog { pub path: String, } -#[get("/_access-logs")] -fn access_logs(token: Token) -> Result { +async fn access_logs(token: Token) -> Result { if !token.profiles().contains(&"*".to_owned()) { Err(Error::Unauthorized)? } @@ -139,8 +144,7 @@ pub fn log_access( }) } -#[get("/_revoked-tokens")] -fn revoked_tokens(token: Token) -> Result { +async fn revoked_tokens(token: Token) -> Result { if !token.profiles().contains(&"*".to_owned()) { Err(Error::Forbidden)? } @@ -156,49 +160,39 @@ fn revoked_tokens(token: Token) -> Result { #[cfg(feature = "token-generator")] pub mod token_generator { + use axum::{extract::State, Form}; + use axum_extra::response::Html; use orangutan_refresh_token::RefreshToken; - use rocket::{ - form::{Form, Strict}, - get, post, - response::content::RawHtml, - FromForm, State, - }; - - use crate::{ - context, - request_guards::Token, - util::{templating::render, WebsiteRoot}, - Error, - }; + use serde::Deserialize; + + use crate::{context, request_guards::Token, util::templating::render, AppState, Error}; fn token_generation_form_( - tera: &State, + tera: &tera::Tera, link: Option, base_url: &str, - ) -> Result, Error> { + ) -> Result, Error> { let html = render( tera, "generate-token.html", context! { page_title: "Access token generator", link, base_url }, )?; - Ok(RawHtml(html)) + Ok(Html(html)) } - #[get("/_generate-token")] - pub fn token_generation_form( + pub async fn token_generation_form( token: Token, - tera: &State, - website_root: WebsiteRoot, - ) -> Result, Error> { + State(app_state): State, + ) -> Result, Error> { if !token.profiles().contains(&"*".to_owned()) { Err(Error::Unauthorized)? } - token_generation_form_(tera, None, &website_root) + token_generation_form_(&app_state.tera, None, &app_state.website_root) } - #[derive(FromForm)] + #[derive(Deserialize)] pub struct GenerateTokenForm { ttl: String, name: String, @@ -206,13 +200,11 @@ pub mod token_generator { url: String, } - #[post("/_generate-token", data = "
")] - pub fn generate_token( + pub async fn generate_token( token: Token, - tera: &State, - form: Form>, - website_root: WebsiteRoot, - ) -> Result, Error> { + State(app_state): State, + Form(form): Form, + ) -> Result, Error> { if !token.profiles().contains(&"*".to_owned()) { Err(Error::Unauthorized)? } @@ -229,6 +221,6 @@ pub mod token_generator { let token_base64 = token.as_base64()?; let link = format!("{}?refresh_token={token_base64}", form.url); - token_generation_form_(tera, Some(link), &website_root) + token_generation_form_(&app_state.tera, Some(link), &app_state.website_root) } } diff --git a/src/orangutan-server/src/routes/main_route.rs b/src/orangutan-server/src/routes/main_route.rs index bd91661..34598ae 100644 --- a/src/orangutan-server/src/routes/main_route.rs +++ b/src/orangutan-server/src/routes/main_route.rs @@ -1,84 +1,111 @@ -use std::{path::Path, time::SystemTime}; +use std::{path::PathBuf, str::FromStr, time::SystemTime}; +use axum::{ + extract::{Request, State}, + http::{ + header::{HeaderMap, ACCEPT}, + Uri, + }, + response::Response, + routing::get, + Router, +}; +use axum_extra::either::Either; use biscuit_auth::macros::authorizer; -use object_reader::{ObjectReader, ReadObjectResponse}; -use orangutan_helpers::{data_file, read_allowed, readers::object_reader, website_id::WebsiteId}; -use rocket::{ - get, - http::{uri::Origin, Accept}, - routes, Route, State, +use mime::Mime; +use orangutan_helpers::{ + page_metadata, + website_id::{website_dir, WebsiteId}, }; +use tower::Service; +use tower_http::services::{fs::ServeFileSystemResponseBody, ServeDir, ServeFile}; use tracing::{debug, trace}; -use crate::{config::*, request_guards::Token, routes::debug_routes::log_access, util::error}; +use crate::{config::*, request_guards::Token, routes::debug_routes::log_access, AppState, Error}; -pub(super) fn routes() -> Vec { - routes![handle_request] +pub(super) fn router() -> Router { + Router::::new() + .route("/", get(handle_request)) + .route("/*path", get(handle_request)) } -#[get("/<_..>")] +// #[axum::debug_handler] async fn handle_request( - origin: &Origin<'_>, + State(_app_state): State, + uri: Uri, token: Option, - object_reader: &State, - accept: Option<&Accept>, -) -> Result, crate::Error> { - // FIXME: Handle error - let path = urlencoding::decode(origin.path().as_str()) - .unwrap() - .into_owned(); + headers: HeaderMap, +) -> Result, Error>, Error> { + let path = uri.path(); trace!("GET {}", &path); let user_profiles: Vec = token.as_ref().map(Token::profiles).unwrap_or_default(); debug!("User has profiles {user_profiles:?}"); let website_id = WebsiteId::from(&user_profiles); + let website_dir = website_dir(&website_id); // Log access only if the page is HTML. // WARN: This solution is far from perfect as someone requesting a page without setting the `Accept` header // would not be logged even though they'd get the file back. - if accept.is_some_and(|a| a.media_types().find(|t| t.is_html()).is_some()) { + let accept = headers + .get(ACCEPT) + .map(|value| { + value + .to_str() + .inspect_err(|err| debug!("{value:?} could not be mapped to a string: {err}")) + .ok() + }) + .flatten() + .map(|value| { + Mime::from_str(value) + .inspect_err(|err| debug!("{value:?} could not be mapped to a MIME type: {err}")) + .ok() + }) + .flatten(); + if accept.is_some_and(|m| m.type_() == mime::HTML) { log_access(user_profiles.to_owned(), path.to_owned()); } - let stored_objects: Vec = - object_reader - .list_objects(&path, &website_id) - .map_err(|err| Error::CannotListObjects { - path: path.to_owned(), - err, - })?; - let Some(object_key) = matching_files(&path, &stored_objects) - .first() - .map(|o| o.to_owned()) - else { - error(format!( - "No file matching '{}' found in stored objects", - &path - )); - return Ok(None); - }; - - let allowed_profiles = allowed_profiles(&object_key); - let Some(allowed_profiles) = allowed_profiles else { - // If allowed profiles is empty, it means it's a static file + async fn serve_file( + website_dir: &PathBuf, + path: &Uri, + ) -> Response { + let fallback = website_dir.join(NOT_FOUND_FILE); trace!( - "File <{}> did not explicitly allow profiles, serving static file", - &path + "Serving {path} at {} falling back to {}…", + website_dir.display(), + fallback.display(), ); + let mut serve_dir = ServeDir::new(website_dir).not_found_service(ServeFile::new(fallback)); + serve_dir + .call(Request::get(path).body(()).unwrap()) + .await + .map_err(|err| match err {}) + .unwrap() + } - return Ok(Some( - object_reader.read_object(&object_key, &website_id).await, - )); + let page_relpath = PathBuf::from_str(&path).unwrap(); + let Some(page_metadata) = page_metadata(&page_relpath) + .map_err(orangutan_helpers::generate::Error::CannotReadPageMetadata)? + else { + // If metadata can't be found, it means it's a static file + trace!("File <{path}> did not explicitly allow profiles, serving static file"); + // TODO: Un-hardcode this value. + let res = serve_file(&website_dir, &Uri::from_str(path).unwrap()).await; + return Ok(Either::E1(res)); }; - debug!( - "Page <{}> can be read by {}", - &path, - allowed_profiles - .iter() - .map(|p| format!("'{}'", p)) - .collect::>() - .join(", ") - ); + + let allowed_profiles = page_metadata.read_allowed; + // debug!( + // "Page <{}> can be read by {}", + // &path, + // allowed_profiles + // .iter() + // .map(|p| format!("'{}'", p)) + // .collect::>() + // .join(", ") + // ); + let mut profile: Option = None; let biscuit = token.map(|t| t.biscuit); for allowed_profile in allowed_profiles { @@ -113,122 +140,13 @@ async fn handle_request( } if profile.is_none() { debug!("No profile allowed in token"); - return Ok(None); - } - - Ok(Some( - object_reader.read_object(object_key, &website_id).await, - )) -} - -fn allowed_profiles<'r>(path: &String) -> Option> { - let path = path.rsplit_once("@").unwrap_or((path, "")).0; - let data_file = data_file(&Path::new(path).to_path_buf()); - read_allowed(&data_file) -} - -fn matching_files<'a>( - query: &str, - stored_objects: &'a Vec, -) -> Vec<&'a String> { - stored_objects - .into_iter() - .filter(|p| { - let query = query.strip_suffix("index.html").unwrap_or(query); - let Some(mut p) = p.strip_prefix(query) else { - return false; - }; - p = p.trim_start_matches('/'); - p = p.strip_prefix("index.html").unwrap_or(p); - return p.is_empty() || p.starts_with('@'); - }) - .collect() -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Error when listing objects matching '{path}': {err}")] - CannotListObjects { - path: String, - err: object_reader::Error, - }, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_index_html() { - let stored_objects = vec![ - "/index.html@_default", - "/whatever/index.html@friends", - "/whatever/index.html@family", - "/whatever/p.html@_default", - "/whatever/index.htmlindex.html@_default", - "/whatever/other-page/index.html@_default", - "/whatever/a/b.html@_default", - ] - .into_iter() - .map(|p| p.to_string()) - .collect::>(); - - assert_eq!(matching_files("", &stored_objects), vec![ - "/index.html@_default", - ]); - assert_eq!(matching_files("/", &stored_objects), vec![ - "/index.html@_default", - ]); - assert_eq!(matching_files("/index.html", &stored_objects), vec![ - "/index.html@_default", - ]); - - assert_eq!(matching_files("/whatever", &stored_objects), vec![ - "/whatever/index.html@friends", - "/whatever/index.html@family", - ]); - assert_eq!(matching_files("/whatever/", &stored_objects), vec![ - "/whatever/index.html@friends", - "/whatever/index.html@family", - ]); - assert_eq!( - matching_files("/whatever/index.html", &stored_objects), - vec![ - "/whatever/index.html@friends", - "/whatever/index.html@family", - ] - ); - - assert_eq!( - matching_files("/whatever/a", &stored_objects), - Vec::<&str>::new() - ); - assert_eq!( - matching_files("/whatever/a/b", &stored_objects), - Vec::<&str>::new() - ); - assert_eq!(matching_files("/whatever/a/b.html", &stored_objects), vec![ - "/whatever/a/b.html@_default", - ]); + return Ok(Either::E2(Error::Forbidden)); } - #[test] - fn test_other_extensions() { - let stored_objects = vec![ - "/style.css@_default", - "/anything.custom@friends", - "/anything.custom@family", - ] - .into_iter() - .map(|p| p.to_string()) - .collect::>(); - - assert_eq!(matching_files("/style.css", &stored_objects), vec![ - "/style.css@_default", - ]); - assert_eq!(matching_files("/anything.custom", &stored_objects), vec![ - "/anything.custom@friends", - "/anything.custom@family", - ]); - } + let res = serve_file( + &website_dir, + &Uri::from_str(page_metadata.path.to_str().unwrap()).unwrap(), + ) + .await; + Ok(Either::E1(res)) } diff --git a/src/orangutan-server/src/routes/mod.rs b/src/orangutan-server/src/routes/mod.rs index e59d060..1649fb8 100644 --- a/src/orangutan-server/src/routes/mod.rs +++ b/src/orangutan-server/src/routes/mod.rs @@ -3,21 +3,19 @@ // Copyright: 2023–2024, Rémi Bardon // License: Mozilla Public License v2.0 (MPL v2.0) -pub mod auth_routes; pub mod debug_routes; pub mod main_route; pub mod update_content_routes; -use rocket::Route; +use axum::Router; -pub(super) fn routes() -> Vec { - vec![ - main_route::routes(), - auth_routes::routes(), - update_content_routes::routes(), - debug_routes::routes(), - ] - .concat() +use crate::AppState; + +pub(super) fn router() -> Router { + Router::::new() + .merge(main_route::router()) + .merge(update_content_routes::router()) + .merge(debug_routes::router()) } #[cfg(feature = "templating")] diff --git a/src/orangutan-server/src/routes/update_content_routes.rs b/src/orangutan-server/src/routes/update_content_routes.rs index 276485e..616f788 100644 --- a/src/orangutan-server/src/routes/update_content_routes.rs +++ b/src/orangutan-server/src/routes/update_content_routes.rs @@ -1,16 +1,16 @@ +use axum::{extract::Path, routing::post, Router}; use orangutan_helpers::generate::{self, *}; -use rocket::{post, response::status::BadRequest, routes, Route}; -use super::auth_routes::REVOKED_TOKENS; -use crate::error; +use crate::{error, request_guards::REVOKED_TOKENS, AppState}; -pub(super) fn routes() -> Vec { - routes![update_content_github, update_content_other] +pub(super) fn router() -> Router { + Router::::new() + .route("/update-content/github", post(update_content_github)) + .route("/update-content/:source", post(update_content_other)) } /// TODO: [Validate webhook deliveries](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries#validating-webhook-deliveries) -#[post("/update-content/github")] -fn update_content_github() -> Result<(), crate::Error> { +async fn update_content_github() -> Result<(), crate::Error> { // Update repository pull_repository().map_err(Error::CannotPullOutdatedRepository)?; @@ -34,9 +34,8 @@ fn update_content_github() -> Result<(), crate::Error> { Ok(()) } -#[post("/update-content/")] -fn update_content_other(source: &str) -> BadRequest { - BadRequest(format!("Source '{source}' is not supported.")) +async fn update_content_other(Path(source): Path) -> crate::Error { + crate::Error::ClientError(format!("Source '{source}' is not supported.")) } #[derive(Debug, thiserror::Error)] diff --git a/src/orangutan-server/src/util/mod.rs b/src/orangutan-server/src/util/mod.rs index 478e7e2..f60c2b9 100644 --- a/src/orangutan-server/src/util/mod.rs +++ b/src/orangutan-server/src/util/mod.rs @@ -3,12 +3,15 @@ pub mod templating; #[cfg(feature = "token-generator")] mod website_root; +use axum_extra::extract::{ + cookie::{Cookie, SameSite}, + CookieJar, +}; use biscuit_auth::{ builder::{Fact, Term}, Biscuit, }; use chrono::Utc; -use rocket::http::{Cookie, CookieJar, SameSite}; use time::Duration; use tracing::error; @@ -56,23 +59,59 @@ pub fn add_padding(base64_string: &str) -> String { } } +/// Returns a new [CookieJar] which _must_ be returned from the handler +/// as part of the response for the changes to be propagated. +/// See [CookieJar]'s documentation for examples. pub fn add_cookie( biscuit: &Biscuit, - cookies: &CookieJar<'_>, -) { - match biscuit.to_base64() { - Ok(base64) => { - cookies.add( - Cookie::build((TOKEN_COOKIE_NAME, base64)) - .path("/") - .max_age(Duration::days(365 * 5)) - .http_only(true) - .secure(true) - .same_site(SameSite::Strict), - ); - }, - Err(err) => { - error(format!("Error setting token cookie: {err}")); - }, + cookies: CookieJar, +) -> Result { + let base64 = biscuit.to_base64().map_err(|err| { + crate::Error::InternalServerError(format!("Error setting token cookie: {err}")) + })?; + let cookie = Cookie::build((TOKEN_COOKIE_NAME, base64)) + .path("/") + .max_age(Duration::days(365 * 5)) + .http_only(true) + .secure(true) + .same_site(SameSite::Strict) + .build(); + Ok(cookies.clone().add(cookie)) +} + +#[cfg(test)] +mod tests { + use super::add_padding; + + #[test] + fn test_base64_padding() { + assert_eq!(add_padding("a"), "a===".to_string()); + assert_eq!(add_padding("ab"), "ab==".to_string()); + assert_eq!(add_padding("abc"), "abc=".to_string()); + assert_eq!(add_padding("abcd"), "abcd".to_string()); + + assert_eq!(add_padding("a==="), "a===".to_string()); + assert_eq!(add_padding("ab=="), "ab==".to_string()); + assert_eq!(add_padding("abc="), "abc=".to_string()); + assert_eq!(add_padding("abcd"), "abcd".to_string()); } + + // #[test] + // fn test_should_force_token_refresh() { + // assert_eq!(should_force_token_refresh(None), false); + // assert_eq!(should_force_token_refresh(Some(Ok(true))), true); + // assert_eq!(should_force_token_refresh(Some(Ok(false))), false); + // assert_eq!( + // should_force_token_refresh(Some(Err(Errors::new().with_name("yes")))), + // true + // ); + // assert_eq!( + // should_force_token_refresh(Some(Err(Errors::new().with_name("no")))), + // true + // ); + // assert_eq!( + // should_force_token_refresh(Some(Err(Errors::new().with_name("")))), + // true + // ); + // } } diff --git a/src/orangutan-server/src/util/templating.rs b/src/orangutan-server/src/util/templating.rs index c4258d7..40ddda8 100644 --- a/src/orangutan-server/src/util/templating.rs +++ b/src/orangutan-server/src/util/templating.rs @@ -1,6 +1,3 @@ -use rocket::serde::Serialize; -use tera::Context; - use super::error; #[derive(Debug, thiserror::Error)] @@ -11,12 +8,12 @@ pub enum Error { RenderError(tera::Error), } -pub fn render( +pub fn render( tera: &tera::Tera, template: &str, context: C, ) -> Result { - let tera_ctx = Context::from_serialize(context).map_err(Error::ContextError)?; + let tera_ctx = tera::Context::from_serialize(context).map_err(Error::ContextError)?; tera.render(template, &tera_ctx).map_err(Error::RenderError) } @@ -90,7 +87,7 @@ pub fn render( #[macro_export] macro_rules! context { ($($key:ident $(: $value:expr)?),*$(,)?) => {{ - use rocket::serde::ser::{Serialize, Serializer, SerializeMap}; + use serde::ser::{Serialize, Serializer, SerializeMap}; use ::std::fmt::{Debug, Formatter}; use ::std::result::Result; diff --git a/src/orangutan-server/src/util/website_root.rs b/src/orangutan-server/src/util/website_root.rs index a0b41be..03c4aa5 100644 --- a/src/orangutan-server/src/util/website_root.rs +++ b/src/orangutan-server/src/util/website_root.rs @@ -1,16 +1,12 @@ use std::ops::Deref; use lazy_static::lazy_static; -use rocket::{ - request::{FromRequest, Outcome}, - Ignite, Request, Rocket, -}; -use tracing::error; lazy_static! { static ref WEBSITE_ROOT: String = std::env::var("WEBSITE_ROOT").unwrap_or_default(); } +#[derive(Clone)] pub struct WebsiteRoot(String); impl Deref for WebsiteRoot { @@ -21,21 +17,12 @@ impl Deref for WebsiteRoot { } } -#[rocket::async_trait] -impl<'r> FromRequest<'r> for WebsiteRoot { - type Error = &'static str; - - async fn from_request(_req: &'r Request<'_>) -> Outcome { - Outcome::Success(Self(WEBSITE_ROOT.to_owned())) - } -} - -impl rocket::Sentinel for WebsiteRoot { - fn abort(_rocket: &Rocket) -> bool { +impl WebsiteRoot { + pub fn try_from_env() -> Result { if WEBSITE_ROOT.is_empty() { - error!("Environment variable `WEBSITE_ROOT` not found."); - return true; + Err("Environment variable `WEBSITE_ROOT` not found.") + } else { + Ok(Self(WEBSITE_ROOT.to_owned())) } - false } }