diff --git a/Cargo.lock b/Cargo.lock index 32d58338..49fe406d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,9 +203,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310c9bcae737a48ef5cdee3174184e6d548b292739ede61a1f955ef76a738861" +checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" dependencies = [ "brotli", "flate2", @@ -360,9 +360,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "base64urlsafedata" @@ -383,16 +383,16 @@ checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" dependencies = [ "base64 0.22.1", "blowfish", - "getrandom 0.3.1", + "getrandom 0.3.2", "subtle", "zeroize", ] [[package]] name = "bigdecimal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" dependencies = [ "autocfg", "libm", @@ -444,9 +444,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5430e3be710b68d984d1391c854eb431a9d548640711faa54eecb1df93db91cc" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", "cfg_aliases", @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b668d39970baad5356d7c83a86fee3a539e6f93bf6764c97368243e17a0487" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", @@ -534,9 +534,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.16" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "jobserver", "libc", @@ -594,7 +594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 2.4.1", + "half 2.6.0", ] [[package]] @@ -889,9 +889,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -899,9 +899,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -913,9 +913,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -955,9 +955,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -1081,9 +1081,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1142,12 +1142,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.5", + "miniz_oxide 0.8.8", ] [[package]] @@ -1169,9 +1169,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -1199,9 +1199,9 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "funty" @@ -1318,14 +1318,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1342,9 +1342,9 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -1438,9 +1438,9 @@ dependencies = [ [[package]] name = "http" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a761d192fbf18bdef69f5ceedd0d1333afcbda0ee23840373b8317570d23c65" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1503,9 +1503,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-util", @@ -1519,14 +1519,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1581,9 +1582,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1605,9 +1606,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1626,9 +1627,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1693,9 +1694,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1756,10 +1757,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -1817,9 +1819,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" @@ -1845,9 +1847,18 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] [[package]] name = "matchit" @@ -1904,9 +1915,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -2063,9 +2074,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -2145,6 +2156,7 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber", + "tracing-test", "utoipa", "utoipa-axum", "utoipa-swagger-ui", @@ -2154,9 +2166,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "3.9.2" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ "num-traits", ] @@ -2265,9 +2277,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror 2.0.12", @@ -2276,9 +2288,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -2286,9 +2298,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", @@ -2299,9 +2311,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", @@ -2500,13 +2512,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -2565,9 +2583,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags", ] @@ -2580,8 +2598,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[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]] @@ -2592,9 +2619,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[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.8.5" @@ -2664,9 +2697,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest", @@ -2729,9 +2762,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.36.0" +version = "1.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" dependencies = [ "arrayvec", "borsh", @@ -2760,9 +2793,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags", "errno", @@ -2856,9 +2889,9 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.32.2" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b731192738ebf56d20580fc8ba2d23940333befe900b04dd08a26a77cd056f02" +checksum = "f5a24d8b9fcd2674a6c878a3d871f4f1380c6c43cc3718728ac96864d888458e" dependencies = [ "bigdecimal", "chrono", @@ -3053,18 +3086,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3388,7 +3421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3452,9 +3485,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -3467,15 +3500,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -3566,9 +3599,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -3721,14 +3754,39 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] +[[package]] +name = "tracing-test" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +dependencies = [ + "quote", + "syn 2.0.100", +] + [[package]] name = "trim-in-place" version = "0.1.7" @@ -3855,9 +3913,9 @@ dependencies = [ [[package]] name = "utoipa-swagger-ui" -version = "9.0.0" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161166ec520c50144922a625d8bc4925cc801b2dda958ab69878527c0e5c5d61" +checksum = "d29519b3c485df6b13f4478ac909a491387e9ef70204487c3b64b53749aec0be" dependencies = [ "axum", "base64 0.22.1", @@ -3883,7 +3941,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", "serde", ] @@ -3923,9 +3981,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -4073,9 +4131,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ "redox_syscall", "wasite", @@ -4114,18 +4172,62 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" @@ -4277,18 +4379,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] @@ -4333,9 +4435,9 @@ dependencies = [ [[package]] name = "yaml-rust2" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232bdb534d65520716bef0bbb205ff8f2db72d807b19c0bc3020853b92a0cd4b" +checksum = "818913695e83ece1f8d2a1c52d54484b7b46d0f9c06beeb2649b9da50d9b512d" dependencies = [ "arraydeque", "encoding_rs", @@ -4374,18 +4476,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", @@ -4457,18 +4559,16 @@ dependencies = [ [[package]] name = "zip" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938cc23ac49778ac8340e366ddc422b2227ea176edb447e23fc0627608dddadd" +checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" dependencies = [ "arbitrary", "crc32fast", "crossbeam-utils", - "displaydoc", "flate2", "indexmap", "memchr", - "thiserror 2.0.12", "zopfli", ] @@ -4497,18 +4597,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.3" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.14+zstd.1.5.7" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index a75ba249..298004e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ http-body-util = "^0.1" mockall = { version = "^0.13" } sea-orm = { version = "*", features = ["mock"]} tempfile = { version = "^3.19" } +tracing-test = { version = "^0.2" } [profile.release] opt-level = 3 diff --git a/src/assignment/backends/sql.rs b/src/assignment/backends/sql.rs index 93ca1f6d..75be4f94 100644 --- a/src/assignment/backends/sql.rs +++ b/src/assignment/backends/sql.rs @@ -20,6 +20,7 @@ use crate::assignment::AssignmentProviderError; use crate::config::Config; mod assignment; +mod implied_role; mod role; #[derive(Clone, Debug, Default)] diff --git a/src/assignment/backends/sql/assignment.rs b/src/assignment/backends/sql/assignment.rs index c27b860c..7dda79a4 100644 --- a/src/assignment/backends/sql/assignment.rs +++ b/src/assignment/backends/sql/assignment.rs @@ -14,7 +14,9 @@ use sea_orm::DatabaseConnection; use sea_orm::entity::*; +use sea_orm::prelude::Expr; use sea_orm::query::*; +use std::collections::{BTreeMap, HashMap}; use crate::assignment::backends::error::AssignmentDatabaseError; use crate::assignment::types::*; @@ -90,14 +92,41 @@ pub async fn list_for_multiple_actors_and_targets( select = select.filter(cond); } - let db_entities: Vec<(db_assignment::Model, Option)> = - select.find_also_related(DbRole).all(db).await?; - let results: Result, _> = db_entities - .into_iter() - .map(TryInto::::try_into) - .collect(); + // Get all implied rules + let imply_rules = super::implied_role::list_rules(db, true).await?; - results + let mut db_assignments: BTreeMap = BTreeMap::new(); + // Get assignments resolving the roles inference + for assignment in select.all(db).await? { + db_assignments.insert(assignment.role_id.clone(), assignment.clone()); + if let Some(implies) = imply_rules.get(&assignment.role_id) { + let mut implied_assignment = assignment.clone(); + for implied in implies.iter() { + implied_assignment.role_id = implied.clone(); + db_assignments.insert(implied.clone(), implied_assignment.clone()); + } + } + } + + if !db_assignments.is_empty() { + // Get roles for the found IDs + let roles: HashMap = HashMap::from_iter( + DbRole::find() + .select_only() + .columns([db_role::Column::Id, db_role::Column::Name]) + .filter(Expr::col(db_role::Column::Id).is_in(db_assignments.keys())) + .into_tuple() + .all(db) + .await?, + ); + let results: Result, _> = db_assignments + .values() + .map(|item| TryInto::::try_into((item, roles.get(&item.role_id)))) + .collect(); + results + } else { + Ok(Vec::new()) + } } impl TryFrom for Assignment { @@ -115,6 +144,39 @@ impl TryFrom for Assignment { } } +impl TryFrom<&db_assignment::Model> for Assignment { + type Error = AssignmentDatabaseError; + + fn try_from(value: &db_assignment::Model) -> Result { + let mut builder = AssignmentBuilder::default(); + builder.role_id(value.role_id.clone()); + builder.actor_id(value.actor_id.clone()); + builder.target_id(value.target_id.clone()); + builder.inherited(value.inherited); + builder.r#type(value.r#type.clone()); + + Ok(builder.build()?) + } +} + +impl TryFrom<(&db_assignment::Model, Option<&String>)> for Assignment { + type Error = AssignmentDatabaseError; + + fn try_from(value: (&db_assignment::Model, Option<&String>)) -> Result { + let mut builder = AssignmentBuilder::default(); + builder.role_id(value.0.role_id.clone()); + builder.actor_id(value.0.actor_id.clone()); + builder.target_id(value.0.target_id.clone()); + builder.inherited(value.0.inherited); + builder.r#type(value.0.r#type.clone()); + if let Some(val) = value.1 { + builder.role_name(val.clone()); + } + + Ok(builder.build()?) + } +} + impl TryFrom<(db_assignment::Model, Option)> for Assignment { type Error = AssignmentDatabaseError; @@ -149,9 +211,10 @@ impl From for AssignmentType { #[cfg(test)] mod tests { use sea_orm::{DatabaseBackend, MockDatabase, Transaction}; + use std::collections::BTreeMap; use crate::config::Config; - use crate::db::entity::{assignment, role, sea_orm_active_enums}; + use crate::db::entity::{assignment, implied_role, role, sea_orm_active_enums}; use super::*; @@ -165,6 +228,20 @@ mod tests { } } + fn get_role_mock(role_id: String, role_name: String) -> BTreeMap { + BTreeMap::from([ + ("id".to_string(), Value::String(Some(Box::new(role_id)))), + ("name".to_string(), Value::String(Some(Box::new(role_name)))), + ]) + } + + fn get_implied_rules_mock() -> Vec { + vec![implied_role::Model { + prior_role_id: "1".to_string(), + implied_role_id: "2".to_string(), + }] + } + fn get_role_assignment_with_role_mock(role_id: String) -> (assignment::Model, role::Model) { ( assignment::Model { @@ -276,17 +353,17 @@ mod tests { } #[tokio::test] - async fn test_list_for_multuple_actor_targets() { + async fn test_list_for_multiple_actor_targets_multiple_actors_single_target() { // Create MockDatabase with mock query results let db = MockDatabase::new(DatabaseBackend::Postgres) - .append_query_results([vec![get_role_assignment_with_role_mock("1".into())]]) - .append_query_results([vec![get_role_assignment_with_role_mock("1".into())]]) - .append_query_results([vec![get_role_assignment_with_role_mock("1".into())]]) - .append_query_results([vec![get_role_assignment_with_role_mock("1".into())]]) - .append_query_results([Vec::::new()]) + .append_query_results([get_implied_rules_mock()]) + .append_query_results([vec![get_role_assignment_mock("1".into())]]) + .append_query_results([vec![ + get_role_mock("1".into(), "rname".into()), + get_role_mock("2".into(), "rname2".into()), + ]]) .into_connection(); let config = Config::default(); - // multiple actors single simple target assert_eq!( list_for_multiple_actors_and_targets( &config, @@ -302,15 +379,67 @@ mod tests { ) .await .unwrap(), - vec![Assignment { - role_id: "1".into(), - role_name: Some("1".into()), - actor_id: "actor".into(), - target_id: "target".into(), - r#type: AssignmentType::UserProject, - inherited: false, - }] + vec![ + Assignment { + role_id: "1".into(), + role_name: Some("rname".into()), + actor_id: "actor".into(), + target_id: "target".into(), + r#type: AssignmentType::UserProject, + inherited: false, + }, + Assignment { + role_id: "2".into(), + role_name: Some("rname2".into()), + actor_id: "actor".into(), + target_id: "target".into(), + r#type: AssignmentType::UserProject, + inherited: false, + } + ] ); + // Checking transaction log + assert_eq!( + db.into_transaction_log(), + [ + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT CAST("assignment"."type" AS text), "assignment"."actor_id", "assignment"."target_id", "assignment"."role_id", "assignment"."inherited" FROM "assignment" WHERE "assignment"."actor_id" IN ($1, $2, $3) AND "assignment"."role_id" = $4 AND "assignment"."target_id" = $5"#, + [ + "uid1".into(), + "gid1".into(), + "gid2".into(), + "rid".into(), + "pid1".into() + ] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "role"."id", "role"."name" FROM "role" WHERE "id" IN ($1, $2)"#, + ["1".into(), "2".into(),] + ), + ] + ); + } + + #[tokio::test] + async fn test_list_for_multiple_actor_targets_multiple_complex_targets() { + // Create MockDatabase with mock query results + + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([get_implied_rules_mock()]) + .append_query_results([vec![get_role_assignment_mock("1".into())]]) + .append_query_results([vec![ + get_role_mock("1".into(), "rname".into()), + get_role_mock("2".into(), "rname2".into()), + ]]) + .into_connection(); + let config = Config::default(); // multiple actors multiple complex targets assert!( list_for_multiple_actors_and_targets( @@ -335,6 +464,49 @@ mod tests { .is_ok() ); + // Checking transaction log + assert_eq!( + db.into_transaction_log(), + [ + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT CAST("assignment"."type" AS text), "assignment"."actor_id", "assignment"."target_id", "assignment"."role_id", "assignment"."inherited" FROM "assignment" WHERE "assignment"."actor_id" IN ($1, $2, $3) AND ("assignment"."target_id" = $4 OR ("assignment"."target_id" = $5 AND "assignment"."inherited" = $6))"#, + [ + "uid1".into(), + "gid1".into(), + "gid2".into(), + "pid1".into(), + "pid2".into(), + true.into() + ] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "role"."id", "role"."name" FROM "role" WHERE "id" IN ($1, $2)"#, + ["1".into(), "2".into(),] + ), + ] + ); + } + + #[tokio::test] + async fn test_list_for_multiple_actor_targets_empty_actors_and_targets() { + // Create MockDatabase with mock query results + + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([get_implied_rules_mock()]) + .append_query_results([vec![get_role_assignment_mock("1".into())]]) + .append_query_results([vec![ + get_role_mock("1".into(), "rname".into()), + get_role_mock("2".into(), "rname2".into()), + ]]) + .into_connection(); + let config = Config::default(); //// empty actors and targets assert!( list_for_multiple_actors_and_targets( @@ -350,6 +522,43 @@ mod tests { .is_ok() ); + // Checking transaction log + assert_eq!( + db.into_transaction_log(), + [ + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT CAST("assignment"."type" AS text), "assignment"."actor_id", "assignment"."target_id", "assignment"."role_id", "assignment"."inherited" FROM "assignment""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "role"."id", "role"."name" FROM "role" WHERE "id" IN ($1, $2)"#, + ["1".into(), "2".into(),] + ), + ] + ); + } + + #[tokio::test] + async fn test_list_for_multiple_actor_targets_mixed_targets() { + // Create MockDatabase with mock query results + + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([get_implied_rules_mock()]) + .append_query_results([vec![get_role_assignment_mock("1".into())]]) + .append_query_results([vec![ + get_role_mock("1".into(), "rname".into()), + get_role_mock("2".into(), "rname2".into()), + ]]) + .into_connection(); + let config = Config::default(); + //// only mixed targets assert!( list_for_multiple_actors_and_targets( @@ -374,6 +583,39 @@ mod tests { .is_ok() ); + // Checking transaction log + assert_eq!( + db.into_transaction_log(), + [ + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT CAST("assignment"."type" AS text), "assignment"."actor_id", "assignment"."target_id", "assignment"."role_id", "assignment"."inherited" FROM "assignment" WHERE "assignment"."target_id" = $1 OR ("assignment"."target_id" = $2 AND "assignment"."inherited" = $3)"#, + ["pid1".into(), "pid2".into(), true.into()] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "role"."id", "role"."name" FROM "role" WHERE "id" IN ($1, $2)"#, + ["1".into(), "2".into(),] + ), + ] + ); + } + + #[tokio::test] + async fn test_list_for_multiple_actor_targets_complex_targets() { + // Create MockDatabase with mock query results + + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([get_implied_rules_mock()]) + .append_query_results([Vec::::new()]) + .into_connection(); + let config = Config::default(); + //// only complex targets assert!( list_for_multiple_actors_and_targets( @@ -404,40 +646,12 @@ mod tests { [ Transaction::from_sql_and_values( DatabaseBackend::Postgres, - r#"SELECT CAST("assignment"."type" AS text) AS "A_type", "assignment"."actor_id" AS "A_actor_id", "assignment"."target_id" AS "A_target_id", "assignment"."role_id" AS "A_role_id", "assignment"."inherited" AS "A_inherited", "role"."id" AS "B_id", "role"."name" AS "B_name", "role"."extra" AS "B_extra", "role"."domain_id" AS "B_domain_id", "role"."description" AS "B_description" FROM "assignment" LEFT JOIN "role" ON "assignment"."role_id" = "role"."id" WHERE "assignment"."actor_id" IN ($1, $2, $3) AND "assignment"."role_id" = $4 AND "assignment"."target_id" = $5"#, - [ - "uid1".into(), - "gid1".into(), - "gid2".into(), - "rid".into(), - "pid1".into() - ] - ), - Transaction::from_sql_and_values( - DatabaseBackend::Postgres, - r#"SELECT CAST("assignment"."type" AS text) AS "A_type", "assignment"."actor_id" AS "A_actor_id", "assignment"."target_id" AS "A_target_id", "assignment"."role_id" AS "A_role_id", "assignment"."inherited" AS "A_inherited", "role"."id" AS "B_id", "role"."name" AS "B_name", "role"."extra" AS "B_extra", "role"."domain_id" AS "B_domain_id", "role"."description" AS "B_description" FROM "assignment" LEFT JOIN "role" ON "assignment"."role_id" = "role"."id" WHERE "assignment"."actor_id" IN ($1, $2, $3) AND ("assignment"."target_id" = $4 OR ("assignment"."target_id" = $5 AND "assignment"."inherited" = $6))"#, - [ - "uid1".into(), - "gid1".into(), - "gid2".into(), - "pid1".into(), - "pid2".into(), - true.into() - ] - ), - Transaction::from_sql_and_values( - DatabaseBackend::Postgres, - r#"SELECT CAST("assignment"."type" AS text) AS "A_type", "assignment"."actor_id" AS "A_actor_id", "assignment"."target_id" AS "A_target_id", "assignment"."role_id" AS "A_role_id", "assignment"."inherited" AS "A_inherited", "role"."id" AS "B_id", "role"."name" AS "B_name", "role"."extra" AS "B_extra", "role"."domain_id" AS "B_domain_id", "role"."description" AS "B_description" FROM "assignment" LEFT JOIN "role" ON "assignment"."role_id" = "role"."id""#, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, [] ), Transaction::from_sql_and_values( DatabaseBackend::Postgres, - r#"SELECT CAST("assignment"."type" AS text) AS "A_type", "assignment"."actor_id" AS "A_actor_id", "assignment"."target_id" AS "A_target_id", "assignment"."role_id" AS "A_role_id", "assignment"."inherited" AS "A_inherited", "role"."id" AS "B_id", "role"."name" AS "B_name", "role"."extra" AS "B_extra", "role"."domain_id" AS "B_domain_id", "role"."description" AS "B_description" FROM "assignment" LEFT JOIN "role" ON "assignment"."role_id" = "role"."id" WHERE "assignment"."target_id" = $1 OR ("assignment"."target_id" = $2 AND "assignment"."inherited" = $3)"#, - ["pid1".into(), "pid2".into(), true.into()] - ), - Transaction::from_sql_and_values( - DatabaseBackend::Postgres, - r#"SELECT CAST("assignment"."type" AS text) AS "A_type", "assignment"."actor_id" AS "A_actor_id", "assignment"."target_id" AS "A_target_id", "assignment"."role_id" AS "A_role_id", "assignment"."inherited" AS "A_inherited", "role"."id" AS "B_id", "role"."name" AS "B_name", "role"."extra" AS "B_extra", "role"."domain_id" AS "B_domain_id", "role"."description" AS "B_description" FROM "assignment" LEFT JOIN "role" ON "assignment"."role_id" = "role"."id" WHERE ("assignment"."target_id" = $1 AND "assignment"."inherited" = $2) OR ("assignment"."target_id" = $3 AND "assignment"."inherited" = $4)"#, + r#"SELECT CAST("assignment"."type" AS text), "assignment"."actor_id", "assignment"."target_id", "assignment"."role_id", "assignment"."inherited" FROM "assignment" WHERE ("assignment"."target_id" = $1 AND "assignment"."inherited" = $2) OR ("assignment"."target_id" = $3 AND "assignment"."inherited" = $4)"#, ["pid1".into(), false.into(), "pid2".into(), true.into()] ), ] diff --git a/src/assignment/backends/sql/implied_role.rs b/src/assignment/backends/sql/implied_role.rs new file mode 100644 index 00000000..b9a277c6 --- /dev/null +++ b/src/assignment/backends/sql/implied_role.rs @@ -0,0 +1,205 @@ +// 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. +// +// SPDX-License-Identifier: Apache-2.0 + +use sea_orm::DatabaseConnection; +use sea_orm::entity::*; +use std::collections::{BTreeMap, BTreeSet}; + +use crate::assignment::backends::error::AssignmentDatabaseError; +use crate::db::entity::prelude::ImpliedRole as DbImpliedRole; + +/// Build a resolved tree of role inference +fn expand_implied_roles( + data: &BTreeMap>, +) -> BTreeMap> { + let mut res: BTreeMap> = BTreeMap::new(); + for (id, imply) in data.iter() { + let mut implied = imply.clone(); + for im in imply.iter() { + implied.append(&mut get_implied_roles(im, data)); + } + res.insert(id.clone(), implied); + } + res +} + +/// Recursively resolve inference tree +fn get_implied_roles(id: &String, data: &BTreeMap>) -> BTreeSet { + let mut res: BTreeSet = BTreeSet::new(); + if let Some(implied) = data.get(id) { + implied.iter().for_each(|imply| { + res.insert(imply.clone()); + res.append(&mut get_implied_roles(imply, data)); + }) + } + res +} + +/// List role recursively resolved imply rules +pub async fn list_rules( + db: &DatabaseConnection, + resolve: bool, +) -> Result>, AssignmentDatabaseError> { + let mut implied_rules: BTreeMap> = BTreeMap::new(); + for imply in DbImpliedRole::find().all(db).await? { + implied_rules + .entry(imply.prior_role_id) + .and_modify(|x| { + x.insert(imply.implied_role_id.clone()); + }) + .or_insert(BTreeSet::from([imply.implied_role_id.clone()])); + } + if resolve { + Ok(expand_implied_roles(&implied_rules)) + } else { + Ok(implied_rules) + } +} + +#[cfg(test)] +mod tests { + use sea_orm::{DatabaseBackend, MockDatabase, Transaction}; + + use crate::config::Config; + use crate::db::entity::implied_role; + + use super::*; + + #[test] + fn test_expand_implied_roles() { + let implied_data: BTreeMap> = BTreeMap::from([ + ( + "1".into(), + BTreeSet::from(["2".to_string(), "3".to_string()]), + ), + ("2".into(), BTreeSet::from(["4".to_string()])), + ( + "4".into(), + BTreeSet::from(["7".to_string(), "8".to_string()]), + ), + ("5".into(), BTreeSet::from(["6".to_string()])), + ]); + assert_eq!( + BTreeMap::from([ + ( + "1".into(), + BTreeSet::from([ + "2".to_string(), + "3".to_string(), + "4".to_string(), + "7".to_string(), + "8".to_string() + ]) + ), + ( + "2".into(), + BTreeSet::from(["4".to_string(), "7".to_string(), "8".to_string()]) + ), + ( + "4".into(), + BTreeSet::from(["7".to_string(), "8".to_string()]) + ), + ("5".into(), BTreeSet::from(["6".to_string()])), + ]), + expand_implied_roles(&implied_data) + ); + } + + fn get_implied_role_mock(id: String, implied_id: String) -> implied_role::Model { + implied_role::Model { + prior_role_id: id.clone(), + implied_role_id: implied_id.clone(), + } + } + + #[tokio::test] + async fn test_list_rules() { + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results([vec![ + get_implied_role_mock("1".into(), "2".into()), + get_implied_role_mock("1".into(), "3".into()), + get_implied_role_mock("2".into(), "4".into()), + get_implied_role_mock("4".into(), "7".into()), + get_implied_role_mock("4".into(), "8".into()), + get_implied_role_mock("5".into(), "6".into()), + ]]) + .append_query_results([vec![ + get_implied_role_mock("1".into(), "2".into()), + get_implied_role_mock("1".into(), "3".into()), + get_implied_role_mock("2".into(), "4".into()), + get_implied_role_mock("4".into(), "7".into()), + get_implied_role_mock("4".into(), "8".into()), + get_implied_role_mock("5".into(), "6".into()), + ]]) + .into_connection(); + let config = Config::default(); + assert_eq!( + list_rules(&db, true).await.unwrap(), + BTreeMap::from([ + ( + "1".into(), + BTreeSet::from([ + "2".to_string(), + "3".to_string(), + "4".to_string(), + "7".to_string(), + "8".to_string() + ]) + ), + ( + "2".into(), + BTreeSet::from(["4".to_string(), "7".to_string(), "8".to_string()]) + ), + ( + "4".into(), + BTreeSet::from(["7".to_string(), "8".to_string()]) + ), + ("5".into(), BTreeSet::from(["6".to_string()])), + ]), + ); + + assert_eq!( + list_rules(&db, false).await.unwrap(), + BTreeMap::from([ + ( + "1".into(), + BTreeSet::from(["2".to_string(), "3".to_string(),]) + ), + ("2".into(), BTreeSet::from(["4".to_string()])), + ( + "4".into(), + BTreeSet::from(["7".to_string(), "8".to_string()]) + ), + ("5".into(), BTreeSet::from(["6".to_string()])), + ]), + ); + + // Checking transaction log + assert_eq!( + db.into_transaction_log(), + [ + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + Transaction::from_sql_and_values( + DatabaseBackend::Postgres, + r#"SELECT "implied_role"."prior_role_id", "implied_role"."implied_role_id" FROM "implied_role""#, + [] + ), + ] + ); + } +} diff --git a/src/db/entity/implied_role.rs b/src/db/entity/implied_role.rs index 38e690fb..4b519769 100644 --- a/src/db/entity/implied_role.rs +++ b/src/db/entity/implied_role.rs @@ -43,6 +43,24 @@ pub enum Relation { on_delete = "Cascade" )] Role1, + #[sea_orm( + belongs_to = "Entity", + from = "Column::ImpliedRoleId", + to = "Column::PriorRoleId" + )] + SelfReferencing, } impl ActiveModelBehavior for ActiveModel {} + +pub struct SelfReferencingLink; + +impl Linked for SelfReferencingLink { + type FromEntity = Entity; + + type ToEntity = Entity; + + fn link(&self) -> Vec { + vec![Relation::SelfReferencing.def()] + } +} diff --git a/typos.toml b/typos.toml index 0f47f48d..9e8aff15 100644 --- a/typos.toml +++ b/typos.toml @@ -28,7 +28,7 @@ ratatui = "ratatui" [type.rust] extend-glob = [] extend-ignore-identifiers-re = [] -extend-ignore-words-re = ["udid"] +extend-ignore-words-re = ["udid", "typ"] extend-ignore-re = [ "gAA.*\\b" ] @@ -41,87 +41,6 @@ wher = "wher" ro = "ro" ser = "ser" -[type.sh] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.sh.extend-identifiers] -ot = "ot" -stap = "stap" - -[type.sh.extend-words] - -[type.jl] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.jl.extend-identifiers] - -[type.jl.extend-words] -modul = "modul" -egal = "egal" -egals = "egals" -usig = "usig" - -[type.vimscript] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.vimscript.extend-identifiers] -windo = "windo" - -[type.vimscript.extend-words] - -[type.css] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.css.extend-identifiers] -nd = "nd" - -[type.css.extend-words] - -[type.py] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.py.extend-identifiers] -NDArray = "NDArray" - -[type.py.extend-words] - -[type.cert] -extend-glob = [] -check-file = false -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.cert.extend-identifiers] - -[type.cert.extend-words] - -[type.man] -extend-glob = [] -extend-ignore-identifiers-re = [] -extend-ignore-words-re = [] -extend-ignore-re = [] - -[type.man.extend-identifiers] -Nd = "Nd" - -[type.man.extend-words] - [type.lock] extend-glob = [] check-file = false