From 66fac9d18550d04ba1523a20e94f8d204ec3cafe Mon Sep 17 00:00:00 2001 From: Paul Z Date: Sun, 27 Aug 2023 22:10:16 +0200 Subject: [PATCH] openid connect integration --- Cargo.lock | 990 ++++++++++++++++++++++++++++---------------- Cargo.toml | 14 +- flake.lock | 129 ++++++ flake.nix | 77 ++++ proto/rebacs.proto | 94 +---- src/grpc_service.rs | 526 +++++++---------------- src/main.rs | 80 ++-- src/relation_set.rs | 651 ++++++++++------------------- 8 files changed, 1304 insertions(+), 1257 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/Cargo.lock b/Cargo.lock index b65b45f..b561260 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,19 +3,34 @@ version = 3 [[package]] -name = "aho-corasick" -version = "1.0.1" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "async-stream" @@ -36,29 +51,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", + "syn 2.0.29", ] [[package]] @@ -69,13 +73,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.16" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113713495a32dd0ab52baf5c10044725aa3aec00b31beda84218e469029b72a3" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -113,10 +117,31 @@ dependencies = [ ] [[package]] -name = "base64" -version = "0.21.0" +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" [[package]] name = "bitflags" @@ -124,6 +149,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block-buffer" version = "0.10.4" @@ -135,9 +166,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -156,9 +187,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -168,9 +202,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "compact_str" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff0805f79ecb1b35163f3957a6934ea8d04fcd36ef98b52e7316f63e72e73d1" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" dependencies = [ "castaway", "cfg-if", @@ -181,9 +215,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -199,10 +233,16 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.6" +name = "deranged" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -216,32 +256,47 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", ] [[package]] -name = "errno" -version = "0.3.1" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -256,12 +311,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fixedbitset" @@ -275,6 +327,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -290,6 +351,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -309,9 +381,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -326,9 +400,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -336,10 +410,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.18" +name = "gimli" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -347,7 +427,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -360,6 +440,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -368,27 +454,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -426,24 +494,21 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -456,13 +521,27 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -475,6 +554,16 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -482,27 +571,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown 0.14.0", ] [[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "ipnet" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "hermit-abi", + "rustix", + "windows-sys", ] [[package]] @@ -516,19 +612,33 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.3", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -537,21 +647,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -559,18 +669,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matchit" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" [[package]] name = "memchr" @@ -585,15 +692,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "mio" -version = "0.8.6" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -603,20 +718,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] -name = "num_cpus" -version = "1.15.0" +name = "num-bigint" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "hermit-abi 0.2.6", + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[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 = "once_cell" -version = "1.17.1" +name = "object" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking_lot" @@ -630,58 +784,67 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.0.0", ] [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.29", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -695,16 +858,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - [[package]] name = "prettyplease" version = "0.1.25" @@ -717,9 +870,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -778,17 +931,11 @@ dependencies = [ "prost", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -824,12 +971,23 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +name = "rebacs" +version = "0.1.0" dependencies = [ - "bitflags", + "compact_str", + "dotenvy", + "env_logger", + "hex", + "jsonwebtoken", + "log", + "prost", + "reqwest", + "serde", + "sha2", + "thiserror", + "tokio", + "tonic", + "tonic-build", ] [[package]] @@ -838,14 +996,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ "aho-corasick", "memchr", @@ -854,9 +1024,48 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64 0.21.3", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] [[package]] name = "ring" @@ -874,24 +1083,29 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.37.15" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.21.0" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring", @@ -901,18 +1115,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64", + "base64 0.21.3", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" dependencies = [ "ring", "untrusted", @@ -920,21 +1134,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -948,29 +1162,52 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "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 = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -987,19 +1224,31 @@ dependencies = [ ] [[package]] -name = "slab" -version = "0.4.8" +name = "simple_asn1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -1011,6 +1260,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" @@ -1036,9 +1295,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -1053,15 +1312,15 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1074,29 +1333,75 @@ dependencies = [ ] [[package]] -name = "themis" -version = "0.1.0" +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ - "compact_str", - "dotenvy", - "hex", - "log", - "pretty_env_logger", - "prost", - "serde", - "sha2", - "tokio", - "tonic", - "tonic-build", + "thiserror-impl", ] [[package]] -name = "tokio" -version = "1.27.0" +name = "thiserror-impl" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ - "autocfg", + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "time" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", "bytes", "libc", "mio", @@ -1104,9 +1409,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1121,20 +1426,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", ] [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", @@ -1174,7 +1479,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.21.3", "bytes", "futures-core", "futures-util", @@ -1217,7 +1522,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand", @@ -1255,20 +1560,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1286,10 +1591,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] -name = "unicode-ident" -version = "1.0.8" +name = "unicode-bidi" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] [[package]] name = "untrusted" @@ -1297,6 +1617,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.4" @@ -1305,11 +1636,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1321,9 +1651,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1331,24 +1661,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.29", "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" +name = "wasm-bindgen-futures" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1356,33 +1698,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + [[package]] name = "which" version = "4.4.0" @@ -1425,134 +1773,78 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] diff --git a/Cargo.toml b/Cargo.toml index db4070d..3c45978 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,16 +6,24 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dotenvy = "0.15.7" +log = "0.4.17" +env_logger = "0.10.0" + serde = { version="1.0", features=["derive"] } tokio = { version = "1.27.0", features = ["full"] } -log = "0.4.17" -pretty_env_logger = "0.4.0" -dotenvy = "0.15.7" + tonic = { version="0.9.2", features=["tls"] } prost = "0.11.9" sha2 = "0.10.6" hex = "0.4.3" compact_str = "0.7.0" +thiserror = "1.0.47" + +jsonwebtoken = "8.3.0" + +reqwest = { version="0.11.20", features=["json", "rustls-tls"], default-features=false} + [build-dependencies] tonic-build = "0.9.2" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4f03d54 --- /dev/null +++ b/flake.lock @@ -0,0 +1,129 @@ +{ + "nodes": { + "crane": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": [ + "rust-overlay" + ] + }, + "locked": { + "lastModified": 1691423162, + "narHash": "sha256-cReUZCo83YEEmFcHX8CcOVTZYUrcWgHQO34zxQzy7WI=", + "owner": "ipetkov", + "repo": "crane", + "rev": "b5d9d42ea3fa8fea1805d9af1416fe207d0dd1dc", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1691472822, + "narHash": "sha256-XVfYZ2oB3lNPVq6sHCY9WkdQ8lHoIDzzbpg8bB6oBxA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "41c7605718399dcfa53dd7083793b6ae3bc969ff", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1691547503, + "narHash": "sha256-l0AIKJucygbDFc2vuAkxmFMjNNJImDd7jYahA88/E+o=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "3380f16b39457b49c8186d5e20e7a68ccf4fc96e", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4376614 --- /dev/null +++ b/flake.nix @@ -0,0 +1,77 @@ +{ + description = "rebacs"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + rust-overlay.follows = "rust-overlay"; + }; + }; + }; + + outputs = { self, nixpkgs, flake-utils, rust-overlay, crane}: + flake-utils.lib.eachDefaultSystem + (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + + rustToolchain = pkgs.rust-bin.nightly.latest.default; + + protoFilter = path: _type: builtins.match ".*proto$" path != null; + tailwindFilter = path: _type: builtins.match "^tailwind.config.js$" path != null; + protoOrCargo = path: type: (protoFilter path type) || (tailwindFilter path type) || (craneLib.filterCargoSources path type); + + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; + src = pkgs.lib.cleanSourceWith { + src = craneLib.path ./.; + filter = protoOrCargo; + }; + + nativeBuildInputs = with pkgs; [ rustToolchain pkg-config ]; + buildInputs = with pkgs; [ protobuf ]; + + commonArgs = { + inherit src buildInputs nativeBuildInputs; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + bin = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; + }); + + dockerImage = pkgs.dockerTools.buildImage { + name = "rebacs"; + tag = "latest"; + config = { + Cmd = [ "${bin}/bin/rebacs" ]; + }; + }; + + in + with pkgs; + { + packages = { + inherit bin dockerImage; + default = bin; + }; + devShells.default = mkShell { + inputsFrom = [ bin ]; + }; + } + ); +} diff --git a/proto/rebacs.proto b/proto/rebacs.proto index 8322645..2e4276d 100644 --- a/proto/rebacs.proto +++ b/proto/rebacs.proto @@ -1,95 +1,45 @@ syntax = "proto3"; package eu.zettoit.rebacs; -service RelationService { - rpc Create(RelationCreateReq) returns (RelationCreateRes); - rpc Delete(RelationDeleteReq) returns (RelationDeleteRes); - rpc Exists(RelationExistsReq) returns (RelationExistsRes); +service RebacService { + rpc Grant(GrantReq) returns (GrantRes); + rpc Revoke(RevokeReq) returns (RevokeRes); + rpc Exists(ExistsReq) returns (ExistsRes); + rpc IsPermitted(IsPermittedReq) returns (IsPermittedRes); } -service QueryService { - // check if one object or objectset is related to another by a relation - rpc IsRelatedTo(QueryIsRelatedToReq) returns (QueryIsRelatedToRes); - // get all objects that are related to one object by a relation - rpc GetRelated(QueryGetRelatedReq) returns (QueryGetRelatedRes); - - // get all objects that the given object has a relation with - rpc GetRelations(QueryGetRelationsReq) returns (QueryGetRelationsRes); -} - -message RelationCreateReq{ - ObjectOrSet src = 1; +message GrantReq{ + Object src = 1; Object dst = 2; - string relation = 3; } -message RelationCreateRes{} +message GrantRes{} -message RelationDeleteReq{ - ObjectOrSet src = 1; - Object dst = 3; - string relation = 4; -} -message RelationDeleteRes{} - -message RelationExistsReq{ - ObjectOrSet src = 1; +message RevokeReq{ + Object src = 1; Object dst = 2; - string relation = 3; } -message RelationExistsRes{ +message RevokeRes{} + +message ExistsReq{ + Object src = 1; + Object dst = 2; +} +message ExistsRes{ bool exists = 1; } -message QueryIsRelatedToReq{ - ObjectOrSet src = 1; - Object dst = 2; - string relation = 3; -} -message QueryIsRelatedToRes{ - bool related = 1; -} - -message QueryGetRelatedReq{ - Object dst = 1; - optional string relation = 2; - optional string namespace = 3; - optional uint32 depth = 4; -} -message QueryGetRelatedRes{ - repeated QueryGetRelatedItem objects = 1; -} -message QueryGetRelatedItem{ - string relation = 1; - Object src = 2; -} - -message QueryGetRelationsReq{ +message IsPermittedReq{ Object src = 1; - optional string relation = 2; - optional string namespace = 3; - optional uint32 depth = 4; -} -message QueryGetRelationsRes{ - repeated QueryGetRelationsItem related = 1; -} -message QueryGetRelationsItem{ - string relation = 1; Object dst = 2; } +message IsPermittedRes{ + bool permitted = 1; +} + message Object{ string namespace = 1; string id = 2; -} -message Set{ - string namespace = 1; - string id = 2; - string relation = 3; -} - -message ObjectOrSet { - string namespace = 1; - string id = 2; optional string relation = 3; } diff --git a/src/grpc_service.rs b/src/grpc_service.rs index 5d33e74..c01b147 100644 --- a/src/grpc_service.rs +++ b/src/grpc_service.rs @@ -1,421 +1,207 @@ -use std::collections::HashMap; use std::sync::Arc; +use jsonwebtoken::{decode, DecodingKey, TokenData, Validation}; use log::info; -use sha2::{Digest, Sha256}; +use serde::Deserialize; use tokio::sync::mpsc::Sender; -use tokio::sync::Mutex; use tonic::metadata::MetadataMap; use tonic::{Request, Response, Status}; +use crate::rebacs_proto::Object; use crate::rebacs_proto::{ - query_service_server::QueryService, relation_service_server::RelationService, Object, - QueryGetRelatedItem, QueryGetRelatedReq, QueryGetRelatedRes, QueryGetRelationsItem, - QueryGetRelationsReq, QueryGetRelationsRes, QueryIsRelatedToReq, QueryIsRelatedToRes, - RelationCreateReq, RelationCreateRes, RelationDeleteReq, RelationDeleteRes, RelationExistsReq, - RelationExistsRes, + rebac_service_server, ExistsReq, ExistsRes, GrantReq, GrantRes, IsPermittedReq, IsPermittedRes, + RevokeReq, RevokeRes, }; -use crate::relation_set::{ObjectOrSet, RelationSet}; +use crate::relation_set::{NodeId, RelationSet}; #[derive(Clone)] -pub struct GraphService { - pub api_keys: Arc>>, - pub graph: Arc>, +pub struct RebacService { + pub graph: Arc, + pub oidc_pubkey: DecodingKey, + pub oidc_validation: Validation, pub save_trigger: Sender<()>, } -const API_KEY_NS: &str = "rebacs_key"; -const NAMESPACE_NS: &str = "rebacs_ns"; +const NAMESPACE_NS: &str = "namespace"; +const USER_NS: &str = "user"; +const GRANT_RELATION: &str = "grant"; +const REVOKE_RELATION: &str = "revoke"; #[tonic::async_trait] -impl RelationService for GraphService { - async fn create( - &self, - request: Request, - ) -> Result, Status> { - let mut graph = self.graph.lock().await; +impl rebac_service_server::RebacService for RebacService { + async fn grant(&self, request: Request) -> Result, Status> { + let token = + extract_token(request.metadata(), &self.oidc_pubkey, &self.oidc_validation).await?; - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; + let (src, dst) = extract_src_dst(&request.get_ref().src, &request.get_ref().dst)?; - let req_src = request - .get_ref() - .src - .as_ref() - .ok_or(Status::invalid_argument("src must be set"))?; - let req_dst = request - .get_ref() - .dst - .as_ref() - .ok_or(Status::invalid_argument("dst must be set"))?; - let req_rel = &request.get_ref().relation; - - if req_rel.is_empty() { - return Err(Status::invalid_argument("relation must be set")); - } - if req_dst.namespace.is_empty() { - return Err(Status::invalid_argument("dst.namespace must be set")); - } - if req_dst.id.is_empty() { - return Err(Status::invalid_argument("dst.id must be set")); - } - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "write", - (NAMESPACE_NS, &*req_dst.namespace), - u32::MAX, - ) { + if !is_permitted(&token, &dst, GRANT_RELATION, &self.graph).await { return Err(Status::permission_denied( - "missing dst.namespace write permissions", - ))?; + "token not permitted to grant permissions on dst", + )); } - if req_src.namespace.is_empty() { - return Err(Status::invalid_argument("src.namespace must be set")); - } - if req_src.id.is_empty() { - return Err(Status::invalid_argument("src.id must be set")); - } - let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { - if req_src_relation.is_empty() { - return Err(Status::invalid_argument("src.relation must be set")); - } - - (&*req_src.namespace, &*req_src.id, req_src_relation).into() - } else { - (&*req_src.namespace, &*req_src.id).into() - }; - - graph.insert( - src.clone(), - req_rel.clone(), - (req_dst.namespace.clone(), req_dst.id.clone()), + info!( + "created relation {}:{}#{}@{}:{}#{} for {}", + dst.namespace, + dst.id, + dst.relation.clone().unwrap_or_default(), + src.namespace, + src.id, + src.relation.clone().unwrap_or_default(), + token.claims.sub ); - info!("created relation"); + self.graph.insert(src, dst).await; self.save_trigger.send(()).await.unwrap(); - Ok(Response::new(RelationCreateRes {})) + Ok(Response::new(GrantRes {})) } - async fn delete( - &self, - request: Request, - ) -> Result, Status> { - let mut graph = self.graph.lock().await; + async fn revoke(&self, request: Request) -> Result, Status> { + let token = + extract_token(request.metadata(), &self.oidc_pubkey, &self.oidc_validation).await?; - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; + let (src, dst) = extract_src_dst(&request.get_ref().src, &request.get_ref().dst)?; - let req_src = request - .get_ref() - .src - .as_ref() - .ok_or(Status::invalid_argument("src must be set"))?; - let req_dst = request - .get_ref() - .dst - .as_ref() - .ok_or(Status::invalid_argument("dst must be set"))?; - let req_rel = &request.get_ref().relation; - - if req_rel.is_empty() { - return Err(Status::invalid_argument("relation must be set")); - } - if req_dst.namespace.is_empty() { - return Err(Status::invalid_argument("dst.namespace must be set")); - } - if req_dst.id.is_empty() { - return Err(Status::invalid_argument("dst.id must be set")); - } - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "write", - (NAMESPACE_NS, &*req_dst.namespace), - u32::MAX, - ) { + if !is_permitted(&token, &dst, REVOKE_RELATION, &self.graph).await { return Err(Status::permission_denied( - "missing dst.namespace write permissions", - ))?; + "token not permitted to revoke permissions on dst", + )); } - if req_src.namespace.is_empty() { - return Err(Status::invalid_argument("src.namespace must be set")); - } - if req_src.id.is_empty() { - return Err(Status::invalid_argument("src.id must be set")); - } - let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { - if req_src_relation.is_empty() { - return Err(Status::invalid_argument("src.relation must be set")); - } + self.graph + .remove( + ( + src.namespace.to_string(), + src.id.to_string(), + src.relation.clone(), + ), + (dst.namespace.clone(), dst.id.clone(), dst.relation.clone()), + ) + .await; - (&*req_src.namespace, &*req_src.id, req_src_relation).into() - } else { - (&*req_src.namespace, &*req_src.id).into() - }; - - graph.remove(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id)); - - info!("deleted relation"); + info!( + "delted relation {}:{}#{}@{}:{}#{} for {}", + dst.namespace, + dst.id, + dst.relation.clone().unwrap_or_default(), + src.namespace, + src.id, + src.relation.clone().unwrap_or_default(), + token.claims.sub + ); self.save_trigger.send(()).await.unwrap(); - Ok(Response::new(RelationDeleteRes {})) + Ok(Response::new(RevokeRes {})) } - async fn exists( + async fn exists(&self, request: Request) -> Result, Status> { + let token = + extract_token(request.metadata(), &self.oidc_pubkey, &self.oidc_validation).await?; + + let (src, dst) = extract_src_dst(&request.get_ref().src, &request.get_ref().dst)?; + + let exists = self.graph.has(src, dst).await; + + Ok(Response::new(ExistsRes { exists })) + } + + async fn is_permitted( &self, - request: Request, - ) -> Result, Status> { - let graph = self.graph.lock().await; + request: Request, + ) -> Result, Status> { + let token = + extract_token(request.metadata(), &self.oidc_pubkey, &self.oidc_validation).await?; - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; + let (src, dst) = extract_src_dst(&request.get_ref().src, &request.get_ref().dst)?; - let req_src = request - .get_ref() - .src - .as_ref() - .ok_or(Status::invalid_argument("src must be set"))?; - let req_dst = request - .get_ref() - .dst - .as_ref() - .ok_or(Status::invalid_argument("dst must be set"))?; - let req_rel = &request.get_ref().relation; + let permitted = self.graph.has_recursive(src, dst, None).await; - if req_rel.is_empty() { - return Err(Status::invalid_argument("relation must be set")); - } - if req_dst.namespace.is_empty() { - return Err(Status::invalid_argument("dst.namespace must be set")); - } - if req_dst.id.is_empty() { - return Err(Status::invalid_argument("dst.id must be set")); - } - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "read", - (NAMESPACE_NS, &*req_dst.namespace), - u32::MAX, - ) { - return Err(Status::permission_denied( - "missing dst.namespace write permissions", - ))?; - } - if req_src.namespace.is_empty() { - return Err(Status::invalid_argument("src.namespace must be set")); - } - if req_src.id.is_empty() { - return Err(Status::invalid_argument("src.id must be set")); - } - let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { - if req_src_relation.is_empty() { - return Err(Status::invalid_argument("src.relation must be set")); - } - - (&*req_src.namespace, &*req_src.id, req_src_relation).into() - } else { - (&*req_src.namespace, &*req_src.id).into() - }; - - let exists = graph.has(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id)); - - Ok(Response::new(RelationExistsRes { exists })) + Ok(Response::new(IsPermittedRes { permitted })) } } -#[tonic::async_trait] -impl QueryService for GraphService { - async fn is_related_to( - &self, - request: Request, - ) -> Result, Status> { - let graph = self.graph.lock().await; +#[derive(Debug, Clone, Deserialize)] +pub struct Claims { + pub aud: Vec, + pub exp: usize, + pub iat: usize, + pub iss: String, + pub sub: String, + pub azp: String, - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; - - let req_src = request - .get_ref() - .src - .as_ref() - .ok_or(Status::invalid_argument("src must be set"))?; - let req_dst = request - .get_ref() - .dst - .as_ref() - .ok_or(Status::invalid_argument("dst must be set"))?; - let req_rel = &request.get_ref().relation; - - if req_rel.is_empty() { - return Err(Status::invalid_argument("relation must be set")); - } - if req_dst.namespace.is_empty() { - return Err(Status::invalid_argument("dst.namespace must be set")); - } - if req_dst.id.is_empty() { - return Err(Status::invalid_argument("dst.id must be set")); - } - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "read", - (NAMESPACE_NS, &*req_dst.namespace), - u32::MAX, - ) { - return Err(Status::permission_denied( - "missing dst.namespace read permissions", - ))?; - } - - if req_src.namespace.is_empty() { - return Err(Status::invalid_argument("src.namespace must be set")); - } - if req_src.id.is_empty() { - return Err(Status::invalid_argument("src.id must be set")); - } - let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() { - if req_src_relation.is_empty() { - return Err(Status::invalid_argument("src.relation must be set")); - } - - (&*req_src.namespace, &*req_src.id, req_src_relation).into() - } else { - (&*req_src.namespace, &*req_src.id).into() - }; - - let related = graph.has_recursive( - src, - req_rel.as_str(), - (&*req_dst.namespace, &*req_dst.id), - u32::MAX, - ); - - Ok(Response::new(QueryIsRelatedToRes { related })) - } - async fn get_related( - &self, - request: Request, - ) -> Result, Status> { - let graph = self.graph.lock().await; - - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; - - let req_dst = request - .get_ref() - .dst - .as_ref() - .ok_or(Status::invalid_argument("dst must be set"))?; - let req_rel = &request.get_ref().relation; - - if req_dst.namespace.is_empty() { - return Err(Status::invalid_argument("dst.namespace must be set")); - } - if req_dst.id.is_empty() { - return Err(Status::invalid_argument("dst.id must be set")); - } - - let req_namespace = &request.get_ref().namespace; - let req_depth = &request.get_ref().depth; - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "read", - (NAMESPACE_NS, &*req_dst.namespace), - u32::MAX, - ) { - return Err(Status::permission_denied( - "missing dst.namespace read permissions", - ))?; - } - - let dst = (req_dst.namespace.as_ref(), req_dst.id.as_ref()); - - let objects = graph - .related_to( - dst, - req_rel.as_deref(), - req_namespace.as_deref(), - req_depth.unwrap_or(u32::MAX), - ) - .into_iter() - .map(|x| QueryGetRelatedItem { - src: Some(Object { - namespace: x.1.namespace.to_string(), - id: x.1.id.to_string(), - }), - relation: x.0 .0.to_string(), - }) - .collect::<_>(); - - Ok(Response::new(QueryGetRelatedRes { objects })) - } - async fn get_relations( - &self, - request: Request, - ) -> Result, Status> { - let graph = self.graph.lock().await; - - let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?; - - let req_src = request - .get_ref() - .src - .as_ref() - .ok_or(Status::invalid_argument("src must be set"))?; - let src = (&*req_src.namespace, &*req_src.id); - - let req_rel = &request.get_ref().relation; - let req_namespace = &request.get_ref().namespace; - let req_depth = &request.get_ref().depth; - - if !graph.has_recursive( - (API_KEY_NS, &*api_key), - "read", - (NAMESPACE_NS, &*req_src.namespace), - u32::MAX, - ) { - return Err(Status::permission_denied( - "missing src.namespace read permissions", - ))?; - } - - let related = graph - .relations( - src, - req_rel.as_deref(), - req_namespace.as_deref(), - req_depth.unwrap_or(u32::MAX), - ) - .into_iter() - .map(|x| QueryGetRelationsItem { - dst: Some(Object { - namespace: x.1.namespace.to_string(), - id: x.1.id.to_string(), - }), - relation: x.0 .0.to_string(), - }) - .collect::<_>(); - - Ok(Response::new(QueryGetRelationsRes { related })) - } + pub name: String, + pub preferred_username: String, + pub given_name: String, + pub family_name: String, + pub email: String, } -async fn api_key_from_req( +async fn extract_token( metadata: &MetadataMap, - api_keys: &Arc>>, -) -> Result { - let api_key = metadata - .get("x-api-key") + pubkey: &DecodingKey, + validation: &Validation, +) -> Result, Status> { + let token = metadata + .get("authorization") .map(|x| x.to_str().unwrap()) - .ok_or(Status::unauthenticated("x-api-key required"))?; + .ok_or(Status::unauthenticated("authorization header required"))?; - let mut hasher = Sha256::new(); - hasher.update(api_key); - let api_key = hex::encode(hasher.finalize()); - let api_keys = api_keys.lock().await; - let api_key = api_keys - .get(&api_key) - .ok_or(Status::unauthenticated("api-key invalid"))?; - Ok(api_key.to_string()) + let token = decode::(token, pubkey, validation) + .map_err(|_| Status::unauthenticated("authorization header invalid"))?; + + Ok(token) +} + +async fn is_permitted( + token: &TokenData, + dst: &NodeId, + relation: &str, + graph: &RelationSet, +) -> bool { + let s1 = graph + .has_recursive( + (USER_NS, token.claims.sub.as_str()), + (dst.namespace.as_str(), dst.id.as_str(), relation), + None, + ) + .await; + + let s2 = graph + .has_recursive( + (USER_NS, token.claims.sub.as_str()), + (NAMESPACE_NS, dst.namespace.as_str(), relation), + None, + ) + .await; + + s1 || s2 +} + +fn extract_src_dst(src: &Option, dst: &Option) -> Result<(NodeId, NodeId), Status> { + let src = src + .as_ref() + .ok_or(Status::invalid_argument("src must be set"))?; + let src: NodeId = (src.namespace.clone(), src.id.clone(), src.relation.clone()).into(); + let dst = dst + .as_ref() + .ok_or(Status::invalid_argument("dst must be set"))?; + let dst: NodeId = (dst.namespace.clone(), dst.id.clone(), dst.relation.clone()).into(); + + if dst.namespace.is_empty() { + return Err(Status::invalid_argument("dst.namespace must be set")); + } + if dst.id.is_empty() { + return Err(Status::invalid_argument("dst.id must be set")); + } + + if src.namespace.is_empty() { + return Err(Status::invalid_argument("src.namespace must be set")); + } + if src.id.is_empty() { + return Err(Status::invalid_argument("src.id must be set")); + } + + Ok((src, dst)) } diff --git a/src/main.rs b/src/main.rs index c15850d..df1e0ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,16 @@ #![feature(btree_cursors)] -use std::{collections::HashMap, sync::Arc, time::Duration}; +use std::{env, sync::Arc, time::Duration}; -use grpc_service::GraphService; +use grpc_service::RebacService; +use jsonwebtoken::{Algorithm, DecodingKey, Validation}; +use log::info; use relation_set::RelationSet; -//use grpc_service::GraphService; +use serde::Deserialize; use tokio::{ fs::{self, File}, - io::{AsyncBufReadExt, BufReader}, select, - sync::{mpsc::channel, Mutex}, + sync::mpsc::channel, }; use tonic::transport::Server; @@ -17,35 +18,26 @@ pub mod grpc_service; pub mod rebacs_proto; pub mod relation_set; -use crate::rebacs_proto::{ - query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer, -}; +use crate::rebacs_proto::rebac_service_server; + +#[derive(Deserialize)] +struct IssuerDiscovery { + public_key: String, +} #[tokio::main] async fn main() { dotenvy::dotenv().ok(); - pretty_env_logger::init(); - - let mut api_keys = HashMap::new(); - if let Ok(file) = File::open("api_keys.dat").await { - let reader = BufReader::new(file); - let mut lines = reader.lines(); - while let Ok(Some(line)) = lines.next_line().await { - let line = line.replace(' ', ""); - let mut line = line.split('='); - let name = line.next().unwrap().to_string(); - let hash = line.next().unwrap().to_string(); - api_keys.insert(hash, name); - } - } + env_logger::init(); + info!("loading graph from graph.dat"); let graph = if let Ok(mut file) = File::open("graph.dat").await { RelationSet::from_file(&mut file).await } else { - RelationSet::new() + RelationSet::default() }; - let graph = Arc::new(Mutex::new(graph)); + let graph = Arc::new(graph); let (save_tx, mut save_rx) = channel::<()>(32); let save_thread_graph = graph.clone(); @@ -55,24 +47,48 @@ async fn main() { _ = tokio::time::sleep(Duration::from_secs(30)) => {} _ = save_rx.recv() => {} }; - let graph = save_thread_graph.lock().await; - + info!("saving graph"); let _ = fs::copy("graph.dat", "graph.dat.bak").await; let mut file = File::create("graph.dat").await.unwrap(); - graph.to_file(&mut file).await; + save_thread_graph.to_file(&mut file).await; } }); - let graph_service = GraphService { - api_keys: Arc::new(Mutex::new(api_keys)), + let issuer = env::var("OIDC_ISSUER").expect("OIDC_ISSUER env var"); + info!("loading public key from {issuer}"); + let issuer_key = reqwest::get(&issuer) + .await + .unwrap() + .json::() + .await + .unwrap() + .public_key; + + let pem = format!( + "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----", + issuer_key + ); + + let oidc_pubkey = DecodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); + + let mut oidc_validation = Validation::new(Algorithm::RS256); + oidc_validation.set_issuer(&[&issuer]); + oidc_validation.set_audience(&[env::var("OIDC_AUDIENCE").expect("OIDC_AUDIENCE env var")]); + + let rebac_service = RebacService { graph: graph.clone(), save_trigger: save_tx.clone(), + oidc_pubkey, + oidc_validation, }; + let listen = "[::]:50051"; + info!("starting grpc server on {listen}"); Server::builder() - .add_service(RelationServiceServer::new(graph_service.clone())) - .add_service(QueryServiceServer::new(graph_service)) - .serve("[::]:50051".parse().unwrap()) + .add_service(rebac_service_server::RebacServiceServer::new( + rebac_service.clone(), + )) + .serve(listen.parse().unwrap()) .await .unwrap() } diff --git a/src/relation_set.rs b/src/relation_set.rs index 98d2ec8..217cd2e 100644 --- a/src/relation_set.rs +++ b/src/relation_set.rs @@ -1,399 +1,197 @@ use std::{ + borrow::Borrow, cmp::Ordering, - collections::{BTreeMap, BinaryHeap, HashMap, HashSet}, - ops::{Bound, Deref}, + collections::{BTreeSet, BinaryHeap, HashSet}, + fmt::Debug, + hash::Hash, + ops::Deref, sync::Arc, }; -use compact_str::CompactString; use tokio::{ fs::File, io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + sync::RwLock, }; -#[derive(Hash, PartialEq, Eq, Clone, Debug)] -pub struct Object { - pub namespace: CompactString, - pub id: CompactString, +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct NodeId { + pub namespace: String, + pub id: String, + pub relation: Option, } -#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] -pub struct ObjectRef<'a> { - pub namespace: &'a str, - pub id: &'a str, +pub struct Node { + pub id: NodeId, + pub edges_in: RwLock>>, + pub edges_out: RwLock>>, } -#[derive(PartialEq, Eq, Hash, Clone, Debug)] -pub enum ObjectOrSet { - Object(Object), - Set((Object, Relation)), +#[derive(Debug, PartialEq, Eq)] +struct Distanced { + distance: u32, + data: T, } -#[derive(Hash, PartialEq, Eq, Clone, Debug)] -pub struct Relation(pub CompactString); - -type S = ObjectOrSet; -type R = Relation; -type D = Object; +#[derive(Default)] pub struct RelationSet { - src_to_dst: BTreeMap, HashMap, HashSet>>>, - dst_to_src: BTreeMap, HashMap, HashSet>>>, + nodes: RwLock>>, } impl RelationSet { - pub fn new() -> Self { - Self { - src_to_dst: BTreeMap::new(), - dst_to_src: BTreeMap::new(), - } - } - - pub fn insert(&mut self, src: impl Into, rel: impl Into, dst: impl Into) { - let src = Arc::new(src.into()); - let rel = Arc::new(rel.into()); - let dst = Arc::new(dst.into()); - - if let Some(rels_dsts) = self.src_to_dst.get_mut(&src) { - if let Some(dsts) = rels_dsts.get_mut(&rel) { - dsts.insert(dst.clone()); - } else { - let mut dsts = HashSet::new(); - dsts.insert(dst.clone()); - rels_dsts.insert(rel.clone(), dsts); - } - } else { - let mut rels_dsts = HashMap::new(); - let mut dsts = HashSet::new(); - dsts.insert(dst.clone()); - rels_dsts.insert(rel.clone(), dsts); - self.src_to_dst.insert(src.clone(), rels_dsts); - } - - if let Some(rels_srcs) = self.dst_to_src.get_mut(&dst) { - if let Some(srcs) = rels_srcs.get_mut(&rel) { - srcs.insert(src.clone()); - } else { - let mut srcs = HashSet::new(); - srcs.insert(src.clone()); - rels_srcs.insert(rel.clone(), srcs); - } - } else { - let mut rels_srcs = HashMap::new(); - let mut srcs = HashSet::new(); - srcs.insert(src.clone()); - rels_srcs.insert(rel.clone(), srcs); - self.dst_to_src.insert(dst.clone(), rels_srcs); - } - } - - pub fn remove(&mut self, src: impl Into, rel: impl Into, dst: impl Into) { + pub async fn insert(&self, src: impl Into, dst: impl Into) { let src = src.into(); - let rel = rel.into(); let dst = dst.into(); - if let Some(dsts) = self - .src_to_dst - .get_mut(&src) - .and_then(|rels_dsts| rels_dsts.get_mut(&rel)) - { - dsts.remove(&dst); - } + let mut nodes = self.nodes.write().await; - if let Some(srcs) = self - .dst_to_src - .get_mut(&dst) - .and_then(|rels_srcs| rels_srcs.get_mut(&rel)) - { - srcs.remove(&src); - } - } - - pub fn remove_by_src(&mut self, src: &S) { - for (rel, dsts) in self.src_to_dst.remove(src).iter().flat_map(|x| x.iter()) { - for dst in dsts { - if let Some(srcs) = self - .dst_to_src - .get_mut(dst) - .and_then(|rels_srcs| rels_srcs.get_mut(rel)) - { - srcs.remove(src); - } + let src_node = match nodes.get(&src) { + Some(node) => node.clone(), + None => { + let node = Arc::new(Node { + id: src, + edges_out: RwLock::new(vec![]), + edges_in: RwLock::new(vec![]), + }); + nodes.insert(node.clone()); + node } - } - } - - pub fn remove_by_dst(&mut self, dst: &D) { - for (rel, srcs) in self.dst_to_src.remove(dst).iter().flat_map(|x| x.iter()) { - for src in srcs { - if let Some(dsts) = self - .src_to_dst - .get_mut(src) - .and_then(|rels_dsts| rels_dsts.get_mut(rel)) - { - dsts.remove(dst); - } + }; + let dst_node = match nodes.get(&dst).cloned() { + Some(node) => node.clone(), + None => { + let node = Arc::new(Node { + id: dst, + edges_out: RwLock::new(vec![]), + edges_in: RwLock::new(vec![]), + }); + nodes.insert(node.clone()); + node } - } + }; + add_edge(src_node, dst_node).await; } - pub fn has(&self, src: impl Into, rel: impl Into, dst: impl Into) -> bool { + pub async fn remove(&self, src: impl Into, dst: impl Into) { let src = src.into(); - let rel = rel.into(); let dst = dst.into(); - self.src_to_dst - .get(&src) - .and_then(|rels_dsts| rels_dsts.get(&rel)) - .and_then(|dsts| dsts.get(&dst)) - .is_some() + let mut nodes = self.nodes.write().await; + + let src = nodes.get(&src).cloned(); + let dst = nodes.get(&dst).cloned(); + + if let (Some(src), Some(dst)) = (src, dst) { + src.edges_out.write().await.retain(|x| x != &dst); + dst.edges_in.write().await.retain(|x| x != &src); + + if src.edges_in.read().await.is_empty() && src.edges_out.read().await.is_empty() { + nodes.remove(&src.id); + } + if dst.edges_in.read().await.is_empty() && dst.edges_out.read().await.is_empty() { + nodes.remove(&dst.id); + } + } } - pub fn has_object<'a>(&self, obj: impl Into<&'a Object>) -> bool { - let obj = obj.into(); - let has_dst_obj = self.dst_to_src.contains_key(obj); + pub async fn has(&self, src: impl Into, dst: impl Into) -> bool { + let src = src.into(); + let dst = dst.into(); - let cursor = self - .src_to_dst - .lower_bound(Bound::Included(&ObjectOrSet::Object(obj.clone()))); - - let has_src_obj = if let Some(key) = cursor.key() { - obj.namespace == key.object().namespace && obj.id == key.object().id - } else { - false + let (src, dst) = { + let nodes = self.nodes.read().await; + (nodes.get(&src).cloned(), nodes.get(&dst).cloned()) }; - has_dst_obj || has_src_obj + if let (Some(src), Some(dst)) = (src, dst) { + src.edges_out.read().await.contains(&dst) + } else { + false + } } - pub fn has_recursive( + pub async fn has_recursive<'a>( &self, - src: impl Into, - rel: impl Into, - dst: impl Into, - limit: u32, + src: impl Into, + dst: impl Into, + limit: Option, ) -> bool { let src = src.into(); - let rel = rel.into(); let dst = dst.into(); - let mut dist: HashMap<(Arc, Arc), u32> = HashMap::new(); - let mut q: BinaryHeap, Arc)>> = BinaryHeap::new(); + let src = self.nodes.read().await.get(&src).unwrap().clone(); - for (nrel, ndst) in self - .src_to_dst - .get(&src) + let src_neighbors = src + .edges_out + .read() + .await .iter() - .flat_map(|x| x.iter()) - .flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone()))) - { - if *nrel == rel && *ndst == dst { - return true; - } - dist.insert((ndst.clone(), nrel.clone()), 1); - q.push(Distanced::one((ndst, nrel))); - } + .map(|x| Distanced::one(x.clone())) + .collect::>(); + + let mut q: BinaryHeap>> = BinaryHeap::from(src_neighbors); + let mut visited: HashSet> = HashSet::new(); while let Some(distanced) = q.pop() { - let node_dist = distanced.distance() + 1; - if node_dist > limit { - break; + if distanced.id == dst { + return true; } - let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone())); - for (nrel, ndst) in self - .src_to_dst - .get(&node) - .iter() - .flat_map(|x| x.iter()) - .flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone()))) - { - if *nrel == rel && *ndst == dst { - return true; + if let Some(limit) = limit { + if distanced.distance() > limit { + return false; } - if let Some(existing_node_dist) = dist.get(&*distanced) { - if *existing_node_dist <= node_dist { - continue; - } - } - dist.insert((ndst.clone(), nrel.clone()), node_dist); - q.push(Distanced::one((ndst, nrel))); } + + for neighbor in distanced.edges_out.read().await.iter() { + if !visited.contains(neighbor) { + q.push(Distanced::new(neighbor.clone(), distanced.distance() + 1)) + } + } + + visited.insert(distanced.clone()); } false } - pub fn related_to( - &self, - dst: impl Into, - rel: Option>, - namespace: Option<&str>, - limit: u32, - ) -> Vec<(Relation, Object)> { - let rel = rel.map(|x| x.into()); - let dst = dst.into(); - - let mut related: Vec<(Relation, Object)> = vec![]; - - let mut dist: HashMap<(Arc, Arc), u32> = HashMap::new(); - let mut q: BinaryHeap, Arc)>> = BinaryHeap::new(); - - for (nrel, ndst) in self - .dst_to_src - .get(&dst) - .iter() - .flat_map(|x| x.iter()) - .flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone()))) - { - match &*ndst { - ObjectOrSet::Object(obj) => { - if (rel.is_none() || rel.as_ref() == Some(&nrel)) - && (namespace.is_none() || namespace == Some(&obj.namespace)) - { - related.push(((*nrel).clone(), obj.clone())); - } - } - ObjectOrSet::Set((obj, rel)) => { - let obj = Arc::new(obj.clone()); - let rel = Arc::new(rel.clone()); - dist.insert((obj.clone(), rel.clone()), 1); - q.push(Distanced::one((obj, rel))); - } - } - } - - while let Some(distanced) = q.pop() { - let node_dist = distanced.distance() + 1; - if node_dist > limit { - break; - } - - for ndst in self - .dst_to_src - .get(&distanced.0) - .and_then(|x| x.get(&distanced.1)) - .iter() - .flat_map(|x| x.iter()) - { - match &**ndst { - ObjectOrSet::Object(obj) => { - if (rel.is_none() || rel.as_ref() == Some(&distanced.1)) - && (namespace.is_none() || namespace == Some(&obj.namespace)) - { - related.push(((*distanced.1).clone(), obj.clone())); - } - } - ObjectOrSet::Set((obj, rel)) => { - let obj = Arc::new(obj.clone()); - let rel = Arc::new(rel.clone()); - dist.insert((obj.clone(), rel.clone()), node_dist); - q.push(Distanced::one((obj, rel))); - } - } - } - } - - related - } - - pub fn relations( - &self, - src: impl Into, - rel: Option>, - namespace: Option<&str>, - limit: u32, - ) -> Vec<(Relation, Object)> { - let rel = rel.map(|x| x.into()); - let src = src.into(); - - let mut related: Vec<(Relation, Object)> = vec![]; - - let mut dist: HashMap, u32> = HashMap::new(); - let mut q: BinaryHeap>> = BinaryHeap::new(); - - for (nrel, ndst) in self - .src_to_dst - .get(&src) - .iter() - .flat_map(|x| x.iter()) - .flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone()))) - { - if (rel.is_none() || rel.as_ref() == Some(&nrel)) - && (namespace.is_none() || namespace == Some(&ndst.namespace)) - { - related.push(((*nrel).clone(), (*ndst).clone())); - } - let obj = Arc::new(ObjectOrSet::Set(((*ndst).clone(), (*nrel).clone()))); - dist.insert(obj.clone(), 1); - q.push(Distanced::one(obj)); - } - - while let Some(distanced) = q.pop() { - let node_dist = distanced.distance() + 1; - if node_dist > limit { - break; - } - - for (nrel, ndsts) in self - .src_to_dst - .get(&*distanced) - .iter() - .flat_map(|x| x.iter()) - { - for ndst in ndsts { - if (rel.is_none() || rel.as_ref() == Some(nrel)) - && (namespace.is_none() || namespace == Some(&ndst.namespace)) - { - related.push(((**nrel).clone(), (**ndst).clone())); - } - let obj = Arc::new(ObjectOrSet::Set(((**ndst).clone(), (**nrel).clone()))); - dist.insert(obj.clone(), node_dist); - q.push(Distanced::one(obj)); - } - } - } - - related - } - pub async fn to_file(&self, file: &mut File) { - for (dst, rels_srcs) in self.dst_to_src.iter() { - file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes()) - .await - .unwrap(); - for (rel, srcs) in rels_srcs.iter() { - if srcs.is_empty() { - continue; - } - let srcs = srcs - .iter() - .map(|src| { - let src_obj = src.object(); - let src_str = if src_obj.namespace == dst.namespace && src_obj.id == dst.id - { - "self".to_string() - } else { - format!("{}:{}", src_obj.namespace, src_obj.id) - }; - match &**src { - ObjectOrSet::Object(_) => src_str, - ObjectOrSet::Set(set) => { - format!("{}#{}", src_str, set.1 .0) - } - } - }) - .reduce(|acc, x| acc + ", " + &x) - .unwrap_or_default(); - - file.write_all(format!("{} = [{}]\n", &rel.0, &srcs).as_bytes()) + let mut current: (String, String) = (String::new(), String::new()); + for node in self.nodes.read().await.iter() { + if current != (node.id.namespace.clone(), node.id.id.clone()) { + current = (node.id.namespace.clone(), node.id.id.clone()); + file.write_all("\n".as_bytes()).await.unwrap(); + file.write_all(format!("[{}:{}]\n", ¤t.0, ¤t.1).as_bytes()) + .await + .unwrap(); + } + + let srcs = node + .edges_in + .read() + .await + .iter() + .map(|src| { + if src.id.namespace == current.0 && src.id.id == current.1 { + "self".to_string() + } else if let Some(rel) = &src.id.relation { + format!("{}:{}#{}", &src.id.namespace, &src.id.id, &rel) + } else { + format!("{}:{}", &src.id.namespace, &src.id.id) + } + }) + .reduce(|acc, x| acc + ", " + &x) + .unwrap_or_default(); + + if let Some(rel) = &node.id.relation { + file.write_all(format!("{} = [ {} ]\n", &rel, &srcs).as_bytes()) .await .unwrap(); } - file.write_all("\n".as_bytes()).await.unwrap(); } } pub async fn from_file(file: &mut File) -> Self { let reader = BufReader::new(file); let mut lines = reader.lines(); - let mut graph = Self::new(); + let graph = Self::default(); let mut node: Option<(String, String)> = None; while let Ok(Some(line)) = lines.next_line().await { if line.starts_with('[') && line.ends_with(']') { @@ -408,10 +206,10 @@ impl RelationSet { let arr_stop = line.find(']').unwrap(); let rel = line[..equals_pos].trim(); - let arr = line[arr_start + 1..arr_stop].split(", "); + let arr = line[arr_start + 1..arr_stop].trim().split(", "); for obj in arr { - let src: ObjectOrSet = if obj.contains('#') { + let src: NodeId = if obj.contains('#') { let sep_1 = obj.find(':'); let sep_2 = obj.find('#').unwrap(); @@ -435,7 +233,9 @@ impl RelationSet { (namespace, id).into() }; - graph.insert(src, rel, dst.clone()); + graph + .insert(src, (dst.0.as_str(), dst.1.as_str(), rel)) + .await; } } } @@ -444,13 +244,51 @@ impl RelationSet { } } -#[derive(PartialEq, Eq)] -struct Distanced { - distance: u32, - data: T, +impl Debug for Node { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Node").field("id", &self.id).finish() + } +} + +async fn add_edge(from: Arc, to: Arc) { + from.edges_out.write().await.push(to.clone()); + to.edges_in.write().await.push(from); +} + +impl Borrow for Arc { + fn borrow(&self) -> &NodeId { + &self.id + } +} + +impl PartialEq for Node { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} +impl Eq for Node {} + +impl PartialOrd for Node { + fn partial_cmp(&self, other: &Self) -> Option { + self.id.partial_cmp(&other.id) + } +} +impl Ord for Node { + fn cmp(&self, other: &Self) -> Ordering { + self.id.cmp(&other.id) + } +} + +impl Hash for Node { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } } impl Distanced { + pub fn new(data: T, distance: u32) -> Self { + Self { distance, data } + } pub fn one(data: T) -> Self { Self { distance: 1, data } } @@ -478,111 +316,62 @@ impl Ord for Distanced { } } -impl PartialOrd for Relation { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} -impl Ord for Relation { - fn cmp(&self, other: &Self) -> Ordering { - self.0.cmp(&other.0) - } -} - -impl PartialOrd for ObjectOrSet { - fn partial_cmp(&self, other: &Self) -> Option { - match ( - self.object().partial_cmp(other.object()), - self.relation(), - other.relation(), - ) { - (Some(Ordering::Equal), self_rel, other_rel) => self_rel.partial_cmp(&other_rel), - (ord, _, _) => ord, - } - } -} -impl Ord for ObjectOrSet { - fn cmp(&self, other: &Self) -> Ordering { - self.object() - .cmp(other.object()) - .then(self.relation().cmp(&other.relation())) - } -} - -impl PartialOrd for Object { - fn partial_cmp(&self, other: &Self) -> Option { - match self.namespace.partial_cmp(&other.namespace) { - Some(core::cmp::Ordering::Equal) => self.id.partial_cmp(&other.id), - ord => ord, - } - } -} -impl Ord for Object { - fn cmp(&self, other: &Self) -> Ordering { - self.namespace - .cmp(&other.namespace) - .then(self.id.cmp(&other.id)) - } -} - -impl From<(&str, &str)> for ObjectOrSet { - fn from((namespace, id): (&str, &str)) -> Self { - ObjectOrSet::Object(Object { - namespace: namespace.into(), - id: id.into(), - }) - } -} -impl From<(&str, &str, &str)> for ObjectOrSet { - fn from((namespace, id, rel): (&str, &str, &str)) -> Self { - ObjectOrSet::Set(((namespace, id).into(), Relation(rel.into()))) - } -} - -impl From<(&str, &str)> for Object { - fn from((namespace, id): (&str, &str)) -> Self { +impl From<(&str, &str)> for NodeId { + fn from(value: (&str, &str)) -> Self { Self { - namespace: namespace.into(), - id: id.into(), + namespace: value.0.to_string(), + id: value.1.to_string(), + relation: None, } } } -impl From<(String, String)> for Object { - fn from((namespace, id): (String, String)) -> Self { + +impl From<(&str, &str, &str)> for NodeId { + fn from(value: (&str, &str, &str)) -> Self { Self { - namespace: namespace.into(), - id: id.into(), + namespace: value.0.to_string(), + id: value.1.to_string(), + relation: Some(value.2.to_string()), } } } -impl From<&str> for Relation { - fn from(value: &str) -> Self { - Relation(value.into()) - } -} -impl From for Relation { - fn from(value: String) -> Self { - Relation(value.into()) +impl From<(&str, &str, Option<&str>)> for NodeId { + fn from(value: (&str, &str, Option<&str>)) -> Self { + Self { + namespace: value.0.to_string(), + id: value.1.to_string(), + relation: value.2.map(|x| x.to_string()), + } } } -impl ObjectOrSet { - pub fn object(&self) -> &Object { - match self { - ObjectOrSet::Object(obj) => obj, - ObjectOrSet::Set((obj, _)) => obj, - } - } - pub fn relation(&self) -> Option<&Relation> { - match self { - ObjectOrSet::Object(_) => None, - ObjectOrSet::Set((_, rel)) => Some(rel), +impl From<(String, String)> for NodeId { + fn from(value: (String, String)) -> Self { + Self { + namespace: value.0, + id: value.1, + relation: None, } } } -impl Relation { - pub fn new(relation: &str) -> Self { - Self(relation.into()) + +impl From<(String, String, String)> for NodeId { + fn from(value: (String, String, String)) -> Self { + Self { + namespace: value.0, + id: value.1, + relation: Some(value.2), + } + } +} + +impl From<(String, String, Option)> for NodeId { + fn from(value: (String, String, Option)) -> Self { + Self { + namespace: value.0, + id: value.1, + relation: value.2, + } } }