From e32ff3f6232a6c6e3fa3f1708c6e06bdaa9a8520 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Tue, 30 Aug 2022 16:11:05 +0200 Subject: [PATCH 1/4] Start work on session store for sqlx --- .gitignore | 1 + Cargo.lock | 2118 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 23 + src/lib.rs | 186 ++++ tests/session_store.rs | 57 ++ 5 files changed, 2385 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 tests/session_store.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1a7d22e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2118 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-http" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f9ffb6db08c1c3a1f4aef540f1a63193adc73c4fbd40b75a95fc8c5258f6e51" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tracing", +] + +[[package]] +name = "actix-http-test" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40511826540d084fbcd68ee65b75b1849961c1760a193b09180a4851f20075b" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-server", + "actix-service", + "actix-tls", + "actix-utils", + "awc", + "base64", + "bytes", + "futures-core", + "http", + "log", + "serde", + "serde_json", + "serde_urlencoded", + "slab", + "socket2", + "tokio", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb60846b52c118f2f04a56cc90880a274271c489b2498623d58176f8ca21fa80" +dependencies = [ + "bytestring", + "firestorm", + "http", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da34f8e659ea1b077bb4637948b815cd3768ad5a188fdcd74ff4d84240cd824" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-session" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d324d2a6e670f8746ae64f333c2c0fe856bdf624bcb72fb56e250e62a7e9a85f" +dependencies = [ + "actix-service", + "actix-utils", + "actix-web", + "anyhow", + "async-trait", + "derive_more", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "actix-session-sqlx" +version = "0.1.0" +dependencies = [ + "actix-session", + "actix-test", + "actix-web", + "anyhow", + "async-trait", + "chrono", + "rand", + "serde", + "serde_json", + "sqlx", + "testcontainers", + "time 0.3.14", +] + +[[package]] +name = "actix-test" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546b075f2ee13e081a040b60b95a08f0eceaac6bc759309026611234dc80abfe" +dependencies = [ + "actix-codec", + "actix-http", + "actix-http-test", + "actix-rt", + "actix-service", + "actix-utils", + "actix-web", + "awc", + "futures-core", + "futures-util", + "log", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", +] + +[[package]] +name = "actix-tls" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "http", + "log", + "pin-project-lite", + "tokio-util", +] + +[[package]] +name = "actix-utils" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27e8fe9ba4ae613c21f677c2cfaf0696c3744030c6f485b34634e502d6bb379" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time 0.3.14", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f270541caec49c15673b0af0e9a00143421ad4f118d2df7edcb68b627632f56" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" + +[[package]] +name = "async-trait" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "awc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ca7ff88063086d2e2c70b9f3b29b2fcd999bac68ac21731e66781970d68519" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "actix-tls", + "actix-utils", + "ahash", + "base64", + "bytes", + "cfg-if", + "cookie", + "derive_more", + "futures-core", + "futures-util", + "h2", + "http", + "itoa", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bollard-stubs" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2f2e73fffe9455141e170fb9c1feb0ac521ec7e7dcd47a7cab72a658490fb8" +dependencies = [ + "chrono", + "serde", + "serde_with", +] + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "bytestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b6a75fd3048808ef06af5cd79712be8111960adaf89d90250974b38fc3928a" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.44", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +dependencies = [ + "aes-gcm", + "base64", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time 0.3.14", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crossbeam-queue" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3db6fcad7c1fc4abdd99bf5276a4db30d6a819127903a709ed41e5ff016e84" +dependencies = [ + "dirs", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "firestorm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5f6c2c942da57e2aaaa84b8a521489486f14e75e7fa91dab70aba913975f98" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "iana-time-zone" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "js-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "libc" +version = "0.2.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" + +[[package]] +name = "local-channel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" + +[[package]] +name = "serde" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "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 = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "sqlformat" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788841def501aabde58d3666fcea11351ec3962e6ea75dbcd05c84a71d68bcd1" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d3b5e7cadfe9ba7cdc1295f72cc556c750b4419c27c219c0693198901f8e" +dependencies = [ + "ahash", + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "dirs", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac", + "indexmap", + "itoa", + "libc", + "log", + "md-5", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rand", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha-1", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "time 0.3.14", + "tokio-stream", + "url", + "webpki-roots", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4adfd2df3557bddd3b91377fc7893e8fa899e9b4061737cbade4e1bb85f1b45c" +dependencies = [ + "dotenvy", + "either", + "heck", + "once_cell", + "proc-macro2", + "quote", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-rt", + "syn", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be52fc7c96c136cedea840ed54f7d446ff31ad670c9dea95ebcb998530971a3" +dependencies = [ + "once_cell", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "testcontainers" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e2b1567ca8a2b819ea7b28c92be35d9f76fb9edb214321dcc86eb96023d1f87" +dependencies = [ + "bollard-stubs", + "futures", + "hex", + "hmac", + "log", + "rand", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "thiserror" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" + +[[package]] +name = "web-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +dependencies = [ + "webpki", +] + +[[package]] +name = "whoami" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ea453d0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "actix-session-sqlx" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-session = "0.7.1" +anyhow = "1.0.62" +async-trait = "0.1.57" +chrono = { version = "0.4.19", features = ["serde"] } +rand = "0.8.5" +serde = { version = "1.0.144", features = ["derive"]} +serde_json = { version = "1.0.85" } +sqlx = { version = "0.6.0", features = ["json", "chrono", "runtime-actix-rustls", "time", "postgres"] } +time = "0.3.14" + +[dev-dependencies] +testcontainers = { version ="0.14.0"} +actix-test = "0.1.0" +actix-web = { version = "4", default_features = false, features = ["cookies", "secure-cookies", "macros"] } +actix-session = { version = "0.7.1" } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..040dd5d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,186 @@ +use std::collections::HashMap; +use std::sync::Arc; +use actix_session::storage::{LoadError, SaveError, SessionKey, SessionStore, UpdateError}; +use chrono::Utc; +use sqlx::{Pool, Postgres, Row}; +use sqlx::postgres::PgPoolOptions; +use rand::{distributions::Alphanumeric, rngs::OsRng, Rng as _}; +use time::Duration; +use serde_json; + +/// Use Postgres via Sqlx as session storage backend. +/// +/// ```no_run +/// use actix_web::{web, App, HttpServer, HttpResponse, Error}; +/// use actix_session_sqlx::SqlxPostgresqlSessionStore; +/// use actix_session::SessionMiddleware; +/// use actix_web::cookie::Key; +/// +/// // The secret key would usually be read from a configuration file/environment variables. +/// fn get_secret_key() -> Key { +/// # todo!() +/// // [...] +/// } +/// +/// #[actix_web::main] +/// async fn main() -> std::io::Result<()> { +/// let secret_key = get_secret_key(); +/// let psql_connection_string = "postgres://:@127.0.0.1:5432/"; +/// let store = SqlxPostgresqlSessionStore::new(psql_connection_string).await.unwrap(); +/// +/// HttpServer::new(move || +/// App::new() +/// .wrap(SessionMiddleware::new( +/// store.clone(), +/// secret_key.clone() +/// )) +/// .default_service(web::to(|| HttpResponse::Ok()))) +/// .bind(("127.0.0.1", 8080))? +/// .run() +/// .await +/// } + + +#[derive(Clone)] +struct CacheConfiguration { + cache_keygen: Arc String + Send + Sync>, +} + +impl Default for CacheConfiguration { + fn default() -> Self { + Self { + cache_keygen: Arc::new(str::to_owned), + } + } +} + +#[derive(Clone)] +pub struct SqlxPostgresqlSessionStore { + client_pool: Pool, + configuration: CacheConfiguration, +} + +fn generate_session_key() -> SessionKey { + let value = std::iter::repeat(()) + .map(|()| OsRng.sample(Alphanumeric)) + .take(64) + .collect::>(); + + // These unwraps will never panic because pre-conditions are always verified + // (i.e. length and character set) + String::from_utf8(value).unwrap().try_into().unwrap() +} + +impl SqlxPostgresqlSessionStore { + pub fn builder>(connection_string: S) -> SqlxPostgresqlSessionStoreBuilder { + SqlxPostgresqlSessionStoreBuilder { + connection_string: connection_string.into(), + configuration: CacheConfiguration::default() + } + } + + pub async fn new>(connection_string: S) -> Result { + Self::builder(connection_string).build().await + } +} + +#[must_use] +pub struct SqlxPostgresqlSessionStoreBuilder { + connection_string: String, + configuration: CacheConfiguration, +} + +impl SqlxPostgresqlSessionStoreBuilder { + pub async fn build(self) -> Result { + PgPoolOptions::new() + .max_connections(1) + .connect(self.connection_string.as_str()) + .await + .map_err(Into::into) + .map(|pool| { + SqlxPostgresqlSessionStore { + client_pool: pool, + configuration: self.configuration + } + }) + } +} +pub(crate) type SessionState = HashMap; + +#[async_trait::async_trait(?Send)] +impl SessionStore for SqlxPostgresqlSessionStore { + async fn load(&self, session_key: &SessionKey) -> Result, LoadError> { + let key = (self.configuration.cache_keygen)(session_key.as_ref()); + let row = sqlx::query("SELECT session_state FROM sessions WHERE key = $1 AND expiry > NOW()") + .bind( key) + .fetch_optional(&self.client_pool) + .await + .map_err(Into::into) + .map_err(LoadError::Other)?; + match row { + None => Ok(None), + Some(r) => { + let data: String = r.get("session_state"); + let state: SessionState = serde_json::from_str(&data).map_err(Into::into).map_err(LoadError::Deserialization)?; + Ok(Some(state)) + } + } + } + + async fn save(&self, session_state: SessionState, ttl: &Duration) -> Result { + let body = serde_json::to_string(&session_state) + .map_err(Into::into) + .map_err(SaveError::Serialization)?; + let key = generate_session_key(); + let cache_key = (self.configuration.cache_keygen)(key.as_ref()); + let expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); + sqlx::query("INSERT INTO sessions(key, value, expiry) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING") + .bind(cache_key) + .bind( body) + .bind( expiry) + .execute(&self.client_pool) + .await + .map_err(Into::into) + .map_err(SaveError::Other)?; + Ok(key) + } + + async fn update(&self, session_key: SessionKey, session_state: SessionState, ttl: &Duration) -> Result { + let body = serde_json::to_string(&session_state).map_err(Into::into).map_err(UpdateError::Serialization)?; + let cache_key = (self.configuration.cache_keygen)(session_key.as_ref()); + let new_expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds()); + sqlx::query("UPDATE sessions SET value = $1, expiry = $2 WHERE key = $3") + .bind( body) + .bind( new_expiry) + .bind( cache_key) + .execute(&self.client_pool) + .await + .map_err(Into::into) + .map_err(UpdateError::Other)?; + Ok(session_key) + } + + async fn update_ttl(&self, session_key: &SessionKey, ttl: &Duration) -> Result<(), anyhow::Error> { + let new_expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); + let key = (self.configuration.cache_keygen)(session_key.as_ref()); + sqlx::query("UPDATE sessions SET expiry = $1 WHERE key = $2") + .bind(new_expiry) + .bind( key) + .execute(&self.client_pool) + .await + .map_err(Into::into) + .map_err(UpdateError::Other)?; + Ok(()) + } + + async fn delete(&self, session_key: &SessionKey) -> Result<(), anyhow::Error> { + let key = (self.configuration.cache_keygen)(session_key.as_ref()); + sqlx::query("DELETE FROM sessions WHERE key = $1") + .bind(key) + .execute(&self.client_pool) + .await + .map_err(Into::into) + .map_err(UpdateError::Other)?; + Ok(()) + } +} diff --git a/tests/session_store.rs b/tests/session_store.rs new file mode 100644 index 0000000..8b133ea --- /dev/null +++ b/tests/session_store.rs @@ -0,0 +1,57 @@ +#[cfg(test)] +pub mod tests { + use actix_session::storage::SessionStore; + use actix_session_sqlx::SqlxPostgresqlSessionStore; + use sqlx::postgres::PgPoolOptions; + use std::collections::HashMap; + use testcontainers::clients; + use testcontainers::core::WaitFor; + use testcontainers::images::generic; + + async fn postgres_store(url: String) -> SqlxPostgresqlSessionStore { + SqlxPostgresqlSessionStore::new(url).await.expect("") + } + + #[actix_web::test] + async fn test_session_workflow() { + let docker = clients::Cli::default(); + let postgres = docker.run( + generic::GenericImage::new("postgres", "14-alpine") + .with_wait_for(WaitFor::message_on_stderr( + "database system is ready to accept connections", + )) + .with_env_var("POSTGRES_DB", "sessions") + .with_env_var("POSTGRES_PASSWORD", "example") + .with_env_var("POSTGRES_HOST_AUTH_METHOD", "trust") + .with_env_var("POSTGRES_USER", "tests"), + ); + let url = format!( + "postgres://tests:example@localhost:{}/sessions", + postgres.get_host_port_ipv4(5432) + ); + + let postgres_store = postgres_store(url.clone()).await; + let pool = PgPoolOptions::new() + .max_connections(1) + .connect(url.as_str()) + .await + .expect("Could not connect to database"); + sqlx::query( + r#"CREATE TABLE sessions( + key TEXT PRIMARY KEY NOT NULL, + session_state TEXT, + expires TIMESTAMP WITH TIME ZONE NOT NULL + );"#, + ) + .execute(&pool) + .await + .expect("Could not create table"); + let mut session = HashMap::new(); + session.insert("key".to_string(), "value".to_string()); + assert!(postgres_store + .save(session, &time::Duration::days(1)) + .await + .is_ok()); +// acceptance_test_suite(move || postgres_store.clone(), true).await; + } +} From 7d123545c9e57bd7451939a9cbc7376a8a6be00a Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Tue, 30 Aug 2022 16:18:02 +0200 Subject: [PATCH 2/4] Use correct values --- src/lib.rs | 20 ++++++++++---------- tests/session_store.rs | 8 +++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 040dd5d..95f58e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ pub(crate) type SessionState = HashMap; impl SessionStore for SqlxPostgresqlSessionStore { async fn load(&self, session_key: &SessionKey) -> Result, LoadError> { let key = (self.configuration.cache_keygen)(session_key.as_ref()); - let row = sqlx::query("SELECT session_state FROM sessions WHERE key = $1 AND expiry > NOW()") + let row = sqlx::query("SELECT session_state FROM sessions WHERE key = $1 AND expires > NOW()") .bind( key) .fetch_optional(&self.client_pool) .await @@ -133,11 +133,11 @@ impl SessionStore for SqlxPostgresqlSessionStore { .map_err(SaveError::Serialization)?; let key = generate_session_key(); let cache_key = (self.configuration.cache_keygen)(key.as_ref()); - let expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); - sqlx::query("INSERT INTO sessions(key, value, expiry) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING") + let expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); + sqlx::query("INSERT INTO sessions(key, session_state, expires) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING") .bind(cache_key) .bind( body) - .bind( expiry) + .bind( expires) .execute(&self.client_pool) .await .map_err(Into::into) @@ -148,10 +148,10 @@ impl SessionStore for SqlxPostgresqlSessionStore { async fn update(&self, session_key: SessionKey, session_state: SessionState, ttl: &Duration) -> Result { let body = serde_json::to_string(&session_state).map_err(Into::into).map_err(UpdateError::Serialization)?; let cache_key = (self.configuration.cache_keygen)(session_key.as_ref()); - let new_expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds()); - sqlx::query("UPDATE sessions SET value = $1, expiry = $2 WHERE key = $3") + let new_expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds()); + sqlx::query("UPDATE sessions SET session_state = $1, expires = $2 WHERE key = $3") .bind( body) - .bind( new_expiry) + .bind( new_expires) .bind( cache_key) .execute(&self.client_pool) .await @@ -161,10 +161,10 @@ impl SessionStore for SqlxPostgresqlSessionStore { } async fn update_ttl(&self, session_key: &SessionKey, ttl: &Duration) -> Result<(), anyhow::Error> { - let new_expiry = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); + let new_expires = Utc::now() + chrono::Duration::seconds(ttl.whole_seconds() as i64); let key = (self.configuration.cache_keygen)(session_key.as_ref()); - sqlx::query("UPDATE sessions SET expiry = $1 WHERE key = $2") - .bind(new_expiry) + sqlx::query("UPDATE sessions SET expires = $1 WHERE key = $2") + .bind(new_expires) .bind( key) .execute(&self.client_pool) .await diff --git a/tests/session_store.rs b/tests/session_store.rs index 8b133ea..9f73623 100644 --- a/tests/session_store.rs +++ b/tests/session_store.rs @@ -48,10 +48,12 @@ pub mod tests { .expect("Could not create table"); let mut session = HashMap::new(); session.insert("key".to_string(), "value".to_string()); - assert!(postgres_store + let data = postgres_store .save(session, &time::Duration::days(1)) - .await + .await; + println!("{:#?}", data); + assert!(data .is_ok()); -// acceptance_test_suite(move || postgres_store.clone(), true).await; + //acceptance_test_suite(move || postgres_store.clone(), true).await; } } From 5b0304f1962ec25757e70376a4c26410b23ad1a9 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Tue, 30 Aug 2022 16:52:18 +0200 Subject: [PATCH 3/4] Add tests --- Cargo.toml | 1 + src/lib.rs | 79 +++++- tests/session_store.rs | 3 +- tests/test_helpers.rs | 579 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 647 insertions(+), 15 deletions(-) create mode 100644 tests/test_helpers.rs diff --git a/Cargo.toml b/Cargo.toml index ea453d0..a22a4f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "actix-session-sqlx" version = "0.1.0" edition = "2021" +authors = ["Christopher Kolstad ", "Simon Hornby "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/lib.rs b/src/lib.rs index 95f58e3..de1c57b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ use sqlx::postgres::PgPoolOptions; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng as _}; use time::Duration; use serde_json; +use crate::ConnectionData::{ConnectionPool, ConnectionString}; /// Use Postgres via Sqlx as session storage backend. /// @@ -39,8 +40,39 @@ use serde_json; /// .run() /// .await /// } - - +/// ``` +/// If you already have a connection pool, you can use something like +/*/// ```no_run +/// use actix_web::{web, App, HttpServer, HttpResponse, Error}; +/// use actix_session_sqlx::SqlxPostgresqlSessionStore; +/// use actix_session::SessionMiddleware; +/// use actix_web::cookie::Key; +/// +/// // The secret key would usually be read from a configuration file/environment variables. +/// fn get_secret_key() -> Key { +/// # todo!() +/// // [...] +/// } +/// #[actix_web::main] +/// async fn main() -> std::io::Result<()> { +/// use sqlx::postgres::PgPoolOptions; +/// let secret_key = get_secret_key(); +/// let pool = PgPoolOptions::find_some_way_to_build_your_pool(psql_connection_string); +/// let store = SqlxPostgresqlSessionStore::from_pool(pool).await.expect("Could not build session store"); +/// +/// HttpServer::new(move || +/// App::new() +/// .wrap(SessionMiddleware::new( +/// store.clone(), +/// secret_key.clone() +/// )) +/// .default_service(web::to(|| HttpResponse::Ok()))) +/// .bind(("127.0.0.1", 8080))? +/// .run() +/// .await +/// } +/// ``` +*/ #[derive(Clone)] struct CacheConfiguration { cache_keygen: Arc String + Send + Sync>, @@ -74,7 +106,7 @@ fn generate_session_key() -> SessionKey { impl SqlxPostgresqlSessionStore { pub fn builder>(connection_string: S) -> SqlxPostgresqlSessionStoreBuilder { SqlxPostgresqlSessionStoreBuilder { - connection_string: connection_string.into(), + connection_data: ConnectionString(connection_string.into()), configuration: CacheConfiguration::default() } } @@ -82,27 +114,46 @@ impl SqlxPostgresqlSessionStore { pub async fn new>(connection_string: S) -> Result { Self::builder(connection_string).build().await } + + pub async fn from_pool(pool: Pool) -> SqlxPostgresqlSessionStoreBuilder { + SqlxPostgresqlSessionStoreBuilder { + connection_data: ConnectionPool(pool.clone()), + configuration: CacheConfiguration::default() + } + } +} + +pub enum ConnectionData { + ConnectionString(String), + ConnectionPool(Pool) } #[must_use] pub struct SqlxPostgresqlSessionStoreBuilder { - connection_string: String, + connection_data: ConnectionData, configuration: CacheConfiguration, } impl SqlxPostgresqlSessionStoreBuilder { pub async fn build(self) -> Result { - PgPoolOptions::new() - .max_connections(1) - .connect(self.connection_string.as_str()) - .await - .map_err(Into::into) - .map(|pool| { - SqlxPostgresqlSessionStore { - client_pool: pool, - configuration: self.configuration - } + match self.connection_data { + ConnectionString(conn_string) => { + PgPoolOptions::new() + .max_connections(1) + .connect(conn_string.as_str()) + .await + .map_err(Into::into) + .map(|pool| { + SqlxPostgresqlSessionStore { + client_pool: pool, + configuration: self.configuration + } + }) + }, + ConnectionPool(pool) => Ok(SqlxPostgresqlSessionStore { + client_pool: pool, configuration: self.configuration }) + } } } pub(crate) type SessionState = HashMap; diff --git a/tests/session_store.rs b/tests/session_store.rs index 9f73623..6d3c78e 100644 --- a/tests/session_store.rs +++ b/tests/session_store.rs @@ -1,3 +1,4 @@ +mod test_helpers; #[cfg(test)] pub mod tests { use actix_session::storage::SessionStore; @@ -54,6 +55,6 @@ pub mod tests { println!("{:#?}", data); assert!(data .is_ok()); - //acceptance_test_suite(move || postgres_store.clone(), true).await; + super::test_helpers::acceptance_test_suite(move || postgres_store.clone(), true).await; } } diff --git a/tests/test_helpers.rs b/tests/test_helpers.rs new file mode 100644 index 0000000..9f44e0f --- /dev/null +++ b/tests/test_helpers.rs @@ -0,0 +1,579 @@ +use actix_session::config::CookieContentSecurity; +use actix_session::storage::SessionStore; +use actix_web::cookie::Key; + +/// Generate a random cookie signing/encryption key. +pub fn key() -> Key { + Key::generate() +} + +/// A ready-to-go acceptance test suite to verify that sessions behave as expected +/// regardless of the underlying session store. +/// +/// `is_invalidation_supported` must be set to `true` if the backend supports +/// "remembering" that a session has been invalidated (e.g. by logging out). +/// It should be to `false` if the backend allows multiple cookies to be active +/// at the same time (e.g. cookie store backend). +pub async fn acceptance_test_suite(store_builder: F, is_invalidation_supported: bool) + where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, +{ + for policy in &[ + CookieContentSecurity::Signed, + CookieContentSecurity::Private, + ] { + println!("Using {:?} as cookie content security policy.", policy); + acceptance_tests::basic_workflow(store_builder.clone(), *policy).await; + acceptance_tests::expiration_is_refreshed_on_changes(store_builder.clone(), *policy) + .await; + acceptance_tests::expiration_is_always_refreshed_if_configured_to_refresh_on_every_request( + store_builder.clone(), + *policy, + ) + .await; + acceptance_tests::complex_workflow( + store_builder.clone(), + is_invalidation_supported, + *policy, + ) + .await; + acceptance_tests::guard(store_builder.clone(), *policy).await; + } +} + +mod acceptance_tests { + use actix_session::config::{CookieContentSecurity, PersistentSession, TtlExtensionPolicy}; + use actix_session::{Session, SessionExt, SessionMiddleware}; + use actix_session::storage::SessionStore; + use actix_web::{ + cookie::time, + dev::{Service, ServiceResponse}, + guard, middleware, test, + web::{self, get, post, resource, Bytes}, + App, HttpResponse, Result, + }; + use serde::{Deserialize, Serialize}; + use serde_json::json; + use super::key; + + pub(super) async fn basic_workflow( + store_builder: F, + policy: CookieContentSecurity, + ) where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, + { + let app = test::init_service( + App::new() + .wrap( + SessionMiddleware::builder(store_builder(), key()) + .cookie_path("/test/".into()) + .cookie_name("actix-test".into()) + .cookie_domain(Some("localhost".into())) + .cookie_content_security(policy) + .session_lifecycle( + PersistentSession::default() + .session_ttl(time::Duration::seconds(100)), + ) + .build(), + ) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.insert("counter", 100); + "test" + })) + .service(web::resource("/test/").to(|ses: Session| async move { + let val: usize = ses.get("counter").unwrap().unwrap(); + format!("counter: {}", val) + })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie = response.get_cookie("actix-test").unwrap().clone(); + assert_eq!(cookie.path().unwrap(), "/test/"); + + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie) + .to_request(); + let body = test::call_and_read_body(&app, request).await; + assert_eq!(body, Bytes::from_static(b"counter: 100")); + } + + pub(super) async fn expiration_is_always_refreshed_if_configured_to_refresh_on_every_request< + F, + Store, + >( + store_builder: F, + policy: CookieContentSecurity, + ) where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, + { + let session_ttl = time::Duration::seconds(60); + let app = test::init_service( + App::new() + .wrap( + SessionMiddleware::builder(store_builder(), key()) + .cookie_content_security(policy) + .session_lifecycle( + PersistentSession::default() + .session_ttl(session_ttl) + .session_ttl_extension_policy( + TtlExtensionPolicy::OnEveryRequest, + ), + ) + .build(), + ) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.insert("counter", 100); + "test" + })) + .service(web::resource("/test/").to(|| async move { "no-changes-in-session" })), + ) + .await; + + // Create session + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie_1 = response.get_cookie("id").expect("Cookie is set"); + assert_eq!(cookie_1.max_age(), Some(session_ttl)); + println!("cookie_max_age_1"); + + // Fire a request that doesn't touch the session state, check + // that the session cookie is present and its expiry is set to the maximum we configured. + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie_1) + .to_request(); + let response = app.call(request).await.unwrap(); + let cookie_2 = response.get_cookie("id").expect("Cookie is set"); + assert_eq!(cookie_2.max_age(), Some(session_ttl)); + println!("cookie_max_age_2") + } + + pub(super) async fn expiration_is_refreshed_on_changes( + store_builder: F, + policy: CookieContentSecurity, + ) where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, + { + let session_ttl = time::Duration::seconds(60); + let app = test::init_service( + App::new() + .wrap( + SessionMiddleware::builder(store_builder(), key()) + .cookie_content_security(policy) + .session_lifecycle( + PersistentSession::default().session_ttl(session_ttl), + ) + .build(), + ) + .service(web::resource("/").to(|ses: Session| async move { + let _ = ses.insert("counter", 100); + "test" + })) + .service(web::resource("/test/").to(|| async move { "no-changes-in-session" })), + ) + .await; + + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie_1 = response.get_cookie("id").expect("Cookie is set"); + assert_eq!(cookie_1.max_age(), Some(session_ttl)); + + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie_1.clone()) + .to_request(); + let response = app.call(request).await.unwrap(); + assert!(response.response().cookies().next().is_none()); + + let request = test::TestRequest::get().cookie(cookie_1).to_request(); + let response = app.call(request).await.unwrap(); + let cookie_2 = response.get_cookie("id").expect("Cookie is set"); + assert_eq!(cookie_2.max_age(), Some(session_ttl)); + } + + pub(super) async fn guard(store_builder: F, policy: CookieContentSecurity) + where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, + { + let srv = actix_test::start(move || { + App::new() + .wrap( + SessionMiddleware::builder(store_builder(), key()) + .cookie_name("test-session".into()) + .cookie_content_security(policy) + .session_lifecycle( + PersistentSession::default().session_ttl(time::Duration::days(7)), + ) + .build(), + ) + .wrap(middleware::Logger::default()) + .service(resource("/").route(get().to(index))) + .service(resource("/do_something").route(post().to(do_something))) + .service(resource("/login").route(post().to(login))) + .service(resource("/logout").route(post().to(logout))) + .service( + web::scope("/protected") + .guard(guard::fn_guard(|g| { + g.get_session().get::("user_id").unwrap().is_some() + })) + .service(resource("/count").route(get().to(count))), + ) + }); + + // Step 1: GET without session info + // - response should be a unsuccessful status + let req_1 = srv.get("/protected/count").send(); + let resp_1 = req_1.await.unwrap(); + assert!(!resp_1.status().is_success()); + + // Step 2: POST to login + // - set-cookie actix-session will be in response (session cookie #1) + // - updates session state: {"counter": 0, "user_id": "ferris"} + let req_2 = srv.post("/login").send_json(&json!({"user_id": "ferris"})); + let resp_2 = req_2.await.unwrap(); + let cookie_1 = resp_2 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + + // Step 3: POST to do_something + // - adds new session state: {"counter": 1, "user_id": "ferris" } + // - set-cookie actix-session should be in response (session cookie #2) + // - response should be: {"counter": 1, "user_id": None} + let req_3 = srv.post("/do_something").cookie(cookie_1.clone()).send(); + let mut resp_3 = req_3.await.unwrap(); + let result_3 = resp_3.json::().await.unwrap(); + assert_eq!( + result_3, + IndexResponse { + user_id: Some("ferris".into()), + counter: 1 + } + ); + let cookie_2 = resp_3 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + + // Step 4: GET using a existing user id + // - response should be: {"counter": 3, "user_id": "ferris"} + let req_4 = srv.get("/protected/count").cookie(cookie_2.clone()).send(); + let mut resp_4 = req_4.await.unwrap(); + let result_4 = resp_4.json::().await.unwrap(); + assert_eq!( + result_4, + IndexResponse { + user_id: Some("ferris".into()), + counter: 1 + } + ); + } + + pub(super) async fn complex_workflow( + store_builder: F, + is_invalidation_supported: bool, + policy: CookieContentSecurity, + ) where + Store: SessionStore + 'static, + F: Fn() -> Store + Clone + Send + 'static, + { + let session_ttl = time::Duration::days(7); + let srv = actix_test::start(move || { + App::new() + .wrap( + SessionMiddleware::builder(store_builder(), key()) + .cookie_name("test-session".into()) + .cookie_content_security(policy) + .session_lifecycle( + PersistentSession::default().session_ttl(session_ttl), + ) + .build(), + ) + .wrap(middleware::Logger::default()) + .service(resource("/").route(get().to(index))) + .service(resource("/do_something").route(post().to(do_something))) + .service(resource("/login").route(post().to(login))) + .service(resource("/logout").route(post().to(logout))) + }); + + // Step 1: GET index + // - set-cookie actix-session should NOT be in response (session data is empty) + // - response should be: {"counter": 0, "user_id": None} + let req_1a = srv.get("/").send(); + let mut resp_1 = req_1a.await.unwrap(); + assert!(resp_1.cookies().unwrap().is_empty()); + let result_1 = resp_1.json::().await.unwrap(); + assert_eq!( + result_1, + IndexResponse { + user_id: None, + counter: 0 + } + ); + + // Step 2: POST to do_something + // - adds new session state in postgres: {"counter": 1} + // - set-cookie actix-session should be in response (session cookie #1) + // - response should be: {"counter": 1, "user_id": None} + let req_2 = srv.post("/do_something").send(); + let mut resp_2 = req_2.await.unwrap(); + let result_2 = resp_2.json::().await.unwrap(); + assert_eq!( + result_2, + IndexResponse { + user_id: None, + counter: 1 + } + ); + println!("index_response"); + let cookie_1 = resp_2 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + assert_eq!(cookie_1.max_age(), Some(session_ttl)); + + // Step 3: GET index, including session cookie #1 in request + // - set-cookie will *not* be in response + // - response should be: {"counter": 1, "user_id": None} + let req_3 = srv.get("/").cookie(cookie_1.clone()).send(); + let mut resp_3 = req_3.await.unwrap(); + assert!(resp_3.cookies().unwrap().is_empty()); + let result_3 = resp_3.json::().await.unwrap(); + assert_eq!( + result_3, + IndexResponse { + user_id: None, + counter: 1 + } + ); + + // Step 4: POST again to do_something, including session cookie #1 in request + // - set-cookie will be in response (session cookie #2) + // - updates session state: {"counter": 2} + // - response should be: {"counter": 2, "user_id": None} + let req_4 = srv.post("/do_something").cookie(cookie_1.clone()).send(); + let mut resp_4 = req_4.await.unwrap(); + let result_4 = resp_4.json::().await.unwrap(); + assert_eq!( + result_4, + IndexResponse { + user_id: None, + counter: 2 + } + ); + let cookie_2 = resp_4 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + assert_eq!(cookie_2.max_age(), cookie_1.max_age()); + + // Step 5: POST to login, including session cookie #2 in request + // - set-cookie actix-session will be in response (session cookie #3) + // - updates session state: {"counter": 2, "user_id": "ferris"} + let req_5 = srv + .post("/login") + .cookie(cookie_2.clone()) + .send_json(&json!({"user_id": "ferris"})); + let mut resp_5 = req_5.await.unwrap(); + let cookie_3 = resp_5 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + assert_ne!(cookie_2.value(), cookie_3.value()); + + let result_5 = resp_5.json::().await.unwrap(); + assert_eq!( + result_5, + IndexResponse { + user_id: Some("ferris".into()), + counter: 2 + } + ); + + // Step 6: GET index, including session cookie #3 in request + // - response should be: {"counter": 2, "user_id": "ferris"} + let req_6 = srv.get("/").cookie(cookie_3.clone()).send(); + let mut resp_6 = req_6.await.unwrap(); + let result_6 = resp_6.json::().await.unwrap(); + assert_eq!( + result_6, + IndexResponse { + user_id: Some("ferris".into()), + counter: 2 + } + ); + + // Step 7: POST again to do_something, including session cookie #3 in request + // - updates session state: {"counter": 3, "user_id": "ferris"} + // - response should be: {"counter": 3, "user_id": "ferris"} + let req_7 = srv.post("/do_something").cookie(cookie_3.clone()).send(); + let mut resp_7 = req_7.await.unwrap(); + let result_7 = resp_7.json::().await.unwrap(); + assert_eq!( + result_7, + IndexResponse { + user_id: Some("ferris".into()), + counter: 3 + } + ); + + // Step 8: GET index, including session cookie #2 in request + // If invalidation is supported, no state will be found associated to this session. + // If invalidation is not supported, the old state will still be retrieved. + let req_8 = srv.get("/").cookie(cookie_2.clone()).send(); + let mut resp_8 = req_8.await.unwrap(); + if is_invalidation_supported { + assert!(resp_8.cookies().unwrap().is_empty()); + let result_8 = resp_8.json::().await.unwrap(); + assert_eq!( + result_8, + IndexResponse { + user_id: None, + counter: 0 + } + ); + } else { + let result_8 = resp_8.json::().await.unwrap(); + assert_eq!( + result_8, + IndexResponse { + user_id: None, + counter: 2 + } + ); + } + + // Step 9: POST to logout, including session cookie #3 + // - set-cookie actix-session will be in response with session cookie #3 + // invalidation logic + let req_9 = srv.post("/logout").cookie(cookie_3.clone()).send(); + let resp_9 = req_9.await.unwrap(); + let cookie_3 = resp_9 + .cookies() + .unwrap() + .clone() + .into_iter() + .find(|c| c.name() == "test-session") + .unwrap(); + assert_eq!(0, cookie_3.max_age().map(|t| t.whole_seconds()).unwrap()); + assert_eq!("/", cookie_3.path().unwrap()); + + // Step 10: GET index, including session cookie #3 in request + // - set-cookie actix-session should NOT be in response if invalidation is supported + // - response should be: {"counter": 0, "user_id": None} + let req_10 = srv.get("/").cookie(cookie_3.clone()).send(); + let mut resp_10 = req_10.await.unwrap(); + if is_invalidation_supported { + assert!(resp_10.cookies().unwrap().is_empty()); + } + let result_10 = resp_10.json::().await.unwrap(); + assert_eq!( + result_10, + IndexResponse { + user_id: None, + counter: 0 + } + ); + } + + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + pub struct IndexResponse { + user_id: Option, + counter: i32, + } + + async fn index(session: Session) -> Result { + let user_id: Option = session.get::("user_id").unwrap(); + let counter: i32 = session + .get::("counter") + .unwrap_or(Some(0)) + .unwrap_or(0); + + Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter })) + } + + async fn do_something(session: Session) -> Result { + let user_id: Option = session.get::("user_id").unwrap(); + let counter: i32 = session + .get::("counter") + .unwrap_or(Some(0)) + .map_or(1, |inner| inner + 1); + session.insert("counter", &counter)?; + + Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter })) + } + + async fn count(session: Session) -> Result { + let user_id: Option = session.get::("user_id").unwrap(); + let counter: i32 = session.get::("counter").unwrap().unwrap(); + + Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter })) + } + + #[derive(Deserialize)] + struct Identity { + user_id: String, + } + + async fn login(user_id: web::Json, session: Session) -> Result { + let id = user_id.into_inner().user_id; + session.insert("user_id", &id)?; + session.renew(); + + let counter: i32 = session + .get::("counter") + .unwrap_or(Some(0)) + .unwrap_or(0); + + Ok(HttpResponse::Ok().json(&IndexResponse { + user_id: Some(id), + counter, + })) + } + + async fn logout(session: Session) -> Result { + let id: Option = session.get("user_id")?; + + let body = if let Some(x) = id { + session.purge(); + format!("Logged out: {}", x) + } else { + "Could not log out anonymous user".to_owned() + }; + + Ok(HttpResponse::Ok().body(body)) + } + + trait ServiceResponseExt { + fn get_cookie(&self, cookie_name: &str) -> Option>; + } + + impl ServiceResponseExt for ServiceResponse { + fn get_cookie(&self, cookie_name: &str) -> Option> { + self.response() + .cookies() + .into_iter() + .find(|c| c.name() == cookie_name) + } + } +} From ad96e94f862ce1e77ac5d0a33c43f3baf2488346 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jan 2023 22:17:54 +0000 Subject: [PATCH 4/4] Bump bumpalo from 3.11.0 to 3.12.0 Bumps [bumpalo](https://github.com/fitzgen/bumpalo) from 3.11.0 to 3.12.0. - [Release notes](https://github.com/fitzgen/bumpalo/releases) - [Changelog](https://github.com/fitzgen/bumpalo/blob/main/CHANGELOG.md) - [Commits](https://github.com/fitzgen/bumpalo/compare/3.11.0...3.12.0) --- updated-dependencies: - dependency-name: bumpalo dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a7d22e..37f9156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,9 +444,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder"