admin authentication
This commit is contained in:
parent
71199aa9ce
commit
d2c4e9a06b
12 changed files with 1089 additions and 264 deletions
359
Cargo.lock
generated
359
Cargo.lock
generated
|
@ -91,12 +91,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"regex",
|
||||
"serde 1.0.130",
|
||||
"sha-1",
|
||||
"smallvec",
|
||||
"time",
|
||||
"time 0.2.27",
|
||||
"tokio",
|
||||
"zstd",
|
||||
]
|
||||
|
@ -112,7 +112,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"serde 1.0.130",
|
||||
"serde_json",
|
||||
"time",
|
||||
"time 0.2.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -242,7 +242,7 @@ dependencies = [
|
|||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"time",
|
||||
"time 0.2.27",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -346,7 +346,7 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.3",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
@ -360,6 +360,43 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ammonia"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e445c26125ff80316eaea16e812d717b147b82a68682bd4730f74d4845c8b35"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"lazy_static",
|
||||
"maplit",
|
||||
"markup5ever_rcdom",
|
||||
"matches",
|
||||
"tendril",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2-creds"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/realaravinth/argon2-creds?branch=master#2a3df16a6148ac1f48121b87232f24975f45a9c0"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"derive_builder",
|
||||
"derive_more",
|
||||
"lazy_static",
|
||||
"rand 0.8.4",
|
||||
"regex",
|
||||
"rust-argon2",
|
||||
"unicode-normalization",
|
||||
"validator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
|
@ -430,6 +467,17 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
|
@ -561,6 +609,12 @@ version = "0.4.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -578,10 +632,10 @@ dependencies = [
|
|||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"time",
|
||||
"time 0.2.27",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -903,6 +957,16 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
|
||||
dependencies = [
|
||||
"mac",
|
||||
"new_debug_unreachable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.17"
|
||||
|
@ -1018,6 +1082,17 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
|
@ -1026,7 +1101,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1129,6 +1204,20 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.5"
|
||||
|
@ -1278,7 +1367,7 @@ dependencies = [
|
|||
"log",
|
||||
"pow_sha256",
|
||||
"pretty_env_logger",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"redis",
|
||||
"serde 1.0.130",
|
||||
"serde_json",
|
||||
|
@ -1327,6 +1416,44 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever_rcdom"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"markup5ever",
|
||||
"tendril",
|
||||
"xml5ever",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
|
@ -1404,6 +1531,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -1627,6 +1760,44 @@ dependencies = [
|
|||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.8"
|
||||
|
@ -1694,6 +1865,12 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
|
@ -1764,6 +1941,20 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
|
@ -1771,9 +1962,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.3",
|
||||
"rand_hc 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1783,7 +1984,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1792,7 +2002,16 @@ version = "0.6.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1801,7 +2020,16 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
|
@ -1819,7 +2047,7 @@ dependencies = [
|
|||
"itoa",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"sha1",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -1841,7 +2069,7 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.3",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
|
@ -1877,6 +2105,18 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.2.0"
|
||||
|
@ -2152,6 +2392,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.4"
|
||||
|
@ -2234,7 +2480,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"rustls",
|
||||
"serde 1.0.130",
|
||||
"serde_json",
|
||||
|
@ -2245,7 +2491,7 @@ dependencies = [
|
|||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time",
|
||||
"time 0.2.27",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"uuid",
|
||||
|
@ -2353,6 +2599,31 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde 1.0.130",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache_codegen"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
|
@ -2387,6 +2658,7 @@ dependencies = [
|
|||
"actix-service",
|
||||
"actix-web",
|
||||
"actix-web-codegen 0.5.0-beta.4 (git+https://github.com/realaravinth/actix-web)",
|
||||
"argon2-creds",
|
||||
"cache-buster",
|
||||
"config",
|
||||
"derive_builder",
|
||||
|
@ -2399,7 +2671,7 @@ dependencies = [
|
|||
"mime_guess",
|
||||
"openssl",
|
||||
"pretty_env_logger",
|
||||
"rand",
|
||||
"rand 0.8.4",
|
||||
"rust-embed",
|
||||
"sailfish",
|
||||
"serde 1.0.130",
|
||||
|
@ -2422,6 +2694,17 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tendril"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9ef557cb397a4f0a5a3a628f06515f78563f2209e64d47055d9dc6052bf5e33"
|
||||
dependencies = [
|
||||
"futf",
|
||||
"mac",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
|
@ -2451,6 +2734,16 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.27"
|
||||
|
@ -2670,13 +2963,19 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2745,6 +3044,12 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
|
@ -2875,6 +3180,18 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"time 0.1.43",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
|
|
@ -37,6 +37,8 @@ futures = "0.3.15"
|
|||
|
||||
sqlx = { version = "0.5.5", features = [ "runtime-actix-rustls", "postgres", "time", "offline" ] }
|
||||
|
||||
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
|
||||
|
||||
derive_builder = "0.10"
|
||||
validator = { version = "0.14", features = ["derive"]}
|
||||
derive_more = "0.99"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
CREATE TABLE IF NOT EXISTS survey_users (
|
||||
ID UUID PRIMARY KEY NOT NULL UNIQUE,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
)
|
||||
);
|
||||
|
|
17
migrations/20211011041634_survey_admins.sql
Normal file
17
migrations/20211011041634_survey_admins.sql
Normal file
|
@ -0,0 +1,17 @@
|
|||
-- Add migration script here
|
||||
CREATE TABLE IF NOT EXISTS survey_admins (
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
email VARCHAR(100) UNIQUE DEFAULT NULL,
|
||||
email_verified BOOLEAN DEFAULT NULL,
|
||||
secret varchar(50) NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
ID SERIAL PRIMARY KEY NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS survey_challenges (
|
||||
ID UUID PRIMARY KEY NOT NULL UNIQUE,
|
||||
user_id INTEGER NOT NULL references survey_admins(ID) ON DELETE CASCADE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
difficulties INTEGER[] NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
)
|
154
sqlx-data.json
154
sqlx-data.json
|
@ -1,7 +1,110 @@
|
|||
{
|
||||
"db": "PostgreSQL",
|
||||
"0d27c6787e145f980063f008266ec00bd96f5a761d6f23bbe2bdc63a5163ba69": {
|
||||
"query": "SELECT ID FROM survey_responses \n WHERE user_id = $1 AND device_software_recognised = $2",
|
||||
"07dd9f4c2edd99714b3de90365fdae4f874c66a736c308df6e668ef9b86737dc": {
|
||||
"query": "INSERT INTO survey_responses (\n user_id, \n device_user_provided,\n device_software_recognised,\n threads\n ) VALUES ($1, $2, $3, $4);",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"0d22134cc5076304b7895827f006ee8269cc500f400114a7472b83f0f1c568b5": {
|
||||
"query": "INSERT INTO survey_admins \n (name , password, secret) VALUES ($1, $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"1373df097fa0e58b23a374753318ae53a44559aa0e7eb64680185baf1c481723": {
|
||||
"query": "SELECT password FROM survey_admins WHERE name = ($1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"19686bfe8772cbc6831d46d18994e2b9aa40c7181eae9a31e51451cce95f04e8": {
|
||||
"query": "SELECT name, password FROM survey_admins WHERE email = ($1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"1b7e17bfc949fa97e8dec1f95e35a02bcf3aa1aa72a1f6f6c8884e885fc3b953": {
|
||||
"query": "insert into survey_admins \n (name , password, email, secret) values ($1, $2, $3, $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"43b3e771f38bf8059832169227705be06a28925af1b3799ffef5371d511fd138": {
|
||||
"query": "\n INSERT INTO survey_users (created_at, id) VALUES($1, $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Timestamptz",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"8320dda2b3e107d1451fdfb35eb2a4b8e97364e7b1b74ffe4d6913faf132fb61": {
|
||||
"query": "SELECT ID \n FROM survey_responses \n WHERE \n user_id = $1 \n AND \n device_software_recognised = $2;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -21,37 +124,8 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"b50210741c2fa689a8bd7f211a650c6616178f927c2c9320e5b6e25dc7c3cc1a": {
|
||||
"query": "INSERT INTO survey_response_tokens (resp_id, user_id, id)\n VALUES ($1, $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Uuid",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"cbf1bac274a6a1b1339615b406b9127c4f314080450e7bd5a5652f5febfb0d21": {
|
||||
"query": "INSERT INTO survey_responses (user_id, device_user_provided, device_software_recognised,\n threads) VALUES ($1, $2, $3, $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"d958feaec808739d8fddc2d6a3b560fae2e5aa496c44b24a6f70e73ebb291568": {
|
||||
"query": "INSERT INTO survey_benches \n (resp_id, difficulty, duration) VALUES ($1, $2, $3)",
|
||||
"a721cfa249acf328c2f29c4cf8c2aeba1a635bcf49d18ced5474caa10b7cae4f": {
|
||||
"query": "INSERT INTO survey_benches \n (resp_id, difficulty, duration) \n VALUES ($1, $2, $3);",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
|
@ -63,5 +137,19 @@
|
|||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"fcdc5fe5d496eb516c805e64ec96d9626b74ab33cd6e75e5a08ae88967403b72": {
|
||||
"query": "INSERT INTO survey_response_tokens \n (resp_id, user_id, id)\n VALUES ($1, $2, $3);",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Uuid",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,72 +14,221 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::borrow::Cow;
|
||||
|
||||
use actix_identity::Identity;
|
||||
use actix_web::http::header;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::get_uuid;
|
||||
use super::get_random;
|
||||
use crate::errors::*;
|
||||
use crate::AppData;
|
||||
|
||||
pub mod routes {
|
||||
pub struct Auth {
|
||||
pub logout: &'static str,
|
||||
pub login: &'static str,
|
||||
pub register: &'static str,
|
||||
}
|
||||
|
||||
impl Auth {
|
||||
pub const fn new() -> Auth {
|
||||
let login = "/api/v1/signin";
|
||||
let logout = "/logout";
|
||||
let register = "/api/v1/signup";
|
||||
Auth { register }
|
||||
Auth {
|
||||
logout,
|
||||
login,
|
||||
register,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runners {
|
||||
// use std::borrow::Cow;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn register_runner(data: &AppData) -> ServiceResult<uuid::Uuid> {
|
||||
let mut uuid;
|
||||
let now = OffsetDateTime::now_utc();
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Register {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub confirm_password: String,
|
||||
pub email: Option<String>,
|
||||
}
|
||||
|
||||
loop {
|
||||
uuid = get_uuid();
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Login {
|
||||
// login accepts both username and email under "username field"
|
||||
// TODO update all instances where login is used
|
||||
pub login: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
let res = sqlx::query!(
|
||||
"
|
||||
INSERT INTO survey_users (created_at, id) VALUES($1, $2)",
|
||||
&now,
|
||||
&uuid
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Password {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
/// returns Ok(()) when everything checks out and the user is authenticated. Erros otherwise
|
||||
pub async fn login_runner(payload: &Login, data: &AppData) -> ServiceResult<String> {
|
||||
use argon2_creds::Config;
|
||||
use sqlx::Error::RowNotFound;
|
||||
|
||||
let verify = |stored: &str, received: &str| {
|
||||
if Config::verify(stored, received)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ServiceError::WrongPassword)
|
||||
}
|
||||
};
|
||||
|
||||
if payload.login.contains('@') {
|
||||
#[derive(Clone, Debug)]
|
||||
struct EmailLogin {
|
||||
name: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
let email_fut = sqlx::query_as!(
|
||||
EmailLogin,
|
||||
r#"SELECT name, password FROM survey_admins WHERE email = ($1)"#,
|
||||
&payload.login,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match email_fut {
|
||||
Ok(s) => {
|
||||
verify(&s.password, &payload.password)?;
|
||||
Ok(s.name)
|
||||
}
|
||||
|
||||
Err(RowNotFound) => Err(ServiceError::AccountNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
} else {
|
||||
let username_fut = sqlx::query_as!(
|
||||
Password,
|
||||
r#"SELECT password FROM survey_admins WHERE name = ($1)"#,
|
||||
&payload.login,
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match username_fut {
|
||||
Ok(s) => {
|
||||
verify(&s.password, &payload.password)?;
|
||||
Ok(payload.login.clone())
|
||||
}
|
||||
Err(RowNotFound) => Err(ServiceError::AccountNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn register_runner(
|
||||
payload: &Register,
|
||||
data: &AppData,
|
||||
) -> ServiceResult<()> {
|
||||
if !crate::SETTINGS.allow_registration {
|
||||
return Err(ServiceError::ClosedForRegistration);
|
||||
}
|
||||
|
||||
if payload.password != payload.confirm_password {
|
||||
return Err(ServiceError::PasswordsDontMatch);
|
||||
}
|
||||
let username = data.creds.username(&payload.username)?;
|
||||
let hash = data.creds.password(&payload.password)?;
|
||||
|
||||
if let Some(email) = &payload.email {
|
||||
data.creds.email(email)?;
|
||||
}
|
||||
|
||||
let mut secret;
|
||||
|
||||
loop {
|
||||
secret = get_random(32);
|
||||
let res;
|
||||
if let Some(email) = &payload.email {
|
||||
res = sqlx::query!(
|
||||
"insert into survey_admins
|
||||
(name , password, email, secret) values ($1, $2, $3, $4)",
|
||||
&username,
|
||||
&hash,
|
||||
&email,
|
||||
&secret,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await;
|
||||
} else {
|
||||
res = sqlx::query!(
|
||||
"INSERT INTO survey_admins
|
||||
(name , password, secret) VALUES ($1, $2, $3)",
|
||||
&username,
|
||||
&hash,
|
||||
&secret,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await;
|
||||
}
|
||||
if res.is_ok() {
|
||||
break;
|
||||
} else if let Err(sqlx::Error::Database(err)) = res {
|
||||
if err.code() == Some(Cow::from("23505"))
|
||||
&& err.message().contains("survey_users_id_key")
|
||||
{
|
||||
continue;
|
||||
if err.code() == Some(Cow::from("23505")) {
|
||||
let msg = err.message();
|
||||
if msg.contains("survey_admins_name_key") {
|
||||
return Err(ServiceError::UsernameTaken);
|
||||
} else if msg.contains("survey_admins_email_key") {
|
||||
return Err(ServiceError::EmailTaken);
|
||||
} else if msg.contains("survey_admins_secret_key") {
|
||||
continue;
|
||||
} else {
|
||||
return Err(ServiceError::InternalServerError);
|
||||
}
|
||||
} else {
|
||||
return Err(sqlx::Error::Database(err).into());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(uuid)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(register);
|
||||
cfg.service(login);
|
||||
cfg.service(signout);
|
||||
}
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.register")]
|
||||
async fn register(data: AppData, id: Identity) -> ServiceResult<impl Responder> {
|
||||
let uuid = runners::register_runner(&data).await?;
|
||||
id.remember(uuid.to_string());
|
||||
async fn register(
|
||||
payload: web::Json<runners::Register>,
|
||||
data: AppData,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
runners::register_runner(&payload, &data).await?;
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.login")]
|
||||
async fn login(
|
||||
id: Identity,
|
||||
payload: web::Json<runners::Login>,
|
||||
data: AppData,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
let payload = payload.into_inner();
|
||||
let username = runners::login_runner(&payload, &data).await?;
|
||||
id.remember(username);
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.auth.logout", wrap = "crate::CheckLogin")]
|
||||
async fn signout(id: Identity) -> impl Responder {
|
||||
if id.identity().is_some() {
|
||||
id.forget();
|
||||
}
|
||||
HttpResponse::Found()
|
||||
.append_header((header::LOCATION, crate::middleware::auth::AUTH))
|
||||
.finish()
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use actix_identity::Identity;
|
|||
use actix_web::{web, HttpResponse, Responder};
|
||||
use futures::future::try_join_all;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::get_uuid;
|
||||
|
@ -30,20 +31,69 @@ use crate::AppData;
|
|||
pub mod routes {
|
||||
pub struct Benches {
|
||||
pub submit: &'static str,
|
||||
pub register: &'static str,
|
||||
pub scope: &'static str,
|
||||
}
|
||||
|
||||
impl Benches {
|
||||
pub const fn new() -> Benches {
|
||||
let submit = "/api/v1/benches";
|
||||
Benches { submit }
|
||||
let submit = "/api/v1/benches/submit";
|
||||
let register = "/api/v1/benches/register";
|
||||
let scope = "/api/v1/benches/";
|
||||
Benches { submit, register, scope }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(submit);
|
||||
cfg.service(register);
|
||||
}
|
||||
|
||||
|
||||
pub mod runners {
|
||||
use super::*;
|
||||
|
||||
pub async fn register_runner(data: &AppData) -> ServiceResult<uuid::Uuid> {
|
||||
let mut uuid;
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
loop {
|
||||
uuid = get_uuid();
|
||||
|
||||
let res = sqlx::query!(
|
||||
"
|
||||
INSERT INTO survey_users (created_at, id) VALUES($1, $2)",
|
||||
&now,
|
||||
&uuid
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await;
|
||||
|
||||
if res.is_ok() {
|
||||
break;
|
||||
} else if let Err(sqlx::Error::Database(err)) = res {
|
||||
if err.code() == Some(Cow::from("23505"))
|
||||
&& err.message().contains("survey_users_id_key")
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
return Err(sqlx::Error::Database(err).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.benches.register")]
|
||||
async fn register(data: AppData, id: Identity) -> ServiceResult<impl Responder> {
|
||||
let uuid = runners::register_runner(&data).await?;
|
||||
id.remember(uuid.to_string());
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Bench {
|
||||
duration: f32,
|
||||
|
|
85
src/api/v1/challenges.rs
Normal file
85
src/api/v1/challenges.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::borrow::Cow;
|
||||
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
|
||||
use super::get_uuid;
|
||||
use crate::errors::*;
|
||||
use crate::AppData;
|
||||
|
||||
pub mod routes {
|
||||
pub struct Challenges {
|
||||
pub add: &'static str,
|
||||
}
|
||||
|
||||
impl Challenges {
|
||||
pub const fn new() -> Challenges {
|
||||
let add = "/api/v1/challenges/add";
|
||||
Challenges { add }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runners {
|
||||
// use std::borrow::Cow;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn add_runner(data: &AppData) -> ServiceResult<uuid::Uuid> {
|
||||
let mut uuid;
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
loop {
|
||||
uuid = get_uuid();
|
||||
|
||||
let res = sqlx::query!(
|
||||
"
|
||||
INSERT INTO survey_users (created_at, id) VALUES($1, $2)",
|
||||
&now,
|
||||
&uuid
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await;
|
||||
|
||||
if res.is_ok() {
|
||||
break;
|
||||
} else if let Err(sqlx::Error::Database(err)) = res {
|
||||
if err.code() == Some(Cow::from("23505"))
|
||||
&& err.message().contains("survey_users_id_key")
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
return Err(sqlx::Error::Database(err).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(add);
|
||||
}
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.add")]
|
||||
async fn add(data: AppData, id: Identity) -> ServiceResult<impl Responder> {
|
||||
let uuid = runners::add_runner(&data).await?;
|
||||
id.remember(uuid.to_string());
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
26
src/data.rs
26
src/data.rs
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
//! App data: database connections, etc.
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::PgPool;
|
||||
use argon2_creds::{Config, ConfigBuilder, PasswordPolicy};
|
||||
|
||||
use crate::SETTINGS;
|
||||
|
||||
|
@ -26,19 +28,41 @@ use crate::SETTINGS;
|
|||
pub struct Data {
|
||||
/// databse pool
|
||||
pub db: PgPool,
|
||||
pub creds: Config,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn get_creds() -> Config {
|
||||
ConfigBuilder::default()
|
||||
.username_case_mapped(true)
|
||||
.profanity(true)
|
||||
.blacklist(true)
|
||||
.password_policy(PasswordPolicy::default())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
/// create new instance of app data
|
||||
pub async fn new() -> Arc<Self> {
|
||||
let creds = Self::get_creds();
|
||||
let c = creds.clone();
|
||||
#[allow(unused_variables)]
|
||||
let init = thread::spawn(move || {
|
||||
log::info!("Initializing credential manager");
|
||||
c.init();
|
||||
log::info!("Initialized credential manager");
|
||||
});
|
||||
|
||||
let db = PgPoolOptions::new()
|
||||
.max_connections(SETTINGS.database.pool)
|
||||
.connect(&SETTINGS.database.url)
|
||||
.await
|
||||
.expect("Unable to form database pool");
|
||||
|
||||
let data = Data { db };
|
||||
#[cfg(not(debug_assertions))]
|
||||
init.join().unwrap();
|
||||
let data = Data { db, creds };
|
||||
|
||||
Arc::new(data)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
use std::convert::From;
|
||||
|
||||
use actix::MailboxError;
|
||||
use argon2_creds::errors::CredsError;
|
||||
|
||||
use actix_web::{
|
||||
error::ResponseError,
|
||||
http::{header, StatusCode},
|
||||
|
@ -39,15 +41,48 @@ pub enum ServiceError {
|
|||
#[display(fmt = "Username not available")]
|
||||
UsernameTaken,
|
||||
|
||||
#[display(
|
||||
fmt = "This server is is closed for registration. Contact admin if this is unexpecter"
|
||||
)]
|
||||
ClosedForRegistration,
|
||||
|
||||
/// email is already taken
|
||||
#[display(fmt = "Email not available")]
|
||||
EmailTaken,
|
||||
|
||||
#[display(fmt = "The value you entered for email is not an email")] //405j
|
||||
NotAnEmail,
|
||||
|
||||
/// when the a token name is already taken
|
||||
/// token not found
|
||||
#[display(fmt = "Token not found. Is token registered?")]
|
||||
TokenNotFound,
|
||||
|
||||
#[display(fmt = "Wrong password")]
|
||||
WrongPassword,
|
||||
#[display(fmt = "Account not found")]
|
||||
AccountNotFound,
|
||||
|
||||
/// when the value passed contains profainity
|
||||
#[display(fmt = "Can't allow profanity in usernames")]
|
||||
ProfainityError,
|
||||
/// when the value passed contains blacklisted words
|
||||
/// see [blacklist](https://github.com/shuttlecraft/The-Big-Username-Blacklist)
|
||||
#[display(fmt = "Username contains blacklisted words")]
|
||||
BlacklistError,
|
||||
/// when the value passed contains characters not present
|
||||
/// in [UsernameCaseMapped](https://tools.ietf.org/html/rfc8265#page-7)
|
||||
/// profile
|
||||
#[display(fmt = "username_case_mapped violation")]
|
||||
UsernameCaseMappedError,
|
||||
|
||||
#[display(fmt = "Passsword too short")]
|
||||
PasswordTooShort,
|
||||
#[display(fmt = "Username too long")]
|
||||
PasswordTooLong,
|
||||
#[display(fmt = "Passwords don't match")]
|
||||
PasswordsDontMatch,
|
||||
|
||||
#[display(fmt = "{}", _0)]
|
||||
CaptchaError(CaptchaError),
|
||||
}
|
||||
|
@ -76,10 +111,22 @@ impl ResponseError for ServiceError {
|
|||
#[cfg(not(tarpaulin_include))]
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
ServiceError::ClosedForRegistration => StatusCode::FORBIDDEN,
|
||||
ServiceError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
|
||||
ServiceError::UsernameTaken => StatusCode::BAD_REQUEST,
|
||||
ServiceError::EmailTaken => StatusCode::BAD_REQUEST,
|
||||
ServiceError::NotAnEmail => StatusCode::BAD_REQUEST,
|
||||
ServiceError::WrongPassword => StatusCode::UNAUTHORIZED,
|
||||
ServiceError::AccountNotFound => StatusCode::NOT_FOUND,
|
||||
|
||||
ServiceError::ProfainityError => StatusCode::BAD_REQUEST,
|
||||
ServiceError::BlacklistError => StatusCode::BAD_REQUEST,
|
||||
ServiceError::UsernameCaseMappedError => StatusCode::BAD_REQUEST,
|
||||
|
||||
ServiceError::PasswordTooShort => StatusCode::BAD_REQUEST,
|
||||
ServiceError::PasswordTooLong => StatusCode::BAD_REQUEST,
|
||||
ServiceError::PasswordsDontMatch => StatusCode::BAD_REQUEST,
|
||||
|
||||
ServiceError::TokenNotFound => StatusCode::NOT_FOUND,
|
||||
ServiceError::CaptchaError(e) => {
|
||||
|
@ -111,6 +158,21 @@ impl From<sqlx::Error> for ServiceError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<CredsError> for ServiceError {
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn from(e: CredsError) -> ServiceError {
|
||||
match e {
|
||||
CredsError::UsernameCaseMappedError => ServiceError::UsernameCaseMappedError,
|
||||
CredsError::ProfainityError => ServiceError::ProfainityError,
|
||||
CredsError::BlacklistError => ServiceError::BlacklistError,
|
||||
CredsError::NotAnEmail => ServiceError::NotAnEmail,
|
||||
CredsError::Argon2Error(_) => ServiceError::InternalServerError,
|
||||
CredsError::PasswordTooLong => ServiceError::PasswordTooLong,
|
||||
CredsError::PasswordTooShort => ServiceError::PasswordTooShort,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl From<RecvError> for ServiceError {
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -103,6 +103,7 @@ async fn main() -> std::io::Result<()> {
|
|||
.header("Permissions-Policy", "interest-cohort=()"),
|
||||
)
|
||||
.wrap(get_identity_service())
|
||||
.wrap(get_survey_identity_service())
|
||||
.wrap(actix_middleware::NormalizePath::new(
|
||||
actix_middleware::TrailingSlash::Trim,
|
||||
))
|
||||
|
@ -124,18 +125,32 @@ pub fn get_json_err() -> JsonConfig {
|
|||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn get_identity_service() -> IdentityService<CookieIdentityPolicy> {
|
||||
pub fn get_survey_identity_service() -> IdentityService<CookieIdentityPolicy> {
|
||||
let cookie_secret = &SETTINGS.server.cookie_secret;
|
||||
IdentityService::new(
|
||||
CookieIdentityPolicy::new(cookie_secret.as_bytes())
|
||||
.name("survey-id")
|
||||
//TODO change cookie age
|
||||
.max_age_secs(216000)
|
||||
.path(V1_API_ROUTES.benches.scope)
|
||||
.max_age_secs(30 * 60)
|
||||
.domain(&SETTINGS.server.domain)
|
||||
.secure(false),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn get_identity_service() -> IdentityService<CookieIdentityPolicy> {
|
||||
let cookie_secret = &SETTINGS.server.cookie_secret;
|
||||
IdentityService::new(
|
||||
CookieIdentityPolicy::new(cookie_secret.as_bytes())
|
||||
.path("/admin")
|
||||
.name("survey-auth")
|
||||
.max_age_secs(60 * 24)
|
||||
.domain(&SETTINGS.server.domain)
|
||||
.secure(false),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
//pages::services(cfg);
|
||||
api::v1::services(cfg);
|
||||
|
|
372
src/tests.rs
372
src/tests.rs
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::cookie::Cookie;
|
||||
|
@ -81,181 +97,181 @@ macro_rules! get_app {
|
|||
};
|
||||
}
|
||||
|
||||
/// register and signin utility
|
||||
pub async fn register_and_signin(
|
||||
name: &str,
|
||||
email: &str,
|
||||
password: &str,
|
||||
) -> (Arc<data::Data>, Login, ServiceResponse) {
|
||||
register(name, email, password).await;
|
||||
signin(name, password).await
|
||||
}
|
||||
|
||||
/// register utility
|
||||
pub async fn register(name: &str, email: &str, password: &str) {
|
||||
let data = Data::new().await;
|
||||
let app = get_app!(data).await;
|
||||
|
||||
// 1. Register
|
||||
let msg = Register {
|
||||
username: name.into(),
|
||||
password: password.into(),
|
||||
confirm_password: password.into(),
|
||||
email: Some(email.into()),
|
||||
};
|
||||
let resp =
|
||||
test::call_service(&app, post_request!(&msg, ROUTES.auth.register).to_request())
|
||||
.await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
/// signin util
|
||||
pub async fn signin(name: &str, password: &str) -> (Arc<Data>, Login, ServiceResponse) {
|
||||
let data = Data::new().await;
|
||||
let app = get_app!(data.clone()).await;
|
||||
|
||||
// 2. signin
|
||||
let creds = Login {
|
||||
login: name.into(),
|
||||
password: password.into(),
|
||||
};
|
||||
let signin_resp =
|
||||
test::call_service(&app, post_request!(&creds, ROUTES.auth.login).to_request())
|
||||
.await;
|
||||
assert_eq!(signin_resp.status(), StatusCode::OK);
|
||||
(data, creds, signin_resp)
|
||||
}
|
||||
|
||||
/// pub duplicate test
|
||||
pub async fn bad_post_req_test<T: Serialize>(
|
||||
name: &str,
|
||||
password: &str,
|
||||
url: &str,
|
||||
payload: &T,
|
||||
err: ServiceError,
|
||||
) {
|
||||
let (data, _, signin_resp) = signin(name, password).await;
|
||||
let cookies = get_cookie!(signin_resp);
|
||||
let app = get_app!(data).await;
|
||||
|
||||
let resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&payload, url)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), err.status_code());
|
||||
let resp_err: ErrorToResponse = test::read_body_json(resp).await;
|
||||
//println!("{}", txt.error);
|
||||
assert_eq!(resp_err.error, format!("{}", err));
|
||||
}
|
||||
|
||||
/// bad post req test without payload
|
||||
pub async fn bad_post_req_test_witout_payload(
|
||||
name: &str,
|
||||
password: &str,
|
||||
url: &str,
|
||||
err: ServiceError,
|
||||
) {
|
||||
let (data, _, signin_resp) = signin(name, password).await;
|
||||
let cookies = get_cookie!(signin_resp);
|
||||
let app = get_app!(data).await;
|
||||
|
||||
let resp = test::call_service(
|
||||
&app,
|
||||
post_request!(url).cookie(cookies.clone()).to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), err.status_code());
|
||||
let resp_err: ErrorToResponse = test::read_body_json(resp).await;
|
||||
//println!("{}", txt.error);
|
||||
assert_eq!(resp_err.error, format!("{}", err));
|
||||
}
|
||||
|
||||
pub async fn create_new_campaign(
|
||||
campaign_name: &str,
|
||||
data: Arc<Data>,
|
||||
cookies: Cookie<'_>,
|
||||
) -> CreateResp {
|
||||
let new = CreateReq {
|
||||
name: campaign_name.into(),
|
||||
};
|
||||
|
||||
let app = get_app!(data).await;
|
||||
let new_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&new, crate::V1_API_ROUTES.campaign.new)
|
||||
.cookie(cookies)
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(new_resp.status(), StatusCode::OK);
|
||||
let uuid: CreateResp = test::read_body_json(new_resp).await;
|
||||
uuid
|
||||
}
|
||||
|
||||
pub async fn delete_campaign(
|
||||
camapign: &CreateResp,
|
||||
data: Arc<Data>,
|
||||
cookies: Cookie<'_>,
|
||||
) {
|
||||
let del_route = V1_API_ROUTES.campaign.get_delete_route(&camapign.uuid);
|
||||
let app = get_app!(data).await;
|
||||
let del_resp =
|
||||
test::call_service(&app, post_request!(&del_route).cookie(cookies).to_request())
|
||||
.await;
|
||||
assert_eq!(del_resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
pub async fn list_campaings(
|
||||
data: Arc<Data>,
|
||||
cookies: Cookie<'_>,
|
||||
) -> Vec<ListCampaignResp> {
|
||||
let app = get_app!(data).await;
|
||||
let list_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(crate::V1_API_ROUTES.campaign.list)
|
||||
.cookie(cookies)
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(list_resp.status(), StatusCode::OK);
|
||||
test::read_body_json(list_resp).await
|
||||
}
|
||||
|
||||
pub async fn add_feedback(
|
||||
rating: &RatingReq,
|
||||
campaign: &CreateResp,
|
||||
data: Arc<Data>,
|
||||
) -> RatingResp {
|
||||
let add_feedback_route = V1_API_ROUTES.feedback.add_feedback_route(&campaign.uuid);
|
||||
let app = get_app!(data).await;
|
||||
let add_feedback_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&rating, &add_feedback_route).to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(add_feedback_resp.status(), StatusCode::OK);
|
||||
|
||||
test::read_body_json(add_feedback_resp).await
|
||||
}
|
||||
|
||||
pub async fn get_feedback(
|
||||
campaign: &CreateResp,
|
||||
data: Arc<Data>,
|
||||
cookies: Cookie<'_>,
|
||||
) -> GetFeedbackResp {
|
||||
let get_feedback_route = V1_API_ROUTES.campaign.get_feedback_route(&campaign.uuid);
|
||||
let app = get_app!(data).await;
|
||||
|
||||
let get_feedback_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&get_feedback_route)
|
||||
.cookie(cookies)
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(get_feedback_resp.status(), StatusCode::OK);
|
||||
test::read_body_json(get_feedback_resp).await
|
||||
}
|
||||
///// register and signin utility
|
||||
//pub async fn register_and_signin(
|
||||
// name: &str,
|
||||
// email: &str,
|
||||
// password: &str,
|
||||
//) -> (Arc<data::Data>, Login, ServiceResponse) {
|
||||
// register(name, email, password).await;
|
||||
// signin(name, password).await
|
||||
//}
|
||||
//
|
||||
///// register utility
|
||||
//pub async fn register(name: &str, email: &str, password: &str) {
|
||||
// let data = Data::new().await;
|
||||
// let app = get_app!(data).await;
|
||||
//
|
||||
// // 1. Register
|
||||
// let msg = Register {
|
||||
// username: name.into(),
|
||||
// password: password.into(),
|
||||
// confirm_password: password.into(),
|
||||
// email: Some(email.into()),
|
||||
// };
|
||||
// let resp =
|
||||
// test::call_service(&app, post_request!(&msg, ROUTES.auth.register).to_request())
|
||||
// .await;
|
||||
// assert_eq!(resp.status(), StatusCode::OK);
|
||||
//}
|
||||
//
|
||||
///// signin util
|
||||
//pub async fn signin(name: &str, password: &str) -> (Arc<Data>, Login, ServiceResponse) {
|
||||
// let data = Data::new().await;
|
||||
// let app = get_app!(data.clone()).await;
|
||||
//
|
||||
// // 2. signin
|
||||
// let creds = Login {
|
||||
// login: name.into(),
|
||||
// password: password.into(),
|
||||
// };
|
||||
// let signin_resp =
|
||||
// test::call_service(&app, post_request!(&creds, ROUTES.auth.login).to_request())
|
||||
// .await;
|
||||
// assert_eq!(signin_resp.status(), StatusCode::OK);
|
||||
// (data, creds, signin_resp)
|
||||
//}
|
||||
//
|
||||
///// pub duplicate test
|
||||
//pub async fn bad_post_req_test<T: Serialize>(
|
||||
// name: &str,
|
||||
// password: &str,
|
||||
// url: &str,
|
||||
// payload: &T,
|
||||
// err: ServiceError,
|
||||
//) {
|
||||
// let (data, _, signin_resp) = signin(name, password).await;
|
||||
// let cookies = get_cookie!(signin_resp);
|
||||
// let app = get_app!(data).await;
|
||||
//
|
||||
// let resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(&payload, url)
|
||||
// .cookie(cookies.clone())
|
||||
// .to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(resp.status(), err.status_code());
|
||||
// let resp_err: ErrorToResponse = test::read_body_json(resp).await;
|
||||
// //println!("{}", txt.error);
|
||||
// assert_eq!(resp_err.error, format!("{}", err));
|
||||
//}
|
||||
//
|
||||
///// bad post req test without payload
|
||||
//pub async fn bad_post_req_test_witout_payload(
|
||||
// name: &str,
|
||||
// password: &str,
|
||||
// url: &str,
|
||||
// err: ServiceError,
|
||||
//) {
|
||||
// let (data, _, signin_resp) = signin(name, password).await;
|
||||
// let cookies = get_cookie!(signin_resp);
|
||||
// let app = get_app!(data).await;
|
||||
//
|
||||
// let resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(url).cookie(cookies.clone()).to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(resp.status(), err.status_code());
|
||||
// let resp_err: ErrorToResponse = test::read_body_json(resp).await;
|
||||
// //println!("{}", txt.error);
|
||||
// assert_eq!(resp_err.error, format!("{}", err));
|
||||
//}
|
||||
//
|
||||
//pub async fn create_new_campaign(
|
||||
// campaign_name: &str,
|
||||
// data: Arc<Data>,
|
||||
// cookies: Cookie<'_>,
|
||||
//) -> CreateResp {
|
||||
// let new = CreateReq {
|
||||
// name: campaign_name.into(),
|
||||
// };
|
||||
//
|
||||
// let app = get_app!(data).await;
|
||||
// let new_resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(&new, crate::V1_API_ROUTES.campaign.new)
|
||||
// .cookie(cookies)
|
||||
// .to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(new_resp.status(), StatusCode::OK);
|
||||
// let uuid: CreateResp = test::read_body_json(new_resp).await;
|
||||
// uuid
|
||||
//}
|
||||
//
|
||||
//pub async fn delete_campaign(
|
||||
// camapign: &CreateResp,
|
||||
// data: Arc<Data>,
|
||||
// cookies: Cookie<'_>,
|
||||
//) {
|
||||
// let del_route = V1_API_ROUTES.campaign.get_delete_route(&camapign.uuid);
|
||||
// let app = get_app!(data).await;
|
||||
// let del_resp =
|
||||
// test::call_service(&app, post_request!(&del_route).cookie(cookies).to_request())
|
||||
// .await;
|
||||
// assert_eq!(del_resp.status(), StatusCode::OK);
|
||||
//}
|
||||
//
|
||||
//pub async fn list_campaings(
|
||||
// data: Arc<Data>,
|
||||
// cookies: Cookie<'_>,
|
||||
//) -> Vec<ListCampaignResp> {
|
||||
// let app = get_app!(data).await;
|
||||
// let list_resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(crate::V1_API_ROUTES.campaign.list)
|
||||
// .cookie(cookies)
|
||||
// .to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(list_resp.status(), StatusCode::OK);
|
||||
// test::read_body_json(list_resp).await
|
||||
//}
|
||||
//
|
||||
//pub async fn add_feedback(
|
||||
// rating: &RatingReq,
|
||||
// campaign: &CreateResp,
|
||||
// data: Arc<Data>,
|
||||
//) -> RatingResp {
|
||||
// let add_feedback_route = V1_API_ROUTES.feedback.add_feedback_route(&campaign.uuid);
|
||||
// let app = get_app!(data).await;
|
||||
// let add_feedback_resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(&rating, &add_feedback_route).to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(add_feedback_resp.status(), StatusCode::OK);
|
||||
//
|
||||
// test::read_body_json(add_feedback_resp).await
|
||||
//}
|
||||
//
|
||||
//pub async fn get_feedback(
|
||||
// campaign: &CreateResp,
|
||||
// data: Arc<Data>,
|
||||
// cookies: Cookie<'_>,
|
||||
//) -> GetFeedbackResp {
|
||||
// let get_feedback_route = V1_API_ROUTES.campaign.get_feedback_route(&campaign.uuid);
|
||||
// let app = get_app!(data).await;
|
||||
//
|
||||
// let get_feedback_resp = test::call_service(
|
||||
// &app,
|
||||
// post_request!(&get_feedback_route)
|
||||
// .cookie(cookies)
|
||||
// .to_request(),
|
||||
// )
|
||||
// .await;
|
||||
// assert_eq!(get_feedback_resp.status(), StatusCode::OK);
|
||||
// test::read_body_json(get_feedback_resp).await
|
||||
//}
|
||||
|
|
Loading…
Reference in a new issue