diff --git a/Cargo.lock b/Cargo.lock index 02e376d7..d206a9fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,16 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli", + "gimli 0.31.1", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli 0.32.3", ] [[package]] @@ -92,9 +101,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -107,9 +116,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -281,9 +290,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "axum-macros", @@ -301,8 +310,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", @@ -316,9 +324,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", @@ -327,7 +335,6 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -347,17 +354,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line", + "addr2line 0.25.1", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.37.3", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -575,9 +582,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.40" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ "find-msvc-tools", "jobserver", @@ -608,7 +615,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -654,7 +661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 2.6.0", + "half 2.7.0", ] [[package]] @@ -780,9 +787,9 @@ dependencies = [ [[package]] name = "config" -version = "0.15.16" +version = "0.15.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef036f0ecf99baef11555578630e2cca559909b4c50822dbba828c252d21c49" +checksum = "180e549344080374f9b32ed41bf3b6b57885ff6a289367b3dbc10eea8acc1918" dependencies = [ "async-trait", "convert_case", @@ -826,9 +833,9 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -880,36 +887,36 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce81edaca6167d1f78da026afa92d7ff957a80aa82a79076e11cd34cde20165" +checksum = "0ae7b60ec3fd7162427d3b3801520a1908bef7c035b52983cd3ca11b8e7deb51" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0d51e12f958551165969c6e8767e1e461729f6c1ccae923b0ba1d5cbcbbbf8" +checksum = "6511c200fed36452697b4b6b161eae57d917a2044e6333b1c1389ed63ccadeee" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41294c755094d2c8a514cea903039742474423f2e91601332eab5f4094f76333" +checksum = "5f7086a645aa58bae979312f64e3029ac760ac1b577f5cd2417844842a2ca07f" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb6f5d0df5bd0d02c63ec48e8f2e38a176b123f59e084f22caf89a0d0593e7e" +checksum = "5225b4dec45f3f3dbf383f12560fac5ce8d780f399893607e21406e12e77f491" dependencies = [ "serde", "serde_derive", @@ -917,9 +924,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e543cdb278b7c15f739021cf880ee1808c68fa2402febb87edb9307f552c8fec" +checksum = "858fb3331e53492a95979378d6df5208dd1d0d315f19c052be8115f4efc888e0" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -930,7 +937,7 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.31.1", "hashbrown 0.15.5", "log", "pulley-interpreter", @@ -939,14 +946,14 @@ dependencies = [ "serde", "smallvec", "target-lexicon", - "wasmtime-math", + "wasmtime-internal-math", ] [[package]] name = "cranelift-codegen-meta" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f979c75cfd712dbc754799dfe4a4d0db7a51defc2e36d006b27a8a63e018eece" +checksum = "456715b9d5f12398f156d5081096e7b5d039f01b9ecc49790a011c8e43e65b5f" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -956,24 +963,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f36e74ba4033490587a47952f74390cb7d4f1fc1fa28ace50564e491f1e38f" +checksum = "0306041099499833f167a0ddb707e1e54100f1a84eab5631bc3dad249708f482" [[package]] name = "cranelift-control" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6671962c7d65b9a7ad038cd92da6784744d8a9ecf8ded8bb9a1f7046dbe2ccf" +checksum = "1672945e1f9afc2297f49c92623f5eabc64398e2cb0d824f8f72a2db2df5af23" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee832f8329fa87c5df6c1d64a8506a58031e6f8a190d9b21b1900272a4dbb47d" +checksum = "aa3cd55eb5f3825b9ae5de1530887907360a6334caccdc124c52f6d75246c98a" dependencies = [ "cranelift-bitset", "serde", @@ -982,9 +989,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7bc17aa3277214eab4b63a03544b1b46962154012b751c9f14c2a5419c6471" +checksum = "781f9905f8139b8de22987b66b522b416fe63eb76d823f0b3a8c02c8fd9500c7" dependencies = [ "cranelift-codegen", "log", @@ -994,15 +1001,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff02dcecae2e7e9c61b713f1fb46eabecdca9f55b49f99859ceb1a3e7f4a9cb" +checksum = "a05337a2b02c3df00b4dd9a263a027a07b3dff49f61f7da3b5d195c21eaa633d" [[package]] name = "cranelift-native" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f76fd681f35bdf17be9c3e516b9acc0c7bd61b81faf95496decd8e0000979c" +checksum = "2eee7a496dd66380082c9c5b6f2d5fa149cec0ec383feec5caf079ca2b3671c2" dependencies = [ "cranelift-codegen", "libc", @@ -1011,9 +1018,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.121.2" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3d9071bc5ee5573e723d9d84a45b7025a29e8f2c5ad81b3b9d0293129541d9" +checksum = "b530783809a55cb68d070e0de60cfbb3db0dc94c8850dd5725411422bedcf6bb" [[package]] name = "crc" @@ -1265,12 +1272,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1315,6 +1322,27 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -1489,7 +1517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -1571,15 +1599,15 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" dependencies = [ "crc32fast", "libz-rs-sys", @@ -1792,6 +1820,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "glob" version = "0.3.3" @@ -1836,12 +1870,13 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "e54c115d4f30f52c67202f079c5f9d8b49db4691f460fdb0b4c2e838261b2ba5" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -2330,9 +2365,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -2373,9 +2408,9 @@ dependencies = [ [[package]] name = "keycloak" -version = "26.3.200" +version = "26.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61d2ba7d275689304179711ef8b0f12facfdaa351270bc8d6aaea8295d88315a" +checksum = "ed451521a2e0523fb954fa585815bd9d131d6d51095d23eced49381816e40944" dependencies = [ "async-trait", "percent-encoding", @@ -2402,9 +2437,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -2456,11 +2491,10 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -2554,6 +2588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2634,11 +2669,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2710,7 +2745,7 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "chrono", "getrandom 0.2.16", "http", @@ -2736,6 +2771,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.7.1" @@ -2791,7 +2835,7 @@ dependencies = [ "sha1", "sha2", "sprintf", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tracing", "urlencoding", @@ -2897,6 +2941,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", + "itertools 0.14.0", "keycloak", "mockall", "mockall_double", @@ -2985,9 +3030,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "4.2.2" +version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" [[package]] name = "p256" @@ -3021,9 +3066,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3031,15 +3076,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -3083,20 +3128,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", - "thiserror 2.0.17", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" dependencies = [ "pest", "pest_generator", @@ -3104,9 +3148,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", @@ -3117,9 +3161,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" dependencies = [ "pest", "sha2", @@ -3343,15 +3387,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "psm" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" -dependencies = [ - "cc", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -3374,21 +3409,21 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "34.0.2" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14280b69a9cbb6ada02a7aa5f7b3f1b72d1043b5bc9336990b700525dea6e3" +checksum = "b89c4319786b16c1a6a38ee04788d32c669b61ba4b69da2162c868c18be99c1b" dependencies = [ "cranelift-bitset", "log", "pulley-macros", - "wasmtime-math", + "wasmtime-internal-math", ] [[package]] name = "pulley-macros" -version = "34.0.2" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076f1be746801280af4c96c4407b5fd1d09cfa53ab27ba0ac7dd8f207e7bbf83" +checksum = "938543690519c20c3a480d20a8efcc8e69abeb44093ab1df4e7c1f81f26c677a" dependencies = [ "proc-macro2", "quote", @@ -3552,27 +3587,27 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", @@ -3595,9 +3630,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433" dependencies = [ "aho-corasick", "memchr", @@ -3607,9 +3642,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" dependencies = [ "aho-corasick", "memchr", @@ -3618,9 +3653,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "c3160422bbd54dd5ecfdca71e5fd59b7b8fe2b1697ab2baf64f6d05dcc66d298" [[package]] name = "rend" @@ -3875,7 +3910,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -3940,7 +3975,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -4001,14 +4036,15 @@ dependencies = [ [[package]] name = "sea-orm" -version = "1.1.16" +version = "1.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335d87ec8e5c6eb4b2afb866dc53ed57a5cba314af63ce288db83047aa0fed4d" +checksum = "699b1ec145a6530c8f862eed7529d8a6068392e628d81cc70182934001e9c2a3" dependencies = [ "async-stream", "async-trait", "bigdecimal", "chrono", + "derive_more", "futures-util", "log", "ouroboros", @@ -4030,9 +4066,9 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "1.1.16" +version = "1.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d4ff77d7c27f64942273513e7c103a1594c88deba33dfb2f490b362ea220b4" +checksum = "500cd31ebb07814d4c7b73796708bfab6c13d22f8db072cdb5115f967f4d5d2c" dependencies = [ "chrono", "clap", @@ -4049,9 +4085,9 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "1.1.16" +version = "1.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68de7a2258410fd5e6ba319a4fe6c4af7811507fc714bbd76534ae6caa60f95f" +checksum = "b0c964f4b7f34f53decf381bc88f03187b9355e07f356ce65544626e781a9585" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4063,9 +4099,9 @@ dependencies = [ [[package]] name = "sea-orm-migration" -version = "1.1.16" +version = "1.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4649155dbfd88f92e2aa9f5defe0b020e43d7c4d52a2189658d8813e26b61bd" +checksum = "977e3f71486b04371026d1ecd899f49cf437f832cd11d463f8948ee02e47ed9e" dependencies = [ "async-trait", "clap", @@ -4341,9 +4377,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] @@ -4362,9 +4398,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" dependencies = [ "base64 0.22.1", "chrono", @@ -4373,8 +4409,7 @@ dependencies = [ "indexmap 2.11.4", "schemars 0.9.0", "schemars 1.0.4", - "serde", - "serde_derive", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -4382,9 +4417,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -4525,9 +4560,9 @@ dependencies = [ [[package]] name = "sprintf" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78222247fc55e10208ed1ba60f8296390bc67a489bc27a36231765d8d6f60ec5" +checksum = "b0e59842c3aac5c7901ea11bbb02e60e5d67263b43d4361dec9303cca8e764eb" dependencies = [ "thiserror 2.0.17", ] @@ -4742,9 +4777,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -4875,7 +4910,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -5132,9 +5167,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "serde_core", "serde_spanned", @@ -5145,18 +5180,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap 2.11.4", "toml_datetime", @@ -5166,9 +5201,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] @@ -5343,9 +5378,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -5589,9 +5624,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", @@ -5602,9 +5637,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -5616,9 +5651,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -5629,9 +5664,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5639,9 +5674,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -5652,18 +5687,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-encoder" -version = "0.233.0" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9679ae3cf7cfa2ca3a327f7fab97f27f3294d402fd1a76ca8ab514e17973e4d3" +checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a" dependencies = [ "leb128fmt", "wasmparser", @@ -5671,9 +5706,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.233.0" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51cb03afce7964bbfce46602d6cb358726f36430b6ba084ac6020d8ce5bc102" +checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags", "hashbrown 0.15.5", @@ -5684,9 +5719,9 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.233.0" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf8e5b732895c99b21aa615f1b73352e51bbe2b2cb6c87eae7f990d07c1ac18" +checksum = "75aa8e9076de6b9544e6dab4badada518cca0bf4966d35b131bbd057aed8fa0a" dependencies = [ "anyhow", "termcolor", @@ -5695,11 +5730,11 @@ dependencies = [ [[package]] name = "wasmtime" -version = "34.0.2" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec10e50038f22ab407fdd8708120b8feed3450a02618efcf26ca47e82122927d" +checksum = "b6fe976922a16af3b0d67172c473d1fd4f1aa5d0af9c8ba6538c741f3af686f4" dependencies = [ - "addr2line", + "addr2line 0.24.2", "anyhow", "async-trait", "bitflags", @@ -5712,10 +5747,9 @@ dependencies = [ "log", "mach2", "memfd", - "object", + "object 0.36.7", "once_cell", "postcard", - "psm", "pulley-interpreter", "rayon", "rustix", @@ -5725,53 +5759,77 @@ dependencies = [ "target-lexicon", "trait-variant", "wasmparser", - "wasmtime-asm-macros", - "wasmtime-component-macro", - "wasmtime-cranelift", "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit-icache-coherence", - "wasmtime-math", - "wasmtime-slab", - "wasmtime-versioned-export-macros", + "wasmtime-internal-asm-macros", + "wasmtime-internal-component-macro", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-math", + "wasmtime-internal-slab", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", "windows-sys 0.59.0", ] [[package]] -name = "wasmtime-asm-macros" -version = "34.0.2" +name = "wasmtime-environ" +version = "35.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b6264a78d806924abbc76bbc75eac24976bc83bdfb938e5074ae551242436f" +dependencies = [ + "anyhow", + "cranelift-bitset", + "cranelift-entity", + "gimli 0.31.1", + "indexmap 2.11.4", + "log", + "object 0.36.7", + "postcard", + "serde", + "serde_derive", + "smallvec", + "target-lexicon", + "wasm-encoder", + "wasmparser", + "wasmprinter", +] + +[[package]] +name = "wasmtime-internal-asm-macros" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d379cda46d6fd18619e282a75fbb09b70b3d0f166b605f45b4059dfaf9dc6ce" +checksum = "6775a9b516559716e5710e95a8014ca0adcc81e5bf4d3ad7899d89ae40094d1a" dependencies = [ "cfg-if", ] [[package]] -name = "wasmtime-component-macro" -version = "34.0.2" +name = "wasmtime-internal-component-macro" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b08be093e0a876da45f79070c2ada4656f2785eb77c01b86ce60be3153920a5" +checksum = "dc3d098205e405e6b5ced06c1815621b823464b6ea289eaafe494139b0aee287" dependencies = [ "anyhow", "proc-macro2", "quote", "syn 2.0.106", - "wasmtime-component-util", - "wasmtime-wit-bindgen", + "wasmtime-internal-component-util", + "wasmtime-internal-wit-bindgen", "wit-parser", ] [[package]] -name = "wasmtime-component-util" -version = "34.0.2" +name = "wasmtime-internal-component-util" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0451ce0dd94a33d0dbd57934ce666a04c2753a5262ca2bc84cf6a67cf5303dc" +checksum = "219252067216242ed2b32665611b0ee356d6e92cbb897ecb9a10cae0b97bdeca" [[package]] -name = "wasmtime-cranelift" -version = "34.0.2" +name = "wasmtime-internal-cranelift" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15aa836683d7398f13f2f26bbe74c404ceaba66b6bbb96700d6b7f91bec90e03" +checksum = "7ec9ad7565e6a8de7cb95484e230ff689db74a4a085219e0da0cbd637a29c01c" dependencies = [ "anyhow", "cfg-if", @@ -5780,64 +5838,41 @@ dependencies = [ "cranelift-entity", "cranelift-frontend", "cranelift-native", - "gimli", + "gimli 0.31.1", "itertools 0.14.0", "log", - "object", + "object 0.36.7", "pulley-interpreter", "smallvec", "target-lexicon", "thiserror 2.0.17", "wasmparser", "wasmtime-environ", - "wasmtime-math", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-environ" -version = "34.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317081a0cbbb1f749d348b262575608fc082d47ab11b6247bbe9163eeb955777" -dependencies = [ - "anyhow", - "cranelift-bitset", - "cranelift-entity", - "gimli", - "indexmap 2.11.4", - "log", - "object", - "postcard", - "serde", - "serde_derive", - "smallvec", - "target-lexicon", - "wasm-encoder", - "wasmparser", - "wasmprinter", + "wasmtime-internal-math", + "wasmtime-internal-versioned-export-macros", ] [[package]] -name = "wasmtime-fiber" -version = "34.0.2" +name = "wasmtime-internal-fiber" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6763b33eceefc443f6477d84dc8751df5f23d280d7e01f28339fa3ec4b00ff13" +checksum = "8b636ff8b220ebaf29dfe3b23770e4b2bad317b9683e3bf7345e162387385b39" dependencies = [ "anyhow", "cc", "cfg-if", "libc", "rustix", - "wasmtime-asm-macros", - "wasmtime-versioned-export-macros", + "wasmtime-internal-asm-macros", + "wasmtime-internal-versioned-export-macros", "windows-sys 0.59.0", ] [[package]] -name = "wasmtime-jit-icache-coherence" -version = "34.0.2" +name = "wasmtime-internal-jit-icache-coherence" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea6b740d1a35f2cebfe88e013ac8a4a84ff8dabc3a392df920abf554e871cf2" +checksum = "4417e06b7f80baff87d9770852c757a39b8d7f11d78b2620ca992b8725f16f50" dependencies = [ "anyhow", "cfg-if", @@ -5846,25 +5881,38 @@ dependencies = [ ] [[package]] -name = "wasmtime-math" -version = "34.0.2" +name = "wasmtime-internal-math" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fa317691aedc64aae3a86b3d786e4b2b0007bc0b56e0b6098b8b5a85ab2134" +checksum = "7710d5c4ecdaa772927fd11e5dc30a9a62d1fc8fe933e11ad5576ad596ab6612" dependencies = [ "libm", ] [[package]] -name = "wasmtime-slab" -version = "34.0.2" +name = "wasmtime-internal-slab" +version = "35.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ab22fabe1eed27ab01fd47cd89deacf43ad222ed7fd169ba6f4dd1fbddc53b" + +[[package]] +name = "wasmtime-internal-unwinder" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a06819d24370273021054b50589e3078e7f5cfac15515e58b3fbbebf5e5b39" +checksum = "307708f302f5dcf19c1bbbfb3d9f2cbc837dd18088a7988747b043a46ba38ecc" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "log", + "object 0.36.7", +] [[package]] -name = "wasmtime-versioned-export-macros" -version = "34.0.2" +name = "wasmtime-internal-versioned-export-macros" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca100ed168ffc9b37aefc07a5be440645eab612a2ff6e2ff884e8cc3740e666" +checksum = "342b0466f92b7217a4de9e114175fedee1907028567d2548bcd42f71a8b5b016" dependencies = [ "proc-macro2", "quote", @@ -5872,10 +5920,10 @@ dependencies = [ ] [[package]] -name = "wasmtime-wit-bindgen" -version = "34.0.2" +name = "wasmtime-internal-wit-bindgen" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233fdcb96f9097be697319ba647ef42bdbdb40e89f04c8ae3713103813b5b793" +checksum = "1ae057d44a5b60e6ec529b0c21809a9d1fc92e91ef6e0f6771ed11dd02a94a08" dependencies = [ "anyhow", "heck 0.5.0", @@ -5885,9 +5933,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -5973,9 +6021,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" dependencies = [ "rustls-pki-types", ] @@ -5996,27 +6044,27 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -6025,9 +6073,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -6042,9 +6090,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" @@ -6068,11 +6116,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6086,11 +6134,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6126,16 +6174,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6171,19 +6219,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -6200,9 +6248,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -6218,9 +6266,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -6236,9 +6284,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -6248,9 +6296,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -6266,9 +6314,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -6284,9 +6332,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -6302,9 +6350,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -6320,9 +6368,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -6341,9 +6389,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wit-parser" -version = "0.233.0" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22f1cd55247a2e616870b619766e9522df36b7abafbb29bbeb34b7a9da7e9f0" +checksum = "0a1f95a87d03a33e259af286b857a95911eb46236a0f726cbaec1227b3dfc67a" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 3523650d..62208d9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" edition = "2024" license = "Apache-2.0" authors = ["Artem Goncharov (gtema)"] -rust-version = "1.85" # MSRV +rust-version = "1.90" # MSRV repository = "https://github.com/gtema/keystone" [package.metadata.clippy] @@ -26,7 +26,7 @@ harness = false [dependencies] async-trait = { version = "0.1" } axum = { version = "0.8", features = ["macros"] } -base64 = "0.22" +base64 = { version = "0.22" } bcrypt = { version = "0.17", features = ["alloc"] } bytes = { version = "1.10" } chrono = { version = "0.4" } @@ -38,10 +38,11 @@ dyn-clone = { version = "1.0" } eyre = { version = "0.6" } fernet = { version = "0.2", default-features = false, features = ["rustcrypto"] } futures-util = { version = "0.3" } +itertools = { version = "0.14" } mockall_double = { version = "0.3" } opa-wasm = { version = "^0.1", optional = true } openidconnect = { version = "4.0" } -regex = { version = "1.11"} +regex = { version = "1.12" } reqwest = { version = "0.12", features = ["json", "http2", "gzip", "deflate"] } rmp = { version = "0.8" } schemars = { version = "1.0" } @@ -71,7 +72,7 @@ criterion = { version = "0.7", features = ["async_tokio"] } http-body-util = "0.1" hyper = { version = "1.7", features = ["http1"] } hyper-util = { version = "0.1", features = ["tokio", "http1"] } -keycloak = { version = "26.3" } +keycloak = { version = "26.4" } mockall = { version = "0.13" } reqwest = { version = "0.12", features = ["json", "multipart"] } sea-orm = { version = "1.1", features = ["mock"]} diff --git a/Dockerfile b/Dockerfile index c990d9e0..a98d8fc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ ################ ##### Builder -FROM rust:1.89.0-slim-bookworm AS builder +FROM rust:1.90.0-slim-bookworm AS builder #RUN rustup target add x86_64-unknown-linux-gnu &&\ RUN apt update &&\ diff --git a/benches/fernet_token.rs b/benches/fernet_token.rs index dcb58d1b..35a96fac 100644 --- a/benches/fernet_token.rs +++ b/benches/fernet_token.rs @@ -5,7 +5,7 @@ use tempfile::tempdir; use openstack_keystone::config::Config; use openstack_keystone::token::fernet::FernetTokenProvider; -use openstack_keystone::token::types::TokenBackend; +//use openstack_keystone::token::types::TokenBackend; fn decode(backend: &FernetTokenProvider, token: &str) { backend.decrypt(token).unwrap(); @@ -24,10 +24,9 @@ fn bench_decrypt_token(c: &mut Criterion) { .set_override("database.connection", "dummy") .unwrap(); let mut config: Config = Config::try_from(builder).expect("can build a valid config"); - let mut backend = FernetTokenProvider::default(); - config.fernet_tokens.key_repository = tmp_dir.keep(); - backend.set_config(config); + + let mut backend = FernetTokenProvider::new(config.clone()); backend.load_keys().unwrap(); let token = "gAAAAABns2ixy75K_KfoosWLrNNqG6KW8nm3Xzv0_2dOx8ODWH7B8i2g8CncGLO6XBEH_TYLg83P6XoKQ5bU8An8Kqgw9WX3bvmEQXphnwPM6aRAOQUSdVhTlUm_8otDG9BS2rc70Q7pfy57S3_yBgimy-174aKdP8LPusvdHZsQPEJO9pfeXWw"; diff --git a/src/api/v3/auth/token/mod.rs b/src/api/v3/auth/token/mod.rs index 49f555dc..e64927f1 100644 --- a/src/api/v3/auth/token/mod.rs +++ b/src/api/v3/auth/token/mod.rs @@ -70,31 +70,31 @@ async fn authenticate_request( .await?, ); } - } else if method == "token" { - if let Some(token) = &req.auth.identity.token { - let mut authz = state + } else if method == "token" + && let Some(token) = &req.auth.identity.token + { + let mut authz = state + .provider + .get_token_provider() + .authenticate_by_token(&token.id, Some(false), None) + .await?; + // Resolve the user + authz.user = Some( + state .provider - .get_token_provider() - .authenticate_by_token(&token.id, Some(false), None) - .await?; - // Resolve the user - authz.user = Some( - state - .provider - .get_identity_provider() - .get_user(&state.db, &authz.user_id) - .await - .map(|x| { - x.ok_or_else(|| KeystoneApiError::NotFound { - resource: "user".into(), - identifier: authz.user_id.clone(), - }) - })??, - ); - authenticated_info = Some(authz); - - {} - } + .get_identity_provider() + .get_user(&state.db, &authz.user_id) + .await + .map(|x| { + x.ok_or_else(|| KeystoneApiError::NotFound { + resource: "user".into(), + identifier: authz.user_id.clone(), + }) + })??, + ); + authenticated_info = Some(authz); + + {} } } authenticated_info diff --git a/src/api/v4/auth/token/mod.rs b/src/api/v4/auth/token/mod.rs index 4776ba1b..507c8a02 100644 --- a/src/api/v4/auth/token/mod.rs +++ b/src/api/v4/auth/token/mod.rs @@ -54,31 +54,31 @@ async fn authenticate_request( .await?, ); } - } else if method == "token" { - if let Some(token) = &req.auth.identity.token { - let mut authz = state + } else if method == "token" + && let Some(token) = &req.auth.identity.token + { + let mut authz = state + .provider + .get_token_provider() + .authenticate_by_token(&token.id, Some(false), None) + .await?; + // Resolve the user + authz.user = Some( + state .provider - .get_token_provider() - .authenticate_by_token(&token.id, Some(false), None) - .await?; - // Resolve the user - authz.user = Some( - state - .provider - .get_identity_provider() - .get_user(&state.db, &authz.user_id) - .await - .map(|x| { - x.ok_or_else(|| KeystoneApiError::NotFound { - resource: "user".into(), - identifier: authz.user_id.clone(), - }) - })??, - ); - authenticated_info = Some(authz); - - {} - } + .get_identity_provider() + .get_user(&state.db, &authz.user_id) + .await + .map(|x| { + x.ok_or_else(|| KeystoneApiError::NotFound { + resource: "user".into(), + identifier: authz.user_id.clone(), + }) + })??, + ); + authenticated_info = Some(authz); + + {} } } authenticated_info diff --git a/src/api/v4/federation/common.rs b/src/api/v4/federation/common.rs index c7736613..2376a61f 100644 --- a/src/api/v4/federation/common.rs +++ b/src/api/v4/federation/common.rs @@ -79,13 +79,13 @@ pub(super) fn validate_bound_claims( claims: &IdTokenClaims, claims_as_json: &Value, ) -> Result<(), OidcError> { - if let Some(bound_subject) = &mapping.bound_subject { - if bound_subject != claims.subject().as_str() { - return Err(OidcError::BoundSubjectMismatch { - expected: bound_subject.to_string(), - found: claims.subject().as_str().into(), - }); - } + if let Some(bound_subject) = &mapping.bound_subject + && bound_subject != claims.subject().as_str() + { + return Err(OidcError::BoundSubjectMismatch { + expected: bound_subject.to_string(), + found: claims.subject().as_str().into(), + }); } if let Some(bound_audiences) = &mapping.bound_audiences { let mut bound_audiences_match: bool = false; @@ -106,23 +106,23 @@ pub(super) fn validate_bound_claims( }); } } - if let Some(bound_claims) = &mapping.bound_claims { - if let Some(required_claims) = bound_claims.as_object() { - for (claim, value) in required_claims.iter() { - if !claims_as_json - .get(claim) - .map(|x| x == value) - .is_some_and(|val| val) - { - return Err(OidcError::BoundClaimsMismatch { - claim: claim.to_string(), - expected: value.to_string(), - found: claims_as_json - .get(claim) - .map(|x| x.to_string()) - .unwrap_or_default(), - }); - } + if let Some(bound_claims) = &mapping.bound_claims + && let Some(required_claims) = bound_claims.as_object() + { + for (claim, value) in required_claims.iter() { + if !claims_as_json + .get(claim) + .map(|x| x == value) + .is_some_and(|val| val) + { + return Err(OidcError::BoundClaimsMismatch { + claim: claim.to_string(), + expected: value.to_string(), + found: claims_as_json + .get(claim) + .map(|x| x.to_string()) + .unwrap_or_default(), + }); } } } @@ -192,20 +192,20 @@ pub(super) async fn map_user_data( .ok_or(OidcError::UserDomainUnbound)?, ); - if let Some(groups_claim) = &mapping.groups_claim { - if let Some(group_names_data) = &claims_as_json.get(groups_claim) { - builder.group_names( - group_names_data - .as_array() - .map(|names| { - names - .iter() - .map(|group| group.as_str().map(|v| v.to_string())) - .collect::>>() - }) - .ok_or(OidcError::GroupsClaimNotArrayOfStrings)?, - ); - } + if let Some(groups_claim) = &mapping.groups_claim + && let Some(group_names_data) = &claims_as_json.get(groups_claim) + { + builder.group_names( + group_names_data + .as_array() + .map(|names| { + names + .iter() + .map(|group| group.as_str().map(|v| v.to_string())) + .collect::>>() + }) + .ok_or(OidcError::GroupsClaimNotArrayOfStrings)?, + ); } Ok(builder.build()?) diff --git a/src/api/v4/federation/oidc.rs b/src/api/v4/federation/oidc.rs index 3a3551d2..cc9f8ba3 100644 --- a/src/api/v4/federation/oidc.rs +++ b/src/api/v4/federation/oidc.rs @@ -171,15 +171,14 @@ pub async fn callback( let claims = id_token .claims(&client.id_token_verifier(), &Nonce::new(auth_state.nonce)) .map_err(OidcError::from)?; - if let Some(bound_issuer) = &idp.bound_issuer { - if Url::parse(bound_issuer) + if let Some(bound_issuer) = &idp.bound_issuer + && Url::parse(bound_issuer) .map_err(OidcError::from) .wrap_err_with(|| { format!("while parsing the mapping bound_issuer url: {bound_issuer}") })? == *claims.issuer().url() - {} - } + {} let claims_as_json = serde_json::to_value(claims)?; debug!("Claims data {claims_as_json}"); diff --git a/src/assignment/mod.rs b/src/assignment/mod.rs index c57bb370..c021030a 100644 --- a/src/assignment/mod.rs +++ b/src/assignment/mod.rs @@ -158,15 +158,15 @@ impl AssignmentApi for AssignmentProvider { if let Some(uid) = ¶ms.user_id { actors.push(uid.into()); } - if let Some(true) = ¶ms.effective { - if let Some(uid) = ¶ms.user_id { - let users = provider - .get_identity_provider() - .list_groups_of_user(db, uid) - .await?; - actors.extend(users.into_iter().map(|x| x.id)); - }; - } + if let Some(true) = ¶ms.effective + && let Some(uid) = ¶ms.user_id + { + let users = provider + .get_identity_provider() + .list_groups_of_user(db, uid) + .await?; + actors.extend(users.into_iter().map(|x| x.id)); + }; if let Some(val) = ¶ms.project_id { targets.push(RoleAssignmentTarget { target_id: val.clone(), diff --git a/src/federation/backends/sql/identity_provider.rs b/src/federation/backends/sql/identity_provider.rs index 0013f6a6..4864c6c6 100644 --- a/src/federation/backends/sql/identity_provider.rs +++ b/src/federation/backends/sql/identity_provider.rs @@ -50,18 +50,18 @@ impl TryFrom for IdentityProvider { if let Some(val) = &value.oidc_response_mode { builder.oidc_response_mode(val); } - if let Some(val) = &value.oidc_response_types { - if !val.is_empty() { - builder.oidc_response_types(Vec::from_iter(val.split(",").map(Into::into))); - } + if let Some(val) = &value.oidc_response_types + && !val.is_empty() + { + builder.oidc_response_types(Vec::from_iter(val.split(",").map(Into::into))); } if let Some(val) = &value.jwks_url { builder.jwks_url(val); } - if let Some(val) = &value.jwt_validation_pubkeys { - if !val.is_empty() { - builder.jwt_validation_pubkeys(Vec::from_iter(val.split(",").map(Into::into))); - } + if let Some(val) = &value.jwt_validation_pubkeys + && !val.is_empty() + { + builder.jwt_validation_pubkeys(Vec::from_iter(val.split(",").map(Into::into))); } if let Some(val) = &value.bound_issuer { builder.bound_issuer(val); diff --git a/src/federation/backends/sql/mapping.rs b/src/federation/backends/sql/mapping.rs index f6dd40b6..1dd31a4b 100644 --- a/src/federation/backends/sql/mapping.rs +++ b/src/federation/backends/sql/mapping.rs @@ -63,10 +63,10 @@ impl TryFrom for Mapping { db_mapping_type::Oidc => MappingType::Oidc, db_mapping_type::Jwt => MappingType::Jwt, }); - if let Some(val) = &value.allowed_redirect_uris { - if !val.is_empty() { - builder.allowed_redirect_uris(Vec::from_iter(val.split(",").map(Into::into))); - } + if let Some(val) = &value.allowed_redirect_uris + && !val.is_empty() + { + builder.allowed_redirect_uris(Vec::from_iter(val.split(",").map(Into::into))); } builder.user_id_claim(value.user_id_claim.clone()); builder.user_name_claim(value.user_name_claim.clone()); @@ -76,10 +76,10 @@ impl TryFrom for Mapping { if let Some(val) = &value.groups_claim { builder.groups_claim(val); } - if let Some(val) = &value.bound_audiences { - if !val.is_empty() { - builder.bound_audiences(Vec::from_iter(val.split(",").map(Into::into))); - } + if let Some(val) = &value.bound_audiences + && !val.is_empty() + { + builder.bound_audiences(Vec::from_iter(val.split(",").map(Into::into))); } if let Some(val) = &value.bound_subject { builder.bound_subject(val); @@ -87,10 +87,10 @@ impl TryFrom for Mapping { if let Some(val) = &value.bound_claims { builder.bound_claims(val.clone()); } - if let Some(val) = &value.oidc_scopes { - if !val.is_empty() { - builder.oidc_scopes(Vec::from_iter(val.split(",").map(Into::into))); - } + if let Some(val) = &value.oidc_scopes + && !val.is_empty() + { + builder.oidc_scopes(Vec::from_iter(val.split(",").map(Into::into))); } if let Some(val) = &value.token_project_id { builder.token_project_id(val.clone()); diff --git a/src/identity/backends/sql.rs b/src/identity/backends/sql.rs index eaf150b6..33bbf900 100644 --- a/src/identity/backends/sql.rs +++ b/src/identity/backends/sql.rs @@ -71,36 +71,32 @@ impl IdentityBackend for SqlBackend { .await?; if let Some((local_user, password)) = user_with_passwords { let passwords: Vec = password.into_iter().collect(); - if let Some(latest_password) = passwords.first() { - if let Some(expected_hash) = &latest_password.password_hash { - let user_opts = user_option::get(db, local_user.user_id.clone()).await?; - - if password_hashing::verify_password( - &self.config, - auth.password, - expected_hash, - )? { - if let Some(user) = user::get(db, &local_user.user_id).await? { - // TODO: Check password is expired - // TODO: reset failed login attempt - let user_builder = common::get_local_user_builder( - &self.config, - &user, - local_user, - Some(passwords), - user_opts, - ); - let user = user_builder.build()?; - return Ok(AuthenticatedInfo::builder() - .user_id(user.id.clone()) - .user(user) - .methods(vec!["password".into()]) - .build() - .map_err(AuthenticationError::from)?); - } - } else { - return Err(IdentityProviderError::WrongUsernamePassword); + if let Some(latest_password) = passwords.first() + && let Some(expected_hash) = &latest_password.password_hash + { + let user_opts = user_option::get(db, local_user.user_id.clone()).await?; + + if password_hashing::verify_password(&self.config, auth.password, expected_hash)? { + if let Some(user) = user::get(db, &local_user.user_id).await? { + // TODO: Check password is expired + // TODO: reset failed login attempt + let user_builder = common::get_local_user_builder( + &self.config, + &user, + local_user, + Some(passwords), + user_opts, + ); + let user = user_builder.build()?; + return Ok(AuthenticatedInfo::builder() + .user_id(user.id.clone()) + .user(user) + .methods(vec!["password".into()]) + .build() + .map_err(AuthenticationError::from)?); } + } else { + return Err(IdentityProviderError::WrongUsernamePassword); } } } diff --git a/src/identity/backends/sql/common.rs b/src/identity/backends/sql/common.rs index 6d95c2f9..4791110d 100644 --- a/src/identity/backends/sql/common.rs +++ b/src/identity/backends/sql/common.rs @@ -55,22 +55,16 @@ pub fn get_local_user_builder< ) -> UserResponseBuilder { let mut user_builder: UserResponseBuilder = get_user_builder(user, opts); user_builder.name(data.name.clone()); - if let Some(password_expires_days) = conf.security_compliance.password_expires_days { - if let Some(pass) = passwords { - if let (Some(current_password), Some(options)) = - (pass.into_iter().next(), user_builder.get_options()) - { - if let Some(false) = options.ignore_password_expiry.or(Some(false)) { - if let Some(dt) = - DateTime::from_timestamp_micros(current_password.created_at_int) - .expect("invalid timestamp") - .checked_add_days(Days::new(password_expires_days)) - { - user_builder.password_expires_at(dt); - } - } - } - } + if let Some(password_expires_days) = conf.security_compliance.password_expires_days + && let Some(pass) = passwords + && let (Some(current_password), Some(options)) = + (pass.into_iter().next(), user_builder.get_options()) + && let Some(false) = options.ignore_password_expiry.or(Some(false)) + && let Some(dt) = DateTime::from_timestamp_micros(current_password.created_at_int) + .expect("invalid timestamp") + .checked_add_days(Days::new(password_expires_days)) + { + user_builder.password_expires_at(dt); } user_builder } diff --git a/src/identity/backends/sql/local_user.rs b/src/identity/backends/sql/local_user.rs index 7be0e37b..368b31ea 100644 --- a/src/identity/backends/sql/local_user.rs +++ b/src/identity/backends/sql/local_user.rs @@ -117,14 +117,13 @@ pub async fn create( failed_auth_at: NotSet, }; // Set failed_auth_count to 0 if compliance disabling is on - if let Some(true) = &user.enabled { - if conf + if let Some(true) = &user.enabled + && conf .security_compliance .disable_user_account_days_inactive .is_some() - { - entry.failed_auth_count = Set(Some(0)); - } + { + entry.failed_auth_count = Set(Some(0)); } let db_user: local_user::Model = entry.insert(db).await?; diff --git a/src/token/application_credential.rs b/src/token/application_credential.rs index 501f5676..a302b493 100644 --- a/src/token/application_credential.rs +++ b/src/token/application_credential.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Project; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -85,12 +84,12 @@ impl MsgPackToken for ApplicationCredentialPayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.project_id)?; @@ -103,11 +102,12 @@ impl MsgPackToken for ApplicationCredentialPayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let project_id = fernet_utils::read_uuid(rd)?; @@ -132,6 +132,7 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] @@ -145,12 +146,14 @@ mod tests { expires_at: Local::now().trunc_subsecs(0).into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "password".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - ApplicationCredentialPayload::disassemble(&mut encoded_buf.as_slice(), &auth_map) + ApplicationCredentialPayload::disassemble(&mut encoded_buf.as_slice(), &provider) .unwrap(); assert_eq!(token, decoded); } diff --git a/src/token/domain_scoped.rs b/src/token/domain_scoped.rs index 617d17b4..34453022 100644 --- a/src/token/domain_scoped.rs +++ b/src/token/domain_scoped.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Domain; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -84,12 +83,12 @@ impl MsgPackToken for DomainScopePayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.domain_id)?; @@ -101,11 +100,12 @@ impl MsgPackToken for DomainScopePayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let domain_id = fernet_utils::read_uuid(rd)?; @@ -127,6 +127,7 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] @@ -139,12 +140,14 @@ mod tests { expires_at: Local::now().trunc_subsecs(0).into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "password".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - DomainScopePayload::disassemble(&mut encoded_buf.as_slice(), &auth_map).unwrap(); + DomainScopePayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap(); assert_eq!(token, decoded); } } diff --git a/src/token/error.rs b/src/token/error.rs index 7f7e6ee6..af2e059e 100644 --- a/src/token/error.rs +++ b/src/token/error.rs @@ -191,8 +191,8 @@ pub enum TokenProviderError { #[error("user cannot be found: {0}")] UserNotFound(String), - #[error("unsupported authentication methods in token payload")] - UnsupportedAuthMethods, + #[error("unsupported authentication methods {0} in token payload")] + UnsupportedAuthMethods(String), #[error("token with restrictions can be only project scoped")] RestrictedTokenNotProjectScoped, diff --git a/src/token/federation_domain_scoped.rs b/src/token/federation_domain_scoped.rs index 0b0bb202..d895c19e 100644 --- a/src/token/federation_domain_scoped.rs +++ b/src/token/federation_domain_scoped.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Domain; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -88,12 +87,12 @@ impl MsgPackToken for FederationDomainScopePayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.domain_id)?; @@ -108,12 +107,13 @@ impl MsgPackToken for FederationDomainScopePayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; println!("u: {user_id:?}"); - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let domain_id = fernet_utils::read_uuid(rd)?; @@ -141,13 +141,14 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] fn test_roundtrip() { let token = FederationDomainScopePayload { user_id: Uuid::new_v4().simple().to_string(), - methods: vec!["oidc".into()], + methods: vec!["openid".into()], audit_ids: vec!["Zm9vCg".into()], expires_at: Local::now().trunc_subsecs(0).into(), domain_id: "pid".into(), @@ -156,12 +157,14 @@ mod tests { protocol_id: "proto".into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "oidc".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - FederationDomainScopePayload::disassemble(&mut encoded_buf.as_slice(), &auth_map) + FederationDomainScopePayload::disassemble(&mut encoded_buf.as_slice(), &provider) .unwrap(); assert_eq!(token, decoded); } diff --git a/src/token/federation_project_scoped.rs b/src/token/federation_project_scoped.rs index 92a35a59..cdc0ca10 100644 --- a/src/token/federation_project_scoped.rs +++ b/src/token/federation_project_scoped.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Project; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -88,12 +87,12 @@ impl MsgPackToken for FederationProjectScopePayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.project_id)?; @@ -108,11 +107,12 @@ impl MsgPackToken for FederationProjectScopePayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let project_id = fernet_utils::read_uuid(rd)?; @@ -140,13 +140,14 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] fn test_roundtrip() { let token = FederationProjectScopePayload { user_id: Uuid::new_v4().simple().to_string(), - methods: vec!["oidc".into()], + methods: vec!["openid".into()], audit_ids: vec!["Zm9vCg".into()], expires_at: Local::now().trunc_subsecs(0).into(), project_id: "pid".into(), @@ -155,12 +156,14 @@ mod tests { protocol_id: "proto".into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "oidc".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - FederationProjectScopePayload::disassemble(&mut encoded_buf.as_slice(), &auth_map) + FederationProjectScopePayload::disassemble(&mut encoded_buf.as_slice(), &provider) .unwrap(); assert_eq!(token, decoded); } diff --git a/src/token/federation_unscoped.rs b/src/token/federation_unscoped.rs index 3763715d..2edc7931 100644 --- a/src/token/federation_unscoped.rs +++ b/src/token/federation_unscoped.rs @@ -16,13 +16,12 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::identity::types::UserResponse; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -81,12 +80,12 @@ impl MsgPackToken for FederationUnscopedPayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_list_of_uuids(wd, self.group_ids.iter())?; @@ -100,11 +99,12 @@ impl MsgPackToken for FederationUnscopedPayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let group_ids = fernet_utils::read_list_of_uuids(rd)?; @@ -130,13 +130,14 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] fn test_roundtrip() { let token = FederationUnscopedPayload { user_id: Uuid::new_v4().simple().to_string(), - methods: vec!["oidc".into()], + methods: vec!["openid".into()], audit_ids: vec!["Zm9vCg".into()], expires_at: Local::now().trunc_subsecs(0).into(), group_ids: vec!["g1".into()], @@ -144,12 +145,14 @@ mod tests { protocol_id: "proto".into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "oidc".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - FederationUnscopedPayload::disassemble(&mut encoded_buf.as_slice(), &auth_map).unwrap(); + FederationUnscopedPayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap(); assert_eq!(token, decoded); } } diff --git a/src/token/fernet.rs b/src/token/fernet.rs index 9a29b8b8..42147424 100644 --- a/src/token/fernet.rs +++ b/src/token/fernet.rs @@ -14,6 +14,7 @@ use bytes::Bytes; use fernet::MultiFernet; +use itertools::Itertools; use rmp::{ Marker, decode::{ValueReadError, read_marker, read_u8}, @@ -23,6 +24,7 @@ use std::collections::BTreeMap; use std::collections::HashSet; use std::fmt; use std::io::Write; +use tracing::trace; use crate::config::Config; use crate::token::{ @@ -34,12 +36,15 @@ use crate::token::{ unscoped::UnscopedPayload, }; -#[derive(Default, Clone)] +#[derive(Clone)] pub struct FernetTokenProvider { config: Config, utils: FernetUtils, fernet: Option, - auth_map: BTreeMap, + /// Map of the configured authentication methods. + auth_map: BTreeMap, + /// Cached permutations of auth_methods to the payload code. + auth_methods_code_cache: BTreeMap>, } pub trait MsgPackToken { @@ -49,7 +54,7 @@ pub trait MsgPackToken { fn assemble( &self, _wd: &mut W, - _auth_map: &BTreeMap, + _fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { Ok(()) } @@ -57,7 +62,7 @@ pub trait MsgPackToken { /// Parse MsgPack payload into the Token fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result; } @@ -76,62 +81,130 @@ fn read_payload_token_type(rd: &mut &[u8]) -> Result { } } -/// Decode the integer into the list of auth_methods -pub(crate) fn decode_auth_methods( - value: usize, - auth_map: &BTreeMap, -) -> Result + use<>, TokenProviderError> { - let mut results: Vec = Vec::new(); - let mut auth: usize = value; - for (idx, name) in auth_map.iter() { - // (lbragstad): By dividing the method_int by each key in the - // method_map, we know if the division results in an integer of 1, that - // key was used in the construction of the total sum of the method_int. - // In that case, we should confirm the key value and store it so we can - // look it up later. Then we should take the remainder of what is - // confirmed and the method_int and continue the process. In the end, we - // should have a list of integers that correspond to indexes in our - // method_map and we can reinflate the methods that the original - // method_int represents. - let result: usize = auth / idx; - if result == 1 { - results.push(name.into()); - auth -= idx; +/// Calculate possible combinations of the vector string elements. +fn all_combinations(iter: I) -> impl IntoIterator> +where + I: IntoIterator, +{ + let items: Vec = iter.into_iter().collect(); + let n = items.len(); + let mut result = Vec::new(); + + // There are 2^n possible subsets + for mask in 0..(1 << n) { + let mut subset = HashSet::new(); + for (i, am) in items.iter().enumerate() { + if (mask & (1 << i)) != 0 { + subset.insert(am.clone()); + } } + result.push(subset); } - Ok(results.into_iter()) -} - -/// Encode the list of auth_methods into a single integer -pub(crate) fn encode_auth_methods>( - methods: I, - auth_map: &BTreeMap, -) -> Result { - let me: HashSet = HashSet::from_iter(methods); - let res = auth_map - .iter() - .fold(0, |acc, (k, v)| acc + if me.contains(v) { *k } else { 0 }); - - // TODO: Improve unit tests to ensure unsupporte auth method immediately raises error. - if res == 0 { - return Err(TokenProviderError::UnsupportedAuthMethods); - } - Ok(res) + result.into_iter().filter(|v| !v.is_empty()) } impl FernetTokenProvider { + /// Construct new FernetTokenProvider + pub fn new(config: Config) -> Self { + let mut slf = Self { + utils: FernetUtils { + key_repository: config.fernet_tokens.key_repository.clone(), + max_active_keys: config.fernet_tokens.max_active_keys, + }, + config, + fernet: None, + auth_map: BTreeMap::new(), + auth_methods_code_cache: BTreeMap::new(), + }; + slf.reload_config(); + slf + } + + pub fn reload_config(&mut self) { + self.auth_map = BTreeMap::from_iter( + self.config + .auth + .methods + .iter() + .enumerate() + .map(|(k, v)| (1 << k, v.clone())), + ); + self.set_auth_methods_cache_combinations(); + } + + fn set_auth_methods_cache_combinations(&mut self) { + self.auth_methods_code_cache.clear(); + for auth_pairs in all_combinations(self.auth_map.values().cloned()) { + let pair: HashSet = HashSet::from_iter(auth_pairs.into_iter()); + let res = self + .encode_auth_methods(pair.clone()) + .expect("Can encode auth_methods combination"); + self.auth_methods_code_cache.insert(res, pair); + } + } + + /// Encode the list of auth_methods into a single integer + #[tracing::instrument(level = "trace", skip(self, methods))] + pub(crate) fn encode_auth_methods(&self, methods: I) -> Result + where + I: IntoIterator, + { + let me: HashSet = HashSet::from_iter(methods.into_iter()); + let res = self + .auth_map + .iter() + .fold(0, |acc, (k, v)| acc + if me.contains(v) { *k } else { 0 }); + + // TODO: Improve unit tests to ensure unsupported auth method immediately raises error. + if res == 0 { + return Err(TokenProviderError::UnsupportedAuthMethods( + me.iter().join(","), + )); + } + Ok(res) + } + + /// Decode the integer into the list of auth_methods + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn decode_auth_methods(&self, value: u8) -> Result, TokenProviderError> { + if let Some(res) = self.auth_methods_code_cache.get(&value) { + Ok(res.iter().cloned().collect()) + } else { + trace!("Auth methods cache miss."); + let mut results: Vec = Vec::new(); + let mut auth: u8 = value; + for (idx, name) in self.auth_map.iter() { + // (lbragstad): By dividing the method_int by each key in the + // method_map, we know if the division results in an integer of 1, that + // key was used in the construction of the total sum of the method_int. + // In that case, we should confirm the key value and store it so we can + // look it up later. Then we should take the remainder of what is + // confirmed and the method_int and continue the process. In the end, we + // should have a list of integers that correspond to indexes in our + // method_map and we can reinflate the methods that the original + // method_int represents. + let result: u8 = auth / idx; + if result == 1 { + results.push(name.clone()); + auth -= idx; + } + } + Ok(results) + } + } + /// Parse binary blob as MessagePack after encrypting it with Fernet fn decode(&self, rd: &mut &[u8]) -> Result { if let Marker::FixArray(_) = read_marker(rd).map_err(ValueReadError::from)? { match read_payload_token_type(rd)? { - 0 => Ok(UnscopedPayload::disassemble(rd, &self.auth_map)?.into()), - 1 => Ok(DomainScopePayload::disassemble(rd, &self.auth_map)?.into()), - 2 => Ok(ProjectScopePayload::disassemble(rd, &self.auth_map)?.into()), - 4 => Ok(FederationUnscopedPayload::disassemble(rd, &self.auth_map)?.into()), - 5 => Ok(FederationProjectScopePayload::disassemble(rd, &self.auth_map)?.into()), - 6 => Ok(FederationDomainScopePayload::disassemble(rd, &self.auth_map)?.into()), - 9 => Ok(ApplicationCredentialPayload::disassemble(rd, &self.auth_map)?.into()), - 11 => Ok(RestrictedPayload::disassemble(rd, &self.auth_map)?.into()), + 0 => Ok(UnscopedPayload::disassemble(rd, self)?.into()), + 1 => Ok(DomainScopePayload::disassemble(rd, self)?.into()), + 2 => Ok(ProjectScopePayload::disassemble(rd, self)?.into()), + 4 => Ok(FederationUnscopedPayload::disassemble(rd, self)?.into()), + 5 => Ok(FederationProjectScopePayload::disassemble(rd, self)?.into()), + 6 => Ok(FederationDomainScopePayload::disassemble(rd, self)?.into()), + 9 => Ok(ApplicationCredentialPayload::disassemble(rd, self)?.into()), + 11 => Ok(RestrictedPayload::disassemble(rd, self)?.into()), other => Err(TokenProviderError::InvalidTokenType(other)), } } else { @@ -148,62 +221,63 @@ impl FernetTokenProvider { .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 0) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::DomainScope(data) => { write_array_len(&mut buf, 6) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 1) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::ProjectScope(data) => { write_array_len(&mut buf, 6) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 2) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::FederationUnscoped(data) => { write_array_len(&mut buf, 8) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 4) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::FederationProjectScope(data) => { write_array_len(&mut buf, 9) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 5) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::FederationDomainScope(data) => { write_array_len(&mut buf, 9) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 6) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::ApplicationCredential(data) => { write_array_len(&mut buf, 7) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 9) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } Token::Restricted(data) => { write_array_len(&mut buf, 9) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 11) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - data.assemble(&mut buf, &self.auth_map)?; + data.assemble(&mut buf, self)?; } } Ok(buf.into()) } /// Get MultiFernet initialized with repository keys + #[tracing::instrument(level = "trace", skip(self))] pub fn get_fernet(&self) -> Result { Ok(MultiFernet::new( self.utils.load_keys()?.into_iter().collect::>(), @@ -211,6 +285,7 @@ impl FernetTokenProvider { } /// Load fernet keys from FS + #[tracing::instrument(level = "trace", skip(self))] pub fn load_keys(&mut self) -> Result<(), TokenProviderError> { self.fernet = Some(self.get_fernet()?); Ok(()) @@ -244,27 +319,18 @@ impl FernetTokenProvider { impl TokenBackend for FernetTokenProvider { /// Set config fn set_config(&mut self, config: Config) { - self.utils = FernetUtils { - key_repository: config.fernet_tokens.key_repository.clone(), - max_active_keys: config.fernet_tokens.max_active_keys, - }; - self.auth_map = BTreeMap::from_iter( - config - .auth - .methods - .iter() - .enumerate() - .map(|(k, v)| (1 << k, v.clone())), - ); self.config = config; + self.reload_config(); } /// Decrypt the token + #[tracing::instrument(level = "trace", skip(self, credential))] fn decode(&self, credential: &str) -> Result { self.decrypt(credential) } /// Encrypt the token + #[tracing::instrument(level = "trace", skip(self, token))] fn encode(&self, token: &Token) -> Result { self.encrypt(token) } @@ -279,7 +345,7 @@ pub(super) mod tests { use tempfile::tempdir; use uuid::Uuid; - fn setup_config() -> Config { + pub(super) fn setup_config() -> Config { let keys_dir = tempdir().unwrap(); // write fernet key used to generate tokens in python let file_path = keys_dir.path().join("0"); @@ -303,14 +369,15 @@ pub(super) mod tests { async fn test_decrypt_unscoped() { let token = "gAAAAABnt12vpnYCuUxl1lWQfTxwkBcZcgdK5wYons4BFHxxZLk326To5afinp29in7f5ZHR5K61Pl2voIjfbPKlL51KempshD4shfSje4RutbeXq-NT498eEcorzige5XBYGaoWuDTOKEDH2eXCMHhw9722j9iPP3Z4r_1Zlmcqq1n2tndmvsA"; - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::Unscoped(decrypted) = backend.decrypt(token).unwrap() { + if let Token::Unscoped(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "4b7d364ad87d400bbd91798e3c15e9c2"); - assert_eq!(decrypted.methods, vec!["token"]); + assert_eq!( + decrypted.methods.clone().sort(), + vec!["password", "token"].sort() + ); assert_eq!( decrypted.expires_at.to_rfc3339(), "2025-02-20T17:40:13+00:00" @@ -334,13 +401,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -348,12 +413,10 @@ pub(super) mod tests { async fn test_decrypt_domain() { let token = "gAAAAABnt16C_ve4dDc7TeU857pwTXGJfGqNA4uJ308_2o_F9T_8WenNBatll0Q36wGz79dSI6RQnuN2PbK17wxQbn9jXscDh2ie3ZrW-WL5gG3gWK6FiPleAiU3kJN5mkskViJOIN-ZpP2B15fmZiYijelQ9TQuhQ"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::DomainScope(decrypted) = backend.decrypt(token).unwrap() { + if let Token::DomainScope(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "4b7d364ad87d400bbd91798e3c15e9c2"); assert_eq!(decrypted.domain_id, "default"); assert_eq!(decrypted.methods, vec!["password"]); @@ -378,13 +441,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -392,12 +453,10 @@ pub(super) mod tests { async fn test_decrypt_project() { let token = "gAAAAABns2ixy75K_KfoosWLrNNqG6KW8nm3Xzv0_2dOx8ODWH7B8i2g8CncGLO6XBEH_TYLg83P6XoKQ5bU8An8Kqgw9WX3bvmEQXphnwPM6aRAOQUSdVhTlUm_8otDG9BS2rc70Q7pfy57S3_yBgimy-174aKdP8LPusvdHZsQPEJO9pfeXWw"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::ProjectScope(decrypted) = backend.decrypt(token).unwrap() { + if let Token::ProjectScope(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "4b7d364ad87d400bbd91798e3c15e9c2"); assert_eq!(decrypted.project_id, "97cd761d581b485792a4afc8cc6a998d"); assert_eq!(decrypted.methods, vec!["password"]); @@ -422,13 +481,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -436,12 +493,10 @@ pub(super) mod tests { async fn test_decrypt_federation_unscoped() { let token = "gAAAAABoMdfwBgwjAfYCp3RisL_XKSdGKmBqg7ia8jkfsKIXnap_bQ5gUTZGwgEERlpFKzbwpkV-cpiFDuhe9RAnCtbQxEhP7Rg1vt1VLm8afGTulDaLclqot2NC-BONFO2k3V3KyIa-Xrq0mCEGOk-BhNZy2C6iwrWanPCjCuZrWCq4FBirtMs2vrnZPWG5FTGqqkvdQvGj"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::FederationUnscoped(decrypted) = backend.decrypt(token).unwrap() { + if let Token::FederationUnscoped(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "8980e124df5245509131bdc5c66c54cc"); assert_eq!(decrypted.methods, vec!["openid"]); assert_eq!( @@ -474,13 +529,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -488,12 +541,10 @@ pub(super) mod tests { async fn test_decrypt_federation_project_scope() { let token = "gAAAAABoNdYE5zCP0qQtHqhdbZHQ7YdLvfDlUTpLou8FJFoMKsd4I9jyVyaWrluYXKXofnwzemA-wybhtbNruwqDYH-wmHdMlgYuZyy21o8ylphU5yd2b-5KvGpXo61fTVTzhdHFTzJKVit_7Lcwq0S45xQ9x14sVRd870NEwfmOvUVR5BGzmnpFLvWtkaPSpbxMAzfn_NSC"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::FederationProjectScope(decrypted) = backend.decrypt(token).unwrap() { + if let Token::FederationProjectScope(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "8980e124df5245509131bdc5c66c54cc"); assert_eq!(decrypted.methods, vec!["openid"]); assert_eq!( @@ -527,13 +578,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -541,12 +590,10 @@ pub(super) mod tests { async fn test_decrypt_federation_domain_scope() { let token = "gAAAAABoNddwFaB2Oq26-4f8nRK3Bph7-QsIh30Rbefbb78owJXaQcjNQm5Qq1gHouS6JSqgfpdna3ML1vdTVnVnFScX-T-CZ-CqtBPUuEBHFEzdNBDKQHloYajZ2sknwbe_uIs1SDS9tBFLvkVth1eVjDhdEawINHjUCFhNPObZKas5V0j7bsvChNeZBKsznruJwCtcrWr5"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::FederationDomainScope(decrypted) = backend.decrypt(token).unwrap() { + if let Token::FederationDomainScope(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "8980e124df5245509131bdc5c66c54cc"); assert_eq!(decrypted.methods, vec!["openid"]); assert_eq!( @@ -580,9 +627,8 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); let config = crate::tests::token::setup_config(); - backend.set_config(config); + let mut backend = FernetTokenProvider::new(config); backend.load_keys().unwrap(); let encrypted = backend.encrypt(&token).unwrap(); @@ -594,12 +640,10 @@ pub(super) mod tests { async fn test_decrypt_application_credential() { let token = "gAAAAABnt11m57ZlI9JU0g2BKJw2EN-InbAIijcIG7SxvPATntgTlcTMwha-Fh7isNNIwDq2WaWglV1nYgftfoUK245ZnEJ0_gXaIhl6COhNommYv2Bs9PnJqfgrrxrIrB8rh4pfeyCtMkv5ePYgFFPyRFE37l3k7qL5p7qVhYT37yT1-K5lYAV0f6Vy70h3KX1HO0m6Rl90"; - let mut backend = FernetTokenProvider::default(); - let config = setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - if let Token::ApplicationCredential(decrypted) = backend.decrypt(token).unwrap() { + if let Token::ApplicationCredential(decrypted) = provider.decrypt(token).unwrap() { assert_eq!(decrypted.user_id, "4b7d364ad87d400bbd91798e3c15e9c2"); assert_eq!(decrypted.project_id, "97cd761d581b485792a4afc8cc6a998d"); assert_eq!(decrypted.methods, vec!["application_credential"]); @@ -629,13 +673,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } @@ -653,13 +695,11 @@ pub(super) mod tests { ..Default::default() }); - let mut backend = FernetTokenProvider::default(); - let config = crate::tests::token::setup_config(); - backend.set_config(config); - backend.load_keys().unwrap(); + let mut provider = FernetTokenProvider::new(setup_config()); + provider.load_keys().unwrap(); - let encrypted = backend.encrypt(&token).unwrap(); - let dec_token = backend.decrypt(&encrypted).unwrap(); + let encrypted = provider.encrypt(&token).unwrap(); + let dec_token = provider.decrypt(&encrypted).unwrap(); assert_eq!(token, dec_token); } } diff --git a/src/token/fernet_utils.rs b/src/token/fernet_utils.rs index 08c20ede..b9c47d15 100644 --- a/src/token/fernet_utils.rs +++ b/src/token/fernet_utils.rs @@ -47,30 +47,30 @@ impl FernetUtils { if self.validate_key_repository()? { for entry in fs::read_dir(&self.key_repository)? { let entry = entry?; - if let Ok(fname) = entry.file_name().into_string() { - if let Ok(key_order) = fname.parse::() { - // We are only interested in files named as integer (0, 1, 2, ...) - trace!("Loading key {:?}", entry.file_name()); - if let Some(fernet) = Fernet::new( - fs::read_to_string(entry.path()) - .map_err(|e| TokenProviderError::FernetKeyRead { - source: e, - path: entry.path(), - })? - .trim_end(), - ) { - keys.insert(key_order, fernet); - } else { - warn!( - "The key {:?} is not usable for Fernet library", - entry.file_name() - ) - } + if let Ok(fname) = entry.file_name().into_string() + && let Ok(key_order) = fname.parse::() + { + // We are only interested in files named as integer (0, 1, 2, ...) + trace!("Loading key {:?}", entry.file_name()); + if let Some(fernet) = Fernet::new( + fs::read_to_string(entry.path()) + .map_err(|e| TokenProviderError::FernetKeyRead { + source: e, + path: entry.path(), + })? + .trim_end(), + ) { + keys.insert(key_order, fernet); + } else { + warn!( + "The key {:?} is not usable for Fernet library", + entry.file_name() + ) } } } } - if keys.len() == 0 { + if keys.is_empty() { return Err(TokenProviderError::FernetKeysMissing); } Ok(keys.into_values().rev()) @@ -83,30 +83,30 @@ impl FernetUtils { if self.validate_key_repository()? { let mut entries = fs_async::read_dir(&self.key_repository).await?; while let Some(entry) = entries.next_entry().await? { - if let Ok(fname) = entry.file_name().into_string() { - if let Ok(key_order) = fname.parse::() { - // We are only interested in files named as integer (0, 1, 2, ...) - trace!("Loading key {:?}", entry.file_name()); - if let Some(fernet) = Fernet::new( - fs::read_to_string(entry.path()) - .map_err(|e| TokenProviderError::FernetKeyRead { - source: e, - path: entry.path(), - })? - .trim_end(), - ) { - keys.insert(key_order, fernet); - } else { - warn!( - "The key {:?} is not usable for Fernet library", - entry.file_name() - ) - } + if let Ok(fname) = entry.file_name().into_string() + && let Ok(key_order) = fname.parse::() + { + // We are only interested in files named as integer (0, 1, 2, ...) + trace!("Loading key {:?}", entry.file_name()); + if let Some(fernet) = Fernet::new( + fs::read_to_string(entry.path()) + .map_err(|e| TokenProviderError::FernetKeyRead { + source: e, + path: entry.path(), + })? + .trim_end(), + ) { + keys.insert(key_order, fernet); + } else { + warn!( + "The key {:?} is not usable for Fernet library", + entry.file_name() + ) } } } } - if keys.len() == 0 { + if keys.is_empty() { return Err(TokenProviderError::FernetKeysMissing); } Ok(keys.into_values().rev()) diff --git a/src/token/mod.rs b/src/token/mod.rs index 47b75e02..325cf8eb 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -72,10 +72,9 @@ pub struct TokenProvider { impl TokenProvider { pub fn new(config: &Config) -> Result { - let mut backend_driver = match config.token.provider { - TokenProviderType::Fernet => fernet::FernetTokenProvider::default(), + let backend_driver = match config.token.provider { + TokenProviderType::Fernet => fernet::FernetTokenProvider::new(config.clone()), }; - backend_driver.set_config(config.clone()); Ok(Self { config: config.clone(), backend_driver: Box::new(backend_driver), @@ -415,10 +414,10 @@ impl TokenApi for TokenProvider { .validate_token(credential, allow_expired, window_seconds) .await?; tracing::debug!("The token is {:?}", token); - if let Token::Restricted(restriction) = &token { - if !restriction.allow_renew { - return Err(AuthenticationError::TokenRenewalForbidden)?; - } + if let Token::Restricted(restriction) = &token + && !restriction.allow_renew + { + return Err(AuthenticationError::TokenRenewalForbidden)?; } let mut auth_info_builder = AuthenticatedInfo::builder(); auth_info_builder.user_id(token.user_id()); @@ -820,6 +819,9 @@ mock! { #[cfg(test)] mod tests { use sea_orm::DatabaseConnection; + use std::fs::File; + use std::io::Write; + use tempfile::tempdir; use super::*; use crate::assignment::{ @@ -831,6 +833,26 @@ mod tests { use crate::token::{DomainScopePayload, ProjectScopePayload, Token, UnscopedPayload}; + pub(super) fn setup_config() -> Config { + let keys_dir = tempdir().unwrap(); + // write fernet key used to generate tokens in python + let file_path = keys_dir.path().join("0"); + let mut tmp_file = File::create(file_path).unwrap(); + write!(tmp_file, "BFTs1CIVIBLTP4GOrQ26VETrJ7Zwz1O4wbEcCQ966eM=").unwrap(); + + let builder = config::Config::builder() + .set_override( + "auth.methods", + "password,token,openid,application_credential", + ) + .unwrap() + .set_override("database.connection", "dummy") + .unwrap(); + let mut config: Config = Config::try_from(builder).expect("can build a valid config"); + config.fernet_tokens.key_repository = keys_dir.keep(); + config + } + #[tokio::test] async fn test_populate_role_assignments() { let token_provider = TokenProvider::new(&Config::default()).unwrap(); diff --git a/src/token/project_scoped.rs b/src/token/project_scoped.rs index 07b11d41..69ab020c 100644 --- a/src/token/project_scoped.rs +++ b/src/token/project_scoped.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Project; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -84,12 +83,12 @@ impl MsgPackToken for ProjectScopePayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.project_id)?; @@ -101,11 +100,12 @@ impl MsgPackToken for ProjectScopePayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let project_id = fernet_utils::read_uuid(rd)?; @@ -127,6 +127,7 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] @@ -139,12 +140,14 @@ mod tests { expires_at: Local::now().trunc_subsecs(0).into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "password".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - ProjectScopePayload::disassemble(&mut encoded_buf.as_slice(), &auth_map).unwrap(); + ProjectScopePayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap(); assert_eq!(token, decoded); } } diff --git a/src/token/restricted.rs b/src/token/restricted.rs index 408a3ebb..71ffbaa6 100644 --- a/src/token/restricted.rs +++ b/src/token/restricted.rs @@ -16,7 +16,6 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::assignment::types::Role; @@ -24,7 +23,7 @@ use crate::identity::types::UserResponse; use crate::resource::types::Project; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -96,12 +95,12 @@ impl MsgPackToken for RestrictedPayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_uuid(wd, &self.token_restriction_id)?; @@ -116,11 +115,12 @@ impl MsgPackToken for RestrictedPayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of reading is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let token_restriction_id = fernet_utils::read_uuid(rd)?; @@ -149,13 +149,14 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] fn test_roundtrip() { let token = RestrictedPayload { user_id: Uuid::new_v4().simple().to_string(), - methods: vec!["oidc".into()], + methods: vec!["openid".into()], audit_ids: vec!["Zm9vCg".into()], expires_at: Local::now().trunc_subsecs(0).into(), token_restriction_id: "trid".into(), @@ -164,12 +165,14 @@ mod tests { allow_rescope: true, ..Default::default() }; - let auth_map = BTreeMap::from([(1, "oidc".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); let decoded = - RestrictedPayload::disassemble(&mut encoded_buf.as_slice(), &auth_map).unwrap(); + RestrictedPayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap(); assert_eq!(token, decoded); } } diff --git a/src/token/unscoped.rs b/src/token/unscoped.rs index bf2211f7..a82a1201 100644 --- a/src/token/unscoped.rs +++ b/src/token/unscoped.rs @@ -16,13 +16,12 @@ use chrono::{DateTime, Utc}; use derive_builder::Builder; use rmp::{decode::read_pfix, encode::write_pfix}; use serde::Serialize; -use std::collections::BTreeMap; use std::io::Write; use crate::identity::types::UserResponse; use crate::token::{ error::TokenProviderError, - fernet::{self, MsgPackToken}, + fernet::{FernetTokenProvider, MsgPackToken}, fernet_utils, types::Token, }; @@ -77,12 +76,12 @@ impl MsgPackToken for UnscopedPayload { fn assemble( &self, wd: &mut W, - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result<(), TokenProviderError> { fernet_utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, - fernet::encode_auth_methods(self.methods.clone(), auth_map)? as u8, + fernet_provider.encode_auth_methods(self.methods.clone())?, ) .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; fernet_utils::write_time(wd, self.expires_at)?; @@ -93,11 +92,12 @@ impl MsgPackToken for UnscopedPayload { fn disassemble( rd: &mut &[u8], - auth_map: &BTreeMap, + fernet_provider: &FernetTokenProvider, ) -> Result { // Order of writing is important let user_id = fernet_utils::read_uuid(rd)?; - let methods: Vec = fernet::decode_auth_methods(read_pfix(rd)?.into(), auth_map)? + let methods: Vec = fernet_provider + .decode_auth_methods(read_pfix(rd)?)? .into_iter() .collect(); let expires_at = fernet_utils::read_time(rd)?; @@ -117,6 +117,7 @@ mod tests { use chrono::{Local, SubsecRound}; use uuid::Uuid; + use super::super::tests::setup_config; use super::*; #[test] @@ -128,11 +129,13 @@ mod tests { expires_at: Local::now().trunc_subsecs(0).into(), ..Default::default() }; - let auth_map = BTreeMap::from([(1, "password".into())]); + + let provider = FernetTokenProvider::new(setup_config()); + let mut buf = vec![]; - token.assemble(&mut buf, &auth_map).unwrap(); + token.assemble(&mut buf, &provider).unwrap(); let encoded_buf = buf.clone(); - let decoded = UnscopedPayload::disassemble(&mut encoded_buf.as_slice(), &auth_map).unwrap(); + let decoded = UnscopedPayload::disassemble(&mut encoded_buf.as_slice(), &provider).unwrap(); assert_eq!(token, decoded); } }