diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34970cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +.idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fad2dde --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2809 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "atomic" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +dependencies = [ + "autocfg", +] + +[[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", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cedar-agent" +version = "0.1.0" +dependencies = [ + "async-lock", + "async-trait", + "cedar-policy", + "cedar-policy-core", + "clap", + "envy", + "log", + "log4rs", + "rocket", + "rocket_okapi", + "serde", + "thiserror", + "tokio", +] + +[[package]] +name = "cedar-policy" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49558864ae5abfe1025d24696683501c9deda8b4c05eb1ea53123c2178bbf29a" +dependencies = [ + "cedar-policy-core", + "cedar-policy-validator", + "itertools", + "lalrpop-util", + "ref-cast", + "serde", + "serde_json", + "smol_str", + "thiserror", +] + +[[package]] +name = "cedar-policy-core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1ebfc1b22e03a4fd47e2fc835180d3b024d86a38a3ef0f50cb181babe095e1" +dependencies = [ + "arbitrary", + "either", + "ipnet", + "itertools", + "lalrpop", + "lalrpop-util", + "lazy_static", + "regex", + "rustc_lexer", + "serde", + "serde_json", + "serde_with", + "smol_str", + "stacker", + "thiserror", +] + +[[package]] +name = "cedar-policy-validator" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377d418530d4f36b1428c6133fc08053981a9c9997a6db58d3aa8cbf178097cc" +dependencies = [ + "arbitrary", + "cedar-policy-core", + "itertools", + "serde", + "serde_json", + "serde_with", + "smol_str", + "stacker", + "thiserror", + "unicode-security", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time 0.3.21", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.15", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core 0.20.1", + "darling_macro 0.20.1", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.15", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core 0.20.1", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "devise" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" +dependencies = [ + "bitflags", + "proc-macro2", + "proc-macro2-diagnostics 0.9.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "ena" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +dependencies = [ + "log", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "figment" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e56602b469b2201400dec66a66aec5a9b8761ee97cd1b8c96ab2483fcc16cc9" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e123d9ae7c02966b4d892e550bdc32164f05853cd40ab570650ad600596a8a" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +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" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lalrpop" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax 0.6.29", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +dependencies = [ + "regex", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "parking_lot", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror", + "thread-id", + "typemap-ors", + "winapi", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "okapi" +version = "0.7.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce66b6366e049880a35c378123fddb630b1a1a3c37fa1ca70caaf4a09f6e2893" +dependencies = [ + "log", + "schemars", + "serde", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "pear" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec95680a7087503575284e5063e14b694b7a9c0b065e5dceec661e0497127e8" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9661a3a53f93f09f2ea882018e4d7c88f6ff2956d809a276060476fd8c879d3c" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics 0.10.0", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", + "yansi", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", + "version_check", + "yansi", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.1", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "rocket" +version = "0.5.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" +dependencies = [ + "async-stream", + "async-trait", + "atomic", + "atty", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap", + "log", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "serde_json", + "state", + "tempfile", + "time 0.3.21", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" +dependencies = [ + "devise", + "glob", + "indexmap", + "proc-macro2", + "quote", + "rocket_http", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "rocket_http" +version = "0.5.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" +dependencies = [ + "cookie", + "either", + "futures", + "http", + "hyper", + "indexmap", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time 0.3.21", + "tokio", + "uncased", +] + +[[package]] +name = "rocket_okapi" +version = "0.8.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489f4f5b120762f7974e65b919fc462d0660fd8b839026d8985b850fe5acccb0" +dependencies = [ + "either", + "log", + "okapi", + "rocket", + "rocket_okapi_codegen", + "schemars", + "serde", + "serde_json", +] + +[[package]] +name = "rocket_okapi_codegen" +version = "0.8.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f94d1ffe41472e08463d7a2674f1db04dc4df745285e8369b33d3cfd6b0308" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "rocket_http", + "syn 1.0.109", +] + +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "indexmap", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "serde" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +dependencies = [ + "base64 0.21.0", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time 0.3.21", +] + +[[package]] +name = "serde_with_macros" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +dependencies = [ + "darling 0.20.1", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "thread-id" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f" +dependencies = [ + "libc", + "redox_syscall 0.2.16", + "winapi", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[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.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ubyte" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c81f0dae7d286ad0d9366d7679a77934cfc3cf3a8d67e82669794412b2368fe6" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-script" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" + +[[package]] +name = "unicode-security" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef5756b3097992b934b06608c69f48448a0fbe804bb1e72b982f6d7983e9e63" +dependencies = [ + "unicode-normalization", + "unicode-script", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[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", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +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" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +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" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +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" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +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" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +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" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +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" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..77a5d55 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "cedar-agent" +version = "0.1.0" +edition = "2021" +license-file = "LICENSE" +readme = "README.md" +homepage = "https://github.com/permitio/cedar-agent" +description = "Cedar-Agent is an HTTP server designed to efficiently manage a policy store and a data store. It provides a seamless integration with Cedar, a language for defining permissions as policies." +repository = "https://github.com/permitio/cedar-agent" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-lock = "2.7.0" +async-trait = "0.1.68" +cedar-policy = "2.0.1" +cedar-policy-core = "2.0.0" +clap = { version = "4.2.5", features = ["derive"] } +envy = "0.4.2" +log = "0.4.17" +log4rs = "1.2.0" +rocket = "0.5.0-rc.2" +rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger", "rapidoc"] } +serde = "1.0.160" +thiserror = "1.0.40" +tokio = "1.28.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bc1aae0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM rust:1.69-bullseye as build + +WORKDIR /agent +ARG CARGO_FLAGS="--release" + +COPY src src +COPY Cargo.toml Cargo.toml +COPY Cargo.lock Cargo.lock + +RUN cargo build ${CARGO_FLAGS} + +FROM debian:bullseye-slim as agent + +WORKDIR /agent + +COPY --from=build /agent/target/release/cedar-agent /agent/cedar-agent + +ENTRYPOINT ["/agent/cedar-agent"] + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d9d04f --- /dev/null +++ b/README.md @@ -0,0 +1,239 @@ +

+Cedar Agent +

+ +[![Current Crates.io Version](https://img.shields.io/crates/v/cedar-agent.svg)](https://crates.io/crates/cedar-agent) +![License](https://img.shields.io/crates/l/cedar-agent) + +## What is Cedar-Agent? + +Cedar-Agent is an HTTP server designed to efficiently manage a policy store and a data store. +It provides a seamless integration with [Cedar](https://www.cedarpolicy.com/en), a language for defining permissions as +policies. +With Cedar-Agent, you can easily control and monitor access to your application's resources by leveraging Cedar +policies. +If you are not familiar with Cedar, we encourage you to visit the [Cedar website](https://www.cedarpolicy.com/en) +and [playground](https://www.cedarpolicy.com/en/playground) to learn more about it. + +### Policy Store Management + +Cedar-Agent includes a store that allows you to create, retrieve, update, and delete policies. +These policies define who should have access to what resources within your application. +The policy store provides a centralized and flexible way to manage permissions, enabling fine-grained control over user +access. +Featured Policy Stores : + +- [x] In-Memory +- [ ] Redis + +### Data Store Management + +In addition to the policy store, Cedar-Agent also provides an in-memory data store. This data store allows you to store +and manage your application's data efficiently. By integrating the data store with Cedar-Agent, you can perform +authorized checks on the stored data based on incoming HTTP requests. +Featured Data Stores : + +- [x] In-Memory +- [ ] Redis + +### Authorization Checks + +One of the key features of Cedar-Agent is its ability to perform authorization checks on stored policies and data. +By evaluating the Cedar policies, Cedar-Agent ensures that each user's access is restricted to the resources they are +permitted to access. +Authorization checks are performed based on the incoming HTTP requests, providing an easy-to-use robust and secure +mechanism for controlling access to your application. + +Cedar-Agent offers a comprehensive solution for managing policies, data, and authorization checks within your +application. With its seamless integration with Cedar and its robust HTTP server capabilities, Cedar-Agent empowers you +to enforce fine-grained access control and protect your resources effectively. + +## How to Use + +To use Cedar-Agent, follow the steps below: + +### Prerequisites + +Before proceeding, ensure that you have Rust and Cargo installed on your system. If you don't have them installed, you +can visit the official [Rust installation page](https://www.rust-lang.org/tools/install) and follow the instructions +specific to your operating system. + +### Clone the Repository + +Start by cloning the Cedar-Agent repository to your local machine: + +```shell +git clone https://github.com/permitio/cedar-agent.git +cd cedar-agent +``` + +### Build + +To build Cedar-Agent, use the following command: + +```shell +cargo build +``` + +### Configuration + +Cedar Agent configuration is available using environment variables and command line arguments. + +- The port on which the Cedar Agent will listen for incoming HTTP requests. Defaults to `8180`. + `PORT` environment variable. + `--port`, `-p` command line argument. +- Authentication token to enforce using the `Authorization` header. Defaults to `None`. + `AUTHENTICATION` environment variable. + `--authentication`, `-a` command line argument. +- The address of the HTTP server. Defaults to `127.0.0.1`. + `ADDR` environment variable. + `--addr` command line argument. +- The log level to filter logs. Defaults to `info`. + `LOG_LEVEL` environment variable. + `--log-level`, `-l` command line argument. + +**command line arguments take precedence over environment variables when configuring the Cedar Agent** + +### Run + +There are several ways to run the Cedar Agent + +#### Run with cargo + +To run Cedar-Agent, use the following command: + +```shell +cargo run +``` + +to add any arguments to the command append them after `--`, for example: + +```shell +cargo run -- --port 8080 +``` + +#### Run the binary + +To run the binary, make sure you've done the [build step](#build), and run this command: + +```shell +./target/debug/cedar-agent +``` + +To check the arguments you can pass to the binary, run: + +```shell +./target/debug/cedar-agent --help +``` + +#### Run with docker + +To execute the Cedar Agent docker image, use the following command: + +```shell +docker run permitio/cedar-agent +``` + +### Test + +To test Cedar-Agent, use the following command: + +```shell +cargo test +``` + +### API Endpoints + +After running Cedar-Agent, the application provides comprehensive API documentation and endpoint schema +using Rapidoc and Swagger UI, that you can access through the following routes: + +- http://localhost:8180/rapidoc: Visit this route in your web browser to explore the interactive API + documentation powered by the Rapidoc tool. It provides detailed information about each endpoint, + including their parameters, + request bodies, and response structures. +- http://localhost:8180/swagger-ui: Access this route to interact with the Swagger UI, + which offers a user-friendly interface to browse the API endpoints. + It presents a visual representation of the available routes, along with their descriptions, + request and response schemas, and example requests. + +### Quickstart + +1. [Run the Cedar Agent](#run) +2. Store policy using this command: + + ```shell + curl -X PUT -H "Content-Type: application/json" -d @./examples/policies.json http://localhost:8180/v1/policies + ``` + +3. Store data using this command: + + ```shell + curl -X PUT -H "Content-Type: application/json" -d @./examples/data.json http://localhost:8180/v1/data + ``` + +4. Perform IsAuthorized check using this command: + + ```shell + curl -X POST -H "Content-Type: application/json" -d @./examples/allowed_authorization_query.json http://localhost:8180/v1/is_authorized + ``` + + The response is: + + ```json + { + "decision": "Allow", + "diagnostics": { + "reason": [ + "admins-policy" + ], + "errors": [] + } + } + ``` + As you can see the user is allowed to access the resource because policy id `admins-policy` permits it. + Check for a user that is not allowed to access the resource: + + ```shell + curl -X POST -H "Content-Type: application/json" -d @./examples/denied_authorization_query.json http://localhost:8180/v1/is_authorized + ``` + + The response is: + + ```json + { + "decision": "Deny", + "diagnostics": { + "reason": [], + "errors": [] + } + } + ``` + As you can see the user is denied access to the resource because no policy allows this request. + +**For more details about the performed requests you can check the [examples directory](examples)** + +## Community + +Come talk to us about Cedar Agent, or authorization in general - we would love to hear from you ❤️ + +You can raise questions and ask for features to be added to the road-map in our [**GitHub +discussions**](https://github.com/permitio/cedar-agent/discussions), +report issues in [**GitHub issues**](https://github.com/permitio/cedar-agent/issues), +join our Slack community to chat about authorization, open-source, realtime communication, tech, or anything else! + +If you are using our project, please consider giving us a ⭐️ + +[![Button][join-slack-link]][badge-slack-link] + +## Contributing + +If you encounter any issues or have suggestions for improvement, please open +an [issue](https://github.com/permitio/cedar-agent/issues), on the Cedar-Agent GitHub repository to get assistance from +the community. + +- Pull requests are welcome! (please make sure to include passing tests and docs) +- Prior to submitting a PR - open an issue on GitHub, or make sure your PR addresses an existing issue well. + +[join-slack-link]: https://i.ibb.co/wzrGHQL/Group-749.png + +[badge-slack-link]: https://io.permit.io/opalcommunity diff --git a/examples/allowed_authorization_query.json b/examples/allowed_authorization_query.json new file mode 100644 index 0000000..1f9fbf6 --- /dev/null +++ b/examples/allowed_authorization_query.json @@ -0,0 +1,5 @@ +{ + "principal": "User::\"admin.1@domain.com\"", + "action": "Action::\"create\"", + "resource": "Document::\"cedar-agent.pdf\"" +} \ No newline at end of file diff --git a/examples/data.json b/examples/data.json new file mode 100644 index 0000000..e0614a9 --- /dev/null +++ b/examples/data.json @@ -0,0 +1,158 @@ +[ + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + } + ], + "uid": { + "id": "admin.1@domain.com", + "type": "User" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "editor.1@domain.com", + "type": "User" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Viewer", + "type": "Role" + } + ], + "uid": { + "id": "viewer.1@domain.com", + "type": "User" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + } + ], + "uid": { + "id": "delete", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + } + ], + "uid": { + "id": "create", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "cedar-agent.pdf", + "type": "Document" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + }, + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "update", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + }, + { + "id": "Editor", + "type": "Role" + }, + { + "id": "Viewer", + "type": "Role" + } + ], + "uid": { + "id": "list", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Admin", + "type": "Role" + }, + { + "id": "Editor", + "type": "Role" + }, + { + "id": "Viewer", + "type": "Role" + } + ], + "uid": { + "id": "get", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "Admin", + "type": "Role" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "Editor", + "type": "Role" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "Viewer", + "type": "Role" + } + } +] \ No newline at end of file diff --git a/examples/denied_authorization_query.json b/examples/denied_authorization_query.json new file mode 100644 index 0000000..f587812 --- /dev/null +++ b/examples/denied_authorization_query.json @@ -0,0 +1,5 @@ +{ + "principal": "User::\"viewer.1@domain.com\"", + "action": "Action::\"create\"", + "resource": "Document::\"cedar-agent.pdf\"" +} \ No newline at end of file diff --git a/examples/policies.json b/examples/policies.json new file mode 100644 index 0000000..3bba00a --- /dev/null +++ b/examples/policies.json @@ -0,0 +1,14 @@ +[ + { + "id": "admins-policy", + "content": "permit(principal in Role::\"Admin\",action in [Action::\"get\",Action::\"list\",Action::\"update\",Action::\"create\",Action::\"delete\"],resource == Document::\"cedar-agent.pdf\");" + }, + { + "id": "editors-policy", + "content": "permit(principal in Role::\"Editor\",action in [Action::\"get\",Action::\"list\",Action::\"update\"],resource == Document::\"cedar-agent.pdf\");" + }, + { + "id": "viewers-policy", + "content": "permit(principal in Role::\"Viewer\",action in [Action::\"get\",Action::\"list\"],resource == Document::\"cedar-agent.pdf\");" + } +] diff --git a/src/authn.rs b/src/authn.rs new file mode 100644 index 0000000..a6f5359 --- /dev/null +++ b/src/authn.rs @@ -0,0 +1,101 @@ +use rocket::request::{FromRequest, Outcome}; +use rocket_okapi::gen::OpenApiGenerator; +use rocket_okapi::okapi; +use rocket_okapi::okapi::openapi3::{ + Object, Responses, SecurityRequirement, SecurityScheme, SecuritySchemeData, +}; +use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput}; + +use crate::config::Config; + +const AUTHENTICATION_HEADER: &'static str = "Authorization"; + +pub struct ApiKey(Option); + +impl ApiKey { + fn validate_matching_header(&self, request: &rocket::Request) -> bool { + let required_token = self.0.clone(); + if required_token.is_none() { + return true; + } + let token = request.headers().get_one(AUTHENTICATION_HEADER); + match token { + Some(token) => token == required_token.unwrap(), + None => false, + } + } +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for ApiKey { + type Error = (); + + async fn from_request(request: &'r rocket::Request<'_>) -> Outcome { + let token = request + .rocket() + .state::() + .map(|my_config| ApiKey(my_config.authentication.clone())); + match token { + Some(token) => { + if token.validate_matching_header(request) { + Outcome::Success(token) + } else { + Outcome::Failure((rocket::http::Status::Unauthorized, ())) + } + } + None => Outcome::Success(ApiKey(None)), + } + } +} + +impl<'a> OpenApiFromRequest<'a> for ApiKey { + fn from_request_input( + _gen: &mut OpenApiGenerator, + _name: String, + _required: bool, + ) -> rocket_okapi::Result { + // Setup global requirement for Security scheme + let security_scheme = SecurityScheme { + description: Some( + r#"Optional API key to access, + used if the agent was started with authentication configuration."# + .to_owned(), + ), + // Setup data requirements. + // This can be part of the `header`, `query` or `cookie`. + // In this case the header `authorization:` needs to be set. + data: SecuritySchemeData::ApiKey { + name: "authorization".to_owned(), + location: "header".to_owned(), + }, + extensions: Object::default(), + }; + // Add the requirement for this route/endpoint + // This can change between routes. + let mut security_req = SecurityRequirement::new(); + // Each security requirement needs to be met before access is allowed. + security_req.insert("ApiKeyAuth".to_owned(), Vec::new()); + // These vvvvvvv-----^^^^^^^^^^ values need to match exactly! + Ok(RequestHeaderInput::Security( + "ApiKeyAuth".to_owned(), + security_scheme, + security_req, + )) + } + + // Optionally add responses + // Also see `main.rs` part of this. + fn get_responses(gen: &mut OpenApiGenerator) -> rocket_okapi::Result { + use rocket_okapi::okapi::openapi3::RefOr; + // Can switch between to the but both are checked if they compile correctly + Ok(Responses { + // Recommended and most strait forward. + // And easy to add or remove new responses. + responses: okapi::map! { + "400".to_owned() => RefOr::Object(crate::errors::schemas::bad_request_response(gen)), + "401".to_owned() => RefOr::Object(crate::errors::schemas::unauthorized_response(gen)), + }, + ..Default::default() + }) + } +} diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000..db2ee9c --- /dev/null +++ b/src/common.rs @@ -0,0 +1,13 @@ +use std::fmt; +use std::fmt::{Display, Formatter}; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) struct EmptyError; + +impl Display for EmptyError { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + return Ok(()); + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5d5b666 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,81 @@ +use fmt::Debug; +use std::borrow::Borrow; +use std::fmt; + +use clap::Parser; +use log::LevelFilter; + +use serde::{Deserialize, Serialize}; + +#[derive(Parser, Serialize, Deserialize, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Config { + #[arg(short, long)] + pub authentication: Option, + #[arg(long)] + pub addr: Option, + #[arg(short, long)] + pub port: Option, + #[arg(short, long, value_enum)] + pub log_level: Option, +} + +impl Into for &Config { + fn into(self) -> rocket::figment::Figment { + let mut config = rocket::Config::figment(); + if let Some(authentication) = self.authentication.borrow() { + config = config.merge(("authentication", authentication)); + } + if let Some(addr) = self.addr.borrow() { + config = config.merge(("address", addr)); + } + if let Some(port) = self.port.borrow() { + config = config.merge(("port", port)); + } else { + config = config.merge(("port", 8180)) + } + + config + } +} + +impl Config { + fn new() -> Self { + Config { + authentication: None, + addr: None, + port: None, + log_level: None, + } + } + + fn merge(configs: Vec) -> Config { + let mut config = Config::new(); + for c in configs { + config.authentication = c.authentication.or(config.authentication); + config.addr = c.addr.or(config.addr); + config.port = c.port.or(config.port); + config.log_level = c.log_level.or(config.log_level); + } + + config + } + + fn from_args() -> Self { + Self::parse() + } + + fn from_env() -> Self { + match envy::from_env() { + Ok(env) => env, + Err(_) => Self::new(), + } + } +} + +pub fn init() -> Config { + let args = Config::from_args(); + let env = Config::from_env(); + + Config::merge(vec![args, env]) +} diff --git a/src/errors/catchers.rs b/src/errors/catchers.rs new file mode 100644 index 0000000..358ec6f --- /dev/null +++ b/src/errors/catchers.rs @@ -0,0 +1,36 @@ +use rocket::catch; +use rocket::http::Status; + +use rocket::Request; + +use crate::errors::response::ErrorResponse; + +#[catch(500)] +pub fn handle_500(status: Status, req: &Request<'_>) -> ErrorResponse { + let req_url = req.uri(); + return ErrorResponse { + reason: format!("An error occurred during handling {req_url}"), + description: "An unexpected error has occurred".to_owned(), + code: status.code, + }; +} + +#[catch(400)] +pub fn handle_400(_req: &Request<'_>) -> ErrorResponse { + return ErrorResponse { + description: "The request could not be understood by the server due to malformed syntax." + .to_owned(), + reason: "The request content is not valid".to_owned(), + code: 400, + }; +} + +#[catch(404)] +pub fn handle_404(req: &Request<'_>) -> ErrorResponse { + let req_url = req.uri(); + return ErrorResponse { + description: format!("The requested resource {req_url} was not found"), + reason: "The requested resource was not found".to_owned(), + code: 404, + }; +} diff --git a/src/errors/mod.rs b/src/errors/mod.rs new file mode 100644 index 0000000..988860b --- /dev/null +++ b/src/errors/mod.rs @@ -0,0 +1,3 @@ +pub mod catchers; +pub mod response; +pub(crate) mod schemas; diff --git a/src/errors/response.rs b/src/errors/response.rs new file mode 100644 index 0000000..f98f0ff --- /dev/null +++ b/src/errors/response.rs @@ -0,0 +1,119 @@ +use std::borrow::Borrow; + +use rocket::http::{ContentType, Status}; +use rocket::response::Responder; +use rocket::serde::json::serde_json; +use rocket::{response, Request, Response}; +use rocket_okapi::gen::OpenApiGenerator; +use rocket_okapi::okapi::openapi3::Responses; +use rocket_okapi::okapi::schemars; +use rocket_okapi::okapi::schemars::JsonSchema; +use rocket_okapi::response::OpenApiResponderInner; +use rocket_okapi::{okapi, OpenApiError}; +use serde::Serialize; +use thiserror::Error; + +use schemas::{bad_request_response, unauthorized_response}; + +use crate::errors::schemas; + +/// Error messages returned to user +#[derive(Debug, Serialize, JsonSchema)] +pub struct ErrorResponse { + /// The title of the error message + pub reason: String, + /// The description of the error + pub description: String, + // HTTP Status Code returned + pub code: u16, +} + +impl<'r> Responder<'r, 'static> for ErrorResponse { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + // Convert object to json + let body = serde_json::to_string(&self).unwrap(); + Response::build() + .sized_body(body.len(), std::io::Cursor::new(body)) + .header(ContentType::JSON) + .status(Status::new(self.code)) + .ok() + } +} + +#[derive(Debug, Error)] +pub enum AgentError { + #[error(" {} with the given id({}) not found", object, id)] + NotFound { object: &'static str, id: String }, + #[error("{} with the given id({}) already exists", object, id)] + Duplicate { object: &'static str, id: String }, + #[error( + "The content in the request does not match the specifications: {}", + reason + )] + BadRequest { reason: String }, +} + +impl AgentError { + fn status(&self) -> Status { + use self::AgentError::*; + match self { + NotFound { object: _, id: _ } => Status::NotFound, + Duplicate { object: _, id: _ } => Status::Conflict, + BadRequest { reason: _ } => Status::BadRequest, + } + } + + fn title(&self) -> String { + let status = self.status(); + // use if else if because + // the traits must be derived, manual `impl`s are not sufficient + // compile error is raised when using match + if status == Status::BadRequest { + "You have malformed a bad request".to_owned() + } else if status == Status::Unauthorized { + "You are not authorized to perform this action".to_owned() + } else if status == Status::NotFound { + "The requested resource was not found".to_owned() + } else if status == Status::Conflict { + "The requested resource already exists".to_owned() + } else if status.code >= 400 && status.code < 500 { + "An unexpected client error has occurred".to_owned() + } else { + "An unexpected server error has occurred".to_owned() + } + } + + fn message(&self) -> String { + format!("{self}") + } +} + +impl<'r> Responder<'r, 'static> for AgentError { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + let res = ErrorResponse { + code: self.status().code, + reason: self.title(), + description: self.message(), + }; + // Convert object to json + let body = serde_json::to_string(res.borrow()).unwrap(); + Response::build() + .sized_body(body.len(), std::io::Cursor::new(body)) + .header(ContentType::JSON) + .status(Status::new(res.code)) + .ok() + } +} + +impl OpenApiResponderInner for AgentError { + fn responses(gen: &mut OpenApiGenerator) -> Result { + use okapi::openapi3::RefOr; + Ok(Responses { + responses: okapi::map! { + "400".to_owned() => RefOr::Object(bad_request_response(gen)), + "401".to_owned() => RefOr::Object(unauthorized_response(gen)), + }, + ..Default::default() + }) + } +} diff --git a/src/errors/schemas.rs b/src/errors/schemas.rs new file mode 100644 index 0000000..69ff6b9 --- /dev/null +++ b/src/errors/schemas.rs @@ -0,0 +1,44 @@ +use crate::errors::response::ErrorResponse; +use rocket_okapi::gen::OpenApiGenerator; +use rocket_okapi::okapi; +use rocket_okapi::okapi::openapi3::MediaType; + +/// Create my custom response +/// +/// Putting this in a separate function somewhere will resolve issues like +/// +pub fn bad_request_response(gen: &mut OpenApiGenerator) -> okapi::openapi3::Response { + let schema = gen.json_schema::(); + okapi::openapi3::Response { + description: "\ + # 400 Bad Request\n\ + The request given is wrongly formatted or data was missing. \ + " + .to_owned(), + content: okapi::map! { + "application/json".to_owned() => MediaType { + schema: Some(schema), + ..Default::default() + } + }, + ..Default::default() + } +} + +pub fn unauthorized_response(gen: &mut OpenApiGenerator) -> okapi::openapi3::Response { + let schema = gen.json_schema::(); + okapi::openapi3::Response { + description: "\ + # 401 Unauthorized\n\ + The authentication given was incorrect or insufficient. \ + " + .to_owned(), + content: okapi::map! { + "application/json".to_owned() => MediaType { + schema: Some(schema), + ..Default::default() + } + }, + ..Default::default() + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..79c40c6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] + +mod authn; +mod common; +mod config; +mod errors; +mod routes; +pub mod schemas; +mod services; + +pub use services::*; diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..22fa7f1 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,17 @@ +use crate::config; +use log::LevelFilter; +use log4rs::append::console::{ConsoleAppender, Target}; +use log4rs::config::{Appender, Root}; +use log4rs::Config; + +pub(crate) fn init(conf: &config::Config) { + let log_level = conf.log_level.unwrap_or(LevelFilter::Info); + let stderr = ConsoleAppender::builder().target(Target::Stderr).build(); + + let config = Config::builder() + .appender(Appender::builder().build("stderr", Box::new(stderr))) + .build(Root::builder().appender("stderr").build(log_level)) + .unwrap(); + + let _handle = log4rs::init_config(config).unwrap(); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9edd2fb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,86 @@ +extern crate core; +extern crate rocket; + +use std::borrow::Borrow; + +use rocket::catchers; +use rocket_okapi::settings::UrlObject; +use rocket_okapi::{openapi_get_routes, rapidoc::*, swagger_ui::*}; + +use crate::services::data::memory::MemoryDataStore; +use crate::services::data::DataStore; +use crate::services::policies::memory::MemoryPolicyStore; +use crate::services::policies::PolicyStore; + +mod authn; +mod common; +mod config; +mod errors; +mod logger; +mod routes; +mod schemas; +mod services; + +#[rocket::main] +async fn main() { + let config = config::init(); + logger::init(&config); + let server_config: rocket::figment::Figment = config.borrow().into(); + let launch_result = rocket::custom(server_config) + .manage(config) + .manage(Box::new(MemoryPolicyStore::new()) as Box) + .manage(Box::new(MemoryDataStore::new()) as Box) + .manage(cedar_policy::Authorizer::new()) + .register( + "/", + catchers![ + errors::catchers::handle_500, + errors::catchers::handle_404, + errors::catchers::handle_400, + ], + ) + .mount( + "/v1", + openapi_get_routes![ + routes::healthy, + routes::policies::get_policies, + routes::policies::get_policy, + routes::policies::create_policy, + routes::policies::update_policies, + routes::policies::update_policy, + routes::policies::delete_policy, + routes::data::get_entities, + routes::data::update_entities, + routes::data::delete_entities, + routes::authorization::is_authorized, + ], + ) + .mount( + "/swagger-ui/", + make_swagger_ui(&SwaggerUIConfig { + url: "../v1/openapi.json".to_owned(), + ..Default::default() + }), + ) + .mount( + "/rapidoc/", + make_rapidoc(&RapiDocConfig { + general: GeneralConfig { + spec_urls: vec![UrlObject::new("General", "../v1/openapi.json")], + ..Default::default() + }, + hide_show: HideShowConfig { + allow_spec_url_load: false, + allow_spec_file_load: false, + ..Default::default() + }, + ..Default::default() + }), + ) + .launch() + .await; + match launch_result { + Ok(_) => println!("Rocket shut down gracefully."), + Err(err) => println!("Rocket had an error: {}", err), + }; +} diff --git a/src/routes/authorization.rs b/src/routes/authorization.rs new file mode 100644 index 0000000..4eea18e --- /dev/null +++ b/src/routes/authorization.rs @@ -0,0 +1,36 @@ +use cedar_policy::Authorizer; + +use log::info; + +use rocket::serde::json::Json; +use rocket::{post, State}; +use rocket_okapi::openapi; + +use crate::authn::ApiKey; +use crate::errors::response::AgentError; +use crate::schemas::authorization::{AuthorizationAnswer, AuthorizationCall}; +use crate::{DataStore, PolicyStore}; + +#[openapi] +#[post("/is_authorized", format = "json", data = "")] +pub async fn is_authorized( + _auth: ApiKey, + policy_store: &State>, + data_store: &State>, + authorizer: &State, + authorization_call: Json, +) -> Result, AgentError> { + let entities: cedar_policy::Entities = data_store.entities().await; + let policies = policy_store.policy_set().await; + let query: cedar_policy::Request = match authorization_call.into_inner().try_into() { + Ok(query) => query, + Err(err) => { + return Err(AgentError::BadRequest { + reason: err.to_string(), + }) + } + }; + info!("Querying cedar using {}", query); + let answer = authorizer.is_authorized(&query, &policies, &entities); + Ok(Json::from(AuthorizationAnswer::from(answer))) +} diff --git a/src/routes/data.rs b/src/routes/data.rs new file mode 100644 index 0000000..7f9ec3c --- /dev/null +++ b/src/routes/data.rs @@ -0,0 +1,44 @@ +use rocket::response::status; + +use rocket::serde::json::Json; +use rocket::{delete, get, put, State}; +use rocket_okapi::openapi; + +use crate::authn::ApiKey; +use crate::errors::response::AgentError; +use crate::schemas::data as schemas; +use crate::DataStore; + +#[openapi] +#[get("/data")] +pub async fn get_entities( + _auth: ApiKey, + data_store: &State>, +) -> Result, AgentError> { + Ok(Json::from(data_store.get_entities().await)) +} + +#[openapi] +#[put("/data", format = "json", data = "")] +pub async fn update_entities( + _auth: ApiKey, + data_store: &State>, + entities: Json, +) -> Result, AgentError> { + match data_store.update_entities(entities.into_inner()).await { + Ok(entities) => Ok(Json::from(entities)), + Err(err) => Err(AgentError::BadRequest { + reason: err.to_string(), + }), + } +} + +#[openapi] +#[delete("/data")] +pub async fn delete_entities( + _auth: ApiKey, + data_store: &State>, +) -> Result { + data_store.delete_entities().await; + Ok(status::NoContent) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..46d15b8 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,13 @@ +use rocket::get; +use rocket::response::status; +use rocket_okapi::openapi; + +pub mod authorization; +pub mod data; +pub mod policies; + +#[openapi] +#[get("/")] +pub async fn healthy() -> status::NoContent { + status::NoContent +} diff --git a/src/routes/policies.rs b/src/routes/policies.rs new file mode 100644 index 0000000..be85510 --- /dev/null +++ b/src/routes/policies.rs @@ -0,0 +1,103 @@ +use std::borrow::Borrow; + +use rocket::response::status; +use rocket::serde::json::Json; +use rocket::{delete, get, post, put, State}; +use rocket_okapi::openapi; + +use crate::authn::ApiKey; +use crate::errors::response::AgentError; +use crate::schemas::policies as schemas; +use crate::services::policies::PolicyStore; + +#[openapi] +#[get("/policies")] +pub async fn get_policies( + _auth: ApiKey, + policy_store: &State>, +) -> Result>, AgentError> { + Ok(Json::from(policy_store.get_policies().await)) +} + +#[openapi] +#[get("/policies/")] +pub async fn get_policy( + _auth: ApiKey, + id: String, + policy_store: &State>, +) -> Result, AgentError> { + match policy_store.get_policy(id.borrow()).await { + Ok(policy) => Ok(Json::from(policy)), + Err(_) => Err(AgentError::NotFound { + id, + object: "policy", + }), + } +} + +#[openapi] +#[post("/policies", format = "json", data = "")] +pub async fn create_policy( + _auth: ApiKey, + policy: Json, + policy_store: &State>, +) -> Result, AgentError> { + let policy = policy.into_inner(); + let added_policy = policy_store.create_policy(policy.borrow()).await; + match added_policy { + Ok(p) => Ok(Json::from(p)), + Err(_) => Err(AgentError::Duplicate { + id: policy.id, + object: "policy", + }), + } +} + +#[openapi] +#[put("/policies", format = "json", data = "")] +pub async fn update_policies( + _auth: ApiKey, + policy: Json>, + policy_store: &State>, +) -> Result>, AgentError> { + let updated_policy = policy_store.update_policies(policy.into_inner()).await; + match updated_policy { + Ok(p) => Ok(Json::from(p)), + Err(e) => Err(AgentError::BadRequest { + reason: e.to_string(), + }), + } +} + +#[openapi] +#[put("/policies/", format = "json", data = "")] +pub async fn update_policy( + _auth: ApiKey, + id: String, + policy: Json, + policy_store: &State>, +) -> Result, AgentError> { + let updated_policy = policy_store.update_policy(id, policy.into_inner()).await; + match updated_policy { + Ok(p) => Ok(Json::from(p)), + Err(err) => Err(AgentError::BadRequest { + reason: err.to_string(), + }), + } +} + +#[openapi] +#[delete("/policies/")] +pub async fn delete_policy( + _auth: ApiKey, + id: String, + policy_store: &State>, +) -> Result { + match policy_store.delete_policy(id.borrow()).await { + Ok(_p) => Ok(status::NoContent), + Err(_err) => Err(AgentError::NotFound { + id, + object: "Policy", + }), + } +} diff --git a/src/schemas/authorization.rs b/src/schemas/authorization.rs new file mode 100644 index 0000000..d234553 --- /dev/null +++ b/src/schemas/authorization.rs @@ -0,0 +1,129 @@ +use std::collections::HashSet; +use std::error::Error; +use std::str::FromStr; + +use cedar_policy::{Context, EntityUid, EvaluationError, Request, Response}; +use cedar_policy_core::authorizer::Decision; +use cedar_policy_core::parser::err::ParseErrors; + +use rocket::serde::json::serde_json; +use rocket_okapi::okapi::schemars; +use rocket_okapi::okapi::schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct AuthorizationCall { + principal: Option, + action: Option, + resource: Option, + context: Option, + /// Optional schema in JSON format. + /// If present, this will inform the parsing: for instance, it will allow + /// `__entity` and `__extn` escapes to be implicit, and it will error if + /// attributes have the wrong types (e.g., string instead of integer). + /// currently unsupported + #[schemars(skip)] + policies: Option, + /// JSON object containing the entities data, in "natural JSON" form -- same + /// format as expected by EntityJsonParser + /// currently unsupported + #[schemars(skip)] + entities: Option, +} + +fn string_to_euid(optional_str: Option) -> Result, ParseErrors> { + match optional_str { + Some(p) => match EntityUid::from_str(&p) { + Ok(euid) => Ok(Some(euid)), + Err(e) => Err(e), + }, + None => Ok(None), + } +} + +impl TryInto for AuthorizationCall { + type Error = Box; + + fn try_into(self) -> Result { + let principal = match string_to_euid(self.principal) { + Ok(p) => p, + Err(e) => return Err(e.into()), + }; + let action = match string_to_euid(self.action) { + Ok(a) => a, + Err(e) => return Err(e.into()), + }; + let resource = match string_to_euid(self.resource) { + Ok(r) => r, + Err(e) => return Err(e.into()), + }; + let context = match self.context { + Some(c) => match Context::from_json_value(c, None) { + Ok(c) => c, + Err(e) => return Err(e.into()), + }, + None => Context::empty(), + }; + Ok(Request::new(principal, action, resource, context)) + } +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub enum DecisionRef { + Allow, + /// The `Authorizer` determined that the query should be denied. + /// This is also returned if sufficiently fatal errors are encountered such + /// that no decision could be safely reached; for example, errors parsing + /// the policies. + Deny, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct DiagnosticsRef { + /// `PolicyId`s of the policies that contributed to the decision. + /// If no policies applied to the query, this set will be empty. + reason: HashSet, + /// list of error messages which occurred + errors: HashSet, +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct AuthorizationAnswer { + decision: DecisionRef, + diagnostics: DiagnosticsRef, +} + +impl Into for AuthorizationAnswer { + fn into(self) -> Response { + Response::new( + match self.decision { + DecisionRef::Allow => Decision::Allow, + DecisionRef::Deny => Decision::Deny, + }, + HashSet::from_iter( + self.diagnostics + .reason + .iter() + .map(|r| cedar_policy::PolicyId::from_str(r).unwrap()), + ), + self.diagnostics.errors, + ) + } +} + +impl From for AuthorizationAnswer { + fn from(value: Response) -> Self { + AuthorizationAnswer { + decision: match value.decision() { + Decision::Allow => DecisionRef::Allow, + Decision::Deny => DecisionRef::Deny, + }, + diagnostics: DiagnosticsRef { + reason: HashSet::from_iter(value.diagnostics().reason().map(|r| r.to_string())), + errors: HashSet::from_iter(value.diagnostics().errors().map(|e| match e { + EvaluationError::StringMessage(e) => e, + })), + }, + } + } +} diff --git a/src/schemas/data.rs b/src/schemas/data.rs new file mode 100644 index 0000000..644278a --- /dev/null +++ b/src/schemas/data.rs @@ -0,0 +1,80 @@ +use std::error::Error; + +use cedar_policy_core::entities::{ + EntitiesError, EntityJSON, EntityJsonParser, NullSchema, TCComputation, +}; +use cedar_policy_core::extensions::Extensions; +use cedar_policy_core::{ast, entities}; +use log::debug; +use rocket::serde::json::serde_json::{from_str, json, to_string}; +use rocket::serde::json::Value; + +use rocket_okapi::okapi::schemars; +use rocket_okapi::okapi::schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::common::EmptyError; + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct Entity(Value); + +impl From for Entity { + fn from(value: ast::Entity) -> Self { + let entity_json = EntityJSON::from_entity(&value).unwrap(); + let json_string = to_string(&entity_json).unwrap(); + Self(from_str(&json_string).unwrap()) + } +} + +impl TryInto for Entity { + type Error = Box; + + fn try_into(self) -> Result { + debug!("Parsing entity into ast format"); + let parser: EntityJsonParser = + EntityJsonParser::new(None, Extensions::all_available(), TCComputation::ComputeNow); + let entities = match parser.from_json_value(self.0) { + Ok(entities) => entities, + Err(err) => return Err(err.into()), + }; + for entity in entities.iter() { + return Ok(entity.clone()); + } + Err(EmptyError.into()) + } +} + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct Entities(Vec); + +impl Entities { + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl From for Entities { + fn from(value: entities::Entities) -> Self { + Self(value.iter().map(|v| Entity::from(v.clone())).collect()) + } +} + +impl TryInto for Entities { + type Error = EntitiesError; + + fn try_into(self) -> Result { + debug!("Parsing entities into ast format"); + let parser: EntityJsonParser = + EntityJsonParser::new(None, Extensions::all_available(), TCComputation::ComputeNow); + parser.from_json_value(json!(self.0)) + } +} + +impl TryInto for &Entities { + type Error = EntitiesError; + + fn try_into(self) -> Result { + debug!("Parsing entities into cedar format"); + cedar_policy::Entities::from_json_value(json!(self.0), None) + } +} diff --git a/src/schemas/mod.rs b/src/schemas/mod.rs new file mode 100644 index 0000000..23da6eb --- /dev/null +++ b/src/schemas/mod.rs @@ -0,0 +1,3 @@ +pub mod authorization; +pub mod data; +pub mod policies; diff --git a/src/schemas/policies.rs b/src/schemas/policies.rs new file mode 100644 index 0000000..b69c468 --- /dev/null +++ b/src/schemas/policies.rs @@ -0,0 +1,42 @@ +use cedar_policy_core::parser::err::ParseErrors; +use log::debug; +use rocket_okapi::okapi::schemars; +use rocket_okapi::okapi::schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] +pub struct Policy { + pub id: String, + pub content: String, +} + +impl From for Policy { + fn from(policy: cedar_policy::Policy) -> Self { + Policy { + id: policy.id().to_string(), + content: policy.to_string(), + } + } +} + +impl TryInto for &Policy { + type Error = ParseErrors; + + fn try_into(self) -> Result { + debug!("Parsing policy"); + cedar_policy::Policy::parse(Some(self.id.clone()), self.content.clone()) + } +} + +impl Policy { + pub fn from_policy_update(id: String, policy_update: PolicyUpdate) -> Self { + Policy { + id, + content: policy_update.content, + } + } +} + +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct PolicyUpdate { + pub content: String, +} diff --git a/src/services/data/memory.rs b/src/services/data/memory.rs new file mode 100644 index 0000000..7d62870 --- /dev/null +++ b/src/services/data/memory.rs @@ -0,0 +1,103 @@ +use std::borrow::Borrow; +use std::error::Error; + +use async_lock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use async_trait::async_trait; +use cedar_policy_core::entities; +use log::{debug, error, info}; + +use crate::schemas::data as schemas; +use crate::services::data::DataStore; + +pub struct Entities(cedar_policy::Entities, entities::Entities); + +impl Entities { + fn empty() -> Self { + Self { + 0: cedar_policy::Entities::empty(), + 1: entities::Entities::new(), + } + } + + fn cedar_entities(&self) -> cedar_policy::Entities { + self.0.clone() + } + + #[allow(dead_code)] + fn core_entities(&self) -> entities::Entities { + self.1.clone() + } + + fn new(cedar_entities: cedar_policy::Entities, core_entities: entities::Entities) -> Self { + Self { + 0: cedar_entities, + 1: core_entities, + } + } +} + +pub struct MemoryDataStore { + entities: RwLock, +} + +impl MemoryDataStore { + pub fn new() -> Self { + Self { + entities: RwLock::new(Entities::empty()), + } + } + + async fn read(&self) -> RwLockReadGuard { + debug!("Trying to acquire read lock on entities"); + self.entities.read().await + } + + async fn write(&self) -> RwLockWriteGuard { + debug!("Trying to acquire write lock on entities"); + self.entities.write().await + } +} + +#[async_trait] +impl DataStore for MemoryDataStore { + async fn entities(&self) -> cedar_policy::Entities { + let lock = self.read().await; + lock.cedar_entities() + } + + async fn get_entities(&self) -> schemas::Entities { + info!("Getting stored entities"); + let lock = self.read().await; + schemas::Entities::from(lock.1.clone()) + } + + async fn delete_entities(&self) { + info!("Deleting stored entities"); + let mut lock = self.write().await; + *lock = Entities::empty(); + } + + async fn update_entities( + &self, + entities: schemas::Entities, + ) -> Result> { + info!("Updating stored entities"); + let mut lock = self.write().await; + let core_entities: entities::Entities = match entities.try_into() { + Ok(entities) => entities, + Err(err) => { + return { + error!("Failed to parse entities"); + Err(err.into()) + } + } + }; + let schema_entities: schemas::Entities = core_entities.clone().into(); + let cedar_entities: cedar_policy::Entities = match schema_entities.borrow().try_into() { + Ok(entities) => entities, + Err(err) => return Err(err.into()), + }; + *lock = Entities::new(cedar_entities, core_entities); + Ok(schema_entities) + } +} diff --git a/src/services/data/mod.rs b/src/services/data/mod.rs new file mode 100644 index 0000000..7c43bca --- /dev/null +++ b/src/services/data/mod.rs @@ -0,0 +1,18 @@ +use std::error::Error; + +use async_trait::async_trait; + +use crate::schemas::data as schemas; + +pub mod memory; + +#[async_trait] +pub trait DataStore: Send + Sync { + async fn entities(&self) -> cedar_policy::Entities; + async fn get_entities(&self) -> schemas::Entities; + async fn delete_entities(&self); + async fn update_entities( + &self, + entities: schemas::Entities, + ) -> Result>; +} diff --git a/src/services/mod.rs b/src/services/mod.rs new file mode 100644 index 0000000..bfcbcf1 --- /dev/null +++ b/src/services/mod.rs @@ -0,0 +1,4 @@ +pub mod data; +pub mod policies; +pub use data::DataStore; +pub use policies::PolicyStore; diff --git a/src/services/policies/errors.rs b/src/services/policies/errors.rs new file mode 100644 index 0000000..e7e3405 --- /dev/null +++ b/src/services/policies/errors.rs @@ -0,0 +1,11 @@ +use thiserror::Error; +#[derive(Error, Debug)] +#[non_exhaustive] +pub enum PolicyStoreError { + /// Reference to PolicySetError. + #[error("Unable to modify the policies: {0}")] + PolicySetError(#[from] cedar_policy::PolicySetError), + /// Policy with the given id was not found. + #[error("Unable to find policy with id {0}")] + PolicyNotFoundError(String), +} diff --git a/src/services/policies/memory.rs b/src/services/policies/memory.rs new file mode 100644 index 0000000..cd90045 --- /dev/null +++ b/src/services/policies/memory.rs @@ -0,0 +1,163 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::error::Error; + +use async_lock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use async_trait::async_trait; +use cedar_policy::{PolicySet, PolicySetError}; +use log::{debug, info}; + +use crate::common; +use crate::schemas::policies::{Policy, PolicyUpdate}; +use crate::services::policies::errors::PolicyStoreError; +use crate::services::policies::PolicyStore; + +pub struct Policies(HashMap, PolicySet); + +impl Policies { + fn new() -> Self { + Self { + 0: HashMap::new(), + 1: PolicySet::new(), + } + } + + #[allow(dead_code)] + fn policy_map(&self) -> HashMap { + self.0.clone() + } + + fn policy_set(&self) -> PolicySet { + self.1.clone() + } + + fn update_policy_set(&mut self) { + let mut policy_set = PolicySet::new(); + for policy in self.0.values() { + policy_set.add(policy.clone()).unwrap(); + } + self.1 = policy_set; + } +} + +pub struct MemoryPolicyStore { + policies: RwLock, +} + +impl MemoryPolicyStore { + pub fn new() -> Self { + Self { + policies: RwLock::new(Policies::new()), + } + } + + async fn read(&self) -> RwLockReadGuard { + debug!("Trying to acquire read lock on policies"); + self.policies.read().await + } + + async fn write(&self) -> RwLockWriteGuard { + debug!("Trying to acquire write lock on policies"); + self.policies.write().await + } +} + +#[async_trait] +impl PolicyStore for MemoryPolicyStore { + async fn policy_set(&self) -> PolicySet { + let lock = self.read().await; + lock.policy_set() + } + + async fn get_policies(&self) -> Vec { + info!("Getting policies"); + let lock = self.read().await; + Vec::from_iter(lock.0.values().cloned().map(|p| Policy::from(p))) + } + + async fn get_policy(&self, id: &str) -> Result> { + info!("Getting policy {}", id); + let lock = self.read().await; + let policy = lock.0.get(id); + match policy { + Some(p) => Ok(Policy::from(p.clone())), + None => Err(PolicyStoreError::PolicyNotFoundError(id.to_owned()).into()), + } + } + + async fn create_policy(&self, policy: &Policy) -> Result> { + info!("Creating policy {}", policy.id); + let mut lock = self.write().await; + let stored_policy = lock.0.get(&policy.id); + match stored_policy { + Some(_) => Err(PolicyStoreError::PolicySetError(PolicySetError::AlreadyDefined).into()), + None => { + let policy: cedar_policy::Policy = match policy.borrow().try_into() { + Ok(p) => p, + Err(err) => return Err(err.into()), + }; + let policy_id = policy.id().to_string(); + lock.0.insert(policy_id.clone(), policy); + lock.update_policy_set(); + Ok(Policy::from( + lock.0.get(policy_id.as_str()).unwrap().clone(), + )) + } + } + } + + async fn update_policies(&self, policies: Vec) -> Result, Box> { + info!("Updating policies"); + let mut lock = self.write().await; + let mut new_policies: HashMap = HashMap::new(); + for policy in policies { + match new_policies.get(&policy.id) { + Some(_) => return Err(PolicySetError::AlreadyDefined.into()), + None => { + let policy: cedar_policy::Policy = match policy.borrow().try_into() { + Ok(p) => p, + Err(err) => return Err(err.into()), + }; + new_policies.insert(policy.id().to_string(), policy) + } + }; + } + lock.0 = new_policies; + lock.update_policy_set(); + Ok(Vec::from_iter( + lock.0.values().cloned().map(|p| Policy::from(p)), + )) + } + + async fn update_policy( + &self, + id: String, + policy_update: PolicyUpdate, + ) -> Result> { + info!("Updating policy {}", id); + let mut lock = self.write().await; + let policy = Policy::from_policy_update(id.clone(), policy_update); + let policy: cedar_policy::Policy = match policy.borrow().try_into() { + Ok(p) => p, + Err(err) => return Err(err.into()), + }; + *lock + .0 + .entry(String::from(id)) + .or_insert_with(|| policy.clone()) = policy.clone(); + lock.update_policy_set(); + Ok(Policy::from(policy)) + } + + async fn delete_policy(&self, id: &str) -> Result> { + info!("Deleting policy {}", id); + let mut lock = self.write().await; + match lock.0.remove(id) { + Some(policy) => { + lock.update_policy_set(); + Ok(Policy::from(policy)) + } + None => Err(common::EmptyError.into()), + } + } +} diff --git a/src/services/policies/mod.rs b/src/services/policies/mod.rs new file mode 100644 index 0000000..4f60020 --- /dev/null +++ b/src/services/policies/mod.rs @@ -0,0 +1,24 @@ +use std::error::Error; + +use async_trait::async_trait; +use cedar_policy::PolicySet; + +use crate::schemas::policies::{Policy, PolicyUpdate}; + +pub(crate) mod errors; +pub mod memory; + +#[async_trait] +pub trait PolicyStore: Send + Sync { + async fn policy_set(&self) -> PolicySet; + async fn get_policies(&self) -> Vec; + async fn get_policy(&self, id: &str) -> Result>; + async fn create_policy(&self, policy: &Policy) -> Result>; + async fn update_policies(&self, policies: Vec) -> Result, Box>; + async fn update_policy( + &self, + id: String, + policy: PolicyUpdate, + ) -> Result>; + async fn delete_policy(&self, id: &str) -> Result>; +} diff --git a/tests/mod.rs b/tests/mod.rs new file mode 100644 index 0000000..172de0d --- /dev/null +++ b/tests/mod.rs @@ -0,0 +1 @@ +mod services; diff --git a/tests/services/data_tests.rs b/tests/services/data_tests.rs new file mode 100644 index 0000000..aa4c332 --- /dev/null +++ b/tests/services/data_tests.rs @@ -0,0 +1,20 @@ +use crate::services::utils; +use cedar_agent::data::memory::MemoryDataStore; + +use cedar_agent::DataStore; + +#[tokio::test] +async fn memory_tests() { + let store = MemoryDataStore::new(); + + let entities = store.get_entities().await; + assert_eq!(entities.len(), 0); + let updated_entities = store.update_entities(utils::entities()).await.unwrap(); + assert_eq!(updated_entities.len(), 8); + + let error_entities = store.update_entities(utils::parse_error_entities()).await; + assert!(error_entities.is_err()); + store.delete_entities().await; + let entities = store.get_entities().await; + assert_eq!(entities.len(), 0); +} diff --git a/tests/services/mod.rs b/tests/services/mod.rs new file mode 100644 index 0000000..8d59d37 --- /dev/null +++ b/tests/services/mod.rs @@ -0,0 +1,3 @@ +mod data_tests; +mod policies_tests; +mod utils; diff --git a/tests/services/policies_tests.rs b/tests/services/policies_tests.rs new file mode 100644 index 0000000..11167ae --- /dev/null +++ b/tests/services/policies_tests.rs @@ -0,0 +1,84 @@ +use std::str::FromStr; + +use cedar_policy::PolicyId; + +use crate::services::utils::*; +use cedar_agent::policies::memory::MemoryPolicyStore; +use cedar_agent::schemas::policies::PolicyUpdate; +use cedar_agent::PolicyStore; + +#[tokio::test] +async fn memory_tests() { + let store = MemoryPolicyStore::new(); + + let policies = store + .update_policies(vec![approve_all_policy(None)]) + .await + .unwrap(); + assert_eq!(policies.len(), 1); + let duplicate_policies = store + .update_policies(vec![approve_all_policy(None), approve_all_policy(None)]) + .await; + assert!(duplicate_policies.is_err()); + let error_policies = store.update_policies(vec![parse_error_policy()]).await; + assert!(error_policies.is_err()); + + let created_policy = store + .create_policy(&approve_admin_policy(Some("admin".to_string()))) + .await + .unwrap(); + assert_eq!(created_policy.id, "admin".to_string()); + let policy = store.get_policy("admin").await.unwrap(); + assert_eq!(policy.id, "admin".to_string()); + assert_eq!(policy.content, created_policy.content); + + let error_policy = store + .create_policy(&approve_admin_policy(Some("admin".to_string()))) + .await; + assert!(error_policy.is_err()); + let error_policy = store.create_policy(&parse_error_policy()).await; + assert!(error_policy.is_err()); + + let policies = store.get_policies().await; + assert_eq!(policies.len(), 2); + + let updated_policy = store + .update_policy( + "test".to_string(), + PolicyUpdate { + content: approve_admin_policy(None).content, + }, + ) + .await + .unwrap(); + assert_eq!(updated_policy.id, "test".to_string()); + assert_eq!( + split_content(updated_policy.content.as_str()).1, + split_content(created_policy.content.as_str()).1 + ); + + let error_policy = store + .update_policy( + "test".to_string(), + PolicyUpdate { + content: parse_error_policy().content, + }, + ) + .await; + assert!(error_policy.is_err()); + + let deleted_policy = store.delete_policy("test").await.unwrap(); + assert_eq!(deleted_policy.id, "test".to_string()); + let missing_policy = store.get_policy("test").await; + assert!(missing_policy.is_err()); + + assert!(store.delete_policy("test").await.is_err()); + + let policy_set = store.policy_set().await; + assert!(policy_set + .policy(&PolicyId::from_str("admin").unwrap()) + .is_some()); + assert!(policy_set + .policy(&PolicyId::from_str("test").unwrap()) + .is_none()); +} diff --git a/tests/services/utils.rs b/tests/services/utils.rs new file mode 100644 index 0000000..6535dab --- /dev/null +++ b/tests/services/utils.rs @@ -0,0 +1,188 @@ +use rocket::serde::json::serde_json::from_str; + +use cedar_agent::schemas::data::Entities; +use cedar_agent::schemas::policies::Policy; + +pub(crate) fn split_content(in_string: &str) -> (&str, &str) { + let mut splitter = in_string.splitn(2, ':'); + let id = splitter.next().unwrap(); + let content = splitter.next().unwrap(); + (id, content) +} + +pub(crate) fn parse_error_policy() -> Policy { + Policy { + id: "error".to_string(), + content: "error".to_string(), + } +} + +pub(crate) fn approve_all_policy(id: Option) -> Policy { + let id = match id { + Some(id) => id, + None => "test".to_string(), + }; + Policy { + id: id, + content: "permit(principal,action,resource);".to_string(), + } +} + +pub(crate) fn approve_admin_policy(id: Option) -> Policy { + let id = match id { + Some(id) => id, + None => "test".to_string(), + }; + Policy { + id: id, + content: "permit(principal == User::\"admin@domain.com\",action,resource);".to_string(), + } +} + +pub(crate) fn entities() -> Entities { + let entities_json = r#" + [ + { + "attrs": { + "confidenceScore": { + "__extn": { + "arg": "33.57", + "fn": "decimal" + } + }, + "department": "HardwareEngineering", + "homeIp": { + "__extn": { + "arg": "222.222.222.7", + "fn": "ip" + } + }, + "jobLevel": 5 + }, + "parents": [ + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "editor-1@domain.com", + "type": "User" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "document", + "type": "ResourceType" + }, + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "document:delete", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "document", + "type": "ResourceType" + }, + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "document:create", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "document", + "type": "ResourceType" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "Editor", + "type": "Role" + }, + { + "id": "document", + "type": "ResourceType" + } + ], + "uid": { + "id": "document:update", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "document", + "type": "ResourceType" + }, + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "document:list", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [ + { + "id": "document", + "type": "ResourceType" + }, + { + "id": "Editor", + "type": "Role" + } + ], + "uid": { + "id": "document:get", + "type": "Action" + } + }, + { + "attrs": {}, + "parents": [], + "uid": { + "id": "Editor", + "type": "Role" + } + } + ] + "#; + from_str(entities_json).unwrap() +} + +pub(crate) fn parse_error_entities() -> Entities { + let entities_json = r#" + [ + { + "id": "error" + } + ] + "#; + from_str(entities_json).unwrap() +}