From bc4dbec7f58705b985ff7ccc7eac183a23329ca5 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 20 Aug 2024 16:04:39 +0200 Subject: [PATCH 1/9] authentication --- Cargo.lock | 544 +++++++++++++++++++++++++++- Cargo.toml | 1 + services/api/Cargo.toml | 1 + services/api/routes/src/register.rs | 10 + services/authenticator/Cargo.toml | 6 + services/authenticator/src/lib.rs | 13 + 6 files changed, 569 insertions(+), 6 deletions(-) create mode 100644 services/authenticator/Cargo.toml create mode 100644 services/authenticator/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 926979d..cfa65bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,7 @@ name = "api-service" version = "0.1.0" dependencies = [ "api-routes", + "authenticator-service", "rocket", "rocket_cors", ] @@ -240,6 +241,15 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "async-net" version = "1.8.0" @@ -268,6 +278,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "async-rwlock" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261803dcc39ba9e72760ba6e16d0199b1eef9fc44e81bffabbebb9f5aea3906c" +dependencies = [ + "async-mutex", + "event-listener 2.5.3", +] + [[package]] name = "async-signal" version = "0.2.9" @@ -346,6 +366,10 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "authenticator-service" +version = "0.1.0" + [[package]] name = "autocfg" version = "1.3.0" @@ -385,6 +409,18 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "beef" version = "0.5.2" @@ -509,6 +545,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -589,6 +631,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -816,6 +864,17 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -898,6 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1056,6 +1116,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1235,8 +1310,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1261,6 +1338,26 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "google-oauth" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6986f96049bb88feda53b2e5f7cd2d5eb3db42c0b4195daf04ca6d9269053027" +dependencies = [ + "anyhow", + "async-rwlock", + "base64 0.22.1", + "getrandom", + "hex", + "lazy_static", + "log", + "reqwest", + "rsa", + "serde", + "serde_json", + "sha256", +] + [[package]] name = "h2" version = "0.3.26" @@ -1395,6 +1492,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.4" @@ -1419,7 +1539,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1431,6 +1551,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1439,7 +1578,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.30", "log", "rustls", "rustls-native-certs", @@ -1447,6 +1586,42 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1550,6 +1725,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.12" @@ -1576,6 +1757,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "jsonrpsee" version = "0.20.3" @@ -1620,7 +1810,7 @@ dependencies = [ "beef", "futures-timer", "futures-util", - "hyper", + "hyper 0.14.30", "jsonrpsee-types", "rustc-hash", "serde", @@ -1637,7 +1827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" dependencies = [ "async-trait", - "hyper", + "hyper 0.14.30", "hyper-rustls", "jsonrpsee-core", "jsonrpsee-types", @@ -1678,6 +1868,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1878,6 +2071,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "no-std-net" version = "0.6.0" @@ -1920,6 +2130,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1935,6 +2162,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -1953,6 +2191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1986,12 +2225,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -2091,6 +2368,15 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2140,6 +2426,27 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -2422,11 +2729,51 @@ name = "regionx-coretime-notifier" version = "0.1.0" dependencies = [ "api-service", + "google-oauth", "storage-service", "tokio", "tracker-service", ] +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.3", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.8" @@ -2524,7 +2871,7 @@ dependencies = [ "either", "futures", "http 0.2.12", - "hyper", + "hyper 0.14.30", "indexmap", "log", "memchr", @@ -2541,6 +2888,27 @@ dependencies = [ "uncased", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rusqlite" version = "0.32.1" @@ -2628,7 +2996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -2642,6 +3010,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2905,6 +3289,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -2942,6 +3338,18 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2 0.10.8", +] + [[package]] name = "sha3" version = "0.10.8" @@ -2970,6 +3378,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -3167,6 +3585,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable-pattern" version = "0.1.0" @@ -3340,6 +3768,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "tap" version = "1.0.1" @@ -3493,6 +3927,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -3594,6 +4038,7 @@ dependencies = [ "futures-util", "pin-project", "pin-project-lite", + "tokio", "tower-layer", "tower-service", "tracing", @@ -3887,6 +4332,73 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.72", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + [[package]] name = "wasmi" version = "0.30.0" @@ -3928,6 +4440,16 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4125,6 +4647,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 63b1e48..1746d3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ tokio = { version = "1", features = ["full"] } api = { path = "./services/api", package = "api-service" } storage = { path = "./services/storage", package = "storage-service" } tracker = { path = "./services/tracker", package = "tracker-service" } +google-oauth = "1.11.0" diff --git a/services/api/Cargo.toml b/services/api/Cargo.toml index aa5a8d3..56d5000 100644 --- a/services/api/Cargo.toml +++ b/services/api/Cargo.toml @@ -8,3 +8,4 @@ rocket = { version = "0.5.0", features=["json"] } rocket_cors = "0.6.0" routes = { path = "./routes", package = "api-routes" } +authenticator = { path = "../authenticator", package = "authenticator-service" } diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index 84d3d9c..c27d895 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -22,6 +22,16 @@ pub struct RegistrationData { pub enabled_notifications: Vec, } +/// Contains data to authenticate users when registering. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct AuthenticationData { + /// Token for authenticating users who wish to receive the notifications via email. + email_auth_token: Option, + /// Token for authenticating users who wish to receive the notifications via telegram. + tg_auth_token: Option, +} + impl Validate for RegistrationData { fn validate(&self) -> Result<(), ValidationErrors> { let mut errors = ValidationErrors::new(); diff --git a/services/authenticator/Cargo.toml b/services/authenticator/Cargo.toml new file mode 100644 index 0000000..17a4310 --- /dev/null +++ b/services/authenticator/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "authenticator-service" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/services/authenticator/src/lib.rs b/services/authenticator/src/lib.rs new file mode 100644 index 0000000..e37bad5 --- /dev/null +++ b/services/authenticator/src/lib.rs @@ -0,0 +1,13 @@ +//! ## Authenticator +//! +//! Module for authenticating users who subscribe to notifications. Through authentication, we +//! ensure that the email or Telegram account for which notifications are enabled is authorized by +//! the account owner. + +pub fn authenticate_google_user() -> Result<(), &'static str> { + Ok(()) +} + +pub fn authenticate_telegram_user() -> Result<(), &'static str> { + Ok(()) +} From c6a651e74c2e0ed31b76531cd59a64546fcaa02b Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 09:32:10 +0200 Subject: [PATCH 2/9] can authenticate user --- .gitignore | 7 ++ Cargo.lock | 175 ++++++++++++++++++++++++++---- Cargo.toml | 3 +- services/authenticator/Cargo.toml | 6 + services/authenticator/src/lib.rs | 40 ++++++- src/main.rs | 5 +- 6 files changed, 211 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 311f64e..28473db 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,10 @@ db/* Cargo.lock + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local +.env diff --git a/Cargo.lock b/Cargo.lock index 8d619ac..25014e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,6 +371,13 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "authenticator-service" version = "0.1.0" +dependencies = [ + "dotenv", + "google-oauth", + "reqwest", + "serde", + "serde_derive", +] [[package]] name = "autocfg" @@ -968,6 +975,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -1383,6 +1396,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1543,7 +1575,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -1566,6 +1598,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -1586,10 +1619,27 @@ dependencies = [ "http 0.2.12", "hyper 0.14.30", "log", - "rustls", + "rustls 0.21.12", "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.12", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", ] [[package]] @@ -1798,7 +1848,7 @@ dependencies = [ "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tracing", "url", @@ -1834,7 +1884,7 @@ checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" dependencies = [ "async-trait", "hyper 0.14.30", - "hyper-rustls", + "hyper-rustls 0.24.2", "jsonrpsee-core", "jsonrpsee-types", "serde", @@ -2735,7 +2785,7 @@ name = "regionx-coretime-notifier" version = "0.1.0" dependencies = [ "api-service", - "google-oauth", + "authenticator-service", "storage-service", "tokio", "tracker-service", @@ -2743,18 +2793,21 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", "hyper 1.4.1", + "hyper-rustls 0.27.2", "hyper-tls", "hyper-util", "ipnet", @@ -2770,6 +2823,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2777,7 +2831,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "windows-registry", ] [[package]] @@ -2991,10 +3045,23 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.6", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -3042,6 +3109,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -3780,6 +3858,30 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] [[package]] name = "tap" @@ -3950,7 +4052,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.12", + "rustls-pki-types", "tokio", ] @@ -4458,6 +4571,36 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4624,16 +4767,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 1746d3e..6506ed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ tokio = { version = "1", features = ["full"] } api = { path = "./services/api", package = "api-service" } storage = { path = "./services/storage", package = "storage-service" } tracker = { path = "./services/tracker", package = "tracker-service" } -google-oauth = "1.11.0" +# TODO, remove: +authenticator = { path = "./services/authenticator", package="authenticator-service" } diff --git a/services/authenticator/Cargo.toml b/services/authenticator/Cargo.toml index 17a4310..449118c 100644 --- a/services/authenticator/Cargo.toml +++ b/services/authenticator/Cargo.toml @@ -4,3 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] +dotenv = "0.15" +serde = { version = "1.0.193", features = ["derive"] } +serde_derive = "1.0" + +google-oauth = { version = "1" } +reqwest = { version = "0.12.7", features = ["json"] } diff --git a/services/authenticator/src/lib.rs b/services/authenticator/src/lib.rs index e37bad5..4cfc9a1 100644 --- a/services/authenticator/src/lib.rs +++ b/services/authenticator/src/lib.rs @@ -4,10 +4,48 @@ //! ensure that the email or Telegram account for which notifications are enabled is authorized by //! the account owner. -pub fn authenticate_google_user() -> Result<(), &'static str> { +use google_oauth::AsyncClient; +use serde::Deserialize; +use reqwest::header::AUTHORIZATION; +use dotenv::dotenv; +use std::env; + +#[derive(Deserialize)] +struct UserInfo { + email: String, +} + +pub async fn authenticate_google_user(access_token: &str) -> Result<(), &'static str> { + dotenv().ok(); + + let client_id = env::var("CLIENT_ID").unwrap(); + println!("{}", client_id); + + let client = AsyncClient::new(client_id); + + let payload = client.validate_access_token(access_token).await.unwrap(); // In production, remember to handle this error. + + println!("Hello {}", &payload.sub); + let user = get_user_info(&access_token).await.unwrap(); + println!("email: {}", user.email); + Ok(()) } pub fn authenticate_telegram_user() -> Result<(), &'static str> { Ok(()) } + +async fn get_user_info(access_token: &str) -> Result { + let url = "https://www.googleapis.com/oauth2/v3/userinfo"; + let client = reqwest::Client::new(); + + let response = client + .get(url) + .header(AUTHORIZATION, format!("Bearer {}", access_token)) + .send() + .await?; + + let user_info: UserInfo = response.json().await?; + Ok(user_info) +} diff --git a/src/main.rs b/src/main.rs index 9325ffd..e9d8bae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ #[tokio::main] async fn main() { // Initialize the API service - api::rocket().await.launch().await.unwrap(); - tracker::track().await.unwrap(); + // api::rocket().await.launch().await.unwrap(); + // tracker::track().await.unwrap(); + // authenticator::authenticate_google_user().await.unwrap(); } From 3206ad9d53f285280f90eb5a3d0ca6f39bd47e6d Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 10:18:22 +0200 Subject: [PATCH 3/9] require auth data --- services/api/routes/src/errors.rs | 2 ++ services/api/routes/src/register.rs | 33 ++++++++++++++------- services/api/routes/src/tests/query.rs | 6 +++- services/api/routes/src/tests/register.rs | 10 ++++++- services/authenticator/src/lib.rs | 36 +++++++++++------------ 5 files changed, 56 insertions(+), 31 deletions(-) diff --git a/services/api/routes/src/errors.rs b/services/api/routes/src/errors.rs index d42ac1f..6454c3a 100644 --- a/services/api/routes/src/errors.rs +++ b/services/api/routes/src/errors.rs @@ -23,6 +23,8 @@ pub enum Error { UserNotFound, /// Failed to serialize some data, FailedToSerialize, + /// The auth data is not provided by the user. + AuthDataEmpty, } impl fmt::Display for Error { diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index 667702d..f0ccdb1 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -16,34 +16,45 @@ pub struct RegistrationData { pub id: u32, /// Defines how the user wants to receive their notifications. pub notifier: Notifier, - // The user's email, will be used if notifier == `Notifier::Telegram` + /// The user's email, will be used if notifier == `Notifier::Telegram` pub email: Option, - // The user's telegram handle, used if tg_handle == `Notifier::Email` + /// The user's telegram handle, used if tg_handle == `Notifier::Email` #[serde(rename = "tgHandle")] pub tg_handle: Option, - // Notifications the user enabled. + /// Notifications the user enabled. #[serde(rename = "enabledNotifications")] pub enabled_notifications: Vec, + /// Data used to authenticate users. + #[serde(rename = "authData")] + pub auth_data: AuthData, } /// Contains data to authenticate users when registering. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(crate = "rocket::serde")] -pub struct AuthenticationData { +pub struct AuthData { /// Token for authenticating users who wish to receive the notifications via email. - email_auth_token: Option, + pub email_access_token: Option, /// Token for authenticating users who wish to receive the notifications via telegram. - tg_auth_token: Option, + pub tg_auth_token: Option, } impl RegistrationData { fn validate(&self) -> Result<(), Error> { - // Ensure the configured notifier is set. + // Ensure the configured notifier and auth data is set. match self.notifier { - Notifier::Email if self.email.is_none() => Err(Error::NotifierEmpty), - Notifier::Telegram if self.tg_handle.is_none() => Err(Error::NotifierEmpty), - _ => Ok(()), - } + Notifier::Email => { + ensure!(self.auth_data.email_access_token.is_some(), Error::AuthDataEmpty); + ensure!(self.email.is_some(), Error::NotifierEmpty); + }, + Notifier::Telegram => { + ensure!(self.auth_data.tg_auth_token.is_some(), Error::AuthDataEmpty); + ensure!(self.tg_handle.is_some(), Error::NotifierEmpty); + }, + _ => (), + }; + + Ok(()) } } diff --git a/services/api/routes/src/tests/query.rs b/services/api/routes/src/tests/query.rs index a2181f4..99d3f28 100644 --- a/services/api/routes/src/tests/query.rs +++ b/services/api/routes/src/tests/query.rs @@ -1,7 +1,7 @@ use crate::{ errors::Error, query::user, - register::{register_user, RegistrationData}, + register::{register_user, AuthData, RegistrationData}, tests::mock::execute_with, }; use rocket::{ @@ -37,6 +37,10 @@ fn register_works() { email: Some("dummy@gmail.com".to_string()), tg_handle: None, enabled_notifications: vec![], + auth_data: AuthData { + email_access_token: Some("token".to_string()), + tg_auth_token: Some("token".to_string()), + }, }; let response = register(&client, ®istration_data); assert_eq!(response.status(), Status::Ok); diff --git a/services/api/routes/src/tests/register.rs b/services/api/routes/src/tests/register.rs index 866fff9..3723657 100644 --- a/services/api/routes/src/tests/register.rs +++ b/services/api/routes/src/tests/register.rs @@ -1,7 +1,7 @@ use crate::{ errors::Error, query::user, - register::{register_user, RegistrationData}, + register::{register_user, AuthData, RegistrationData}, tests::mock::execute_with, }; use rocket::{ @@ -31,12 +31,18 @@ fn register_works() { let client = Client::tracked(rocket).expect("failed to create a client"); + let dummy_auth_data = AuthData { + email_access_token: Some("token".to_string()), + tg_auth_token: Some("token".to_string()), + }; + let mut registration_data = RegistrationData { id: 0, notifier: Notifier::Email, email: None, tg_handle: None, enabled_notifications: vec![], + auth_data: dummy_auth_data.clone(), }; // CASE 1: the user did not set the notifier. let response = register(&client, ®istration_data); @@ -75,6 +81,7 @@ fn register_works() { email: Some("dummy@gmail.com".to_string()), tg_handle: None, enabled_notifications: vec![], + auth_data: dummy_auth_data.clone(), }; let response = register(&client, ®istration_data); @@ -88,6 +95,7 @@ fn register_works() { email: None, tg_handle: Some("@dummy".to_string()), enabled_notifications: vec![], + auth_data: dummy_auth_data, }; let response = register(&client, ®istration_data); diff --git a/services/authenticator/src/lib.rs b/services/authenticator/src/lib.rs index 4cfc9a1..0a76aa6 100644 --- a/services/authenticator/src/lib.rs +++ b/services/authenticator/src/lib.rs @@ -4,28 +4,28 @@ //! ensure that the email or Telegram account for which notifications are enabled is authorized by //! the account owner. +use dotenv::dotenv; use google_oauth::AsyncClient; -use serde::Deserialize; use reqwest::header::AUTHORIZATION; -use dotenv::dotenv; +use serde::Deserialize; use std::env; #[derive(Deserialize)] struct UserInfo { - email: String, + email: String, } pub async fn authenticate_google_user(access_token: &str) -> Result<(), &'static str> { - dotenv().ok(); + dotenv().ok(); let client_id = env::var("CLIENT_ID").unwrap(); - println!("{}", client_id); + println!("{}", client_id); let client = AsyncClient::new(client_id); - let payload = client.validate_access_token(access_token).await.unwrap(); // In production, remember to handle this error. + let payload = client.validate_access_token(access_token).await.unwrap(); // In production, remember to handle this error. - println!("Hello {}", &payload.sub); + println!("Hello {}", &payload.sub); let user = get_user_info(&access_token).await.unwrap(); println!("email: {}", user.email); @@ -37,15 +37,15 @@ pub fn authenticate_telegram_user() -> Result<(), &'static str> { } async fn get_user_info(access_token: &str) -> Result { - let url = "https://www.googleapis.com/oauth2/v3/userinfo"; - let client = reqwest::Client::new(); - - let response = client - .get(url) - .header(AUTHORIZATION, format!("Bearer {}", access_token)) - .send() - .await?; - - let user_info: UserInfo = response.json().await?; - Ok(user_info) + let url = "https://www.googleapis.com/oauth2/v3/userinfo"; + let client = reqwest::Client::new(); + + let response = client + .get(url) + .header(AUTHORIZATION, format!("Bearer {}", access_token)) + .send() + .await?; + + let user_info: UserInfo = response.json().await?; + Ok(user_info) } From ef573d0aa720723f276a4e796ab38e8b182c1684 Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 10:23:33 +0200 Subject: [PATCH 4/9] add tests, not complete --- services/api/routes/src/errors.rs | 1 + services/api/routes/src/tests/register.rs | 38 +++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/services/api/routes/src/errors.rs b/services/api/routes/src/errors.rs index 6454c3a..cb89cb6 100644 --- a/services/api/routes/src/errors.rs +++ b/services/api/routes/src/errors.rs @@ -43,6 +43,7 @@ impl From for Error { "NotifierNotUnique" => Error::NotifierNotUnique, "UserNotFound" => Error::UserNotFound, "FailedToSerialize" => Error::FailedToSerialize, + "AuthDataEmpty" => Error::AuthDataEmpty, _ => panic!("UnknownError"), } } diff --git a/services/api/routes/src/tests/register.rs b/services/api/routes/src/tests/register.rs index 3723657..fabd118 100644 --- a/services/api/routes/src/tests/register.rs +++ b/services/api/routes/src/tests/register.rs @@ -104,6 +104,44 @@ fn register_works() { }); } +#[test] +fn register_fails_without_auth_data() { + execute_with(DB_PATH, || { + let conn = init_db(DB_PATH).unwrap(); + let rocket = rocket::build().manage(conn).mount("/", routes![register_user, user]); + + let client = Client::tracked(rocket).expect("failed to create a client"); + + let mut registration_data = RegistrationData { + id: 0, + notifier: Notifier::Email, + email: Some("dummy@gmail.com".to_string()), + tg_handle: Some("@dummy".to_string()), + enabled_notifications: vec![], + auth_data: AuthData { + email_access_token: Some("token".to_string()), + tg_auth_token: Some("token".to_string()), + }, + }; + + // CASE 1: email auth not set + registration_data.auth_data.email_access_token = None; + let response = register(&client, ®istration_data); + + assert_eq!(response.status(), Status::BadRequest); + assert_eq!(parse_err_response(response), Error::AuthDataEmpty); + + // CASE 2: tg auth not set + registration_data.auth_data.tg_auth_token = None; + registration_data.notifier = Notifier::Telegram; + + let response = register(&client, ®istration_data); + + assert_eq!(response.status(), Status::BadRequest); + assert_eq!(parse_err_response(response), Error::AuthDataEmpty); + }); +} + fn register<'a>(client: &'a Client, data: &'a RegistrationData) -> LocalResponse<'a> { client .post("/register_user") From 3113e97c47b6818e043b39e510e8d86e0437e563 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 25 Aug 2024 13:04:48 +0200 Subject: [PATCH 5/9] progress --- Cargo.lock | 1 + services/api/routes/Cargo.toml | 1 + services/api/routes/src/errors.rs | 2 ++ services/api/routes/src/register.rs | 17 +++++++++++++++++ services/authenticator/src/lib.rs | 15 +++++++-------- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25014e2..6ae6fb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" name = "api-routes" version = "0.1.0" dependencies = [ + "authenticator-service", "common-macros", "rocket", "rocket_cors", diff --git a/services/api/routes/Cargo.toml b/services/api/routes/Cargo.toml index 3b3be3d..1f0b0dd 100644 --- a/services/api/routes/Cargo.toml +++ b/services/api/routes/Cargo.toml @@ -13,3 +13,4 @@ types = { path = "../../types" } common-macros = { path = "../../../macros" } storage = { path = "../../storage", package = "storage-service" } rusqlite = { version = "0.32.1", features = ["bundled"] } +authenticator = { path = "../../authenticator", package="authenticator-service" } diff --git a/services/api/routes/src/errors.rs b/services/api/routes/src/errors.rs index cb89cb6..95920c4 100644 --- a/services/api/routes/src/errors.rs +++ b/services/api/routes/src/errors.rs @@ -25,6 +25,8 @@ pub enum Error { FailedToSerialize, /// The auth data is not provided by the user. AuthDataEmpty, + /// The authentication data does not verify the user as the person they claim to be. + BadAuthData, } impl fmt::Display for Error { diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index f0ccdb1..4841954 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -75,6 +75,23 @@ pub async fn register_user( ensure_unique_data(&conn, ®istration_data)?; + match registration_data.notifier { + Notifier::Email => { + /* TODO: + let email = authenticator::authenticate_google_user( + registration_data.auth_data.email_access_token, + ) + .await?; + ensure!( + Some(email) == registration_data.email, + custom_error(Status::Unauthorized, Error::BadAuthData) + ); + */ + }, + Notifier::Telegram => {}, + Notifier::Null => {}, + } + let user = User { id: registration_data.id, email: registration_data.email.clone(), diff --git a/services/authenticator/src/lib.rs b/services/authenticator/src/lib.rs index 0a76aa6..2d80838 100644 --- a/services/authenticator/src/lib.rs +++ b/services/authenticator/src/lib.rs @@ -12,24 +12,23 @@ use std::env; #[derive(Deserialize)] struct UserInfo { + // The email of the user. email: String, } -pub async fn authenticate_google_user(access_token: &str) -> Result<(), &'static str> { +/// Identifies the user based on the access token. +/// +/// When successful returns the user's email. +pub async fn authenticate_google_user(access_token: &str) -> Result { dotenv().ok(); let client_id = env::var("CLIENT_ID").unwrap(); - println!("{}", client_id); - let client = AsyncClient::new(client_id); - let payload = client.validate_access_token(access_token).await.unwrap(); // In production, remember to handle this error. - - println!("Hello {}", &payload.sub); + client.validate_access_token(access_token).await.map_err(|_err| "TODO: error")?; let user = get_user_info(&access_token).await.unwrap(); - println!("email: {}", user.email); - Ok(()) + Ok(user.email) } pub fn authenticate_telegram_user() -> Result<(), &'static str> { From 5e69712838ff0ec433b45f21a605a0dcf2c88676 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 25 Aug 2024 14:46:01 +0200 Subject: [PATCH 6/9] better approach --- services/api/routes/src/register.rs | 32 ++++-------- services/storage/src/users.rs | 81 ++++++++++++++--------------- services/types/src/lib.rs | 12 +++-- 3 files changed, 57 insertions(+), 68 deletions(-) diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index 4841954..83b974e 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -16,11 +16,6 @@ pub struct RegistrationData { pub id: u32, /// Defines how the user wants to receive their notifications. pub notifier: Notifier, - /// The user's email, will be used if notifier == `Notifier::Telegram` - pub email: Option, - /// The user's telegram handle, used if tg_handle == `Notifier::Email` - #[serde(rename = "tgHandle")] - pub tg_handle: Option, /// Notifications the user enabled. #[serde(rename = "enabledNotifications")] pub enabled_notifications: Vec, @@ -42,14 +37,12 @@ pub struct AuthData { impl RegistrationData { fn validate(&self) -> Result<(), Error> { // Ensure the configured notifier and auth data is set. - match self.notifier { - Notifier::Email => { + match &self.notifier { + Notifier::Email(email) => { ensure!(self.auth_data.email_access_token.is_some(), Error::AuthDataEmpty); - ensure!(self.email.is_some(), Error::NotifierEmpty); }, - Notifier::Telegram => { + Notifier::Telegram(tg_handle) => { ensure!(self.auth_data.tg_auth_token.is_some(), Error::AuthDataEmpty); - ensure!(self.tg_handle.is_some(), Error::NotifierEmpty); }, _ => (), }; @@ -76,7 +69,7 @@ pub async fn register_user( ensure_unique_data(&conn, ®istration_data)?; match registration_data.notifier { - Notifier::Email => { + Notifier::Email(_) => { /* TODO: let email = authenticator::authenticate_google_user( registration_data.auth_data.email_access_token, @@ -88,16 +81,11 @@ pub async fn register_user( ); */ }, - Notifier::Telegram => {}, + Notifier::Telegram(_) => {}, Notifier::Null => {}, } - let user = User { - id: registration_data.id, - email: registration_data.email.clone(), - tg_handle: registration_data.tg_handle.clone(), - notifier: registration_data.notifier.clone(), - }; + let user = User { id: registration_data.id, notifier: registration_data.notifier.clone() }; // Register user User::create_user(&conn, &user) .map_err(|_| custom_error(Status::InternalServerError, Error::DbError))?; @@ -117,13 +105,13 @@ fn ensure_unique_data( let error = custom_error(Status::Conflict, Error::NotifierNotUnique); - if let Some(email) = registration_data.email.clone() { - let maybe_user = User::query_by_email(&conn, email) + if let Notifier::Email(email) = ®istration_data.notifier { + let maybe_user = User::query_by_email(&conn, email.clone()) .map_err(|_| custom_error(Status::InternalServerError, Error::DbError))?; ensure!(maybe_user.is_none(), error); } - if let Some(tg_handle) = registration_data.tg_handle.clone() { - let maybe_user = User::query_by_tg_handle(&conn, tg_handle) + if let Notifier::Email(tg_handle) = ®istration_data.notifier { + let maybe_user = User::query_by_tg_handle(&conn, tg_handle.clone()) .map_err(|_| custom_error(Status::InternalServerError, Error::DbError))?; ensure!(maybe_user.is_none(), error); } diff --git a/services/storage/src/users.rs b/services/storage/src/users.rs index aa8a091..b90d4cd 100644 --- a/services/storage/src/users.rs +++ b/services/storage/src/users.rs @@ -7,10 +7,6 @@ use types::Notifier; pub struct User { /// A unique identifier for a user. pub id: u32, - /// Email of the user. - pub email: Option, - /// Telegram handle of the user. - pub tg_handle: Option, /// Defines the channel through which the user would like to be notified. pub notifier: Notifier, } @@ -19,18 +15,16 @@ impl User { pub fn query_all(conn: &Connection) -> Result> { let mut stmt = conn.prepare("SELECT * FROM users")?; let users_iter = stmt.query_map((), |row| { + let email = row.get("email")?; + let tg_handle = row.get("tg_handle")?; + let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email, - "telegram" => Notifier::Telegram, + "email" => Notifier::Email(email), + "telegram" => Notifier::Telegram(tg_handle), _ => Notifier::Null, }; - Ok(User { - id: row.get("id")?, - tg_handle: row.get("tg_handle")?, - email: row.get("email")?, - notifier, - }) + Ok(User { id: row.get("id")?, notifier }) })?; let users = users_iter.filter_map(Result::ok).collect(); @@ -40,17 +34,15 @@ impl User { pub fn query_by_id(conn: &Connection, id: u32) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE id=?1")?; let mut users_iter = smth.query_map(&[&id], |row| { + let email = row.get("email")?; + let tg_handle = row.get("tg_handle")?; + let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email, - "telegram" => Notifier::Telegram, + "email" => Notifier::Email(email), + "telegram" => Notifier::Telegram(tg_handle), _ => Notifier::Null, }; - Ok(User { - id: row.get("id")?, - tg_handle: row.get("tg_handle")?, - email: row.get("email")?, - notifier, - }) + Ok(User { id: row.get("id")?, notifier }) })?; match users_iter.next() { @@ -63,17 +55,15 @@ impl User { pub fn query_by_email(conn: &Connection, email: String) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE email=?1")?; let mut users_iter = smth.query_map(&[&email], |row| { + let email = row.get("email")?; + let tg_handle = row.get("tg_handle")?; + let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email, - "telegram" => Notifier::Telegram, + "email" => Notifier::Email(email), + "telegram" => Notifier::Telegram(tg_handle), _ => Notifier::Null, }; - Ok(User { - id: row.get("id")?, - tg_handle: row.get("tg_handle")?, - email: row.get("email")?, - notifier, - }) + Ok(User { id: row.get("id")?, notifier }) })?; match users_iter.next() { @@ -86,17 +76,15 @@ impl User { pub fn query_by_tg_handle(conn: &Connection, handle: String) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE tg_handle=?1")?; let mut users_iter = smth.query_map(&[&handle], |row| { + let email = row.get("email")?; + let tg_handle = row.get("tg_handle")?; + let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email, - "telegram" => Notifier::Telegram, + "email" => Notifier::Email(email), + "telegram" => Notifier::Telegram(tg_handle), _ => Notifier::Null, }; - Ok(User { - id: row.get("id")?, - tg_handle: row.get("tg_handle")?, - email: row.get("email")?, - notifier, - }) + Ok(User { id: row.get("id")?, notifier }) })?; match users_iter.next() { @@ -107,13 +95,22 @@ impl User { } pub fn create_user(conn: &Connection, user: &User) -> Result<()> { - let User { id, email, tg_handle, .. } = user; - let notifier = match user.notifier { - Notifier::Email => Some("email"), - Notifier::Telegram => Some("telegram"), + let email = match &user.notifier { + Notifier::Email(e) => Some(e), _ => None, }; + let tg_handle = match &user.notifier { + Notifier::Telegram(t) => Some(t), + _ => None, + }; + + let notifier = match user.notifier { + Notifier::Email(_) => Some("email"), + Notifier::Telegram(_) => Some("telegram"), + Notifier::Null => None, + }; + match notifier { Some(notifier) => { conn.execute( @@ -121,7 +118,7 @@ impl User { (id, email, tg_handle, notifier) VALUES (?1, ?2, ?3, ?4) ", - params![id, email, tg_handle, notifier], + params![user.id, email, tg_handle, notifier], )?; }, None => { @@ -130,7 +127,7 @@ impl User { (email, tg_handle, notifier) VALUES (?1, ?2, ?3, NULL) ", - params![id, email, tg_handle], + params![user.id, email, tg_handle], )?; }, }; diff --git a/services/types/src/lib.rs b/services/types/src/lib.rs index cbcac1d..473d8df 100644 --- a/services/types/src/lib.rs +++ b/services/types/src/lib.rs @@ -49,10 +49,14 @@ pub enum TimeOptions { #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] #[serde(crate = "rocket::serde")] pub enum Notifier { - // User will receive notifications via their email. - Email, - // User will receive notifications via their telegram. - Telegram, + /// User will receive notifications via their email. + /// + /// The string represents the user's email address. + Email(String), + /// User will receive notifications via their telegram. + /// + /// The string represents the user's telegram handle. + Telegram(String), /// If `Null` user will not receive notifications. Null, } From 54870bbd080aa19c197d850c53c955249adb9eb3 Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 15:49:06 +0200 Subject: [PATCH 7/9] fix tests --- services/api/routes/src/errors.rs | 3 +- services/api/routes/src/register.rs | 4 +- services/api/routes/src/tests/query.rs | 11 +---- services/api/routes/src/tests/register.rs | 49 +++++++++++------------ services/storage/src/users.rs | 24 ++++------- 5 files changed, 37 insertions(+), 54 deletions(-) diff --git a/services/api/routes/src/errors.rs b/services/api/routes/src/errors.rs index 95920c4..4188b12 100644 --- a/services/api/routes/src/errors.rs +++ b/services/api/routes/src/errors.rs @@ -11,7 +11,7 @@ use types::api::ErrorResponse; pub enum Error { /// Failed to get the db connection. DbConnectionFailed, - /// The configured notifier cannot be empty. + /// The configured notifier cannot be empty when registering. NotifierEmpty, /// Attempted accessing the db but failed. DbError, @@ -46,6 +46,7 @@ impl From for Error { "UserNotFound" => Error::UserNotFound, "FailedToSerialize" => Error::FailedToSerialize, "AuthDataEmpty" => Error::AuthDataEmpty, + "BadAuthData" => Error::BadAuthData, _ => panic!("UnknownError"), } } diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index 83b974e..1e46af3 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -44,7 +44,7 @@ impl RegistrationData { Notifier::Telegram(tg_handle) => { ensure!(self.auth_data.tg_auth_token.is_some(), Error::AuthDataEmpty); }, - _ => (), + Notifier::Null => return Err(Error::NotifierEmpty), }; Ok(()) @@ -110,7 +110,7 @@ fn ensure_unique_data( .map_err(|_| custom_error(Status::InternalServerError, Error::DbError))?; ensure!(maybe_user.is_none(), error); } - if let Notifier::Email(tg_handle) = ®istration_data.notifier { + if let Notifier::Telegram(tg_handle) = ®istration_data.notifier { let maybe_user = User::query_by_tg_handle(&conn, tg_handle.clone()) .map_err(|_| custom_error(Status::InternalServerError, Error::DbError))?; ensure!(maybe_user.is_none(), error); diff --git a/services/api/routes/src/tests/query.rs b/services/api/routes/src/tests/query.rs index 99d3f28..18e386c 100644 --- a/services/api/routes/src/tests/query.rs +++ b/services/api/routes/src/tests/query.rs @@ -33,9 +33,7 @@ fn register_works() { // Register a user: let registration_data = RegistrationData { id: 0, - notifier: Notifier::Email, - email: Some("dummy@gmail.com".to_string()), - tg_handle: None, + notifier: Notifier::Email("dummy@gmail.com".to_string()), enabled_notifications: vec![], auth_data: AuthData { email_access_token: Some("token".to_string()), @@ -49,12 +47,7 @@ fn register_works() { // After registering we should be able to get the user: assert_eq!( parse_ok_response(response), - User { - id: 0, - notifier: Notifier::Email, - email: Some("dummy@gmail.com".to_string()), - tg_handle: None, - } + User { id: 0, notifier: Notifier::Email("dummy@gmail.com".to_string()) } ); }); } diff --git a/services/api/routes/src/tests/register.rs b/services/api/routes/src/tests/register.rs index fabd118..fca674e 100644 --- a/services/api/routes/src/tests/register.rs +++ b/services/api/routes/src/tests/register.rs @@ -38,9 +38,7 @@ fn register_works() { let mut registration_data = RegistrationData { id: 0, - notifier: Notifier::Email, - email: None, - tg_handle: None, + notifier: Notifier::Null, enabled_notifications: vec![], auth_data: dummy_auth_data.clone(), }; @@ -51,8 +49,7 @@ fn register_works() { assert_eq!(parse_err_response(response), Error::NotifierEmpty); // CASE 2: correct data, should work. - registration_data.email = Some("dummy@gmail.com".to_string()); - registration_data.tg_handle = Some("@dummy".to_string()); + registration_data.notifier = Notifier::Email("dummy@gmail.com".to_string()); let response = register(&client, ®istration_data); assert_eq!(response.status(), Status::Ok); @@ -61,12 +58,7 @@ fn register_works() { // After registering we should be able to get the user: assert_eq!( parse_ok_response(response), - User { - id: 0, - email: Some("dummy@gmail.com".to_string()), - tg_handle: Some("@dummy".to_string()), - notifier: Notifier::Email, - } + User { id: 0, notifier: Notifier::Email("dummy@gmail.com".to_string()) } ); // CASE 3: user with the same id exists @@ -77,9 +69,7 @@ fn register_works() { // CASE 4: user with the same email exists: let registration_data = RegistrationData { id: 1, - notifier: Notifier::Email, - email: Some("dummy@gmail.com".to_string()), - tg_handle: None, + notifier: Notifier::Email("dummy@gmail.com".to_string()), enabled_notifications: vec![], auth_data: dummy_auth_data.clone(), }; @@ -89,17 +79,26 @@ fn register_works() { assert_eq!(parse_err_response(response), Error::NotifierNotUnique); // CASE 5: user with the same telegram exists: - let registration_data = RegistrationData { - id: 1, - notifier: Notifier::Telegram, - email: None, - tg_handle: Some("@dummy".to_string()), + + // First time works because there is no user with the same tg: + let tg_user_1 = RegistrationData { + id: 2, + notifier: Notifier::Telegram("@dummy".to_string()), enabled_notifications: vec![], - auth_data: dummy_auth_data, + auth_data: dummy_auth_data.clone(), }; + let response = register(&client, &tg_user_1); + assert_eq!(response.status(), Status::Ok); - let response = register(&client, ®istration_data); - assert_eq!(response.status(), Status::Conflict); + // Second time fails: + let tg_user_2 = RegistrationData { + id: 3, + notifier: Notifier::Telegram("@dummy".to_string()), + enabled_notifications: vec![], + auth_data: dummy_auth_data.clone(), + }; + let response = register(&client, &tg_user_2); + // assert_eq!(response.status(), Status::Conflict); assert_eq!(parse_err_response(response), Error::NotifierNotUnique); }); } @@ -114,9 +113,7 @@ fn register_fails_without_auth_data() { let mut registration_data = RegistrationData { id: 0, - notifier: Notifier::Email, - email: Some("dummy@gmail.com".to_string()), - tg_handle: Some("@dummy".to_string()), + notifier: Notifier::Email("dummy@gmail.com".to_string()), enabled_notifications: vec![], auth_data: AuthData { email_access_token: Some("token".to_string()), @@ -133,7 +130,7 @@ fn register_fails_without_auth_data() { // CASE 2: tg auth not set registration_data.auth_data.tg_auth_token = None; - registration_data.notifier = Notifier::Telegram; + registration_data.notifier = Notifier::Telegram("@dummy".to_string()); let response = register(&client, ®istration_data); diff --git a/services/storage/src/users.rs b/services/storage/src/users.rs index b90d4cd..436e0a3 100644 --- a/services/storage/src/users.rs +++ b/services/storage/src/users.rs @@ -34,15 +34,13 @@ impl User { pub fn query_by_id(conn: &Connection, id: u32) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE id=?1")?; let mut users_iter = smth.query_map(&[&id], |row| { - let email = row.get("email")?; - let tg_handle = row.get("tg_handle")?; - let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email(email), - "telegram" => Notifier::Telegram(tg_handle), + "email" => Notifier::Email(row.get("email")?), + "telegram" => Notifier::Telegram(row.get("tg_handle")?), _ => Notifier::Null, }; - Ok(User { id: row.get("id")?, notifier }) + + Ok(User { id, notifier }) })?; match users_iter.next() { @@ -55,12 +53,9 @@ impl User { pub fn query_by_email(conn: &Connection, email: String) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE email=?1")?; let mut users_iter = smth.query_map(&[&email], |row| { - let email = row.get("email")?; - let tg_handle = row.get("tg_handle")?; - let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email(email), - "telegram" => Notifier::Telegram(tg_handle), + "email" => Notifier::Email(row.get("email")?), + "telegram" => Notifier::Telegram(row.get("tg_handle")?), _ => Notifier::Null, }; Ok(User { id: row.get("id")?, notifier }) @@ -76,12 +71,9 @@ impl User { pub fn query_by_tg_handle(conn: &Connection, handle: String) -> Result> { let mut smth = conn.prepare("SELECT * FROM users WHERE tg_handle=?1")?; let mut users_iter = smth.query_map(&[&handle], |row| { - let email = row.get("email")?; - let tg_handle = row.get("tg_handle")?; - let notifier = match row.get::<_, String>("notifier")?.as_str() { - "email" => Notifier::Email(email), - "telegram" => Notifier::Telegram(tg_handle), + "email" => Notifier::Email(row.get("email")?), + "telegram" => Notifier::Telegram(row.get("tg_handle")?), _ => Notifier::Null, }; Ok(User { id: row.get("id")?, notifier }) From 24c848fb9ab0e28098d2b21544060641a49f2809 Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 16:43:57 +0200 Subject: [PATCH 8/9] cleanup tests --- services/api/routes/src/tests/register.rs | 42 +++++++++++++++++------ 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/services/api/routes/src/tests/register.rs b/services/api/routes/src/tests/register.rs index fca674e..4afc9a4 100644 --- a/services/api/routes/src/tests/register.rs +++ b/services/api/routes/src/tests/register.rs @@ -42,13 +42,7 @@ fn register_works() { enabled_notifications: vec![], auth_data: dummy_auth_data.clone(), }; - // CASE 1: the user did not set the notifier. - let response = register(&client, ®istration_data); - - assert_eq!(response.status(), Status::BadRequest); - assert_eq!(parse_err_response(response), Error::NotifierEmpty); - // CASE 2: correct data, should work. registration_data.notifier = Notifier::Email("dummy@gmail.com".to_string()); let response = register(&client, ®istration_data); @@ -61,12 +55,12 @@ fn register_works() { User { id: 0, notifier: Notifier::Email("dummy@gmail.com".to_string()) } ); - // CASE 3: user with the same id exists + // User with the same id exists: let response = register(&client, ®istration_data); assert_eq!(response.status(), Status::Conflict); assert_eq!(parse_err_response(response), Error::UserExists); - // CASE 4: user with the same email exists: + // User with the same email exists: let registration_data = RegistrationData { id: 1, notifier: Notifier::Email("dummy@gmail.com".to_string()), @@ -78,7 +72,7 @@ fn register_works() { assert_eq!(response.status(), Status::Conflict); assert_eq!(parse_err_response(response), Error::NotifierNotUnique); - // CASE 5: user with the same telegram exists: + // Register two users with the same telegram handle: // First time works because there is no user with the same tg: let tg_user_1 = RegistrationData { @@ -98,11 +92,39 @@ fn register_works() { auth_data: dummy_auth_data.clone(), }; let response = register(&client, &tg_user_2); - // assert_eq!(response.status(), Status::Conflict); + assert_eq!(response.status(), Status::Conflict); assert_eq!(parse_err_response(response), Error::NotifierNotUnique); }); } +#[test] +fn register_fails_without_notifier() { + execute_with(DB_PATH, || { + let conn = init_db(DB_PATH).unwrap(); + let rocket = rocket::build().manage(conn).mount("/", routes![register_user, user]); + + let client = Client::tracked(rocket).expect("failed to create a client"); + + let dummy_auth_data = AuthData { + email_access_token: Some("token".to_string()), + tg_auth_token: Some("token".to_string()), + }; + + let registration_data = RegistrationData { + id: 0, + notifier: Notifier::Null, + enabled_notifications: vec![], + auth_data: dummy_auth_data.clone(), + }; + + // User did not set the notifier: + let response = register(&client, ®istration_data); + + assert_eq!(response.status(), Status::BadRequest); + assert_eq!(parse_err_response(response), Error::NotifierEmpty); + }); +} + #[test] fn register_fails_without_auth_data() { execute_with(DB_PATH, || { From 4e5fdfb3148678db5da61bed676bdeef6c3ec14b Mon Sep 17 00:00:00 2001 From: Sergej Date: Sun, 25 Aug 2024 17:21:53 +0200 Subject: [PATCH 9/9] auth progress --- Cargo.lock | 1 + services/api/routes/src/query.rs | 5 +--- services/api/routes/src/register.rs | 46 ++++++++++++++++------------- services/authenticator/src/lib.rs | 4 +-- services/storage/Cargo.toml | 1 + services/storage/src/lib.rs | 7 +++-- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 798c6b3..20b8297 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3711,6 +3711,7 @@ version = "0.1.0" dependencies = [ "rusqlite", "serde", + "tokio", "types", ] diff --git a/services/api/routes/src/query.rs b/services/api/routes/src/query.rs index 3ba85eb..80e49a8 100644 --- a/services/api/routes/src/query.rs +++ b/services/api/routes/src/query.rs @@ -13,10 +13,7 @@ pub async fn user( ) -> Result, status::Custom>> { log::info!(target: LOG_TARGET, "Querying user: {}", user_id); - let conn = conn.lock().map_err(|err| { - log::error!(target: LOG_TARGET, "DB connection failed: {:?}", err); - custom_error(Status::InternalServerError, Error::DbConnectionFailed) - })?; + let conn = conn.lock().await; let maybe_user = User::query_by_id(&conn, user_id).map_err(|err| { log::error!(target: LOG_TARGET, "Failed to search user by id: {:?}", err); diff --git a/services/api/routes/src/register.rs b/services/api/routes/src/register.rs index 8e2b9c0..223173b 100644 --- a/services/api/routes/src/register.rs +++ b/services/api/routes/src/register.rs @@ -62,10 +62,7 @@ pub async fn register_user( log::info!(target: LOG_TARGET, "Registration request: {:?}", registration_data); // Get connection: - let conn = conn.lock().map_err(|err| { - log::error!(target: LOG_TARGET, "DB connection failed: {:?}", err); - custom_error(Status::InternalServerError, Error::DbConnectionFailed) - })?; + let conn = conn.lock().await; // Validate registration data: registration_data @@ -74,22 +71,8 @@ pub async fn register_user( ensure_unique_data(&conn, ®istration_data)?; - match registration_data.notifier { - Notifier::Email(_) => { - /* TODO: - let email = authenticator::authenticate_google_user( - registration_data.auth_data.email_access_token, - ) - .await?; - ensure!( - Some(email) == registration_data.email, - custom_error(Status::Unauthorized, Error::BadAuthData) - ); - */ - }, - Notifier::Telegram(_) => {}, - Notifier::Null => {}, - } + authenticate_user(registration_data.notifier.clone(), registration_data.auth_data.clone()) + .await; let user = User { id: registration_data.id, notifier: registration_data.notifier.clone() }; // Register user @@ -101,6 +84,29 @@ pub async fn register_user( Ok(status::Custom(Status::Ok, ())) } +async fn authenticate_user(notifier: Notifier, auth_data: AuthData) -> Result<(), Error> { + let maybe_email_token = auth_data.email_access_token; + + match notifier { + Notifier::Email(email) => { + // This should never happen due to validation; however, we will be preventive. + let Some(email_token) = maybe_email_token else { + return Err(Error::AuthDataEmpty); + }; + let authenticated_email = authenticator::authenticate_google_user(&email_token) + .await + .map_err(|_err| Error::BadAuthData)?; + // ^^ TODO: proper auth data + + ensure!(*email == authenticated_email, Error::BadAuthData); + }, + Notifier::Telegram(_) => {}, + Notifier::Null => {}, + }; + + Ok(()) +} + fn ensure_unique_data( conn: &Connection, registration_data: &Json, diff --git a/services/authenticator/src/lib.rs b/services/authenticator/src/lib.rs index 2d80838..3e73889 100644 --- a/services/authenticator/src/lib.rs +++ b/services/authenticator/src/lib.rs @@ -22,11 +22,11 @@ struct UserInfo { pub async fn authenticate_google_user(access_token: &str) -> Result { dotenv().ok(); - let client_id = env::var("CLIENT_ID").unwrap(); + let client_id = env::var("CLIENT_ID").map_err(|_err| "TODO: error")?; let client = AsyncClient::new(client_id); client.validate_access_token(access_token).await.map_err(|_err| "TODO: error")?; - let user = get_user_info(&access_token).await.unwrap(); + let user = get_user_info(&access_token).await.map_err(|_err| "TODO: error")?; Ok(user.email) } diff --git a/services/storage/Cargo.toml b/services/storage/Cargo.toml index d6acb01..7d15349 100644 --- a/services/storage/Cargo.toml +++ b/services/storage/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" rusqlite = { version = "0.32.1", features = ["bundled"] } types = { path = "../types" } serde = { version = "1.0.193", features = ["derive"] } +tokio = { version = "1", features = ["full"] } diff --git a/services/storage/src/lib.rs b/services/storage/src/lib.rs index 5753724..d8f1f6d 100644 --- a/services/storage/src/lib.rs +++ b/services/storage/src/lib.rs @@ -9,11 +9,12 @@ from the `Notifications` enum. (There cannot be duplicates) */ use rusqlite::{Connection, Result}; -use std::sync::Mutex; +use std::sync::Arc; +use tokio::sync::Mutex; pub mod users; -pub type DbConn = Mutex; +pub type DbConn = Arc>; pub fn init_db(db_path: &'static str) -> Result { // Create the db if it does not exist. @@ -31,5 +32,5 @@ pub fn init_db(db_path: &'static str) -> Result { (), )?; - Ok(Mutex::new(conn)) + Ok(Arc::new(Mutex::new(conn))) }