Get SQLX to work

This commit is contained in:
Jeremy Wall 2022-08-30 17:31:35 -04:00
parent 242cf8eddd
commit 26a98ae89a
15 changed files with 942 additions and 95 deletions

View File

@ -2,4 +2,8 @@
"rust-analyzer.diagnostics.disabled": [
"macro-error"
],
"rust-analyzer.cargo.noDefaultFeatures": false,
"rust-analyzer.cargo.features": [
"sqlite"
],
}

588
Cargo.lock generated
View File

@ -146,6 +146,24 @@ dependencies = [
"event-listener",
]
[[package]]
name = "async-process"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c"
dependencies = [
"async-io",
"autocfg",
"blocking",
"cfg-if 1.0.0",
"event-listener",
"futures-lite",
"libc",
"once_cell",
"signal-hook",
"winapi",
]
[[package]]
name = "async-session"
version = "3.0.0"
@ -164,7 +182,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"sha2",
"sha2 0.9.9",
]
[[package]]
@ -177,6 +195,7 @@ dependencies = [
"async-global-executor",
"async-io",
"async-lock",
"async-process",
"crossbeam-utils",
"futures-channel",
"futures-core",
@ -210,6 +229,15 @@ dependencies = [
"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 = "atomic-waker"
version = "1.0.0"
@ -411,6 +439,12 @@ version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[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.0"
@ -474,7 +508,7 @@ dependencies = [
"num-integer",
"num-traits",
"serde",
"time",
"time 0.1.44",
"winapi",
]
@ -566,6 +600,16 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
dependencies = [
"time 0.3.14",
"version_check",
]
[[package]]
name = "cpufeatures"
version = "0.2.2"
@ -575,6 +619,31 @@ 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 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.10"
@ -667,6 +736,44 @@ dependencies = [
"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"
dependencies = [
"serde",
]
[[package]]
name = "event-listener"
version = "2.5.2"
@ -682,6 +789,18 @@ dependencies = [
"instant",
]
[[package]]
name = "flume"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
dependencies = [
"futures-core",
"futures-sink",
"pin-project",
"spin 0.9.4",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -705,6 +824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -713,6 +833,28 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
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",
]
[[package]]
name = "futures-io"
version = "0.3.21"
@ -734,6 +876,17 @@ dependencies = [
"waker-fn",
]
[[package]]
name = "futures-rustls"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd"
dependencies = [
"futures-io",
"rustls",
"webpki",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
@ -753,9 +906,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
@ -858,6 +1013,18 @@ 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 = "headers"
@ -884,6 +1051,15 @@ dependencies = [
"http",
]
[[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"
@ -893,6 +1069,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.11.0"
@ -967,6 +1149,17 @@ dependencies = [
"want",
]
[[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"
@ -986,6 +1179,15 @@ dependencies = [
"cfg-if 1.0.0",
]
[[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 = "0.4.8"
@ -1028,6 +1230,7 @@ dependencies = [
"axum-auth",
"ciborium",
"clap",
"cookie",
"csv",
"mime_guess",
"recipe-store",
@ -1036,6 +1239,7 @@ dependencies = [
"rust-embed",
"secrecy",
"serde",
"sqlx",
"tower",
"tower-http",
"tracing",
@ -1186,6 +1390,17 @@ dependencies = [
"zstd-sys",
]
[[package]]
name = "libsqlite3-sys"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.8"
@ -1197,6 +1412,16 @@ dependencies = [
"vcpkg",
]
[[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"
@ -1321,6 +1546,15 @@ dependencies = [
"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.0"
@ -1345,6 +1579,31 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[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",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "password-hash"
version = "0.4.2"
@ -1502,6 +1761,26 @@ dependencies = [
"num-rational",
]
[[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"
@ -1534,6 +1813,21 @@ dependencies = [
"gloo-net",
]
[[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 0.5.2",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rocksdb"
version = "0.19.0"
@ -1574,7 +1868,7 @@ version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029"
dependencies = [
"sha2",
"sha2 0.9.9",
"walkdir",
]
@ -1584,6 +1878,27 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[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.10"
@ -1599,6 +1914,22 @@ dependencies = [
"winapi-util",
]
[[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 = "secrecy"
version = "0.8.0"
@ -1676,6 +2007,17 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.3",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -1691,6 +2033,25 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[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"
@ -1716,12 +2077,136 @@ dependencies = [
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
dependencies = [
"lock_api",
]
[[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",
"bitflags",
"byteorder",
"bytes",
"crc",
"crossbeam-queue",
"dotenvy",
"either",
"event-listener",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"hashlink",
"hex",
"indexmap",
"itoa 1.0.2",
"libc",
"libsqlite3-sys",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"rustls",
"rustls-pemfile",
"serde",
"sha2 0.10.3",
"smallvec",
"sqlformat",
"sqlx-rt",
"stringprep",
"thiserror",
"url",
"webpki-roots",
]
[[package]]
name = "sqlx-macros"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4adfd2df3557bddd3b91377fc7893e8fa899e9b4061737cbade4e1bb85f1b45c"
dependencies = [
"dotenvy",
"either",
"heck",
"hex",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
"sha2 0.10.3",
"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 = [
"async-std",
"futures-rustls",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[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"
@ -1851,6 +2336,39 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa 1.0.2",
"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.0"
@ -2023,12 +2541,57 @@ dependencies = [
"version_check",
]
[[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.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
[[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 = "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 = "uuid"
version = "1.1.2"
@ -2184,6 +2747,25 @@ dependencies = [
"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 = "wepoll-ffi"
version = "0.1.2"

View File

@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
sqlx-prepare: wasm kitches/src/*.rs
cd kitchen; cargo sqlx-prepare
kitchen: wasm kitchen/src/*.rs
cd kitchen; cargo build

View File

@ -56,9 +56,7 @@
type = "app";
program = "${kitchen}/bin/kitchen";
};
devShell = pkgs.mkShell {
buildInputs = [ rust-wasm ] ++ (with pkgs; [wasm-bindgen-cli wasm-pack]);
};
devShell = pkgs.callPackage ./nix/devShell/default.nix { inherit rust-wasm; };
}
);
}

View File

@ -17,9 +17,9 @@ mime_guess = "2.0.4"
async-trait = "0.1.57"
async-session = "3.0.0"
ciborium = "0.2.0"
rocksdb = "0.19.0"
tower = "0.4.13"
serde = "1.0.144"
cookie = "0.16.0"
[dependencies.argon2]
version = "0.4.1"
@ -50,4 +50,18 @@ features = [ "cargo" ]
[dependencies.async-std]
version = "1.10.0"
features = ["tokio1"]
features = ["tokio1"]
[dependencies.sqlx]
version = "0.6.1"
features = ["sqlite", "runtime-async-std-rustls", "offline"]
optional = true
[dependencies.rocksdb]
version = "0.19.0"
optional = true
[features]
sqlite = ["dep:sqlx"]
rocksdb = ["dep:rocksdb"]
default = ["sqlite"]

View File

@ -0,0 +1,3 @@
-- Add down migration script here
drop table sessions;
drop table users;

View File

@ -0,0 +1,3 @@
-- Add migration script here
CREATE TABLE sessions(id TEXT PRIMARY KEY, session_value BLOB NOT NULL);
CREATE TABLE users(id TEXT PRIMARY KEY, password_hashed TEXT NOT NULL);

79
kitchen/sqlx-data.json Normal file
View File

@ -0,0 +1,79 @@
{
"db": "SQLite",
"104f07472670436d3eee1733578bbf0c92dc4f965d3d13f9bf4bfbc92958c5b6": {
"describe": {
"columns": [
{
"name": "password_hashed",
"ordinal": 0,
"type_info": "Text"
}
],
"nullable": [
false
],
"parameters": {
"Right": 1
}
},
"query": "select password_hashed from users where id = ?"
},
"5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 2
}
},
"query": "insert into users (id, password_hashed) values (?, ?)"
},
"7578157607967a6a4c60f12408c5d9900d15b429a49681a4cae4e02d31c524ec": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 1
}
},
"query": "delete from sessions where id = ?"
},
"928a479ca0f765ec7715bf8784c5490e214486edbf5b78fd501823feb328375b": {
"describe": {
"columns": [
{
"name": "session_value",
"ordinal": 0,
"type_info": "Blob"
}
],
"nullable": [
false
],
"parameters": {
"Right": 1
}
},
"query": "select session_value from sessions where id = ?"
},
"9ad4acd9b9d32c9f9f441276aa71a17674fe4d65698848044778bd4aef77d42d": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 2
}
},
"query": "insert into sessions (id, session_value) values (?, ?)"
},
"d84685a82585c5e4ae72c86ba1fe6e4a7241c4c3c9e948213e5849d956132bad": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 0
}
},
"query": "delete from sessions"
}
}

View File

@ -45,14 +45,14 @@ fn create_app<'a>() -> clap::App<'a> {
(@subcommand serve =>
(about: "Serve the interface via the web")
(@arg recipe_dir: -d --dir +takes_value "Directory containing recipe files to use")
(@arg session_dir: --sesion_dir + takes_value "Session store directory to use")
(@arg session_dir: --session_dir + takes_value "Session store directory to use")
(@arg listen: --listen +takes_value "address and port to listen on 0.0.0.0:3030")
)
(@subcommand add_user =>
(about: "add users to to the interface")
(@arg user: -u --user +takes_value +required "username to add")
(@arg pass: -p --pass +takes_value +required "password to add for this user")
(@arg session_dir: --sesion_dir +takes_value "Session store directory to use")
(@arg session_dir: --session_dir +takes_value "Session store directory to use")
)
)
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
@ -139,10 +139,13 @@ fn main() {
});
} else if let Some(matches) = matches.subcommand_matches("add_user") {
let session_store_path: PathBuf = get_session_store_path(matches);
web::add_user(
session_store_path,
matches.value_of("user").unwrap().to_owned(),
matches.value_of("pass").unwrap().to_owned(),
);
async_std::task::block_on(async {
web::add_user(
session_store_path,
matches.value_of("user").unwrap().to_owned(),
matches.value_of("pass").unwrap().to_owned(),
)
.await;
});
}
}

View File

@ -20,22 +20,23 @@ use axum::{
response::IntoResponse,
};
use axum_auth::AuthBasic;
use cookie::{Cookie, SameSite};
use secrecy::Secret;
use tracing::{debug, info, instrument};
use super::session;
use super::session::{self, AuthStore};
#[instrument(skip_all, fields(user=%auth.0.0))]
pub async fn handler(
auth: AuthBasic,
Extension(session_store): Extension<session::RocksdbInnerStore>,
Extension(session_store): Extension<session::SqliteStore>,
) -> impl IntoResponse {
// NOTE(jwall): It is very important that you do **not** log the password
// here. We convert the AuthBasic into UserCreds immediately to help prevent
// that. Do not circumvent that protection.
let auth = session::UserCreds::from(auth);
info!("Handling authentication request");
if let Ok(true) = session_store.check_user_creds(&auth) {
if let Ok(true) = session_store.check_user_creds(&auth).await {
debug!("successfully authenticated user");
// 1. Create a session identifier.
let mut session = Session::new();
@ -44,17 +45,11 @@ pub async fn handler(
let cookie_value = session_store.store_session(session).await.unwrap().unwrap();
let mut headers = HeaderMap::new();
// 3. Construct the Session Cookie.
// TODO(jwall): Find or Build a cookie builder.
headers.insert(
header::SET_COOKIE,
format!(
"{}={} SameSite=Strict Secure",
session::AXUM_SESSION_COOKIE_NAME,
cookie_value
)
.parse()
.unwrap(),
);
let cookie = Cookie::build(session::AXUM_SESSION_COOKIE_NAME, cookie_value)
.same_site(SameSite::Strict)
.secure(true)
.finish();
headers.insert(header::SET_COOKIE, cookie.to_string().parse().unwrap());
// Respond with 200 OK
(StatusCode::OK, headers, "Login Successful")
} else {

View File

@ -29,6 +29,8 @@ use tower::ServiceBuilder;
use tower_http::trace::TraceLayer;
use tracing::{debug, info, instrument};
use session::AuthStore;
mod auth;
mod session;
@ -113,8 +115,9 @@ async fn api_categories(
result
}
pub fn add_user(session_store_path: PathBuf, username: String, password: String) {
let session_store = session::RocksdbInnerStore::new(session_store_path)
pub async fn add_user(session_store_path: PathBuf, username: String, password: String) {
let session_store = session::SqliteStore::new(session_store_path)
.await
.expect("Unable to create session_store");
let user_creds = session::UserCreds {
id: session::UserId(username),
@ -122,6 +125,7 @@ pub fn add_user(session_store_path: PathBuf, username: String, password: String)
};
session_store
.store_user_creds(user_creds)
.await
.expect("Failed to store user creds");
}
@ -133,7 +137,8 @@ pub async fn ui_main(
) {
let store = Arc::new(recipe_store::AsyncFileStore::new(recipe_dir_path.clone()));
//let dir_path = (&dir_path).clone();
let session_store = session::RocksdbInnerStore::new(session_store_path)
let session_store = session::SqliteStore::new(session_store_path)
.await
.expect("Unable to create session_store");
let router = Router::new()
.route("/", get(|| async { Redirect::temporary("/ui/plan") }))

View File

@ -18,13 +18,16 @@ use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
use async_session::{async_trait, Session, SessionStore};
use async_session::{Session, SessionStore};
use async_trait::async_trait;
use axum::{
extract::{Extension, FromRequest, RequestParts, TypedHeader},
headers::Cookie,
http::StatusCode,
};
use ciborium;
use cookie::{Cookie as CookieParse, SameSite};
#[cfg(feature = "rocksdb")]
use rocksdb::{
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options,
};
@ -57,12 +60,40 @@ impl UserCreds {
}
}
fn make_id_key(cookie_value: &str) -> async_session::Result<String> {
debug!("deserializing cookie");
Ok(Session::id_from_cookie_value(cookie_value)?)
}
#[instrument(skip_all, fields(hash=payload))]
fn check_pass(payload: &String, pass: &Secret<String>) -> bool {
let parsed_hash = PasswordHash::new(&payload).expect("Invalid Password Hash");
debug!(password_hash=?parsed_hash, "successfuly obtained password hash");
let check = Argon2::default().verify_password(pass.expose_secret().as_bytes(), &parsed_hash);
if let Err(err) = &check {
debug!(err=?err, "Couldn't verify password");
return false;
}
check.is_ok()
}
#[async_trait]
pub trait AuthStore: SessionStore {
/// Check user credentials against the user store.
async fn check_user_creds(&self, user_creds: &UserCreds) -> async_session::Result<bool>;
/// Insert or update user credentials in the user store.
async fn store_user_creds(&self, user_creds: UserCreds) -> async_session::Result<()>;
}
#[cfg(feature = "rocksdb")]
#[derive(Clone, Debug)]
pub struct RocksdbInnerStore {
pub struct RocksdbStore {
db: Arc<DBWithThreadMode<MultiThreaded>>,
}
impl RocksdbInnerStore {
#[cfg(feature = "rocksdb")]
impl RocksdbStore {
pub fn new<P: AsRef<Path>>(name: P) -> Result<Self, rocksdb::Error> {
let session_cf_opts = Options::default();
let session_cf = ColumnFamilyDescriptor::new(SESSION_CF, session_cf_opts);
@ -87,57 +118,14 @@ impl RocksdbInnerStore {
fn get_users_column_family_handle(&self) -> Option<Arc<BoundColumnFamily>> {
self.db.cf_handle(USER_CF)
}
fn make_id_key(cookie_value: &str) -> async_session::Result<String> {
Ok(Session::id_from_cookie_value(cookie_value)?)
}
#[instrument(fields(user=%user_creds.id.0), skip_all)]
pub fn check_user_creds(&self, user_creds: &UserCreds) -> async_session::Result<bool> {
info!("checking credentials for user");
let cf_handle = self
.get_users_column_family_handle()
.expect(&format!("column family {} is missing", USER_CF));
if let Some(payload) = self.db.get_cf(&cf_handle, user_creds.id.0.as_bytes())? {
debug!("Found user in credential store");
let payload = String::from_utf8_lossy(payload.as_slice()).to_string();
let parsed_hash = PasswordHash::new(&payload).expect("Invalid Password Hash");
debug!(password_hash=?parsed_hash, "successfuly obtained password hash");
let check = Argon2::default()
.verify_password(user_creds.pass.expose_secret().as_bytes(), &parsed_hash);
if let Err(err) = &check {
debug!(err=?err, "Couldn't verify password")
}
return Ok(check.is_ok());
}
Ok(false)
}
#[instrument(fields(user=%user_creds.id.0), skip_all)]
pub fn store_user_creds(&self, user_creds: UserCreds) -> async_session::Result<()> {
// TODO(jwall): Enforce a password length?
info!("storing credentials for user {}", user_creds.id.0);
let cf_handle = self
.get_users_column_family_handle()
.expect(&format!("column family {} is missing", USER_CF));
let salt = SaltString::generate(&mut OsRng);
let password_hash = Argon2::default()
.hash_password(user_creds.pass.expose_secret().as_bytes(), &salt)
.expect("failed to hash password");
self.db.put_cf(
&cf_handle,
user_creds.id.0.as_bytes(),
password_hash.to_string().as_bytes(),
)?;
Ok(())
}
}
#[cfg(feature = "rocksdb")]
#[async_trait]
impl SessionStore for RocksdbInnerStore {
impl SessionStore for RocksdbStore {
#[instrument]
async fn load_session(&self, cookie_value: String) -> async_session::Result<Option<Session>> {
let id = Self::make_id_key(&cookie_value)?;
let id = make_id_key(&cookie_value)?;
let cf_handle = self
.get_session_column_family_handle()
.expect(&format!("column family {} is missing", SESSION_CF));
@ -178,6 +166,49 @@ impl SessionStore for RocksdbInnerStore {
}
}
#[cfg(feature = "rocksdb")]
#[async_trait]
impl AuthStore for RocksdbStore {
#[instrument(fields(user=%user_creds.id.0), skip_all)]
async fn check_user_creds(&self, user_creds: &UserCreds) -> async_session::Result<bool> {
// TODO(jwall): Make this function asynchronous.
info!("checking credentials for user");
let cf_handle = self
.get_users_column_family_handle()
.expect(&format!("column family {} is missing", USER_CF));
if let Some(payload) = self
.db
.get_cf(&cf_handle, user_creds.user_id().as_bytes())?
{
debug!("Found user in credential store");
let payload = String::from_utf8_lossy(payload.as_slice()).to_string();
return Ok(check_pass(&payload, &user_creds.pass));
}
Ok(false)
}
// TODO(jwall): Make this function asynchronous.
#[instrument(fields(user=%user_creds.id.0), skip_all)]
async fn store_user_creds(&self, user_creds: UserCreds) -> async_session::Result<()> {
// TODO(jwall): Enforce a password length?
// TODO(jwall): Make this function asynchronous.
info!("storing credentials for user {}", user_creds.id.0);
let cf_handle = self
.get_users_column_family_handle()
.expect(&format!("column family {} is missing", USER_CF));
let salt = SaltString::generate(&mut OsRng);
let password_hash = Argon2::default()
.hash_password(user_creds.pass.expose_secret().as_bytes(), &salt)
.expect("failed to hash password");
self.db.put_cf(
&cf_handle,
user_creds.id.0.as_bytes(),
password_hash.to_string().as_bytes(),
)?;
Ok(())
}
}
#[async_trait]
impl<B> FromRequest<B> for UserIdFromSession
where
@ -187,7 +218,7 @@ where
#[instrument(skip_all)]
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let Extension(session_store) = Extension::<RocksdbInnerStore>::from_request(req)
let Extension(session_store) = Extension::<SqliteStore>::from_request(req)
.await
.expect("No Session store configured!");
let cookies = Option::<TypedHeader<Cookie>>::from_request(req)
@ -197,20 +228,24 @@ where
.as_ref()
.and_then(|c| c.get(AXUM_SESSION_COOKIE_NAME))
{
if let Some(session) = session_store
.load_session(session_cookie.to_owned())
.await
.unwrap()
{
if let Some(user_id) = session.get::<UserId>("user_id") {
return Ok(Self::FoundUserId(user_id));
} else {
error!("No user id found in session");
debug!(?session_cookie, "processing session cookie");
match session_store.load_session(session_cookie.to_owned()).await {
Ok(Some(session)) => {
if let Some(user_id) = session.get::<UserId>("user_id") {
return Ok(Self::FoundUserId(user_id));
} else {
error!("No user id found in session");
return Ok(Self::NoUserId);
}
}
Ok(None) => {
debug!("no session defined in headers.");
return Ok(Self::NoUserId);
}
Err(e) => {
debug!(err=?e, "error deserializing session");
return Ok(Self::NoUserId);
}
} else {
debug!("no session defined in headers.");
return Ok(Self::NoUserId);
}
} else {
debug!("no cookies defined in headers.");
@ -218,3 +253,119 @@ where
}
}
}
#[cfg(feature = "sqlite")]
use sqlx::{
self,
sqlite::{SqliteConnectOptions, SqliteJournalMode},
SqlitePool,
};
#[cfg(feature = "sqlite")]
use std::str::FromStr;
#[cfg(feature = "sqlite")]
#[derive(Clone, Debug)]
pub struct SqliteStore {
pool: Arc<SqlitePool>,
url: String,
}
#[cfg(feature = "sqlite")]
impl SqliteStore {
pub async fn new<P: AsRef<Path>>(path: P) -> sqlx::Result<Self> {
let url = format!("sqlite://{}/store.db", path.as_ref().to_string_lossy());
let options = SqliteConnectOptions::from_str(&url)?.journal_mode(SqliteJournalMode::Wal);
let pool = Arc::new(sqlx::SqlitePool::connect_with(options).await?);
Ok(Self { pool, url })
}
}
#[cfg(feature = "sqlite")]
#[async_trait]
impl SessionStore for SqliteStore {
#[instrument(fields(conn_string=self.url), skip_all)]
async fn load_session(&self, cookie_value: String) -> async_session::Result<Option<Session>> {
let id = make_id_key(&cookie_value)?;
debug!(id, "fetching session from sqlite");
if let Some(payload) =
sqlx::query_scalar!("select session_value from sessions where id = ?", id)
.fetch_optional(self.pool.as_ref())
.await?
{
debug!(sesion_id = id, "found session key");
let session: Session = ciborium::de::from_reader(payload.as_slice())?;
return Ok(Some(session));
}
return Ok(None);
}
#[instrument(fields(conn_string=self.url), skip_all)]
async fn store_session(&self, session: Session) -> async_session::Result<Option<String>> {
let id = session.id();
let mut payload: Vec<u8> = Vec::new();
ciborium::ser::into_writer(&session, &mut payload)?;
sqlx::query!(
"insert into sessions (id, session_value) values (?, ?)",
id,
payload
)
.execute(self.pool.as_ref())
.await?;
debug!(sesion_id = id, "successfully inserted session key");
return Ok(session.into_cookie_value());
}
#[instrument(fields(conn_string=self.url), skip_all)]
async fn destroy_session(&self, session: Session) -> async_session::Result {
let id = session.id();
sqlx::query!("delete from sessions where id = ?", id,)
.execute(self.pool.as_ref())
.await?;
return Ok(());
}
#[instrument(fields(conn_string=self.url), skip_all)]
async fn clear_store(&self) -> async_session::Result {
sqlx::query!("delete from sessions")
.execute(self.pool.as_ref())
.await?;
return Ok(());
}
}
#[cfg(feature = "sqlite")]
#[async_trait]
impl AuthStore for SqliteStore {
#[instrument(fields(user=%user_creds.id.0, conn_string=self.url), skip_all)]
async fn check_user_creds(&self, user_creds: &UserCreds) -> async_session::Result<bool> {
let id = user_creds.user_id().to_owned();
if let Some(payload) =
sqlx::query_scalar!("select password_hashed from users where id = ?", id)
.fetch_optional(self.pool.as_ref())
.await?
{
debug!("Testing password for user");
return Ok(check_pass(&payload, &user_creds.pass));
}
Ok(false)
}
#[instrument(fields(user=%user_creds.id.0, conn_string=self.url), skip_all)]
async fn store_user_creds(&self, user_creds: UserCreds) -> async_session::Result<()> {
let salt = SaltString::generate(&mut OsRng);
let password_hash = Argon2::default()
.hash_password(user_creds.pass.expose_secret().as_bytes(), &salt)
.expect("failed to hash password");
let id = user_creds.user_id().to_owned();
let password_hashed = password_hash.to_string();
debug!("adding password for user");
sqlx::query!(
"insert into users (id, password_hashed) values (?, ?)",
id,
password_hashed,
)
.execute(self.pool.as_ref())
.await?;
Ok(())
}
}

6
nix/devShell/default.nix Normal file
View File

@ -0,0 +1,6 @@
{ pkgs, rust-wasm }:
with pkgs;
mkShell {
buildInputs = (if stdenv.isDarwin then [ pkgs.darwin.apple_sdk.frameworks.Security ] else [ ]) ++ (with pkgs; [wasm-bindgen-cli wasm-pack llvm clang rust-wasm]);
#buildInputs = with pkgs; [wasm-bindgen-cli wasm-pack];
}

View File

@ -13,7 +13,7 @@ with pkgs;
buildInputs = [ rust-wasm ];
# However the crate we are building has it's root in specific crate.
src = root;
nativeBuildInputs = if stdenv.isDarwin then [ xcbuild ] else [ ];
nativeBuildInputs = (if stdenv.isDarwin then [ xcbuild pkgs.darwin.apple_sdk.frameworks.Security ] else [ ]) ++ [llvm clang];
cargoBuildOptions = opts: opts ++ ["-p" "${pname}" ];
postPatch = ''
mkdir -p web/dist

View File

@ -56,6 +56,7 @@ pub fn login_form() -> View<G> {
if username != "" && password != "" {
spawn_local_in_scope(async move {
debug!("authenticating against ui");
// TODO(jwall): Navigate to plan if the below is successful.
authenticate(username, password).await;
});
}