diff --git a/Cargo.lock b/Cargo.lock index 6c9d9237..a3bd08dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,12 +327,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5161fb43cb8f231f06983cb2b8f9ee9cee14be0b1356d8383398cc0f312800d1" -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - [[package]] name = "atomic" version = "0.6.1" @@ -376,7 +370,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 1.5.0", + "http", "sha1 0.10.7", "time", "tokio", @@ -414,8 +408,8 @@ dependencies = [ "bytes", "bytes-utils", "fastrand", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "percent-encoding", "pin-project-lite", "tracing", @@ -442,7 +436,7 @@ dependencies = [ "aws-types", "bytes", "fastrand", - "http 1.5.0", + "http", "regex-lite", "tracing", ] @@ -467,7 +461,7 @@ dependencies = [ "aws-types", "bytes", "fastrand", - "http 1.5.0", + "http", "regex-lite", "tracing", ] @@ -493,7 +487,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "fastrand", - "http 1.5.0", + "http", "regex-lite", "tracing", ] @@ -512,7 +506,7 @@ dependencies = [ "form_urlencoded", "hex", "hmac 0.13.0", - "http 1.5.0", + "http", "percent-encoding", "sha2 0.11.0", "time", @@ -542,8 +536,8 @@ dependencies = [ "bytes-utils", "futures-core", "futures-util", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "percent-encoding", "pin-project-lite", @@ -598,8 +592,8 @@ dependencies = [ "aws-smithy-types", "bytes", "fastrand", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "pin-project-lite", "pin-utils", @@ -617,7 +611,7 @@ dependencies = [ "aws-smithy-runtime-api-macros", "aws-smithy-types", "bytes", - "http 1.5.0", + "http", "pin-project-lite", "tokio", "tracing", @@ -643,7 +637,7 @@ checksum = "e8f395d93304280b64b7632fea798d177e74897fe7f063416ce627cd6fa24829" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.5.0", + "http", ] [[package]] @@ -655,8 +649,8 @@ dependencies = [ "base64-simd", "bytes", "bytes-utils", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "itoa", "num-integer", @@ -1896,7 +1890,7 @@ version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ - "atomic 0.6.1", + "atomic", "pear", "serde", "toml 0.8.23", @@ -2203,7 +2197,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.5.0", + "http", "indexmap 2.14.2", "slab", "tokio", @@ -2412,17 +2406,6 @@ dependencies = [ "jetscii", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.5.0" @@ -2433,17 +2416,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.1.0" @@ -2451,7 +2423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", - "http 1.5.0", + "http", ] [[package]] @@ -2462,8 +2434,8 @@ checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "pin-project-lite", ] @@ -2488,29 +2460,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.11.1" @@ -2522,9 +2471,10 @@ dependencies = [ "futures-channel", "futures-core", "h2", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2538,12 +2488,12 @@ version = "0.27.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" dependencies = [ - "http 1.5.0", - "hyper 1.11.1", + "http", + "hyper", "hyper-util", - "rustls 0.23.45", + "rustls", "tokio", - "tokio-rustls 0.26.6", + "tokio-rustls", "tower-service", ] @@ -2557,15 +2507,15 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "httparse", - "hyper 1.11.1", + "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.5", + "socket2", "system-configuration", "tokio", "tower-service", @@ -2742,7 +2692,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.5", + "socket2", "widestring", "windows-registry", "windows-result", @@ -2999,12 +2949,12 @@ dependencies = [ "nom 8.0.0", "percent-encoding", "quoted_printable", - "rustls 0.23.45", + "rustls", "rustls-native-certs", "serde", - "socket2 0.6.5", + "socket2", "tokio", - "tokio-rustls 0.26.6", + "tokio-rustls", "tracing", "url", ] @@ -3217,7 +3167,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.5.0", + "http", "httparse", "memchr", "mime", @@ -3411,7 +3361,7 @@ dependencies = [ "base64 0.22.1", "chrono", "getrandom 0.2.17", - "http 1.5.0", + "http", "rand 0.8.8", "serde", "serde_json", @@ -3462,7 +3412,7 @@ dependencies = [ "base64 0.23.1", "bytes", "futures", - "http 1.5.0", + "http", "jiff", "log", "md-5", @@ -3485,8 +3435,8 @@ checksum = "4d6588678f3040a3eab1819364dcbc6a746a8286d1ceb23a70a6614faa01dc6c" dependencies = [ "bytes", "futures", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "opendal-core", "reqwest", ] @@ -3514,7 +3464,7 @@ dependencies = [ "base64 0.23.1", "bytes", "crc-fast", - "http 1.5.0", + "http", "log", "md-5", "opendal-core", @@ -3537,7 +3487,7 @@ dependencies = [ "dyn-clone", "ed25519-dalek", "hmac 0.12.1", - "http 1.5.0", + "http", "itertools", "log", "oauth2", @@ -4251,6 +4201,12 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "ref-swap" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c30c54dffee5b40af088d5d50aa3455c91a0127164b51f0215efc4cb28fb3c" + [[package]] name = "regex" version = "1.13.1" @@ -4306,7 +4262,7 @@ dependencies = [ "bytes", "form_urlencoded", "hex", - "http 1.5.0", + "http", "log", "percent-encoding", "quick-xml 0.42.0", @@ -4325,7 +4281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "906bfd028f96bfe73fdb892ce26f3b3f99b4f30feb9bd024be58bd6657f68b2b" dependencies = [ "bytes", - "http 1.5.0", + "http", "log", "quick-xml 0.42.0", "reqsign-aws-core", @@ -4345,7 +4301,7 @@ dependencies = [ "bytes", "hex", "hmac 0.13.0", - "http 1.5.0", + "http", "jiff", "log", "percent-encoding", @@ -4379,10 +4335,10 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", - "hyper 1.11.1", + "hyper", "hyper-rustls", "hyper-util", "js-sys", @@ -4390,7 +4346,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustls 0.23.45", + "rustls", "rustls-pki-types", "rustls-platform-verifier", "serde", @@ -4398,7 +4354,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls 0.26.6", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -4441,118 +4397,117 @@ dependencies = [ ] [[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmpv" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417" -dependencies = [ - "rmp", -] - -[[package]] -name = "rocket" -version = "0.5.1" +name = "rkt" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" +checksum = "1105eb4e327458258fa921de564fd9bd45b9f8fd27d771e5fb82314c703eb8a3" dependencies = [ "async-stream", "async-trait", - "atomic 0.5.3", "binascii", "bytes", + "cookie", "either", "figment", "futures", + "http", + "hyper", + "hyper-util", "indexmap 2.14.2", - "log", + "libc", "memchr", "multer", "num_cpus", "parking_lot", "pin-project-lite", - "rand 0.8.8", + "rand 0.10.3", "ref-cast", - "rocket_codegen", - "rocket_http", + "ref-swap", + "rkt_codegen", + "rkt_http", + "rustls", + "rustls-pki-types", "serde", "serde_json", "state", "tempfile", "time", "tokio", + "tokio-rustls", "tokio-stream", "tokio-util", + "tracing", "ubyte", "version_check", "yansi", ] [[package]] -name = "rocket_codegen" -version = "0.5.1" +name = "rkt_codegen" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" +checksum = "55143dea09d0f0c86aeb8d5cd0998b80a7920518bbb412dc860292be9524a259" dependencies = [ "devise", "glob", "indexmap 2.14.2", "proc-macro2", "quote", - "rocket_http", + "rkt_http", "syn 2.0.119", "unicode-xid", "version_check", ] [[package]] -name = "rocket_http" -version = "0.5.1" +name = "rkt_http" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" +checksum = "ccb2598aa857ab8d59d009604d862a7780c1213d3e162321bd30f72e93129637" dependencies = [ "cookie", "either", - "futures", - "http 0.2.12", - "hyper 0.14.32", "indexmap 2.14.2", - "log", "memchr", "pear", "percent-encoding", - "pin-project-lite", "ref-cast", - "rustls 0.21.12", - "rustls-pemfile", "serde", - "smallvec", "stable-pattern", "state", "time", - "tokio", - "tokio-rustls 0.24.1", + "tinyvec", "uncased", ] [[package]] -name = "rocket_ws" -version = "0.1.1" +name = "rkt_ws" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1877668c937b701177c349f21383c556cd3bb4ba8fa1d07fa96ccb3a8782e" +checksum = "cc1a9b1739c2b945e0aae4efb1ccb0cea1a410893143651077977f57e376cfab" dependencies = [ - "rocket", + "rkt", "tokio-tungstenite", ] +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmpv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417" +dependencies = [ + "rmp", +] + [[package]] name = "rpassword" version = "7.5.4" @@ -4645,18 +4600,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - [[package]] name = "rustls" version = "0.23.45" @@ -4667,7 +4610,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.15", + "rustls-webpki", "subtle", "zeroize", ] @@ -4684,15 +4627,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pki-types" version = "1.15.1" @@ -4713,10 +4647,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.45", + "rustls", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.103.15", + "rustls-webpki", "security-framework", "security-framework-sys", "webpki-root-certs", @@ -4729,16 +4663,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.103.15" @@ -4825,16 +4749,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sec1" version = "0.7.3" @@ -5165,16 +5079,6 @@ version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.5" @@ -5500,7 +5404,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.5", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -5516,23 +5420,13 @@ dependencies = [ "syn 3.0.6", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" dependencies = [ - "rustls 0.23.45", + "rustls", "tokio", ] @@ -5549,9 +5443,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "17a073bfed563fa236697a068031408a93cd9522e08abf9933ead3e73411bd71" dependencies = [ "futures-util", "log", @@ -5705,8 +5599,8 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 1.5.0", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "pin-project-lite", "tokio", @@ -5799,21 +5693,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22" dependencies = [ - "byteorder", "bytes", "data-encoding", - "http 1.5.0", + "http", "httparse", "log", - "rand 0.8.8", - "sha1 0.10.7", - "thiserror 1.0.69", - "url", - "utf-8", + "rand 0.10.3", + "sha1 0.11.0", + "thiserror 2.0.21", ] [[package]] @@ -5890,12 +5781,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -5957,7 +5842,7 @@ dependencies = [ "handlebars", "hickory-resolver", "html5gum", - "http 1.5.0", + "http", "ipnet", "job_scheduler_ng", "jsonwebtoken", @@ -5983,11 +5868,11 @@ dependencies = [ "reqsign-core", "reqwest", "ring", + "rkt", + "rkt_ws", "rmpv", - "rocket", - "rocket_ws", "rpassword", - "rustls 0.23.45", + "rustls", "semver", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 764fbd53..1db11751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,9 +79,9 @@ num-traits = "0.2.19" num-derive = "0.5.1" bigdecimal = "0.4.11" -# Web framework -rocket = { version = "0.5.1", default-features = false, features = ["json", "tls"] } -rocket_ws = { version = "0.1.1" } +# Web framework fork from Rocket https://github.com/rustfoo/rkt +rocket = { package = "rkt", version = "1.2.0", default-features = false, features = ["json", "tls"] } +rocket_ws = { package = "rkt_ws", version = "1.2.0" } # WebSockets libraries rmpv = "1.3.1" # MessagePack library diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index dd2e5801..47040697 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1323,6 +1323,7 @@ async fn post_delete_account(data: Json, headers: Headers, co delete_account(data, headers, conn).await } +#[suppress(dubious_payload)] #[delete("/accounts", data = "")] async fn delete_account(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let data: PasswordOrOtpData = data.into_inner(); diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 46766896..6639c406 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -1532,6 +1532,7 @@ async fn delete_cipher_admin(cipher_id: CipherId, headers: Headers, conn: DbConn // permanent delete } +#[suppress(dubious_payload)] #[delete("/ciphers", data = "")] async fn delete_cipher_selected( data: Json, @@ -1565,6 +1566,7 @@ async fn delete_cipher_selected_put( // soft delete } +#[suppress(dubious_payload)] #[delete("/ciphers/admin", data = "")] async fn delete_cipher_selected_admin( data: Json, diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 6fccc850..4a4db164 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -215,6 +215,7 @@ async fn create_organization(headers: Headers, data: Json, conn: DbConn Ok(Json(org.to_json())) } +#[suppress(dubious_payload)] #[delete("/organizations/", data = "")] async fn delete_organization( org_id: OrganizationId, @@ -711,6 +712,7 @@ struct BulkCollectionIds { ids: Vec, } +#[suppress(dubious_payload)] #[delete("/organizations//collections", data = "")] async fn bulk_delete_organization_collections( org_id: OrganizationId, @@ -1593,6 +1595,7 @@ async fn edit_member( member_to_edit.save(&conn).await } +#[suppress(dubious_payload)] #[delete("/organizations//users", data = "")] async fn bulk_delete_member( org_id: OrganizationId, @@ -2677,6 +2680,7 @@ async fn delete_group_impl( group.delete(org_id, conn).await } +#[suppress(dubious_payload)] #[delete("/organizations//groups", data = "")] async fn bulk_delete_groups( org_id: OrganizationId, diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index 55984443..db3d62d9 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -177,6 +177,7 @@ struct DisableAuthenticatorData { user_verification_token: String, } +#[suppress(dubious_payload)] #[delete("/two-factor/authenticator", data = "")] async fn disable_authenticator(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let user = headers.user; diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs index 23116b2c..c58e3e26 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -143,6 +143,7 @@ async fn activate_duo_put(data: Json, headers: Headers, conn: DbC activate_duo(data, headers, conn).await } +#[suppress(dubious_payload)] #[delete("/two-factor/duo", data = "")] async fn disable_duo(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let user = headers.user; diff --git a/src/api/core/two_factor/email.rs b/src/api/core/two_factor/email.rs index 531eea58..6a8bab3a 100644 --- a/src/api/core/two_factor/email.rs +++ b/src/api/core/two_factor/email.rs @@ -255,6 +255,7 @@ async fn email(data: Json, headers: Headers, conn: DbConn) -> JsonRes Ok(Json(json!({}))) } +#[suppress(dubious_payload)] #[delete("/two-factor/email", data = "")] async fn disable_email(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let user = headers.user; diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs index 7eab5546..6ba04ea9 100644 --- a/src/api/core/two_factor/webauthn.rs +++ b/src/api/core/two_factor/webauthn.rs @@ -325,11 +325,13 @@ struct DeleteWebauthnData { user_verification_token: String, } +#[suppress(dubious_payload)] #[delete("/two-factor/webauthn", data = "")] async fn delete_webauthn(data: Json, headers: Headers, conn: DbConn) -> JsonResult { inner_delete_webauthns(&data.user_verification_token, |key| key.id != data.id, headers, &conn).await } +#[suppress(dubious_payload)] #[delete("/two-factor/webauthn/all", data = "")] async fn delete_webauthns(data: Json, headers: Headers, conn: DbConn) -> JsonResult { inner_delete_webauthns(&data.user_verification_token, |_| false, headers, &conn).await diff --git a/src/api/core/two_factor/yubikey.rs b/src/api/core/two_factor/yubikey.rs index 17c4e9a0..1798cdbc 100644 --- a/src/api/core/two_factor/yubikey.rs +++ b/src/api/core/two_factor/yubikey.rs @@ -202,6 +202,7 @@ async fn activate_yubikey_put(data: Json, headers: Headers, c activate_yubikey(data, headers, conn).await } +#[suppress(dubious_payload)] #[delete("/two-factor/yubikey", data = "")] async fn delete_yubikeys(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { let user = headers.user; diff --git a/src/api/mod.rs b/src/api/mod.rs index 8baa221d..0ac78f5c 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -30,7 +30,7 @@ pub use crate::api::{ }, web::catchers as web_catchers, web::routes as web_routes, - web::{invalidate_css_cache, static_files}, + web::{STATIC_FILES, invalidate_css_cache}, }; use crate::{ CONFIG, diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 32204916..dccb1266 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -4,6 +4,7 @@ use std::{ time::{Duration, Instant}, }; +use bytes::Bytes; use chrono::{NaiveDateTime, Utc}; use rmpv::Value; use rocket::{Route, futures::StreamExt}; @@ -212,7 +213,7 @@ fn websockets_hub<'r>( if last_received.elapsed() > WS_CLIENT_TIMEOUT { break; } - yield Message::Ping(create_ping()); + yield Message::Ping(PING); } } } @@ -295,7 +296,7 @@ fn anonymous_websockets_hub<'r>(ws: WebSocket, token: String, ip: ClientIp) -> R if last_received.elapsed() > WS_CLIENT_TIMEOUT { break; } - yield Message::Ping(create_ping()); + yield Message::Ping(PING); } } } @@ -307,7 +308,7 @@ fn anonymous_websockets_hub<'r>(ws: WebSocket, token: String, ip: ClientIp) -> R // Websockets server // -fn serialize(val: &Value) -> Vec { +fn serialize(val: &Value) -> Bytes { use rmpv::encode::write_value; let mut buf = Vec::new(); @@ -335,7 +336,7 @@ fn serialize(val: &Value) -> Vec { } len_buf.append(&mut buf); - len_buf + len_buf.into() } fn serialize_date(date: NaiveDateTime) -> Value { @@ -358,7 +359,9 @@ fn convert_option>(option: Option) -> Value { } const RECORD_SEPARATOR: u8 = 0x1e; -const INITIAL_RESPONSE: [u8; 3] = [0x7b, 0x7d, RECORD_SEPARATOR]; // {, }, +const INITIAL_RESPONSE: Bytes = Bytes::from_static(&[0x7b, 0x7d, RECORD_SEPARATOR]); // {, }, +// Same result as `serialize(&Value::Array(vec![6.into()]))` +const PING: Bytes = Bytes::from_static(&[0x02, 0x91, 0x06]); #[derive(Deserialize, Copy, Clone, Eq, PartialEq)] struct InitialMessage<'a> { @@ -379,10 +382,10 @@ pub struct WebSocketUsers { } impl WebSocketUsers { - async fn send_update(&self, user_id: &UserId, data: &[u8]) { + async fn send_update(&self, user_id: &UserId, data: &Bytes) { if let Some(user) = self.map.get(user_id.as_ref()).map(|v| v.clone()) { for (_, sender) in &user { - if let Err(e) = sender.send(Message::binary(data)).await { + if let Err(e) = sender.send(Message::binary(data.clone())).await { error!("Error sending WS update {e}"); } } @@ -614,11 +617,11 @@ impl AnonymousWebSocketSubscriptions { } } - async fn send_update(&self, token: &str, data: &[u8]) { + async fn send_update(&self, token: &str, data: &Bytes) { // Clone the senders so the map isn't kept locked while sending. let senders = self.map.get(token).map(|v| v.clone()).unwrap_or_default(); for (_, sender) in senders { - if let Err(e) = sender.send(Message::binary(data)).await { + if let Err(e) = sender.send(Message::binary(data.clone())).await { error!("Error sending WS update {e}"); } } @@ -652,7 +655,7 @@ impl AnonymousWebSocketSubscriptions { ] ] */ -fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_id: Option) -> Vec { +fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_id: Option) -> Bytes { use rmpv::Value as V; let value = V::Array(vec![ @@ -670,7 +673,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_id: serialize(&value) } -fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id: &UserId) -> Vec { +fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id: &UserId) -> Bytes { use rmpv::Value as V; let value = V::Array(vec![ @@ -691,10 +694,6 @@ fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id serialize(&value) } -fn create_ping() -> Vec { - serialize(&Value::Array(vec![6.into()])) -} - // https://github.com/bitwarden/server/blob/375af7c43b10d9da03525d41452f95de3f921541/src/Core/Enums/PushType.cs #[derive(Copy, Clone, Eq, PartialEq)] pub enum UpdateType { diff --git a/src/api/web.rs b/src/api/web.rs index aa67e614..22069989 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -1,6 +1,7 @@ use std::{ + collections::HashMap, path::{Path, PathBuf}, - sync::{Arc, RwLock}, + sync::{Arc, LazyLock, RwLock}, }; use rocket::{ @@ -256,7 +257,7 @@ fn alive_head(_conn: DbConn) -> EmptyResult { // This endpoint/function is used during development and development only. // It allows to easily develop the admin interface by always loading the files from disk instead from a slice of bytes // This will only be active during a debug build and only when `RELOAD_TEMPLATES` is set to `true` -// NOTE: Do not forget to add any new files added to the `static_files` function below! +// NOTE: Do not forget to add any new files added to the `STATIC_FILES` HashMap below! #[cfg(debug_assertions)] #[get("/vw_static/", rank = 1)] pub async fn static_files_dev(filename: PathBuf) -> Option { @@ -276,32 +277,61 @@ pub async fn static_files_dev(filename: PathBuf) -> Option { None } +pub struct StaticFile { + pub content_type: ContentType, + pub bytes: &'static [u8], + pub etag: String, +} + +pub static STATIC_FILES: LazyLock> = LazyLock::new(|| { + [ + ("404.png", ContentType::PNG, &include_bytes!("../static/images/404.png")[..]), + ("mail-github.png", ContentType::PNG, &include_bytes!("../static/images/mail-github.png")[..]), + ("logo-gray.png", ContentType::PNG, &include_bytes!("../static/images/logo-gray.png")[..]), + ("error-x.svg", ContentType::SVG, &include_bytes!("../static/images/error-x.svg")[..]), + ("hibp.png", ContentType::PNG, &include_bytes!("../static/images/hibp.png")[..]), + ("vaultwarden-icon.png", ContentType::PNG, &include_bytes!("../static/images/vaultwarden-icon.png")[..]), + ("vaultwarden-favicon.png", ContentType::PNG, &include_bytes!("../static/images/vaultwarden-favicon.png")[..]), + ("404.css", ContentType::CSS, &include_bytes!("../static/scripts/404.css")[..]), + ("admin.css", ContentType::CSS, &include_bytes!("../static/scripts/admin.css")[..]), + ("admin.js", ContentType::JavaScript, &include_bytes!("../static/scripts/admin.js")[..]), + ("admin_settings.js", ContentType::JavaScript, &include_bytes!("../static/scripts/admin_settings.js")[..]), + ("admin_users.js", ContentType::JavaScript, &include_bytes!("../static/scripts/admin_users.js")[..]), + ( + "admin_organizations.js", + ContentType::JavaScript, + &include_bytes!("../static/scripts/admin_organizations.js")[..], + ), + ( + "admin_diagnostics.js", + ContentType::JavaScript, + &include_bytes!("../static/scripts/admin_diagnostics.js")[..], + ), + ("bootstrap.css", ContentType::CSS, &include_bytes!("../static/scripts/bootstrap.css")[..]), + ("bootstrap.bundle.js", ContentType::JavaScript, &include_bytes!("../static/scripts/bootstrap.bundle.js")[..]), + ("jdenticon-3.3.0.js", ContentType::JavaScript, &include_bytes!("../static/scripts/jdenticon-3.3.0.js")[..]), + ("datatables.js", ContentType::JavaScript, &include_bytes!("../static/scripts/datatables.js")[..]), + ("datatables.css", ContentType::CSS, &include_bytes!("../static/scripts/datatables.css")[..]), + ] + .into_iter() + .map(|(name, content_type, bytes)| { + ( + name, + StaticFile { + content_type, + bytes, + etag: sha256_hex(bytes), + }, + ) + }) + .collect() +}); + #[get("/vw_static/", rank = 2)] -pub fn static_files(filename: &str) -> Result<(ContentType, &'static [u8]), Error> { - match filename { - "404.png" => Ok((ContentType::PNG, include_bytes!("../static/images/404.png"))), - "mail-github.png" => Ok((ContentType::PNG, include_bytes!("../static/images/mail-github.png"))), - "logo-gray.png" => Ok((ContentType::PNG, include_bytes!("../static/images/logo-gray.png"))), - "error-x.svg" => Ok((ContentType::SVG, include_bytes!("../static/images/error-x.svg"))), - "hibp.png" => Ok((ContentType::PNG, include_bytes!("../static/images/hibp.png"))), - "vaultwarden-icon.png" => Ok((ContentType::PNG, include_bytes!("../static/images/vaultwarden-icon.png"))), - "vaultwarden-favicon.png" => Ok((ContentType::PNG, include_bytes!("../static/images/vaultwarden-favicon.png"))), - "404.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/404.css"))), - "admin.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/admin.css"))), - "admin.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin.js"))), - "admin_settings.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin_settings.js"))), - "admin_users.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin_users.js"))), - "admin_organizations.js" => { - Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin_organizations.js"))) - } - "admin_diagnostics.js" => { - Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin_diagnostics.js"))) - } - "bootstrap.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/bootstrap.css"))), - "bootstrap.bundle.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap.bundle.js"))), - "jdenticon-3.3.0.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon-3.3.0.js"))), - "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), - "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), - _ => err!(format!("Static file not found: {filename}")), +pub fn static_files(filename: &str) -> Result, Error> { + if let Some(file) = STATIC_FILES.get(filename) { + Ok(EtagCached::new((file.content_type.clone(), file.bytes), &file.etag)) + } else { + err!(format!("Static file not found: {filename}")) } } diff --git a/src/auth.rs b/src/auth.rs index 57720382..9b036785 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -77,7 +77,7 @@ pub async fn initialize_keys() -> Result<(), Error> { let rsa_key_filename = crate::storage::file_name(&CONFIG.private_rsa_key()) .ok_or_else(|| IoError::other("Private RSA key path missing filename"))?; - let operator = CONFIG.opendal_operator_for_path_type(&PathType::RsaKey).map_err(IoError::other)?; + let operator = CONFIG.opendal_operator_for_path_type(&PathType::RsaKey)?; let priv_key_buffer = match operator.read(&rsa_key_filename).await { Ok(buffer) => Some(buffer), @@ -1139,7 +1139,7 @@ impl<'r> FromRequest<'r> for ClientIp { type Error = (); async fn from_request(req: &'r Request<'_>) -> Outcome { - let remote = req.remote().map(|r| r.ip()); + let remote = req.remote().and_then(rocket::listener::Endpoint::ip); let ip = if CONFIG._ip_header_enabled() && ip_header_is_trusted(remote) { client_ip_from_header(req, &CONFIG.ip_header(), |ip| ip_header_is_trusted(Some(ip))) diff --git a/src/error.rs b/src/error.rs index d90c38e3..96d122d9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -165,10 +165,12 @@ impl Error { self } + #[must_use] pub fn get_event(&self) -> &Option { &self.event } + #[must_use] pub fn message(&self) -> &str { &self.message } @@ -425,12 +427,12 @@ macro_rules! err_json { macro_rules! err_handler { ($expr:expr) => {{ error!(target: "auth", "Unauthorized Error: {}", $expr); - return ::rocket::request::Outcome::Error((rocket::http::Status::Unauthorized, $expr)); + return ::rocket::request::Outcome::Error((::rocket::http::Status::Unauthorized, $expr)); }}; ($usr_msg:expr, $log_value:expr) => {{ let usr_msg = $usr_msg; let log_value = $log_value; error!(target: "auth", "Unauthorized Error: {usr_msg}. {log_value}"); - return ::rocket::request::Outcome::Error((rocket::http::Status::Unauthorized, usr_msg)); + return ::rocket::request::Outcome::Error((::rocket::http::Status::Unauthorized, usr_msg)); }}; } diff --git a/src/mail.rs b/src/mail.rs index 2efb8d70..a4109404 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -738,8 +738,8 @@ async fn send_email(address: &str, subject: &str, body_html: String, body_text: let smtp_from = Address::from_str(&CONFIG.smtp_from())?; let body = if CONFIG.smtp_embed_images() { - let logo_gray_body = Body::new(crate::api::static_files("logo-gray.png").unwrap().1.to_vec()); - let mail_github_body = Body::new(crate::api::static_files("mail-github.png").unwrap().1.to_vec()); + let logo_gray_body = Body::new(crate::api::STATIC_FILES["logo-gray.png"].bytes.to_vec()); + let mail_github_body = Body::new(crate::api::STATIC_FILES["mail-github.png"].bytes.to_vec()); MultiPart::alternative().singlepart(SinglePart::plain(body_text)).multipart( MultiPart::related() .singlepart(SinglePart::html(body_html)) diff --git a/src/main.rs b/src/main.rs index f27729d6..4194727e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,9 @@ static GLOBAL: MiMalloc = MiMalloc; #[macro_use] extern crate rocket; +// `rkt` codegen generates code using `rkt::` and not `rocket::` +// This ensures both `rocket` and `rkt` point to the same crate +extern crate rocket as rkt; #[macro_use] extern crate serde; #[macro_use] @@ -273,9 +276,10 @@ fn init_logging() -> Result { log::LevelFilter::Off }; - // Only show Rocket underscore `_` logs when the level is Debug or higher + // Only show some Rocket/Rkt logs when the level is Debug or higher + // These replace the Rocket underscore `_` logs. Rkt logs some of them as Info. // Else this will bloat the log output with useless messages. - let rocket_underscore_level = if level >= log::LevelFilter::Debug { + let rkt_debug_level = if level >= log::LevelFilter::Debug { log::LevelFilter::Warn } else { log::LevelFilter::Off @@ -296,24 +300,18 @@ fn init_logging() -> Result { log::LevelFilter::Off }; + // NOTE: Rocket has been replaced with Rkt, since the crate is called `rkt`, `rocket` does not work even though we use this throughout the code! let mut default_levels = HashMap::from([ // Hide unknown certificate errors if using self-signed ("rustls::session", log::LevelFilter::Off), // Hide failed to close stream messages ("hyper::server", log::LevelFilter::Warn), - // Silence Rocket `_` logs - ("_", rocket_underscore_level), - ("rocket::response::responder::_", rocket_underscore_level), - ("rocket::server::_", rocket_underscore_level), - ("vaultwarden::api::admin::_", rocket_underscore_level), - ("vaultwarden::api::notifications::_", rocket_underscore_level), - // Silence Rocket logs - ("rocket::launch", log::LevelFilter::Error), - ("rocket::launch_", log::LevelFilter::Error), - ("rocket::rocket", log::LevelFilter::Warn), - ("rocket::server", log::LevelFilter::Warn), - ("rocket::fairing::fairings", log::LevelFilter::Warn), - ("rocket::shield::shield", log::LevelFilter::Warn), + // Silence Rkt logs, this covers all `rocket::*`/`rkt::*` targets + ("rkt", log::LevelFilter::Warn), + // Rkt request/data guard logs (previously the Rocket `_` logs) + ("rkt::codegen", rkt_debug_level), + // Rkt per-connection warnings, like client header read timeouts (15s) on idle keep-alive connections and connections cancelled during shutdown + ("rkt::error", log::LevelFilter::Error), ("hyper::proto", log::LevelFilter::Off), ("hyper::client", log::LevelFilter::Off), // Filter handlebars logs @@ -563,20 +561,26 @@ async fn create_db_pool() -> db::DbPool { async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error> { let basepath = &CONFIG.domain_path(); - let mut config = rocket::Config::from(rocket::Config::figment()); - - // We install our own signal handlers below; disable Rocket's built-in handlers - config.shutdown.ctrlc = false; - #[cfg(unix)] - config.shutdown.signals.clear(); - - config.temp_dir = canonicalize(CONFIG.tmp_folder()).unwrap().into(); - config.cli_colors = false; // Make sure Rocket does not color any values for logging. - config.limits = Limits::new() + let limits = Limits::new() .limit("json", 20.megabytes()) // 20MB should be enough for very large imports, something like 5000+ vault entries .limit("data-form", 525.megabytes()) // This needs to match the maximum allowed file size for Send .limit("file", 525.megabytes()); // This needs to match the maximum allowed file size for attachments + let mut config = rocket::Config::figment() + .merge(("shutdown.ctrlc", false)) // We install our own signal handlers below; disable Rocket's built-in handlers + .merge(("temp_dir", canonicalize(CONFIG.tmp_folder()).unwrap())) + .merge(("cli_colors", rocket::config::CliColors::Never)) // Make sure Rocket does not color any values for logging + .merge(("limits", limits)); + + #[cfg(unix)] + { + config = config.merge(("shutdown.signals", Vec::::new())); + } + + if CONFIG._ip_header_enabled() { + config = config.merge(("ip_header", CONFIG.ip_header())); + } + // If adding more paths here, consider also adding them to // crate::utils::LOGGED_ROUTES to make sure they appear in the log let instance = rocket::custom(config) @@ -595,7 +599,7 @@ async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error> .manage(Arc::clone(&WS_ANONYMOUS_SUBSCRIPTIONS)) .attach(util::AppHeaders()) .attach(util::Cors()) - .attach(util::BetterLogging(extra_debug)) + .attach(util::BetterLogging::new(extra_debug, basepath.clone())) .ignite() .await?; diff --git a/src/util.rs b/src/util.rs index 9d319542..556b29d4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -299,72 +299,114 @@ impl<'r, R: 'r + Responder<'r, 'static> + Send> Responder<'r, 'static> for EtagC // Effectively ignores, any static file route, and the alive endpoint const LOGGED_ROUTES: [&str; 7] = ["/api", "/admin", "/identity", "/icons", "/attachments", "/events", "/notifications"]; +// Same format as the `Display` of a Rocket route, which Rkt does not have anymore +struct RouteDisplay<'a>(&'a rocket::Route); +impl fmt::Display for RouteDisplay<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let route = self.0; + if let Some(name) = &route.name { + write!(f, "({name}) ")?; + } + write!(f, "{} {}", route.method.map_or("ANY", |m| m.as_str()), route.uri)?; + if route.rank > 1 { + write!(f, " [{}]", route.rank)?; + } + if let Some(format) = &route.format { + write!(f, " {format}")?; + } + Ok(()) + } +} + // Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts -pub struct BetterLogging(pub bool); +pub struct BetterLogging { + extra: bool, + log_request: bool, + log_response: bool, + domain_path: String, +} + +impl BetterLogging { + pub fn new(extra: bool, domain_path: String) -> Self { + Self { + extra, + log_request: log_enabled!(target: "request", log::Level::Info), + log_response: log_enabled!(target: "response", log::Level::Info), + domain_path, + } + } + + fn should_log(&self, method: Method, path: &str) -> bool { + if self.extra { + return true; + } + if method == Method::Options { + return false; + } + let subpath = path.strip_prefix(&self.domain_path).unwrap_or(path); + LOGGED_ROUTES.iter().any(|r| subpath.starts_with(r)) + } +} + #[rocket::async_trait] impl Fairing for BetterLogging { fn info(&self) -> Info { + let mut kind = Kind::Liftoff; + if self.log_request { + kind = kind | Kind::Request; + } + if self.log_response { + kind = kind | Kind::Response; + } Info { name: "Better Logging", - kind: Kind::Liftoff | Kind::Request | Kind::Response, + kind, } } async fn on_liftoff(&self, rocket: &Rocket) { - if self.0 { + if self.extra { info!(target: "routes", "Routes loaded:"); let mut routes: Vec<_> = rocket.routes().collect(); - routes.sort_by_key(|r| r.uri.path()); + routes.sort_by(|a, b| a.uri.path().as_str().cmp(b.uri.path().as_str())); for route in routes { + let method = route.method.map_or("ANY", |m| m.as_str()); if route.rank < 0 { - info!(target: "routes", "{:<6} {}", route.method, route.uri); + info!(target: "routes", "{:<6} {}", method, route.uri); } else { - info!(target: "routes", "{:<6} {} [{}]", route.method, route.uri, route.rank); + info!(target: "routes", "{:<6} {} [{}]", method, route.uri, route.rank); } } } - let config = rocket.config(); - let scheme = if config.tls_enabled() { - "https" - } else { - "http" - }; - let addr = format!("{scheme}://{}:{}", config.address, config.port); - info!(target: "start", "Rocket has launched from {addr}"); + for endpoint in rocket.endpoints() { + info!(target: "start", "Rocket has launched from {endpoint}"); + } } async fn on_request(&self, request: &mut Request<'_>, _data: &mut Data<'_>) { + let uri = request.uri(); + let uri_path = uri.path(); let method = request.method(); - if !self.0 && method == Method::Options { + if !self.should_log(method, uri_path.as_str()) { return; } - let uri = request.uri(); - let uri_path = uri.path(); - let uri_path_str = uri_path.url_decode_lossy(); - let uri_subpath = uri_path_str.strip_prefix(&CONFIG.domain_path()).unwrap_or(&uri_path_str); - if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) { - match uri.query() { - Some(q) => info!(target: "request", "{method} {uri_path_str}?{}", &q[..q.len().min(30)]), - None => info!(target: "request", "{method} {uri_path_str}"), - } + let path = uri_path.url_decode_lossy(); + match uri.query() { + Some(q) => info!(target: "request", "{method} {path}?{}", &q[..q.len().min(30)]), + None => info!(target: "request", "{method} {path}"), } } async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) { - if !self.0 && request.method() == Method::Options { + let uri = request.uri(); + if !self.should_log(request.method(), uri.path().as_str()) { return; } - let uri_path = request.uri().path(); - let uri_path_str = uri_path.url_decode_lossy(); - let uri_subpath = uri_path_str.strip_prefix(&CONFIG.domain_path()).unwrap_or(&uri_path_str); - if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) { - let status = response.status(); - if let Some(route) = request.route() { - info!(target: "response", "{route} => {status}"); - } else { - info!(target: "response", "{status}"); - } + let status = response.status(); + match request.route() { + Some(r) => info!(target: "response", "{} => {status}", RouteDisplay(r)), + None => info!(target: "response", "{status}"), } } }