From e928c35567880425bdbd081eee7a7e5130d3a225 Mon Sep 17 00:00:00 2001 From: rupansh Date: Sun, 9 Jun 2024 21:30:16 +0530 Subject: [PATCH 1/4] refactor!: initial grpc support preserves legacy metadata endpoints TODO: update client --- Cargo.lock | 866 +++++++++++++-------------- Cargo.toml | 4 +- Dockerfile | 4 +- fly.toml | 8 + jwt-generator/Cargo.toml | 11 + jwt-generator/src/args.rs | 14 + jwt-generator/src/main.rs | 67 +++ server/Cargo.toml | 8 +- server/src/auth.rs | 72 ++- server/src/config.rs | 1 + server/src/consts.rs | 11 +- server/src/error.rs | 84 ++- server/src/legacy/error.rs | 91 +++ server/src/{api.rs => legacy/mod.rs} | 53 +- server/src/legacy/old_types.rs | 39 ++ server/src/main.rs | 75 ++- server/src/state.rs | 9 - server/src/svc.rs | 126 ++++ types/Cargo.toml | 11 + types/build.rs | 16 + types/proto/yral_metadata.proto | 31 + types/src/error.rs | 23 - types/src/lib.rs | 83 ++- 23 files changed, 1104 insertions(+), 603 deletions(-) create mode 100644 jwt-generator/Cargo.toml create mode 100644 jwt-generator/src/args.rs create mode 100644 jwt-generator/src/main.rs create mode 100644 server/src/legacy/error.rs rename server/src/{api.rs => legacy/mod.rs} (69%) create mode 100644 server/src/legacy/old_types.rs delete mode 100644 server/src/state.rs create mode 100644 server/src/svc.rs create mode 100644 types/build.rs create mode 100644 types/proto/yral_metadata.proto delete mode 100644 types/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 9c15c45..57ffeec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - [[package]] name = "android-tzdata" version = "0.1.1" @@ -152,17 +146,38 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -181,14 +196,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] -name = "backoff" -version = "0.4.0" +name = "axum" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ - "getrandom", - "instant", - "rand", + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", ] [[package]] @@ -325,20 +374,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bls12_381" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" -dependencies = [ - "digest 0.9.0", - "ff 0.12.1", - "group 0.12.1", - "pairing", - "rand_core", - "subtle", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -379,19 +414,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cached" -version = "0.46.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c8c50262271cdf5abc979a5f76515c234e764fa025d1ba4862c0f0bcda0e95" -dependencies = [ - "ahash", - "hashbrown 0.14.3", - "instant", - "once_cell", - "thiserror", -] - [[package]] name = "candid" version = "0.10.7" @@ -458,6 +480,46 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "clap" +version = "4.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + [[package]] name = "colorchoice" version = "1.0.0" @@ -620,9 +682,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -671,6 +733,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", "platforms", "rustc_version", @@ -732,7 +795,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] @@ -746,7 +809,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 2.0.60", ] @@ -881,6 +944,16 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + [[package]] name = "ed25519-consensus" version = "2.1.0" @@ -896,6 +969,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.11.0" @@ -992,9 +1080,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ "event-listener", "pin-project-lite", @@ -1033,25 +1121,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" @@ -1168,11 +1247,12 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "186014d53bc231d0090ef8d6f03e0920c54d85a5ed22f4f2f74315ec56cf83fb" dependencies = [ "cc", + "cfg-if", "libc", "log", "rustversion", @@ -1233,16 +1313,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap 2.2.6", "slab", "tokio", @@ -1273,10 +1353,6 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "heck" @@ -1284,6 +1360,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1307,9 +1389,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1317,28 +1399,33 @@ dependencies = [ ] [[package]] -name = "http-body" -version = "1.0.0" +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", - "http", + "fnv", + "itoa", ] [[package]] -name = "http-body-util" -version = "0.1.1" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "futures-core", - "http", - "http-body", + "http 0.2.12", "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + [[package]] name = "httparse" version = "1.8.0" @@ -1359,75 +1446,38 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.3.1" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[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", - "hyper-util", - "native-tls", + "socket2", "tokio", - "tokio-native-tls", "tower-service", + "tracing", + "want", ] [[package]] -name = "hyper-util" -version = "0.1.3" +name = "hyper-timeout" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", "hyper", "pin-project-lite", - "socket2", "tokio", - "tower", - "tower-service", - "tracing", + "tokio-io-timeout", ] [[package]] @@ -1441,7 +1491,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1453,44 +1503,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ic-agent" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555eda283b8ba1a996d44d80b6c9a6d38a6cbb81797a7b7bbee134fdde64ce48" -dependencies = [ - "backoff", - "cached 0.46.1", - "candid", - "ed25519-consensus", - "futures-util", - "hex", - "http", - "http-body", - "ic-certification 2.5.0", - "ic-transport-types", - "ic-verify-bls-signature", - "k256", - "leb128", - "p256", - "pkcs8", - "rand", - "rangemap", - "ring", - "rustls-webpki 0.101.7", - "sec1", - "serde", - "serde_bytes", - "serde_cbor", - "serde_repr", - "sha2 0.10.8", - "simple_asn1", - "thiserror", - "time", - "tokio", - "url", -] - [[package]] name = "ic-base-types" version = "0.9.0" @@ -1545,18 +1557,6 @@ dependencies = [ "tree-deserializer", ] -[[package]] -name = "ic-certification" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20052ce9255fbe2de7041a4f6996fddd095ba1f31ae83b6c0ccdee5be6e7bbcf" -dependencies = [ - "hex", - "serde", - "serde_bytes", - "sha2 0.10.8", -] - [[package]] name = "ic-constants" version = "0.9.0" @@ -1696,7 +1696,7 @@ source = "git+https://github.com/dfinity/ic.git?rev=release-2024-03-20_23-01+hot dependencies = [ "base64 0.13.1", "hex", - "ic-certification 0.9.0", + "ic-certification", "ic-crypto-internal-basic-sig-der-utils", "ic-crypto-internal-types", "ic-crypto-sha2", @@ -1770,7 +1770,7 @@ version = "0.9.0" source = "git+https://github.com/dfinity/ic.git?rev=release-2024-03-20_23-01+hotfix#88e489c293ce2d6ede5aef1866775ca7eab614a6" dependencies = [ "base64 0.13.1", - "cached 0.41.0", + "cached", "hex", "ic-crypto-internal-bls12-381-type", "ic-crypto-internal-seed", @@ -1922,23 +1922,6 @@ dependencies = [ "slog", ] -[[package]] -name = "ic-transport-types" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c06d3c1292368c813f8104d9d8294bda5577ea6fe4501a65997d0b7f155f15f" -dependencies = [ - "candid", - "hex", - "ic-certification 2.5.0", - "leb128", - "serde", - "serde_bytes", - "serde_repr", - "sha2 0.10.8", - "thiserror", -] - [[package]] name = "ic-types" version = "0.9.0" @@ -2016,18 +1999,6 @@ dependencies = [ "ic-validator", ] -[[package]] -name = "ic-verify-bls-signature" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583b1c03380cf86059160cc6c91dcbf56c7b5f141bf3a4f06bc79762d775fac4" -dependencies = [ - "bls12_381", - "lazy_static", - "pairing", - "sha2 0.9.9", -] - [[package]] name = "ic_bls12_381" version = "0.8.0" @@ -2103,12 +2074,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "itertools" version = "0.12.1" @@ -2159,6 +2124,17 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "jwt-generator" +version = "0.1.0" +dependencies = [ + "clap", + "ed25519-dalek", + "jsonwebtoken", + "rand", + "serde", +] + [[package]] name = "k256" version = "0.13.3" @@ -2230,9 +2206,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "loom" -version = "0.5.6" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", "generator", @@ -2257,6 +2233,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.2" @@ -2296,28 +2278,16 @@ dependencies = [ ] [[package]] -name = "nanorand" -version = "0.7.0" +name = "multimap" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] -name = "native-tls" -version = "0.2.11" +name = "nanorand" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" [[package]] name = "nix" @@ -2412,9 +2382,9 @@ dependencies = [ [[package]] name = "ntex-h2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a59d4d2e1ab731a1cd4d8b3f4a626134121f38e5719bfc5f179f1b14902ed" +checksum = "d02baa9fb658c1d2212a4e342007b7b5c05af5c926314ad1ac96ee474ce50879" dependencies = [ "bitflags 2.5.0", "fxhash", @@ -2439,7 +2409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81e205c980c693cb426f55669078bc311973f7e27a34f7ea4d0ce4069dedd05" dependencies = [ "fxhash", - "http", + "http 1.1.0", "itoa", "log", "ntex-bytes", @@ -2448,9 +2418,9 @@ dependencies = [ [[package]] name = "ntex-io" -version = "1.0.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb860292c402d3c81cea6261d4218a88bb1e409560e4fdff1e789299153e1b9" +checksum = "6c3b2c13e80ebac91b90854a4b9f94f00f8e367e669aace5a069e0213dc25f4d" dependencies = [ "bitflags 2.5.0", "log", @@ -2495,7 +2465,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb9c68c26a87ffca54339be5f95223339db3e7bcc5d64733fef20812970a746f" dependencies = [ - "http", + "http 1.1.0", "log", "ntex-bytes", "regex", @@ -2666,6 +2636,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.32.2" @@ -2688,9 +2668,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oneshot" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" +checksum = "071d1cf3298ad8e543dca18217d198cb6a3884443d204757b9624b935ef09fa0" dependencies = [ "loom", ] @@ -2701,50 +2681,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.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.60", -] - [[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.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordered-multimap" version = "0.6.0" @@ -2902,6 +2844,16 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + [[package]] name = "phantom_newtype" version = "0.9.0" @@ -2965,12 +2917,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - [[package]] name = "platforms" version = "3.4.0" @@ -3025,6 +2971,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.60", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3053,6 +3009,27 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.60", + "tempfile", +] + [[package]] name = "prost-derive" version = "0.12.4" @@ -3066,6 +3043,15 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost", +] + [[package]] name = "psm" version = "0.1.21" @@ -3114,12 +3100,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rangemap" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" - [[package]] name = "redis" version = "0.25.3" @@ -3203,51 +3183,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "reqwest" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" -dependencies = [ - "base64 0.22.0", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -3353,7 +3288,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki", "subtle", "zeroize", ] @@ -3387,16 +3322,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.102.3" @@ -3531,26 +3456,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "serde_spanned" version = "0.6.5" @@ -3790,6 +3704,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -3805,7 +3725,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -3965,23 +3885,36 @@ dependencies = [ "bytes", "libc", "mio", + "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.48.0", ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-io-timeout" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "native-tls", + "pin-project-lite", "tokio", ] +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "tokio-retry" version = "0.3.0" @@ -4004,6 +3937,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -4052,6 +3996,66 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "h2", + "http 0.2.12", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tonic-web" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3b0e1cedbf19fdfb78ef3d672cb9928e0a91a9cb4629cc0c916e8cff8aaaa1" +dependencies = [ + "base64 0.21.7", + "bytes", + "http 0.2.12", + "http-body", + "hyper", + "pin-project", + "tokio-stream", + "tonic", + "tower-http", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -4060,14 +4064,36 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" @@ -4086,7 +4112,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4244,12 +4269,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" @@ -4296,18 +4315,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -4337,16 +4344,6 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "web-time" version = "1.1.0" @@ -4357,15 +4354,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "winapi" version = "0.3.9" @@ -4390,11 +4378,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-targets 0.48.5", + "windows-core 0.54.0", + "windows-targets 0.52.5", ] [[package]] @@ -4406,6 +4395,25 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4554,16 +4562,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 = "yaml-rust" version = "0.4.5" @@ -4585,7 +4583,6 @@ version = "0.1.0" source = "git+https://github.com/go-bazzinga/yral-identity.git?rev=346f61f5bf07b619be8dfc3d041dc665c4c66113#346f61f5bf07b619be8dfc3d041dc665c4c66113" dependencies = [ "candid", - "ic-agent", "ic-types", "ic-validator-ingress-message", "serde", @@ -4593,17 +4590,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "yral-metadata-client" -version = "0.1.0" -dependencies = [ - "ic-agent", - "reqwest", - "thiserror", - "yral-identity", - "yral-metadata-types", -] - [[package]] name = "yral-metadata-server" version = "0.1.0" @@ -4619,11 +4605,15 @@ dependencies = [ "ntex", "ntex-cors", "once_cell", + "prost", "redis", "serde", "serde_json", "serde_with 3.8.0", "thiserror", + "tokio", + "tonic", + "tonic-web", "yral-identity", "yral-metadata-types", ] @@ -4633,8 +4623,12 @@ name = "yral-metadata-types" version = "0.1.0" dependencies = [ "candid", + "prost", "serde", + "serde_json", "thiserror", + "tonic", + "tonic-build", "yral-identity", ] diff --git a/Cargo.toml b/Cargo.toml index 263d20e..82e2bbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,12 @@ [workspace] -members = [ "client","server", "types"] +members = [ "jwt-generator","server", "types"] resolver = "2" [workspace.dependencies] serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.117" thiserror = "1.0.58" candid = "0.10.6" log = "0.4.21" yral-identity = { git = "https://github.com/go-bazzinga/yral-identity.git", default-features = false, rev = "346f61f5bf07b619be8dfc3d041dc665c4c66113" } +jsonwebtoken = { version = "9.3.0", features = ["use_pem"] } diff --git a/Dockerfile b/Dockerfile index 57ec11a..e7da019 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,9 @@ COPY ./target/x86_64-unknown-linux-musl/release/yral-metadata-server . COPY ./config.toml . ENV RUST_LOG="debug" -ENV BIND_ADDRESS="0.0.0.0:3000" +ENV BIND_ADDRESS="0.0.0.0:3001" +ENV LEGACY_BIND_ADDRESS="0.0.0.0:3000" EXPOSE 3000 +EXPOSE 3001 CMD ["./yral-metadata-server"] \ No newline at end of file diff --git a/fly.toml b/fly.toml index ae3d4aa..4c9d3ca 100644 --- a/fly.toml +++ b/fly.toml @@ -16,6 +16,14 @@ primary_region = 'sin' min_machines_running = 0 processes = ['app'] +[[services]] + internal_port = 3001 + protocol = "tcp" + + [[services.ports]] + handlers = ["http", "tls"] + port = 8443 + [[vm]] memory = '256mb' cpu_kind = 'shared' diff --git a/jwt-generator/Cargo.toml b/jwt-generator/Cargo.toml new file mode 100644 index 0000000..36390ea --- /dev/null +++ b/jwt-generator/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "jwt-generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +jsonwebtoken.workspace = true +serde.workspace = true +clap = { version = "4.5.6", features = ["derive"] } +ed25519-dalek = { version = "2.1.1", features = ["rand_core", "pem"] } +rand = "0.8.5" diff --git a/jwt-generator/src/args.rs b/jwt-generator/src/args.rs new file mode 100644 index 0000000..f46fbce --- /dev/null +++ b/jwt-generator/src/args.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(version, about)] +pub struct Args { + /// Base64 Encoded private key + /// a new key is generated if not specified + #[clap(short, long)] + pub private_key: Option, + /// The token expiry + /// in days + #[clap(short, long, default_value = "365", value_name = "DAYS")] + pub expiry: u64, +} diff --git a/jwt-generator/src/main.rs b/jwt-generator/src/main.rs new file mode 100644 index 0000000..ab49574 --- /dev/null +++ b/jwt-generator/src/main.rs @@ -0,0 +1,67 @@ +mod args; + +use std::time::{Duration, SystemTime}; + +use args::Args; +use clap::Parser; +use ed25519_dalek::{ + pkcs8::{spki::der::pem::LineEnding, DecodePrivateKey, EncodePrivateKey, EncodePublicKey}, + SigningKey, +}; +use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header}; +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub sub: String, + pub company: String, + pub exp: u64, +} + +fn random_encoding_key() -> (SigningKey, EncodingKey) { + println!("Generating new private key..."); + let key = SigningKey::generate(&mut OsRng); + let key_pem = key.to_pkcs8_pem(LineEnding::default()).unwrap(); + let ec_key = EncodingKey::from_ed_pem(key_pem.as_bytes()).unwrap(); + println!( + "Private Key(Save this to generate new tokens):\n{}", + key_pem.as_str() + ); + (key, ec_key) +} + +fn main() { + let args = Args::parse(); + let (sign_key, ec_key) = args + .private_key + .map(|key| { + ( + SigningKey::from_pkcs8_pem(&key).expect("Invalid Private Key"), + EncodingKey::from_ed_pem(key.as_bytes()).unwrap(), + ) + }) + .unwrap_or_else(random_encoding_key); + + let exp_dur = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + + Duration::from_secs(args.expiry * 24 * 60 * 60); + let exp = exp_dur.as_secs(); + println!("Generating token with expiry {}\n", exp); + let claims = Claims { + sub: "off-chain-agent".to_string(), + company: "gobazzinga".to_string(), + exp, + }; + let token = encode(&Header::new(Algorithm::EdDSA), &claims, &ec_key).unwrap(); + println!("JWT Token (Use this in off-chain agent): {token}\n"); + + let vk = sign_key.verifying_key(); + let vk_pem = vk.to_public_key_pem(LineEnding::default()).unwrap(); + assert!( + DecodingKey::from_ed_pem(vk_pem.as_bytes()).is_ok(), + "ed25519-dalek returned invalid public key" + ); + println!("Public Key(Use this in yral-metadata-server):\n{vk_pem}"); +} diff --git a/server/Cargo.toml b/server/Cargo.toml index c123705..056377b 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -18,7 +18,7 @@ redis = { version = "0.25.2", features = [ "keep-alive", ] } config = { version = "0.14.0", features = ["toml"] } -serde_json = "1.0.115" +serde_json.workspace = true serde_with = "3.7.0" thiserror.workspace = true candid.workspace = true @@ -29,6 +29,10 @@ bb8-redis = "0.15.0" yral-identity = { workspace = true, default-features = false, features = [ "ic-git", ] } -jsonwebtoken = { version = "9.3.0", features = ["use_pem"] } +jsonwebtoken.workspace = true futures = "0.3.30" once_cell = "1.19.0" +tonic = "0.11" +tonic-web = "0.11" +prost = "0.12" +tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } diff --git a/server/src/auth.rs b/server/src/auth.rs index 4f90037..1618847 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -1,7 +1,13 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use serde::{Deserialize, Serialize}; +use tonic::{service::Interceptor, Request, Status}; -use crate::{config::AppConfig, consts::CLAIMS, Error}; +use crate::{ + config::AppConfig, + consts::{OFF_CHAIN_AGENT_SUBJECT, YRAL_METADATA_COMPANY}, + Error, +}; +use std::sync::Arc; #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct Claims { @@ -16,6 +22,55 @@ pub struct JwtDetails { pub validation: Validation, } +#[derive(Clone)] +pub struct JwtInterceptor { + jwt_details: Arc, +} + +impl JwtInterceptor { + pub fn new(jwt_details: JwtDetails) -> Self { + Self { + jwt_details: Arc::new(jwt_details), + } + } + + pub(crate) fn verify_token(&self, token: &str) -> Result<(), Error> { + let JwtDetails { + decoding_key, + validation, + } = self.jwt_details.as_ref(); + + let token_message = + decode::(token, decoding_key, validation).map_err(Error::Jwt)?; + + let claims = token_message.claims; + if claims.sub != OFF_CHAIN_AGENT_SUBJECT || claims.company != YRAL_METADATA_COMPANY { + return Err(Error::AuthTokenInvalid); + } + + Ok(()) + } + + fn check_auth(&self, req: Request<()>) -> Result, Error> { + let auth = req + .metadata() + .get("authorization") + .ok_or(Error::AuthTokenMissing)?; + let token = auth.to_str().map_err(|_| Error::AuthTokenInvalid)?; + let token = token.trim_start_matches("Bearer "); + self.verify_token(token)?; + + Ok(req) + } +} + +impl Interceptor for JwtInterceptor { + fn call(&mut self, request: Request<()>) -> Result, Status> { + let req = self.check_auth(request)?; + Ok(req) + } +} + pub fn init_jwt(conf: &AppConfig) -> JwtDetails { let decoding_key = DecodingKey::from_ed_pem(conf.jwt_public_key.as_bytes()) .expect("failed to create decoding key"); @@ -27,18 +82,3 @@ pub fn init_jwt(conf: &AppConfig) -> JwtDetails { validation, } } - -pub fn verify_token(token: &str, jwt_details: &JwtDetails) -> Result<(), Error> { - let JwtDetails { - decoding_key, - validation, - } = jwt_details; - - let token_message = decode::(token, decoding_key, validation).map_err(Error::Jwt)?; - - if token_message.claims != *CLAIMS { - return Err(Error::AuthTokenInvalid); - } - - Ok(()) -} diff --git a/server/src/config.rs b/server/src/config.rs index 028d746..4ded1f3 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -9,6 +9,7 @@ use serde_with::{serde_as, DisplayFromStr}; #[derive(Deserialize)] pub struct AppConfig { pub bind_address: SocketAddr, + pub legacy_bind_address: SocketAddr, #[serde_as(as = "DisplayFromStr")] pub redis_url: ConnectionInfo, pub jwt_public_key: String, diff --git a/server/src/consts.rs b/server/src/consts.rs index 804e2c0..70bb88a 100644 --- a/server/src/consts.rs +++ b/server/src/consts.rs @@ -1,9 +1,2 @@ -use once_cell::sync::Lazy; - -use crate::auth::Claims; - -pub static CLAIMS: Lazy = Lazy::new(|| Claims { - sub: "off-chain-agent".to_string(), - company: "gobazzinga".to_string(), - exp: 317125598072, // TODO: To be changed later when expiring tokens periodically -}); +pub const OFF_CHAIN_AGENT_SUBJECT: &str = "off-chain-agent"; +pub const YRAL_METADATA_COMPANY: &str = "gobazzinga"; diff --git a/server/src/error.rs b/server/src/error.rs index a251bcf..5d789f1 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -1,10 +1,7 @@ -use ntex::{ - http::{header, StatusCode}, - web, -}; +use candid::types::principal::PrincipalError; use redis::RedisError; use thiserror::Error; -use types::{error::ApiError, ApiResult}; +use tonic::Status; #[derive(Error, Debug)] pub enum Error { @@ -12,69 +9,56 @@ pub enum Error { IO(#[from] std::io::Error), #[error("failed to load config {0}")] Config(#[from] config::ConfigError), - #[error("{0}")] + #[error("invalid identity: {0}")] Identity(#[from] yral_identity::Error), #[error("{0}")] Redis(#[from] RedisError), #[error("{0}")] Bb8(#[from] bb8::RunError), #[error("failed to deserialize json {0}")] - Deser(serde_json::Error), + Deser(#[from] serde_json::Error), #[error("jwt {0}")] Jwt(#[from] jsonwebtoken::errors::Error), #[error("auth token missing")] AuthTokenMissing, #[error("auth token invalid")] AuthTokenInvalid, + #[error("metadata missing")] + MetadataMissing, + #[error("invalid principal {0}")] + Principal(#[from] PrincipalError), + #[error("internal error: redis")] + DeleteKeys, } -impl From<&Error> for ApiResult<()> { - fn from(value: &Error) -> Self { - let err = match value { - Error::IO(_) | Error::Config(_) => { - log::warn!("internal error {value}"); - ApiError::Unknown("internal error, reported".into()) - } - Error::Identity(_) => ApiError::InvalidSignature, - Error::Redis(e) => { - log::warn!("redis error {e}"); - ApiError::Redis +impl From for Status { + fn from(value: Error) -> Self { + use Error::*; + match &value { + IO(e) => { + log::warn!("io error: {e}"); + Status::internal("internal error: IO") } - Error::Bb8(e) => { - log::warn!("bb8 error {e}"); - ApiError::Redis + Config(e) => { + log::warn!("config error: {e}"); + Status::internal("internal error") } - Error::Deser(e) => { - log::warn!("deserialization error {e}"); - ApiError::Deser + Identity(_) => Status::invalid_argument(value.to_string()), + Redis(e) => { + log::warn!("redis error: {e}"); + Status::internal("internal error: redis") } - Error::Jwt(_) => ApiError::Jwt, - Error::AuthTokenMissing => ApiError::AuthTokenMissing, - Error::AuthTokenInvalid => ApiError::AuthToken, - }; - ApiResult::Err(err) - } -} - -impl web::error::WebResponseError for Error { - fn error_response(&self, _: &web::HttpRequest) -> web::HttpResponse { - let api_error = ApiResult::from(self); - web::HttpResponse::build(self.status_code()) - .set_header(header::CONTENT_TYPE, "application/json") - .json(&api_error) - } - - fn status_code(&self) -> StatusCode { - match self { - Error::IO(_) | Error::Config(_) | Error::Redis(_) | Error::Deser(_) | Error::Bb8(_) => { - StatusCode::INTERNAL_SERVER_ERROR + Bb8(e) => { + log::warn!("redis pool error: {e}"); + Status::internal("internal error: redis") } - Error::Identity(_) - | Error::Jwt(_) - | Error::AuthTokenInvalid - | Error::AuthTokenMissing => StatusCode::UNAUTHORIZED, + Deser(_) => Status::invalid_argument(value.to_string()), + Jwt(_) => Status::invalid_argument(value.to_string()), + AuthTokenMissing => Status::unauthenticated(value.to_string()), + AuthTokenInvalid => Status::unauthenticated(value.to_string()), + MetadataMissing => Status::invalid_argument(value.to_string()), + Principal(_) => Status::invalid_argument(value.to_string()), + DeleteKeys => Status::internal(value.to_string()), } } } - -pub type Result = std::result::Result; diff --git a/server/src/legacy/error.rs b/server/src/legacy/error.rs new file mode 100644 index 0000000..e2b5567 --- /dev/null +++ b/server/src/legacy/error.rs @@ -0,0 +1,91 @@ +use ntex::{ + http::{header, StatusCode}, + web, +}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use super::old_types::ApiResult; + +#[derive(Deserialize, Serialize, Error, Debug)] +#[non_exhaustive] +pub enum ApiError { + #[error("invalid signature provided")] + InvalidSignature, + #[error("internal error: redis")] + Redis, + #[error("internal error: deser")] + Deser, + #[error("jwt error - invalid token")] + Jwt, + #[error("invalid authentication token")] + AuthToken, + #[error("missing authentication token")] + AuthTokenMissing, + #[error("failed to delete keys (redis)")] + DeleteKeys, + #[error("unknown: {0}")] + Unknown(String), +} + +impl From<&crate::Error> for ApiResult<()> { + fn from(value: &crate::Error) -> Self { + use crate::Error; + + let err = match value { + Error::IO(_) | Error::Config(_) => { + log::warn!("internal error {value}"); + ApiError::Unknown("internal error, reported".into()) + } + Error::Identity(_) => ApiError::InvalidSignature, + Error::Redis(e) => { + log::warn!("redis error {e}"); + ApiError::Redis + } + Error::Bb8(e) => { + log::warn!("bb8 error {e}"); + ApiError::Redis + } + Error::Deser(e) => { + log::warn!("deserialization error {e}"); + ApiError::Deser + } + Error::Jwt(_) => ApiError::Jwt, + Error::AuthTokenMissing => ApiError::AuthTokenMissing, + Error::AuthTokenInvalid => ApiError::AuthToken, + Error::MetadataMissing | Error::Principal(_) | Error::DeleteKeys => { + log::error!("unexpected error in legacy code {value}"); + ApiError::Unknown("internal".into()) + } + }; + ApiResult::Err(err) + } +} + +impl web::error::WebResponseError for crate::Error { + fn error_response(&self, _: &web::HttpRequest) -> web::HttpResponse { + let api_error = ApiResult::from(self); + web::HttpResponse::build(self.status_code()) + .set_header(header::CONTENT_TYPE, "application/json") + .json(&api_error) + } + + fn status_code(&self) -> StatusCode { + use crate::Error; + + match self { + Error::IO(_) | Error::Config(_) | Error::Redis(_) | Error::Deser(_) | Error::Bb8(_) => { + StatusCode::INTERNAL_SERVER_ERROR + } + Error::Identity(_) + | Error::Jwt(_) + | Error::AuthTokenInvalid + | Error::AuthTokenMissing => StatusCode::UNAUTHORIZED, + Error::MetadataMissing | Error::Principal(_) | Error::DeleteKeys => { + StatusCode::NOT_IMPLEMENTED + } + } + } +} + +pub type Result = std::result::Result; diff --git a/server/src/api.rs b/server/src/legacy/mod.rs similarity index 69% rename from server/src/api.rs rename to server/src/legacy/mod.rs index 11c8b66..526d1db 100644 --- a/server/src/api.rs +++ b/server/src/legacy/mod.rs @@ -1,16 +1,27 @@ +mod error; +mod old_types; +use std::net::SocketAddr; + +use crate::{auth::JwtInterceptor, RedisPool}; use candid::Principal; -use futures::{prelude::*, stream::FuturesUnordered}; +use error::{ApiError, Result}; +use futures::{stream::FuturesUnordered, StreamExt}; use ntex::web::{ self, types::{Json, Path, State}, }; -use redis::{AsyncCommands, RedisError}; -use types::{ - error::ApiError, ApiResult, BulkUsers, GetUserMetadataRes, SetUserMetadataReq, +use ntex_cors::Cors; +use old_types::{ + ApiResult, BulkUsers, DeleteMetadataBulkRes, GetUserMetadataRes, SetUserMetadataReq, SetUserMetadataRes, UserMetadata, }; +use redis::{AsyncCommands, RedisError}; -use crate::{auth::verify_token, state::AppState, Error, Result}; +#[derive(Clone)] +pub struct AppState { + pub redis: RedisPool, + pub jwt: JwtInterceptor, +} const METADATA_FIELD: &str = "metadata"; @@ -44,7 +55,7 @@ async fn get_user_metadata( let Some(meta_raw) = meta_raw else { return Ok(Json(Ok(None))); }; - let meta: UserMetadata = serde_json::from_slice(&meta_raw).map_err(Error::Deser)?; + let meta: UserMetadata = serde_json::from_slice(&meta_raw).map_err(crate::Error::Deser)?; Ok(Json(Ok(Some(meta)))) } @@ -54,16 +65,16 @@ async fn delete_metadata_bulk( state: State, req: Json, http_req: web::HttpRequest, -) -> Result>> { +) -> Result>> { // verify token let token = http_req .headers() .get("Authorization") - .ok_or(Error::AuthTokenMissing)? + .ok_or(crate::Error::AuthTokenMissing)? .to_str() - .map_err(|_| Error::AuthTokenInvalid)?; + .map_err(|_| crate::Error::AuthTokenInvalid)?; let token = token.trim_start_matches("Bearer "); - verify_token(token, &state.jwt_details)?; + state.jwt.verify_token(token)?; let keys = &req.users; @@ -96,3 +107,25 @@ async fn delete_metadata_bulk( Ok(Json(Ok(()))) } + +pub async fn start_legacy_server( + bind_address: SocketAddr, + redis: RedisPool, + jwt: JwtInterceptor, +) -> Result<()> { + let state = AppState { redis, jwt }; + + web::HttpServer::new(move || { + web::App::new() + .wrap(Cors::default()) + .state(state.clone()) + .service(set_user_metadata) + .service(get_user_metadata) + .service(delete_metadata_bulk) + }) + .bind(bind_address)? + .run() + .await?; + + Ok(()) +} diff --git a/server/src/legacy/old_types.rs b/server/src/legacy/old_types.rs new file mode 100644 index 0000000..1ca680a --- /dev/null +++ b/server/src/legacy/old_types.rs @@ -0,0 +1,39 @@ +use super::error::ApiError; +use candid::Principal; +use serde::{Deserialize, Serialize}; +use yral_identity::{msg_builder::Message, Signature}; + +pub type ApiResult = Result; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +pub struct UserMetadata { + pub user_canister_id: Principal, + pub user_name: String, +} + +impl From for Message { + fn from(value: UserMetadata) -> Self { + Message::default() + .method_name("set_user_metadata".into()) + .args((value.user_canister_id, value.user_name)) + // unwrap is safe here because (Principal, String) serialization can't fail + .unwrap() + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub struct SetUserMetadataReq { + pub metadata: UserMetadata, + pub signature: Signature, +} + +pub type SetUserMetadataRes = (); + +pub type GetUserMetadataRes = Option; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +pub struct BulkUsers { + pub users: Vec, +} + +pub type DeleteMetadataBulkRes = (); diff --git a/server/src/main.rs b/server/src/main.rs index 0a32d36..01d79ce 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,17 +1,17 @@ -mod api; mod auth; mod config; mod consts; mod error; -mod state; -use auth::init_jwt; +mod legacy; +mod svc; + +use auth::{init_jwt, JwtInterceptor}; use config::AppConfig; -use ntex::web; -use api::*; use error::*; -use ntex_cors::Cors; -use state::{AppState, RedisPool}; +use svc::*; +use tokio::sync::oneshot; +use tonic::transport::Server; pub async fn init_redis(conf: &AppConfig) -> RedisPool { let manager = bb8_redis::RedisConnectionManager::new(conf.redis_url.clone()) @@ -19,27 +19,56 @@ pub async fn init_redis(conf: &AppConfig) -> RedisPool { RedisPool::builder().build(manager).await.unwrap() } -#[ntex::main] -async fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<(), Error> { let conf = AppConfig::load()?; env_logger::init(); + let redis = init_redis(&conf).await; + let jwt_details = init_jwt(&conf); + + let server = YralMetadataServer::new(redis.clone()); + let auth_interceptor = JwtInterceptor::new(jwt_details); + + let pub_svc = types::public_yral_metadata_server::PublicYralMetadataServer::new(server.clone()); + let priv_svc = types::private_yral_metadata_server::PrivateYralMetadataServer::with_interceptor( + server, + auth_interceptor.clone(), + ); + + let (legacy_cancel_tx, legacy_cancel_rx) = oneshot::channel(); + let legacy_thread = std::thread::spawn(move || { + ntex::rt::System::new("legacy_thread").block_on(async move { + let legacy_fut = + legacy::start_legacy_server(conf.legacy_bind_address, redis, auth_interceptor); + tokio::select! { + legacy_res = legacy_fut => { + panic!("legacy server died: {:?}", legacy_res); + }, + _ = legacy_cancel_rx => { + Ok::<_, Error>(()) + } + } + }) + }); + + let grpc_fut = Server::builder() + .accept_http1(true) + .add_service(tonic_web::enable(pub_svc)) + .add_service(tonic_web::enable(priv_svc)) + .serve(conf.bind_address); + let ctrl_c_fut = tokio::signal::ctrl_c(); - let state = AppState { - redis: init_redis(&conf).await, - jwt_details: init_jwt(&conf), + tokio::select! { + tonic_res = grpc_fut => { + panic!("tonic server died: {:?}", tonic_res); + } + _ = ctrl_c_fut => { + log::info!("Received Ctrl-C, shutting down"); + _ = legacy_cancel_tx.send(()); + } }; - web::HttpServer::new(move || { - web::App::new() - .wrap(Cors::default()) - .state(state.clone()) - .service(set_user_metadata) - .service(get_user_metadata) - .service(delete_metadata_bulk) - }) - .bind(conf.bind_address)? - .run() - .await?; + legacy_thread.join().unwrap().expect("legacy server died"); Ok(()) } diff --git a/server/src/state.rs b/server/src/state.rs deleted file mode 100644 index 82f3e83..0000000 --- a/server/src/state.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::auth::JwtDetails; - -pub type RedisPool = bb8::Pool; - -#[derive(Clone)] -pub struct AppState { - pub redis: RedisPool, - pub jwt_details: JwtDetails, -} diff --git a/server/src/svc.rs b/server/src/svc.rs new file mode 100644 index 0000000..2732420 --- /dev/null +++ b/server/src/svc.rs @@ -0,0 +1,126 @@ +use candid::Principal; +use futures::{prelude::*, stream::FuturesUnordered}; +use redis::AsyncCommands; +use tonic::{Request, Response, Status}; +use types::{ + private_yral_metadata_server::PrivateYralMetadata, + public_yral_metadata_server::PublicYralMetadata, BulkDeleteReq, GetUserMetadataReq, + SetUserMetadataReq, UserMetadata, UserMetadataJson, +}; + +use crate::Error; + +const METADATA_FIELD: &str = "metadata"; + +pub type RedisPool = bb8::Pool; + +#[derive(Debug, Clone)] +pub struct YralMetadataServer { + pub redis: RedisPool, +} + +impl YralMetadataServer { + pub fn new(redis: RedisPool) -> Self { + Self { redis } + } + + async fn set_user_metadata_inner(&self, req: SetUserMetadataReq) -> Result<(), Error> { + let signature = req.signature()?; + let user_principal = req.user_principal()?; + let metadata = req.user_metadata.ok_or(Error::MetadataMissing)?; + let msg = metadata.clone().try_into()?; + signature.verify_identity(user_principal, msg)?; + + let user = user_principal.to_text(); + let mut conn = self.redis.get().await?; + let meta_json = UserMetadataJson::try_from(metadata)?; + let meta_raw = serde_json::to_vec(&meta_json)?; + let _replaced: bool = conn.hset(user, METADATA_FIELD, &meta_raw).await?; + Ok(()) + } + + async fn get_user_metadata_inner( + &self, + req: GetUserMetadataReq, + ) -> Result, Error> { + let user = Principal::try_from_slice(&req.user_principal_id)?.to_text(); + let mut conn = self.redis.get().await?; + let meta_raw: Option> = conn.hget(&user, METADATA_FIELD).await?; + let Some(meta_raw) = meta_raw else { + return Ok(None); + }; + let meta: UserMetadataJson = serde_json::from_slice(&meta_raw)?; + Ok(Some(UserMetadata { + user_canister_id: meta.user_canister_id.as_slice().to_vec(), + user_name: meta.user_name, + })) + } + + async fn delete_bulk_inner(&self, req: BulkDeleteReq) -> Result<(), Error> { + let conn = self.redis.get().await?; + let keys: Vec<_> = req + .users + .into_iter() + .map(|key| { + let principal = Principal::try_from_slice(&key)?; + Ok::<_, Error>(principal.to_text()) + }) + .collect::>()?; + + let deletes: FuturesUnordered<_> = keys + .into_iter() + .map(|key| async { + conn.clone() + .hdel::<_, _, bool>(key.clone(), METADATA_FIELD) + .await + .map_err(|e| (key, Error::from(e))) + }) + .collect(); + + let results = deletes.collect::>>().await; + let errors = results + .into_iter() + .filter_map(|res| res.err()) + .collect::>(); + + if !errors.is_empty() { + log::error!("failed to delete keys: {:?}", errors); + return Err(Error::DeleteKeys); + } + + Ok(()) + } +} + +#[tonic::async_trait] +impl PublicYralMetadata for YralMetadataServer { + async fn set_user_metadata( + &self, + req: Request, + ) -> Result, Status> { + let req = req.into_inner(); + self.set_user_metadata_inner(req).await?; + + Ok(Response::new(())) + } + + async fn get_user_metadata( + &self, + req: Request, + ) -> Result, Status> { + let req = req.into_inner(); + let Some(meta) = self.get_user_metadata_inner(req).await? else { + return Err(Status::not_found("metadata not found")); + }; + Ok(Response::new(meta)) + } +} + +#[tonic::async_trait] +impl PrivateYralMetadata for YralMetadataServer { + async fn bulk_delete(&self, req: Request) -> Result, Status> { + let req = req.into_inner(); + self.delete_bulk_inner(req).await?; + Ok(Response::new(())) + } +} diff --git a/types/Cargo.toml b/types/Cargo.toml index d78c2b6..8ee62dd 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -10,3 +10,14 @@ serde.workspace = true candid.workspace = true thiserror.workspace = true yral-identity = { workspace = true, default-features = false } +tonic = "0.11" +prost = "0.12" +serde_json.workspace = true + +[features] +client = [] +server = [] +default = ["client", "server"] + +[build-dependencies] +tonic-build = "0.11" \ No newline at end of file diff --git a/types/build.rs b/types/build.rs new file mode 100644 index 0000000..0210b22 --- /dev/null +++ b/types/build.rs @@ -0,0 +1,16 @@ +fn main() -> Result<(), Box> { + let mut builder = tonic_build::configure(); + + #[cfg(feature = "client")] + { + builder = builder.build_client(true); + } + + #[cfg(feature = "server")] + { + builder = builder.build_server(true); + } + + builder.compile(&["proto/yral_metadata.proto"], &["proto"])?; + Ok(()) +} diff --git a/types/proto/yral_metadata.proto b/types/proto/yral_metadata.proto new file mode 100644 index 0000000..565e861 --- /dev/null +++ b/types/proto/yral_metadata.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +import "google/protobuf/empty.proto"; +package yral_metadata; + +service PublicYralMetadata { + rpc GetUserMetadata(GetUserMetadataReq) returns (UserMetadata); + rpc SetUserMetadata(SetUserMetadataReq) returns (google.protobuf.Empty); +} + +service PrivateYralMetadata { + rpc BulkDelete(BulkDeleteReq) returns (google.protobuf.Empty); +} + +message GetUserMetadataReq { + bytes user_principal_id = 1; +} + +message UserMetadata { + bytes user_canister_id = 1; + string user_name = 2; +} + +message SetUserMetadataReq { + UserMetadata user_metadata = 1; + string signature_json = 2; + bytes user_principal_id = 3; +} + +message BulkDeleteReq { + repeated bytes users = 1; +} \ No newline at end of file diff --git a/types/src/error.rs b/types/src/error.rs deleted file mode 100644 index d66df5b..0000000 --- a/types/src/error.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -#[derive(Deserialize, Serialize, Error, Debug)] -#[non_exhaustive] -pub enum ApiError { - #[error("invalid signature provided")] - InvalidSignature, - #[error("internal error: redis")] - Redis, - #[error("internal error: deser")] - Deser, - #[error("jwt error - invalid token")] - Jwt, - #[error("invalid authentication token")] - AuthToken, - #[error("missing authentication token")] - AuthTokenMissing, - #[error("failed to delete keys (redis)")] - DeleteKeys, - #[error("unknown: {0}")] - Unknown(String), -} diff --git a/types/src/lib.rs b/types/src/lib.rs index 959c15c..92dcd30 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -1,40 +1,77 @@ -pub mod error; -use candid::Principal; -use error::ApiError; +use candid::{types::principal::PrincipalError, Principal}; use serde::{Deserialize, Serialize}; use yral_identity::{msg_builder::Message, Signature}; -pub type ApiResult = Result; +tonic::include_proto!("yral_metadata"); -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] -pub struct UserMetadata { - pub user_canister_id: Principal, - pub user_name: String, +impl UserMetadata { + pub fn new(user_canister_id: Principal, user_name: String) -> Self { + Self { + user_canister_id: user_canister_id.as_slice().to_vec(), + user_name, + } + } } -impl From for Message { - fn from(value: UserMetadata) -> Self { - Message::default() +impl TryFrom for Message { + type Error = PrincipalError; + fn try_from(value: UserMetadata) -> Result { + let canister_id = Principal::try_from_slice(&value.user_canister_id)?; + Ok(Message::default() .method_name("set_user_metadata".into()) - .args((value.user_canister_id, value.user_name)) + .args((canister_id, value.user_name)) // unwrap is safe here because (Principal, String) serialization can't fail - .unwrap() + .unwrap()) + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct UserMetadataJson { + pub user_canister_id: Principal, + pub user_name: String, +} + +impl From for UserMetadata { + fn from(value: UserMetadataJson) -> Self { + Self { + user_canister_id: value.user_canister_id.as_slice().to_vec(), + user_name: value.user_name, + } } } -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] -pub struct SetUserMetadataReq { - pub metadata: UserMetadata, - pub signature: Signature, +impl TryFrom for UserMetadataJson { + type Error = PrincipalError; + fn try_from(value: UserMetadata) -> Result { + Ok(Self { + user_canister_id: Principal::try_from_slice(&value.user_canister_id)?, + user_name: value.user_name, + }) + } } -pub type SetUserMetadataRes = (); +impl SetUserMetadataReq { + pub fn new(user_principal: Principal, meta: UserMetadata, signature: Signature) -> Self { + Self { + user_metadata: Some(meta), + signature_json: serde_json::to_string(&signature).unwrap(), + user_principal_id: user_principal.as_slice().to_vec(), + } + } -pub type GetUserMetadataRes = Option; + pub fn signature(&self) -> Result { + serde_json::from_str(&self.signature_json) + } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] -pub struct BulkUsers { - pub users: Vec, + pub fn user_principal(&self) -> Result { + Principal::try_from_slice(&self.user_principal_id) + } } -pub type DeleteMetadataBulkRes = (); +impl BulkDeleteReq { + pub fn new(users: impl IntoIterator) -> Self { + Self { + users: users.into_iter().map(|p| p.as_slice().to_vec()).collect(), + } + } +} From 5dd7d832bb2ee8d303c355398286fadba9541fe5 Mon Sep 17 00:00:00 2001 From: rupansh Date: Fri, 12 Jul 2024 20:14:39 +0530 Subject: [PATCH 2/4] feat(CI) add local dockerfile --- Dockerfile.local | 35 +++++++++++++++++++++++++++++++++++ build-server.sh | 2 +- config.docker.toml | 6 ++++++ docker-run.sh | 4 ++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.local create mode 100644 config.docker.toml create mode 100755 docker-run.sh diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 0000000..b1ab19c --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,35 @@ +FROM clux/muslrust:stable AS chef +USER root +RUN cargo install cargo-chef +WORKDIR /app + +FROM chef as planner + +WORKDIR /app +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef as build + +WORKDIR /app + +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json +RUN apt-get update && apt-get install -y protobuf-compiler --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + + +COPY . . + +RUN ./build-server.sh + +FROM redis:alpine as runner + +WORKDIR /app + +COPY --from=build /app/target/x86_64-unknown-linux-musl/release/yral-metadata-server . +COPY ./config.docker.toml config.toml +COPY ./docker-run.sh . +ENV RUST_LOG debug + +CMD ["./docker-run.sh"] diff --git a/build-server.sh b/build-server.sh index 7c76c44..7276419 100755 --- a/build-server.sh +++ b/build-server.sh @@ -1,3 +1,3 @@ #!/bin/bash -cd server && cargo build --release --target x86_64-unknown-linux-musl \ No newline at end of file +cd server && cargo build --release --target x86_64-unknown-linux-musl diff --git a/config.docker.toml b/config.docker.toml new file mode 100644 index 0000000..bcab5d3 --- /dev/null +++ b/config.docker.toml @@ -0,0 +1,6 @@ +bind_address = "0.0.0.0:8000" +legacy_bind_address = "0.0.0.0:8001" +redis_url = "redis://127.0.0.1:6379" +jwt_public_key = """-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEA+7sElHIjIrCJeIMAWr1qN8vt/r1iuGmZDUR9mn7itq0= +-----END PUBLIC KEY-----""" diff --git a/docker-run.sh b/docker-run.sh new file mode 100755 index 0000000..fd3f2e1 --- /dev/null +++ b/docker-run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +redis-server --daemonize yes +./yral-metadata-server From d9b320f3066af3a42f5e06101fcded0973768eb8 Mon Sep 17 00:00:00 2001 From: rupansh Date: Fri, 12 Jul 2024 20:25:29 +0530 Subject: [PATCH 3/4] chore(CI): add protobuf-compiler to workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4b41ee..91d2af0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: - uses: awalsh128/cache-apt-pkgs-action@v1 with: - packages: musl-tools # provides musl-gcc + packages: musl-tools protobuf-compiler # provides musl-gcc version: 1.0 - name: Build the project to `musl` output From a4879e2e711c17beeb12ed6987ba315c110be9e5 Mon Sep 17 00:00:00 2001 From: rupansh Date: Tue, 23 Jul 2024 21:53:51 +0530 Subject: [PATCH 4/4] feat(CI/CD): add push to ghcr.io --- .github/workflows/build.yml | 2 +- .github/workflows/deploy.yml | 4 ++-- .github/workflows/publish-image.yml | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/publish-image.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 91d2af0..5348fb1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Rust Setup uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 27c8f63..8b893a9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,9 +16,9 @@ jobs: needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build-musl - run: chmod +x target/x86_64-unknown-linux-musl/release/yral-metadata-server diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml new file mode 100644 index 0000000..25d142a --- /dev/null +++ b/.github/workflows/publish-image.yml @@ -0,0 +1,25 @@ +name: Deploy to ghcr.io + +on: + workflow_dispatch: + push: + branches: + - rupansh/grpc + +jobs: + docker_publish: + runs-on: "ubuntu-20.04" + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Build and publish docker image + uses: macbre/push-to-ghcr@master + with: + image_name: go-bazzinga/yral-metadata-dev + github_token: ${{ secrets.GITHUB_TOKEN }} + dockerfile: ./Dockerfile.local + image_tag: ${{ github.sha }}