Merge branch 'master' into feature/enqueue_tts_request

This commit is contained in:
Michael Chung committed 2023-10-19 20:12:40 -04:00
commit c3a60ed8ab
62 files changed
+669 -277

No files matched your search

Generated
+130 -176
View File
@@ -126,7 +126,7 @@ dependencies = [
"log",
"mime",
"mime_guess",
"percent-encoding 2.3.0",
"percent-encoding",
"pin-project-lite 0.2.9",
]
@@ -163,7 +163,7 @@ dependencies = [
"lazy_static",
"log",
"mime",
"percent-encoding 2.3.0",
"percent-encoding",
"pin-project 1.0.7",
"rand 0.7.3",
"regex",
@@ -185,7 +185,7 @@ dependencies = [
"actix-rt 2.9.0",
"actix-service 2.0.1",
"actix-utils 3.0.0",
"ahash",
"ahash 0.7.6",
"base64 0.21.0",
"bitflags 1.3.2",
"brotli",
@@ -203,7 +203,7 @@ dependencies = [
"language-tags 0.3.2",
"local-channel",
"mime",
"percent-encoding 2.3.0",
"percent-encoding",
"pin-project-lite 0.2.9",
"rand 0.8.5",
"sha1 0.10.4",
@@ -471,7 +471,7 @@ dependencies = [
"socket2 0.3.19",
"time 0.2.26",
"tinyvec",
"url 2.4.1",
"url",
]
[[package]]
@@ -489,7 +489,7 @@ dependencies = [
"actix-service 2.0.1",
"actix-utils 3.0.0",
"actix-web-codegen 4.2.0",
"ahash",
"ahash 0.7.6",
"bytes 1.3.0",
"bytestring",
"cfg-if 1.0.0",
@@ -511,7 +511,7 @@ dependencies = [
"smallvec 1.10.0",
"socket2 0.4.7",
"time 0.3.14",
"url 2.4.1",
"url",
]
[[package]]
@@ -625,6 +625,18 @@ dependencies = [
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if 1.0.0",
"getrandom 0.2.8",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@@ -713,9 +725,9 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e"
[[package]]
name = "anyhow"
version = "1.0.40"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "archives"
@@ -822,7 +834,7 @@ dependencies = [
"native-tls",
"serde",
"serde_json",
"url 2.4.1",
"url",
]
[[package]]
@@ -859,7 +871,7 @@ dependencies = [
"futures-core",
"log",
"mime",
"percent-encoding 2.3.0",
"percent-encoding",
"rand 0.7.3",
"serde",
"serde_json",
@@ -880,7 +892,7 @@ dependencies = [
"serde",
"thiserror",
"time 0.3.14",
"url 2.4.1",
"url",
]
[[package]]
@@ -1354,7 +1366,7 @@ version = "0.0.1"
dependencies = [
"anyhow",
"easyenv",
"url 2.4.1",
"url",
]
[[package]]
@@ -1404,7 +1416,7 @@ version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951"
dependencies = [
"percent-encoding 2.3.0",
"percent-encoding",
"time 0.2.26",
"version_check",
]
@@ -1415,11 +1427,21 @@ version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"percent-encoding 2.3.0",
"percent-encoding",
"time 0.3.14",
"version_check",
]
[[package]]
name = "cookies"
version = "0.0.1"
dependencies = [
"errors",
"hmac 0.10.1",
"jwt",
"sha2 0.9.5",
]
[[package]]
name = "copyless"
version = "0.1.5"
@@ -1616,29 +1638,25 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.27.2"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be"
dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa 0.4.7",
"matches",
"phf 0.8.0",
"proc-macro2",
"quote",
"itoa 1.0.3",
"phf",
"smallvec 1.10.0",
"syn 1.0.107",
]
[[package]]
name = "cssparser-macros"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn 1.0.107",
"syn 2.0.26",
]
[[package]]
@@ -1825,25 +1843,37 @@ dependencies = [
[[package]]
name = "diesel"
version = "1.4.6"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "047bfc4d5c3bd2ef6ca6f981941046113524b9a9f9a7cbdfdd7ff40f58e6f542"
checksum = "2268a214a6f118fce1838edba3d1561cf0e78d8de785475957a580a7f8c69d33"
dependencies = [
"bitflags 2.4.0",
"byteorder",
"diesel_derives",
"mysqlclient-sys",
"url 1.7.2",
"percent-encoding",
"url",
]
[[package]]
name = "diesel_derives"
version = "1.4.1"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44"
dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
"quote",
"syn 1.0.107",
"syn 2.0.26",
]
[[package]]
name = "diesel_table_macro_syntax"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [
"syn 2.0.26",
]
[[package]]
@@ -2250,7 +2280,7 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"percent-encoding 2.3.0",
"percent-encoding",
]
[[package]]
@@ -2546,7 +2576,7 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
"ahash 0.7.6",
]
[[package]]
@@ -2708,7 +2738,7 @@ dependencies = [
"serde_json",
"serde_qs 0.8.5",
"serde_urlencoded",
"url 2.4.1",
"url",
]
[[package]]
@@ -2844,17 +2874,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "idna"
version = "0.2.3"
@@ -3354,8 +3373,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf 0.10.1",
"phf_codegen 0.10.0",
"phf",
"phf_codegen",
"string_cache",
"string_cache_codegen",
"tendril",
@@ -3603,9 +3622,9 @@ dependencies = [
[[package]]
name = "mysqlclient-sys"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e9637d93448044078aaafea7419aed69d301b4a12bcc4aa0ae856eb169bef85"
checksum = "f61b381528ba293005c42a409dd73d034508e273bf90481f17ec2e964a6e969b"
dependencies = [
"pkg-config",
"vcpkg",
@@ -3679,12 +3698,6 @@ dependencies = [
"uuid 0.8.2",
]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "7.1.2"
@@ -4032,46 +4045,21 @@ dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
[[package]]
name = "percent-encoding"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_shared 0.8.0",
"proc-macro-hack",
]
[[package]]
name = "phf"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_shared 0.10.0",
]
[[package]]
name = "phf_codegen"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
dependencies = [
"phf_generator 0.8.0",
"phf_shared 0.8.0",
"phf_macros",
"phf_shared",
"proc-macro-hack",
]
[[package]]
@@ -4080,18 +4068,8 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared 0.8.0",
"rand 0.7.3",
"phf_generator",
"phf_shared",
]
[[package]]
@@ -4100,33 +4078,24 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"phf_shared",
"rand 0.8.5",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
dependencies = [
"phf_generator 0.8.0",
"phf_shared 0.8.0",
"phf_generator",
"phf_shared",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
@@ -4393,7 +4362,6 @@ dependencies = [
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
"rand_pcg",
]
[[package]]
@@ -4469,15 +4437,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rayon"
version = "1.8.0"
@@ -4518,7 +4477,7 @@ dependencies = [
"dtoa",
"futures 0.1.31",
"itoa 0.4.7",
"percent-encoding 2.3.0",
"percent-encoding",
"sha1 0.6.0",
"tokio-codec",
"tokio-executor",
@@ -4526,7 +4485,7 @@ dependencies = [
"tokio-sync",
"tokio-tcp",
"tokio-uds",
"url 2.4.1",
"url",
]
[[package]]
@@ -4539,9 +4498,9 @@ dependencies = [
"combine 4.6.3",
"dtoa",
"itoa 0.4.7",
"percent-encoding 2.3.0",
"percent-encoding",
"sha1 0.6.0",
"url 2.4.1",
"url",
]
[[package]]
@@ -4556,12 +4515,12 @@ dependencies = [
"dtoa",
"futures-util",
"itoa 0.4.7",
"percent-encoding 2.3.0",
"percent-encoding",
"pin-project-lite 0.2.9",
"sha1 0.6.0",
"tokio 1.25.0",
"tokio-util 0.6.9",
"url 2.4.1",
"url",
]
[[package]]
@@ -4672,7 +4631,7 @@ dependencies = [
"mime",
"native-tls",
"once_cell",
"percent-encoding 2.3.0",
"percent-encoding",
"pin-project-lite 0.2.9",
"serde",
"serde_json",
@@ -4681,7 +4640,7 @@ dependencies = [
"tokio-native-tls",
"tokio-util 0.7.4",
"tower-service",
"url 2.4.1",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
@@ -4744,11 +4703,12 @@ dependencies = [
[[package]]
name = "rpassword"
version = "5.0.1"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb"
checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
dependencies = [
"libc",
"rtoolbox",
"winapi 0.3.9",
]
@@ -4784,6 +4744,16 @@ dependencies = [
"quick-xml 0.27.1",
]
[[package]]
name = "rtoolbox"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]]
name = "rust-ini"
version = "0.18.0"
@@ -4814,7 +4784,7 @@ dependencies = [
"maybe-async",
"md5",
"minidom",
"percent-encoding 2.3.0",
"percent-encoding",
"quick-xml 0.26.0",
"reqwest",
"serde",
@@ -4824,7 +4794,7 @@ dependencies = [
"time 0.3.14",
"tokio 1.25.0",
"tokio-stream",
"url 2.4.1",
"url",
]
[[package]]
@@ -4949,15 +4919,16 @@ dependencies = [
[[package]]
name = "scraper"
version = "0.14.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7cb4dae083699a22a65aa9d2699c27f525e35dffaec38b10801e958ed4cf27"
checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c"
dependencies = [
"ahash 0.8.3",
"cssparser",
"ego-tree",
"getopts",
"html5ever",
"matches",
"once_cell",
"selectors",
"smallvec 1.10.0",
"tendril",
@@ -4998,22 +4969,21 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.22.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.4.0",
"cssparser",
"derive_more",
"fxhash",
"log",
"matches",
"phf 0.8.0",
"phf_codegen 0.8.0",
"new_debug_unreachable",
"phf",
"phf_codegen",
"precomputed-hash",
"servo_arc",
"smallvec 1.10.0",
"thin-slice",
]
[[package]]
@@ -5077,7 +5047,7 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6"
dependencies = [
"percent-encoding 2.3.0",
"percent-encoding",
"serde",
"thiserror",
]
@@ -5088,7 +5058,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af4cee6cd4b23b45e6709150d1e9af5c748131de7e3316a7c2b3008051ed725"
dependencies = [
"percent-encoding 2.3.0",
"percent-encoding",
"serde",
"thiserror",
]
@@ -5153,11 +5123,10 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.1.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44"
dependencies = [
"nodrop",
"stable_deref_trait",
]
@@ -5368,7 +5337,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105"
dependencies = [
"ahash",
"ahash 0.7.6",
"atoi",
"bitflags 1.3.2",
"byteorder",
@@ -5398,7 +5367,7 @@ dependencies = [
"num-bigint 0.4.3",
"once_cell",
"paste",
"percent-encoding 2.3.0",
"percent-encoding",
"rand 0.8.5",
"rsa",
"serde",
@@ -5410,7 +5379,7 @@ dependencies = [
"stringprep",
"thiserror",
"tokio-stream",
"url 2.4.1",
"url",
]
[[package]]
@@ -5432,7 +5401,7 @@ dependencies = [
"sqlx-core",
"sqlx-rt",
"syn 1.0.107",
"url 2.4.1",
"url",
]
[[package]]
@@ -5551,6 +5520,7 @@ dependencies = [
"cloud_storage",
"config",
"container_common",
"cookies",
"data-encoding",
"datetimes",
"derive_more",
@@ -5644,7 +5614,7 @@ dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot 0.12.1",
"phf_shared 0.10.0",
"phf_shared",
"precomputed-hash",
"serde",
]
@@ -5655,8 +5625,8 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
]
@@ -6033,12 +6003,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thin-slice"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
[[package]]
name = "thiserror"
version = "1.0.43"
@@ -6486,7 +6450,7 @@ dependencies = [
"smallvec 1.10.0",
"thiserror",
"tokio 0.2.25",
"url 2.4.1",
"url",
]
[[package]]
@@ -6612,7 +6576,7 @@ dependencies = [
"rustls-native-certs",
"sha-1",
"thiserror",
"url 2.4.1",
"url",
"utf-8",
"webpki",
]
@@ -6705,7 +6669,7 @@ dependencies = [
"thiserror",
"twitch_oauth2",
"typed-builder",
"url 2.4.1",
"url",
"version_check",
]
@@ -6740,7 +6704,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"url 2.4.1",
"url",
]
[[package]]
@@ -6839,17 +6803,6 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
dependencies = [
"idna 0.1.5",
"matches",
"percent-encoding 1.0.1",
]
[[package]]
name = "url"
version = "2.4.1"
@@ -6858,7 +6811,7 @@ checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
dependencies = [
"form_urlencoded",
"idna 0.4.0",
"percent-encoding 2.3.0",
"percent-encoding",
"serde",
]
@@ -6895,6 +6848,7 @@ dependencies = [
"bcrypt",
"chrono",
"container_common",
"cookies",
"dotenv",
"enums",
"errors",
+1
View File
@@ -24,6 +24,7 @@ members = [
"crates/lib/caching/memory_caching",
"crates/lib/caching/redis_caching",
"crates/lib/concurrency",
"crates/lib/cookies",
"crates/lib/crockford",
"crates/lib/datetimes",
"crates/lib/deprecated/config", # TODO(bt, 2023-02-13): Deprecate or repurpose
+1 -1
View File
@@ -18,7 +18,7 @@ path = "src/lib.rs"
# (none)
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
# Serialization
+3
View File
@@ -13,6 +13,8 @@ use strum::EnumIter;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(test, derive(EnumIter, EnumCount))]
pub(crate) enum EntityType {
/// AVTs are not stored as primary keys in any table, but an index in many tables.
AnonymousVisitorTracking,
AuditLog,
Comment,
DownloadJob,
@@ -36,6 +38,7 @@ impl EntityType {
pub fn prefix(self) -> &'static str {
match self {
Self::AnonymousVisitorTracking => "avt_",
Self::AuditLog => "audit_",
Self::Comment => "comment_",
Self::DownloadJob => "jdown_", // NB: Was "JGUP:"
@@ -0,0 +1,16 @@
use std::fmt::Debug;
use serde::Deserialize;
use serde::Serialize;
use crate::prefixes::EntityType;
/// A rare instance that is *not* a primary key.
/// These are generated as cookies to track users for a better logged out experience and for analytics.
/// We use these as indices into several columns (ML result types, uploads, etc.)
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct AnonymousVisitorTrackingToken(pub String);
impl_string_token!(AnonymousVisitorTrackingToken);
impl_crockford_generator!(AnonymousVisitorTrackingToken, 32usize, EntityType::AnonymousVisitorTracking, CrockfordLower);
+1
View File
@@ -1,3 +1,4 @@
pub mod anonymous_visitor_tracking;
pub mod audit_logs;
pub mod comments;
pub mod generic_download_jobs;
-5
View File
@@ -5,11 +5,6 @@ use serde::Serialize;
use crate::prefixes::EntityType;
// FIXME: I think that this file+module structure is an anti-pattern.
// In the future, we should create two top-level directories: /tokens and /ids, and each token or
// ID type should be in its own file. (NB: single files cannot share token definitions due to the
// macro generating test modules that would conflict.)
/// The primary key for users.
#[derive(Clone, PartialEq, Eq, sqlx::Type, Debug, Serialize, Deserialize)]
#[sqlx(transparent)]
+1 -1
View File
@@ -20,7 +20,7 @@ container_common = { path = "../../lib/deprecated/container_common" }
easyenv = { path = "../../lib/easyenv" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
# TODO/NB: Commenting out clap4 as it prevents windows builds
+1 -1
View File
@@ -20,7 +20,7 @@ container_common = { path = "../../lib/deprecated/container_common" }
easyenv = { path = "../../lib/easyenv" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
# TODO/NB: Commenting out clap4 as it prevents windows builds
# clap4 = { package = "clap", version = "4.0" } # TODO/NB: We're using an older version of clap elsewhere, hence the naming.
@@ -19,7 +19,7 @@ buckets = { path = "../../../_types/buckets" }
container_common = { path = "../../deprecated/container_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
log = "0.4.14"
# The "never-encode-slash" feature seems to have been removed:
@@ -19,7 +19,7 @@ container_common = { path = "../../deprecated/container_common" }
subprocess_common = { path = "../../subprocess_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
log = "0.4.14"
tempdir = "0.3.7"
@@ -19,7 +19,7 @@ container_common = { path = "../../deprecated/container_common" }
easyenv = { path = "../../easyenv" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
once_cell = "1.18.0"
regex = "1.5.4"
+1 -1
View File
@@ -20,7 +20,7 @@ container_common = { path = "../../deprecated/container_common" }
errors = { path = "../../errors" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
log = "0.4.14"
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "cookies"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[lib]
# NB: Public "cookies" crate is version 0.0.1 from 10 years ago!
name = "cookies"
path = "src/lib.rs"
[dependencies]
# Internal
errors = { path = "../errors" }
# External
hmac = "0.10.0" # jwt signing
jwt = "0.13.0"
sha2 = "0.9.5" # hmac signing
[dev-dependencies]
# None yet
+95
View File
@@ -0,0 +1,95 @@
use std::collections::BTreeMap;
use hmac::Hmac;
use hmac::NewMac;
use jwt::SignWithKey;
use jwt::VerifyWithKey;
use sha2::Sha256;
use errors::{anyhow, AnyhowResult};
#[derive(Clone)]
pub struct JwtSigner {
hmac_key: Hmac<Sha256>,
}
impl JwtSigner {
pub fn new(hmac_secret: &str) -> AnyhowResult<Self> {
let hmac_key = Hmac::new_varkey(hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
Ok(Self {
hmac_key,
})
}
/// Turn a map of claims into a signed JWT cookie payload (string)
pub fn claims_to_jwt(&self, claims: &impl Claimable) -> AnyhowResult<String> {
let jwt_string = claims.sign(&self.hmac_key)?;
Ok(jwt_string)
}
/// Turn a JWT cookie payload (string) into a map of (key, value) claims
pub fn jwt_to_claims(&self, jwt: &str) -> AnyhowResult<BTreeMap<String, String>> {
let claims = jwt.verify_with_key(&self.hmac_key)?;
Ok(claims)
}
}
pub trait Claimable {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String>;
}
impl Claimable for BTreeMap<String, String> {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String> {
let claims = self.sign_with_key(hmac_key)?;
Ok(claims)
}
}
impl Claimable for BTreeMap<&str, &str> {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String> {
let claims = self.sign_with_key(hmac_key)?;
Ok(claims)
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use crate::jwt_signer::JwtSigner;
#[test]
fn test_round_trip() {
let signer = JwtSigner::new("secret").unwrap();
let mut claims = BTreeMap::new();
claims.insert("foo", "yes");
claims.insert("bar", "no");
let jwt = signer.claims_to_jwt(&claims).unwrap();
let output_claims = signer.jwt_to_claims(&jwt).unwrap();
assert_eq!(claims.len(), output_claims.len());
for (k, v) in output_claims.iter() {
assert_eq!(v, claims[k.as_str()]);
}
}
#[test]
fn test_stability() {
let signer = JwtSigner::new("secret").unwrap();
let mut claims = BTreeMap::new();
claims.insert("foo", "yes");
claims.insert("bar", "no");
let jwt = signer.claims_to_jwt(&claims).unwrap();
assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJiYXIiOiJubyIsImZvbyI6InllcyJ9.k4eRvIrtw4tuGDtQqSmEDngtGYAaQVRYVMCK5N3JcZA", &jwt);
}
}
+20
View File
@@ -0,0 +1,20 @@
//! cookies
//!
//! This library contains reusable, tested, cross-cutting cookie functionality.
//!
// Never allow these
#![forbid(private_in_public)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod jwt_signer;
+1 -1
View File
@@ -19,5 +19,5 @@ path = "src/lib.rs"
easyenv = { path = "../../../lib/easyenv" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
url = "2.4.1"
@@ -18,7 +18,7 @@ errors = { path = "../../errors" }
hashing = { path = "../../files/hashing" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
once_cell = "1.18.0"
rand = "0.8.3"
uuid = { version = "0.8.2", features = ["v4"] }
@@ -20,7 +20,7 @@ errors = { path = "../../errors" }
reusable_types = { path = "../../../_types/reusable_types" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
+1 -1
View File
@@ -17,7 +17,7 @@ path = "src/lib.rs"
# NB: This is one of the most central crates in the monorepo and everything depends on it.
# Consider the dependencies here wisely.
# Ideally we have almost nothing as a dependency of this crate.
anyhow = "1.0.40"
anyhow = "1.0.75"
[dev-dependencies]
# None yet
+1 -1
View File
@@ -23,7 +23,7 @@ errors = { path = "../errors" }
redis_common = { path = "../redis_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
log = "0.4.14"
r2d2_redis = "0.14.0"
+1 -1
View File
@@ -25,7 +25,7 @@ sqlx_mysql_helpers = { path = "../sqlx_mysql_helpers" }
tokens = { path = "../../_types/tokens" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
@@ -4,14 +4,24 @@ use log::warn;
use sqlx::{MySql, MySqlPool};
use sqlx::pool::PoolConnection;
use enums::by_table::media_files::media_file_type::MediaFileType;
use errors::AnyhowResult;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::users::UserToken;
use tokens::tokens::zs_voice_dataset_samples::ZsVoiceDatasetSampleToken;
use tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken;
pub struct DatasetSampleRecordForList {
pub token: ZsVoiceDatasetSampleToken,
pub dataset_token: ZsVoiceDatasetToken,
pub sample_token: ZsVoiceDatasetSampleToken,
pub media_file_token: MediaFileToken,
pub media_type: MediaFileType,
// Bucket path, prefix, and extension are all that are required to construct a path to
// download media from cloud buckets.
pub public_bucket_directory_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
pub maybe_creator_user_token: Option<UserToken>,
@@ -21,7 +31,7 @@ pub struct DatasetSampleRecordForList {
}
pub async fn list_samples(
pub async fn list_dataset_samples_for_dataset_token(
dataset_token: &ZsVoiceDatasetToken,
can_see_deleted: bool,
mysql_pool: &MySqlPool,
@@ -65,8 +75,12 @@ pub async fn list_samples_by_dataset_token_with_connection(
Ok(records.into_iter()
.map(|record| {
DatasetSampleRecordForList {
token: record.token,
dataset_token: record.dataset_token,
sample_token: record.token,
media_file_token: record.media_file_token,
media_type: record.media_type,
public_bucket_directory_hash: record.public_bucket_directory_hash,
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
maybe_creator_user_token: record.maybe_creator_user_token,
created_at: record.created_at,
updated_at: record.updated_at,
@@ -87,11 +101,17 @@ async fn list_samples_by_dataset_token(
r#"
SELECT
zds.token as `token: tokens::tokens::zs_voice_dataset_samples::ZsVoiceDatasetSampleToken`,
zds.dataset_token as `dataset_token: tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken`,
zds.media_file_token as `media_file_token: tokens::tokens::media_files::MediaFileToken`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
zds.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
zds.created_at,
zds.updated_at
FROM zs_voice_dataset_samples as zds
JOIN media_files as m
ON zds.media_file_token = m.token
WHERE zds.dataset_token = ?
AND zds.user_deleted_at IS NULL
AND zds.mod_deleted_at IS NULL
@@ -104,11 +124,17 @@ async fn list_samples_by_dataset_token(
r#"
SELECT
zds.token as `token: tokens::tokens::zs_voice_dataset_samples::ZsVoiceDatasetSampleToken`,
zds.dataset_token as `dataset_token: tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken`,
zds.media_file_token as `media_file_token: tokens::tokens::media_files::MediaFileToken`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
zds.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
zds.created_at,
zds.updated_at
FROM zs_voice_dataset_samples as zds
JOIN media_files as m
ON zds.media_file_token = m.token
WHERE zds.dataset_token = ?
"#,
dataset_token
@@ -121,7 +147,13 @@ async fn list_samples_by_dataset_token(
struct InternalRawDatasetSampleRecordForList {
token: ZsVoiceDatasetSampleToken,
dataset_token: ZsVoiceDatasetToken,
media_file_token: MediaFileToken,
media_type: MediaFileType,
public_bucket_directory_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
maybe_creator_user_token: Option<UserToken>,
@@ -2,4 +2,4 @@ pub mod delete_sample;
pub mod get_dataset_sample;
pub mod get_dataset_sample_by_uuid;
pub mod insert_dataset_sample_and_media_file;
pub mod list_dataset_samples;
pub mod list_dataset_samples_for_dataset_token;
+1 -1
View File
@@ -18,7 +18,7 @@ path = "src/lib.rs"
container_common = { path = "../deprecated/container_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
# Serialization
serde = "1.0.125"
+1 -1
View File
@@ -18,7 +18,7 @@ path = "src/lib.rs"
container_common = { path = "../deprecated/container_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
# Serialization
#serde = "1.0.125"
+1 -1
View File
@@ -19,7 +19,7 @@ container_common = { path = "../deprecated/container_common" }
user_input_common = { path = "../user_input_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
log = "0.4.14"
once_cell = "1.18.0"
+1 -1
View File
@@ -20,4 +20,4 @@ reusable_types = { path = "../../_types/reusable_types" }
# External
actix-web = "4.0.0-beta.10"
anyhow = "1.0.40"
anyhow = "1.0.75"
+1 -1
View File
@@ -18,7 +18,7 @@ path = "src/lib.rs"
container_common = { path = "../deprecated/container_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
log = "0.4.14"
once_cell = "1.18.0"
pulldown-cmark = "0.8.0" # Markdown
+1 -1
View File
@@ -35,7 +35,7 @@ reqwest = { version = "0.11", features = ["json"] } # HTTP Client
# Scraping
rss = "2.0.2"
scraper = "0.14.0"
scraper = "0.17.1"
[dev-dependencies]
# None yet
+1 -1
View File
@@ -26,7 +26,7 @@ reusable_types = { path = "../../../_types/reusable_types" }
tokens = { path = "../../../_types/tokens" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
+1 -1
View File
@@ -38,7 +38,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
+1 -1
View File
@@ -43,7 +43,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
@@ -30,7 +30,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
@@ -29,7 +29,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
@@ -30,7 +30,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
@@ -28,7 +28,7 @@ tts_common = { path = "../../../lib/tts_common" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
chrono = { version = "0.4.19", features = ["serde"] }
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
dotenv = "0.15.0"
@@ -25,7 +25,7 @@ tts_common = { path = "../../../lib/tts_common" }
twitch_common = { path = "../../../lib/api_clients/twitch_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
derive_more = "0.99.14"
@@ -111,8 +111,8 @@ toml = "0.5.8"
pulldown-cmark = "0.8.0"
# Temporary (just to ask for oauth credentials during dev)
rpassword = "5.0.1"
rpassword = "7.2.0"
[build-dependencies]
# NB: Only using diesel for migrations support
diesel = { version = "1.4.6", features = ["mysql"] }
diesel = { version = "2.1.3", features = ["mysql"] }
+1 -1
View File
@@ -22,7 +22,7 @@ reusable_types = { path = "../../../_types/reusable_types" }
url_config = { path = "../../../lib/url_config" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
@@ -18,7 +18,7 @@ path = "src/lib.rs"
container_common = { path = "../../../lib/deprecated/container_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
+2 -1
View File
@@ -16,6 +16,7 @@ path = "src/lib.rs"
# Internal
container_common = { path = "../../../lib/deprecated/container_common" }
cookies = { path = "../../../lib/cookies" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
http_server_common = { path = "../../../lib/deprecated/http_server_common" }
@@ -28,7 +29,7 @@ tokens = { path = "../../../_types/tokens" }
user_input_common = { path = "../../../lib/user_input_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
@@ -0,0 +1,79 @@
use actix_web::cookie::Cookie;
use actix_web::HttpRequest;
use anyhow::anyhow;
use log::warn;
use cookies::jwt_signer::JwtSigner;
use errors::AnyhowResult;
use crate::cookies::anonymous_visitor_tracking::avt_cookie_payload::AvtCookiePayload;
const VISITOR_COOKIE_NAME : &str = "visitor";
/// Handle "anonymous visitor tracking" cookies.
/// This enables us to associate results with an anonymous user for a better experience,
/// as well as do some form of return visitor tracking.
#[derive(Clone)]
pub struct AvtCookieManager {
cookie_domain: String,
jwt_signer: JwtSigner,
}
impl AvtCookieManager {
pub fn new(cookie_domain: &str, hmac_secret: &str) -> AnyhowResult<Self> {
Ok(Self {
cookie_domain: cookie_domain.to_string(),
jwt_signer: JwtSigner::new(hmac_secret)?,
})
}
pub fn make_new_cookie(&self) -> AnyhowResult<Cookie> {
let payload = AvtCookiePayload::new();
let claims = payload.to_map();
let jwt_string = self.jwt_signer.claims_to_jwt(&claims)?;
let make_secure = !self.cookie_domain.to_lowercase().contains("jungle.horse")
&& !self.cookie_domain.to_lowercase().contains("localhost");
Ok(Cookie::build(VISITOR_COOKIE_NAME, jwt_string)
.secure(make_secure) // HTTPS-only
.permanent()
.path("/") // NB: Otherwise it'll be set to `/v1`
//.domain(&self.cookie_domain)
//.http_only(true) // Not exposed to Javascript
//.same_site(SameSite::Lax)
.finish())
}
pub fn make_delete_cookie(&self) -> Cookie {
let mut cookie = Cookie::build(VISITOR_COOKIE_NAME, "DELETED")
.expires(actix_web::cookie::time::OffsetDateTime::UNIX_EPOCH)
.path("/") // NB: Otherwise it'll be set to `/v1`
.finish();
cookie.make_removal();
cookie
}
pub fn decode_cookie_payload(&self, visitor_cookie: &Cookie) -> AnyhowResult<AvtCookiePayload> {
let cookie_contents = visitor_cookie.value().to_string();
let claims = self.jwt_signer.jwt_to_claims(&cookie_contents)?;
let payload = AvtCookiePayload::from_map(claims)?;
Ok(payload)
}
pub fn decode_cookie_payload_from_request(&self, request: &HttpRequest) -> AnyhowResult<Option<AvtCookiePayload>> {
let cookie = match request.cookie(VISITOR_COOKIE_NAME) {
None => return Ok(None),
Some(cookie) => cookie,
};
match self.decode_cookie_payload(&cookie) {
Err(e) => {
warn!("Visitor cookie decode error: {:?}", e);
Err(anyhow!("Could not decode visitor cookie: {:?}", e))
},
Ok(payload) => Ok(Some(payload)),
}
}
}
@@ -0,0 +1,94 @@
use std::collections::BTreeMap;
use std::str::FromStr;
use anyhow::anyhow;
use errors::AnyhowResult;
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
/**
* Cookie version history
*
* Version 1: Claims include "avt_token" and "cookie_version"
*/
const COOKIE_VERSION : u32 = 1;
// TODO: Should probably use protobuf or nom for handling
// data-migration sensitive wire formats rather than
// hand-rolling these.
/// The payload of AVT cookies.
///
/// Version 1 - only `avt_token` and `version`.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct AvtCookiePayload {
// TODO: Strongly type
pub avt_token: AnonymousVisitorTrackingToken,
pub cookie_version: u32,
}
impl AvtCookiePayload {
pub fn new() -> Self {
let avt_token = AnonymousVisitorTrackingToken::generate();
AvtCookiePayload {
avt_token,
cookie_version: COOKIE_VERSION,
}
}
pub fn from_map(map: BTreeMap<String, String>) -> AnyhowResult<Self> {
let avt_token = map.get("avt_token")
.ok_or(anyhow!("avt_token not present"))?
.to_string();
let cookie_version = map
.get("cookie_version")
.ok_or(anyhow!("avt_token not present"))?;
let avt_token = AnonymousVisitorTrackingToken::new_from_str(&avt_token);
let cookie_version = u32::from_str(cookie_version)?;
Ok(AvtCookiePayload {
avt_token,
cookie_version,
})
}
pub fn to_map(&self) -> BTreeMap<String, String> {
let mut map = BTreeMap::new();
map.insert("avt_token".to_string(), self.avt_token.to_string());
map.insert("cookie_version".to_string(), self.cookie_version.to_string());
map
}
}
#[cfg(test)]
mod tests {
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
use crate::cookies::anonymous_visitor_tracking::avt_cookie_payload::AvtCookiePayload;
#[test]
fn test_new() {
let payload = AvtCookiePayload::new();
assert_eq!(payload.cookie_version, 1);
assert_eq!(payload.avt_token.0.len(), 32);
assert!(payload.avt_token.to_string().starts_with("avt_"));
}
#[test]
fn round_trip_test() {
let payload = AvtCookiePayload {
avt_token: AnonymousVisitorTrackingToken::generate(),
cookie_version: 123,
};
let round_trip_payload= AvtCookiePayload::from_map(payload.to_map())
.unwrap();
assert_eq!(&payload, &round_trip_payload);
}
}
@@ -0,0 +1,2 @@
pub mod avt_cookie_manager;
pub mod avt_cookie_payload;
@@ -0,0 +1,2 @@
pub mod anonymous_visitor_tracking;
pub mod session;
@@ -0,0 +1,2 @@
pub mod session_cookie_manager;
pub mod session_cookie_payload;
@@ -8,15 +8,13 @@ use std::collections::BTreeMap;
use actix_web::cookie::Cookie;
use actix_web::HttpRequest;
use anyhow::anyhow;
use hmac::Hmac;
use hmac::NewMac;
use jwt::SignWithKey;
use jwt::VerifyWithKey;
use log::warn;
use sha2::Sha256;
use cookies::jwt_signer::JwtSigner;
use errors::AnyhowResult;
use crate::cookies::session::session_cookie_payload::SessionCookiePayload;
/**
* Cookie version history
*
@@ -34,15 +32,7 @@ const SESSION_COOKIE_NAME : &str = "session";
pub struct SessionCookieManager {
cookie_domain: String,
hmac_secret: String,
}
#[derive(Clone)]
pub struct SessionCookiePayload {
/// The database primary key for the session instance.
pub session_token: String,
/// The primary key identifier of the user.
/// Version 1 cookies do not have a user token, hence it is optional.
pub maybe_user_token: Option<String>,
jwt_signer: JwtSigner,
}
impl SessionCookieManager {
@@ -50,13 +40,11 @@ impl SessionCookieManager {
Self {
cookie_domain: cookie_domain.to_string(),
hmac_secret: hmac_secret.to_string(),
jwt_signer: JwtSigner::new(hmac_secret).unwrap(),//TODO FIXME
}
}
pub fn create_cookie(&self, session_token: &str, user_token: &str) -> AnyhowResult<Cookie> {
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
let cookie_version = COOKIE_VERSION.to_string();
let mut claims = BTreeMap::new();
@@ -64,24 +52,25 @@ impl SessionCookieManager {
claims.insert("user_token", user_token);
claims.insert("cookie_version", &cookie_version);
let jwt_string = claims.sign_with_key(&key)?;
let jwt_string = self.jwt_signer.claims_to_jwt(&claims)?;
let make_secure = !self.cookie_domain.to_lowercase().contains("jungle.horse")
&& !self.cookie_domain.to_lowercase().contains("localhost");
Ok(Cookie::build(SESSION_COOKIE_NAME, jwt_string)
//.domain(&self.cookie_domain)
.secure(make_secure) // HTTPS-only
//.path("/")
.permanent()
.path("/") // NB: Otherwise it'll be set to `/v1`
//.domain(&self.cookie_domain)
//.http_only(true) // Not exposed to Javascript
//.expires(OffsetDateTime::now_utc() + time::Duration::days(365))
.permanent()
//.same_site(SameSite::Lax)
.finish())
}
pub fn delete_cookie(&self) -> Cookie {
let mut cookie = Cookie::build(SESSION_COOKIE_NAME, "DELETED")
.path("/") // NB: Otherwise it'll be set to `/v1`
.expires(actix_web::cookie::time::OffsetDateTime::UNIX_EPOCH)
.finish();
cookie.make_removal();
@@ -91,12 +80,9 @@ impl SessionCookieManager {
pub fn decode_session_cookie_payload(&self, session_cookie: &Cookie)
-> AnyhowResult<SessionCookiePayload>
{
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
let cookie_contents = session_cookie.value().to_string();
let claims: BTreeMap<String, String> = cookie_contents.verify_with_key(&key)?;
let claims = self.jwt_signer.jwt_to_claims(&cookie_contents)?;
let session_token = claims["session_token"].clone();
let maybe_user_token = claims.get("user_token")
@@ -149,3 +135,30 @@ impl SessionCookieManager {
}
}
#[cfg(test)]
mod tests {
use crate::cookies::session::session_cookie_manager::SessionCookieManager;
#[test]
fn test_cookie_payload() {
// NB: Let's make extra sure this always works when migrating cookies, else we'll accidentally log out logged-in users.
// (These are version 2 cookies.)
let manager = SessionCookieManager::new("fakeyou.com", "secret");
let cookie = manager.create_cookie("ex_session_token", "ex_user_token").unwrap();
assert_eq!(cookie.value(), "eyJhbGciOiJIUzI1NiJ9.eyJjb29raWVfdmVyc2lvbiI6IjIiLCJzZXNzaW9uX3Rva2VuIjoiZXhfc2Vzc2lvbl90b2tlbiIsInVzZXJfdG9rZW4iOiJleF91c2VyX3Rva2VuIn0.94ly2gHhlPVtnANsNy6cJozFVmId4imwW5v-mei7jD8");
}
#[test]
fn test_cookie_round_trip() {
// NB: Let's make extra sure this always works when migrating cookies, else we'll accidentally log out logged-in users.
// (These are version 2 cookies.)
let manager = SessionCookieManager::new("fakeyou.com", "secret");
let cookie = manager.create_cookie("ex_session_token", "ex_user_token").unwrap();
let decoded = manager.decode_session_cookie_payload(&cookie).unwrap();
assert_eq!(decoded.session_token, "ex_session_token".to_string());
assert_eq!(decoded.maybe_user_token, Some("ex_user_token".to_string()));
}
}
@@ -0,0 +1,9 @@
#[derive(Clone)]
pub struct SessionCookiePayload {
/// The database primary key for the session instance.
pub session_token: String,
/// The primary key identifier of the user.
/// Version 1 cookies do not have a user token, hence it is optional.
pub maybe_user_token: Option<String>,
}
@@ -29,6 +29,11 @@ pub fn add_suggested_api_v1_account_creation_and_session_routes<T, B> (app: App<
.route(web::post().to(create_account_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(
web::resource("/v1/create_account")
.route(web::post().to(create_account_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(
web::resource("/login")
.route(web::post().to(login_handler))
@@ -21,8 +21,8 @@ use mysql_queries::queries::users::user_sessions::create_user_session::create_us
use mysql_queries::tokens::Tokens;
use user_input_common::check_for_slurs::contains_slurs;
use crate::cookies::session::session_cookie_manager::SessionCookieManager;
use crate::utils::email_to_gravatar::email_to_gravatar;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::validations::is_reserved_username::is_reserved_username;
use crate::validations::validate_passwords::validate_passwords;
use crate::validations::validate_username::validate_username;
@@ -19,7 +19,7 @@ use mysql_queries::queries::users::user::lookup_user_for_login_by_email::lookup_
use mysql_queries::queries::users::user::lookup_user_for_login_by_username::lookup_user_for_login_by_username;
use mysql_queries::queries::users::user_sessions::create_user_session::create_user_session;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session::session_cookie_manager::SessionCookieManager;
#[derive(Deserialize)]
pub struct LoginRequest {
@@ -14,7 +14,7 @@ use sqlx::MySqlPool;
use http_server_common::response::response_error_helpers::to_simple_json_error;
use mysql_queries::queries::users::user_sessions::delete_user_session::delete_user_session;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session::session_cookie_manager::SessionCookieManager;
#[derive(Serialize)]
pub struct LogoutSuccessResponse {
@@ -13,6 +13,7 @@ use sqlx::MySqlPool;
use http_server_common::response::response_error_helpers::to_simple_json_error;
use crate::cookies::anonymous_visitor_tracking::avt_cookie_manager::AvtCookieManager;
use crate::utils::session_checker::SessionChecker;
#[derive(Serialize, Copy, Clone)]
@@ -109,6 +110,7 @@ pub async fn session_info_handler(
http_request: HttpRequest,
mysql_pool: web::Data<MySqlPool>,
session_checker: web::Data<SessionChecker>,
avt_manager: web::Data<AvtCookieManager>,
) -> Result<HttpResponse, SessionInfoError>
{
let mut mysql_connection = mysql_pool.acquire()
@@ -174,6 +176,18 @@ pub async fn session_info_handler(
}
}
let maybe_avt_cookie = match avt_manager.decode_cookie_payload_from_request(&http_request) {
Ok(Some(_avt_cookie)) => None,
_ => {
let cookie = avt_manager.make_new_cookie()
.map_err(|e| {
warn!("avt cookie creation error: {:?}", e);
SessionInfoError::ServerError
})?;
Some(cookie)
}
};
let response = SessionInfoSuccessResponse {
success: true,
logged_in,
@@ -183,7 +197,13 @@ pub async fn session_info_handler(
let body = serde_json::to_string(&response)
.map_err(|_e| SessionInfoError::ServerError)?;
Ok(HttpResponse::Ok()
let mut response_builder = HttpResponse::Ok();
if let Some(cookie) = maybe_avt_cookie {
response_builder.cookie(cookie);
}
Ok(response_builder
.content_type("application/json")
.body(body))
}
+1
View File
@@ -4,6 +4,7 @@
pub const RESERVED_USERNAMES : &str = include_str!("../../../../../includes/binary_includes/reserved_usernames.txt");
pub const RESERVED_SUBSTRINGS : &str = include_str!("../../../../../includes/binary_includes/reserved_usernames_including.txt");
pub mod cookies;
pub mod default_routes;
pub mod endpoints;
pub mod utils;
@@ -2,5 +2,4 @@ pub mod default_avatar_color_from_username;
pub mod default_avatar_from_username;
pub mod email_to_gravatar;
pub mod session_checker;
pub mod session_cookie_manager;
pub mod user_session_extended;
@@ -15,7 +15,7 @@ use mysql_queries::queries::users::user_subscriptions::list_active_user_subscrip
use redis_caching::redis_ttl_cache::{RedisTtlCache, RedisTtlCacheConnection};
use redis_common::redis_cache_keys::RedisCacheKeys;
use crate::utils::session_cookie_manager::SessionCookieManager;
use crate::cookies::session::session_cookie_manager::SessionCookieManager;
use crate::utils::user_session_extended::{UserSessionExtended, UserSessionPreferences, UserSessionPremiumPlanInfo, UserSessionRoleAndPermissions, UserSessionSubscriptionPlan, UserSessionUserDetails};
#[derive(Clone)]
@@ -22,6 +22,7 @@ buckets = { path = "../../../_types/buckets" }
cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
config = { path = "../../../lib/deprecated/config" }
container_common = { path = "../../../lib/deprecated/container_common" }
cookies = { path = "../../../lib/cookies" }
datetimes = { path = "../../../lib/datetimes" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
@@ -51,7 +52,7 @@ users_component = { path = "../../plugins/users" }
newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
derive_more = "0.99.14"
@@ -165,7 +166,7 @@ async-trait = "0.1.58"
[build-dependencies]
# NB: Only using diesel for migrations support
diesel = { version = "1.4.6", features = ["mysql"] }
diesel = { version = "2.1.3", features = ["mysql"] }
[dev-dependencies]
speculoos = "0.9.0" # Fluent assertions
Whitespace-only changes.
@@ -4,17 +4,29 @@ use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use chrono::{DateTime, Utc};
use log::warn;
use log::{info, warn};
use mysql_queries::queries::voice_designer::voice_samples::list_dataset_samples::list_samples;
use enums::by_table::media_files::media_file_type::MediaFileType;
use mysql_queries::queries::voice_designer::voice_samples::list_dataset_samples_for_dataset_token::list_dataset_samples_for_dataset_token;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::users::UserToken;
use tokens::tokens::zs_voice_dataset_samples::ZsVoiceDatasetSampleToken;
use tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken;
use crate::server_state::ServerState;
#[derive(Serialize, Clone)]
pub struct ZsSampleRecordForResponse {
sample_token: String,
dataset_token: String,
maybe_creator_user_token: Option<String>,
sample_token: ZsVoiceDatasetSampleToken,
media_file_token: MediaFileToken,
media_type: MediaFileType,
public_bucket_directory_hash: String,
maybe_public_bucket_prefix: Option<String>,
maybe_public_bucket_extension: Option<String>,
maybe_creator_user_token: Option<UserToken>,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
@@ -76,7 +88,7 @@ pub async fn list_samples_by_dataset_handler(
let dataset_token = ZsVoiceDatasetToken(path.dataset_token.clone());
let is_mod = user_session.can_ban_users;
let query_results = list_samples(
let query_results = list_dataset_samples_for_dataset_token(
&dataset_token,
is_mod,
&server_state.mysql_pool
@@ -96,12 +108,13 @@ pub async fn list_samples_by_dataset_handler(
let samples = samples
.into_iter()
.map(|sample| ZsSampleRecordForResponse {
sample_token: sample.token.to_string(),
dataset_token: sample.dataset_token.to_string(),
maybe_creator_user_token: match sample.maybe_creator_user_token {
Some(user_token) => Some(user_token.to_string()),
None => None,
},
sample_token: sample.sample_token,
media_file_token: sample.media_file_token,
media_type: sample.media_type,
public_bucket_directory_hash: sample.public_bucket_directory_hash,
maybe_public_bucket_prefix: sample.maybe_public_bucket_prefix,
maybe_public_bucket_extension: sample.maybe_public_bucket_extension,
maybe_creator_user_token: sample.maybe_creator_user_token,
created_at: sample.created_at,
updated_at: sample.updated_at,
})
+11 -5
View File
@@ -64,8 +64,9 @@ use redis_caching::redis_ttl_cache::RedisTtlCache;
use reusable_types::server_environment::ServerEnvironment;
use twitch_common::twitch_secrets::TwitchSecrets;
use url_config::third_party_url_redirector::ThirdPartyUrlRedirector;
use users_component::cookies::anonymous_visitor_tracking::avt_cookie_manager::AvtCookieManager;
use users_component::cookies::session::session_cookie_manager::SessionCookieManager;
use users_component::utils::session_checker::SessionChecker;
use users_component::utils::session_cookie_manager::SessionCookieManager;
use crate::billing::internal_product_to_stripe_lookup_impl::InternalProductToStripeLookupImpl;
use crate::billing::internal_session_cache_purge_impl::InternalSessionCachePurgeImpl;
@@ -90,6 +91,7 @@ pub const RESERVED_SUBSTRINGS : &str = include_str!("../../../../../includes/bin
pub mod billing;
pub mod configs;
pub mod cookies;
pub mod http_server;
pub mod memory_cache;
pub mod model;
@@ -239,9 +241,11 @@ async fn main() -> AnyhowResult<()> {
let website_homepage_redirect =
easyenv::get_env_string_or_default("WEBSITE_HOMEPAGE_REDIRECT", "https://vo.codes/");
let cookie_manager = SessionCookieManager::new(&cookie_domain, &hmac_secret);
let session_cookie_manager = SessionCookieManager::new(&cookie_domain, &hmac_secret);
let avt_cookie_manager = AvtCookieManager::new(&cookie_domain, &hmac_secret)?;
let session_checker = SessionChecker::new_with_cache(
&cookie_manager,
&session_cookie_manager,
redis_ttl_cache.clone(),
);
@@ -456,7 +460,8 @@ async fn main() -> AnyhowResult<()> {
},
firehose_publisher,
badge_granter,
cookie_manager,
avt_cookie_manager,
session_cookie_manager,
session_checker,
private_bucket_client,
public_bucket_client,
@@ -633,7 +638,8 @@ pub async fn serve(server_state: ServerState) -> AnyhowResult<()>
.app_data(web::Data::new(server_state_arc.redis_pool.clone()))
.app_data(web::Data::new(server_state_arc.redis_ttl_cache.clone()))
.app_data(web::Data::new(server_state_arc.session_checker.clone()))
.app_data(web::Data::new(server_state_arc.cookie_manager.clone()))
.app_data(web::Data::new(server_state_arc.avt_cookie_manager.clone()))
.app_data(web::Data::new(server_state_arc.session_cookie_manager.clone()))
.app_data(web::Data::new(server_state_arc.stripe.clone().config.clone()))
.app_data(web::Data::new(server_state_arc.stripe.clone().client.clone()))
.app_data(web::Data::new(server_state_arc.third_party_url_redirector.clone()))
@@ -15,8 +15,9 @@ use mysql_queries::queries::w2l::w2l_templates::list_w2l_templates::W2lTemplateR
use redis_caching::redis_ttl_cache::RedisTtlCache;
use reusable_types::server_environment::ServerEnvironment;
use url_config::third_party_url_redirector::ThirdPartyUrlRedirector;
use users_component::cookies::anonymous_visitor_tracking::avt_cookie_manager::AvtCookieManager;
use users_component::cookies::session::session_cookie_manager::SessionCookieManager;
use users_component::utils::session_checker::SessionChecker;
use users_component::utils::session_cookie_manager::SessionCookieManager;
use crate::http_server::endpoints::categories::tts::list_fully_computed_assigned_tts_categories::list_fully_computed_assigned_tts_categories::ModelTokensByCategoryToken;
use crate::http_server::endpoints::leaderboard::get_leaderboard::LeaderboardInfo;
@@ -58,7 +59,8 @@ pub struct ServerState {
pub redis_rate_limiters: RedisRateLimiters,
pub cookie_manager: SessionCookieManager,
pub session_cookie_manager: SessionCookieManager,
pub avt_cookie_manager: AvtCookieManager,
pub session_checker: SessionChecker,
@@ -25,7 +25,7 @@ redis_common = { path = "../../../lib/redis_common" }
twitch_common = { path = "../../../lib/api_clients/twitch_common" }
# External
anyhow = "1.0.40"
anyhow = "1.0.75"
base64 = "0.21.0"
chrono = { version = "0.4.19", features = ["serde"] }
derive_more = "0.99.14"
@@ -112,8 +112,8 @@ toml = "0.5.8"
pulldown-cmark = "0.8.0"
# Temporary (just to ask for oauth credentials during dev)
rpassword = "5.0.1"
rpassword = "7.2.0"
[build-dependencies]
# NB: Only using diesel for migrations support
diesel = { version = "1.4.6", features = ["mysql"] }
diesel = { version = "2.1.3", features = ["mysql"] }