diff --git a/Cargo.lock b/Cargo.lock
index a0a2f74..acb5e29 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index cc67599..2d80bc3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/migrations/20211001071311_survey_users.sql b/migrations/20211001071311_survey_users.sql
index 20dfbd4..027bab1 100644
--- a/migrations/20211001071311_survey_users.sql
+++ b/migrations/20211001071311_survey_users.sql
@@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS survey_users (
ID UUID PRIMARY KEY NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL
-)
+);
diff --git a/migrations/20211011041634_survey_admins.sql b/migrations/20211011041634_survey_admins.sql
new file mode 100644
index 0000000..c5bfada
--- /dev/null
+++ b/migrations/20211011041634_survey_admins.sql
@@ -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
+)
diff --git a/sqlx-data.json b/sqlx-data.json
index 5c5d8a6..0c26f94 100644
--- a/sqlx-data.json
+++ b/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": []
+ }
}
}
\ No newline at end of file
diff --git a/src/api/v1/auth.rs b/src/api/v1/auth.rs
index f127482..50b80c5 100644
--- a/src/api/v1/auth.rs
+++ b/src/api/v1/auth.rs
@@ -14,72 +14,221 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-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 {
- 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,
+ }
- 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 {
+ 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 {
- let uuid = runners::register_runner(&data).await?;
- id.remember(uuid.to_string());
+async fn register(
+ payload: web::Json,
+ data: AppData,
+) -> ServiceResult {
+ 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,
+ data: AppData,
+) -> ServiceResult {
+ 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()
+}
+
diff --git a/src/api/v1/bench.rs b/src/api/v1/bench.rs
index 712a4c2..97a3761 100644
--- a/src/api/v1/bench.rs
+++ b/src/api/v1/bench.rs
@@ -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 {
+ 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 {
+ let uuid = runners::register_runner(&data).await?;
+ id.remember(uuid.to_string());
+ Ok(HttpResponse::Ok())
+}
+
+
#[derive(Serialize, Deserialize)]
struct Bench {
duration: f32,
diff --git a/src/api/v1/challenges.rs b/src/api/v1/challenges.rs
new file mode 100644
index 0000000..e4466e3
--- /dev/null
+++ b/src/api/v1/challenges.rs
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 Aravinth Manivannan
+ *
+ * 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 .
+ */
+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 {
+ 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 {
+ let uuid = runners::add_runner(&data).await?;
+ id.remember(uuid.to_string());
+ Ok(HttpResponse::Ok())
+}
diff --git a/src/data.rs b/src/data.rs
index a86d86e..6ed9bd5 100644
--- a/src/data.rs
+++ b/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 {
+ 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)
}
diff --git a/src/errors.rs b/src/errors.rs
index 38ed977..3eb6f07 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -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 for ServiceError {
}
}
+impl From 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 for ServiceError {
#[cfg(not(tarpaulin_include))]
diff --git a/src/main.rs b/src/main.rs
index de27168..3446863 100644
--- a/src/main.rs
+++ b/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 {
+pub fn get_survey_identity_service() -> IdentityService {
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 {
+ 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);
diff --git a/src/tests.rs b/src/tests.rs
index 098af8a..c462ea1 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2021 Aravinth Manivannan
+ *
+ * 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 .
+ */
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, 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, 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(
- 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,
- 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,
- 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,
- cookies: Cookie<'_>,
-) -> Vec {
- 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,
-) -> 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,
- 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, 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, 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(
+// 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,
+// 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,
+// 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,
+// cookies: Cookie<'_>,
+//) -> Vec {
+// 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,
+//) -> 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,
+// 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
+//}