This commit is contained in:
Firstyear 2025-04-26 02:42:52 +00:00 committed by GitHub
commit db69bf5a0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1464 additions and 1079 deletions

289
Cargo.lock generated
View file

@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"getrandom 0.2.15",
"getrandom 0.2.16",
"once_cell",
"serde",
"version_check",
@ -232,9 +232,9 @@ dependencies = [
[[package]]
name = "async-compression"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64"
checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07"
dependencies = [
"flate2",
"futures-core",
@ -621,9 +621,9 @@ checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32"
[[package]]
name = "bstr"
version = "1.11.3"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata 0.4.9",
@ -668,9 +668,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.17"
version = "1.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
dependencies = [
"shlex",
]
@ -792,8 +792,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "compact_jwt"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bbab6445446e8d0b07468a01d0bfdae15879de5c440c5e47ae4ae0e18a1fba"
source = "git+https://github.com/Firstyear/compact-jwt.git?rev=b3d2b5700cfe567d384c81df35d25537fbf7f110#b3d2b5700cfe567d384c81df35d25537fbf7f110"
dependencies = [
"base64 0.21.7",
"base64urlsafedata",
@ -1053,9 +1052,9 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
@ -1063,9 +1062,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
@ -1077,9 +1076,9 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
@ -1088,15 +1087,15 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "der"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"der_derive",
@ -1132,9 +1131,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.4.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
@ -1223,22 +1222,23 @@ dependencies = [
[[package]]
name = "dirs"
version = "4.0.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"winapi",
"windows-sys 0.59.0",
]
[[package]]
@ -1351,9 +1351,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
@ -1361,9 +1361,9 @@ dependencies = [
[[package]]
name = "escargot"
version = "0.5.13"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05a3ac187a16b5382fef8c69fd1bad123c67b7cf3932240a2d43dcdd32cded88"
checksum = "83f351750780493fc33fa0ce8ba3c7d61f9736cfa3b3bb9ee2342643ffe40211"
dependencies = [
"log",
"once_cell",
@ -1444,19 +1444,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fernet"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66b725fe9483b9ee72ccaec072b15eb8ad95a3ae63a8c798d5748883b72fd33"
dependencies = [
"base64 0.22.1",
"byteorder",
"getrandom 0.2.15",
"openssl",
"zeroize",
]
[[package]]
name = "file-id"
version = "0.2.2"
@ -1486,15 +1473,15 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flagset"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
[[package]]
name = "flate2"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
@ -1573,7 +1560,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4"
dependencies = [
"rustix 1.0.3",
"rustix 1.0.5",
"windows-sys 0.59.0",
]
@ -1687,9 +1674,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"js-sys",
@ -2277,7 +2264,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.8.0",
"indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
@ -2286,9 +2273,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
dependencies = [
"atomic-waker",
"bytes",
@ -2296,7 +2283,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.3.1",
"indexmap 2.8.0",
"indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
@ -2503,7 +2490,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.8",
"h2 0.4.9",
"http 1.3.1",
"http-body 1.0.1",
"httparse",
@ -2565,9 +2552,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
dependencies = [
"bytes",
"futures-channel",
@ -2575,6 +2562,7 @@ dependencies = [
"http 1.3.1",
"http-body 1.0.1",
"hyper 1.6.0",
"libc",
"pin-project-lite",
"socket2",
"tokio",
@ -2584,9 +2572,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.62"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@ -2801,9 +2789,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@ -3277,7 +3265,6 @@ dependencies = [
"concread",
"dhat",
"dyn-clone",
"fernet",
"futures",
"hashbrown 0.15.2",
"hex",
@ -3473,9 +3460,9 @@ dependencies = [
[[package]]
name = "libm"
version = "0.2.11"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
[[package]]
name = "libmimalloc-sys"
@ -3547,9 +3534,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
@ -3732,18 +3719,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mintex"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
[[package]]
name = "mio"
@ -4034,7 +4021,7 @@ checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f"
dependencies = [
"base64 0.13.1",
"chrono",
"getrandom 0.2.15",
"getrandom 0.2.16",
"http 0.2.12",
"rand 0.8.5",
"reqwest 0.11.27",
@ -4054,7 +4041,7 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
dependencies = [
"base64 0.22.1",
"chrono",
"getrandom 0.2.15",
"getrandom 0.2.16",
"http 1.3.1",
"rand 0.8.5",
"reqwest 0.12.15",
@ -4095,9 +4082,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.2"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl"
@ -4228,6 +4215,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "orca"
version = "1.6.0-dev"
@ -4372,7 +4365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
"indexmap 2.8.0",
"indexmap 2.9.0",
"serde",
]
@ -4491,9 +4484,9 @@ dependencies = [
[[package]]
name = "prettyplease"
version = "0.2.31"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
dependencies = [
"proc-macro2",
"syn 2.0.100",
@ -4501,12 +4494,11 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
"toml_edit",
]
[[package]]
@ -4628,9 +4620,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.10"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b"
dependencies = [
"bytes",
"getrandom 0.3.2",
@ -4722,7 +4714,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.15",
"getrandom 0.2.16",
]
[[package]]
@ -4742,22 +4734,22 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "redox_syscall"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "redox_users"
version = "0.4.6"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom 0.2.15",
"getrandom 0.2.16",
"libredox",
"thiserror 1.0.69",
"thiserror 2.0.12",
]
[[package]]
@ -4888,7 +4880,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2 0.4.8",
"h2 0.4.9",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
@ -4942,7 +4934,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.15",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
@ -4980,9 +4972,9 @@ dependencies = [
[[package]]
name = "rust-embed"
version = "8.6.0"
version = "8.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b3aba5104622db5c9fc61098de54708feb732e7763d7faa2fa625899f00bf6f"
checksum = "e5fbc0ee50fcb99af7cebb442e5df7b5b45e9460ffa3f8f549cd26b862bec49d"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@ -4991,9 +4983,9 @@ dependencies = [
[[package]]
name = "rust-embed-impl"
version = "8.6.0"
version = "8.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f198c73be048d2c5aa8e12f7960ad08443e56fd39cc26336719fdb4ea0ebaae"
checksum = "6bf418c9a2e3f6663ca38b8a7134cc2c2167c9d69688860e8961e3faa731702e"
dependencies = [
"proc-macro2",
"quote",
@ -5004,9 +4996,9 @@ dependencies = [
[[package]]
name = "rust-embed-utils"
version = "8.6.0"
version = "8.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a2fcdc9f40c8dc2922842ca9add611ad19f332227fc651d015881ad1552bd9a"
checksum = "08d55b95147fe01265d06b3955db798bdaed52e60e2211c41137701b3aba8e21"
dependencies = [
"sha2",
"walkdir",
@ -5054,22 +5046,22 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.3"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys 0.9.3",
"linux-raw-sys 0.9.4",
"windows-sys 0.59.0",
]
[[package]]
name = "rustls"
version = "0.23.25"
version = "0.23.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
dependencies = [
"once_cell",
"ring",
@ -5352,7 +5344,7 @@ dependencies = [
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.8.0",
"indexmap 2.9.0",
"serde",
"serde_derive",
"serde_json",
@ -5448,9 +5440,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
@ -5483,9 +5475,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
dependencies = [
"serde",
]
@ -5513,9 +5505,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@ -5686,7 +5678,7 @@ dependencies = [
"fastrand",
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.3",
"rustix 1.0.5",
"windows-sys 0.59.0",
]
@ -5917,9 +5909,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.14"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
@ -5937,7 +5929,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.24",
"toml_edit",
]
[[package]]
@ -5949,24 +5941,13 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.8.0",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap 2.8.0",
"indexmap 2.9.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -5984,7 +5965,7 @@ dependencies = [
"axum",
"base64 0.22.1",
"bytes",
"h2 0.4.8",
"h2 0.4.9",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
@ -6309,7 +6290,7 @@ version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
dependencies = [
"indexmap 2.8.0",
"indexmap 2.9.0",
"serde",
"serde_json",
"utoipa-gen",
@ -6729,11 +6710,37 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-targets 0.52.6",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.0",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
@ -6749,7 +6756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
@ -6771,6 +6778,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -7025,15 +7041,6 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.6.26"

View file

@ -101,7 +101,7 @@ codegen-units = 256
## As Kanidm maintains a number of libraries, sometimes during development we need to override them
## with local or git versions. This patch table allows quick uncommenting to achieve that.
# compact_jwt = { path = "../compact_jwt" }
# compact_jwt = { path = "../compact-jwt" }
# concread = { path = "../concread" }
# idlset = { path = "../idlset" }
@ -122,7 +122,10 @@ codegen-units = 256
libnss = { git = "https://github.com/Firstyear/libnss-rs.git", branch = "20250207-freebsd" }
# Allow ssh keys to have comments with spaces.
sshkeys = { git = "https://github.com/Firstyear/rust-sshkeys.git", rev = "3a081cbf7480628223bcb96fc8aaa8c19109d007" }
# Branch currently carrying some needed rs256 signer patches for jwk handling,
# as main is currently working to drop openssl and may need more work before
# we commit to that change here.
compact_jwt = { git = "https://github.com/Firstyear/compact-jwt.git", rev = "b3d2b5700cfe567d384c81df35d25537fbf7f110" }
[workspace.dependencies]
kanidmd_core = { path = "./server/core", version = "=1.6.0-dev" }
@ -173,7 +176,6 @@ csv = "1.3.1"
dialoguer = "0.11.0"
dhat = "0.3.3"
dyn-clone = "^1.0.17"
fernet = "^0.2.1"
filetime = "^0.2.24"
fs4 = "^0.13.0"
futures = "^0.3.31"

View file

@ -1,16 +1,18 @@
use crate::{ClientError, KanidmClient};
use kanidm_proto::attribute::Attribute;
use kanidm_proto::constants::{
ATTR_DISPLAYNAME, ATTR_ES256_PRIVATE_KEY_DER, ATTR_NAME,
ATTR_DISPLAYNAME, ATTR_KEY_ACTION_REVOKE, ATTR_KEY_ACTION_ROTATE, ATTR_NAME,
ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE, ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT,
ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE, ATTR_OAUTH2_PREFER_SHORT_USERNAME,
ATTR_OAUTH2_RS_BASIC_SECRET, ATTR_OAUTH2_RS_ORIGIN, ATTR_OAUTH2_RS_ORIGIN_LANDING,
ATTR_OAUTH2_RS_TOKEN_KEY, ATTR_OAUTH2_STRICT_REDIRECT_URI, ATTR_RS256_PRIVATE_KEY_DER,
ATTR_OAUTH2_STRICT_REDIRECT_URI,
};
use kanidm_proto::internal::{ImageValue, Oauth2ClaimMapJoin};
use kanidm_proto::v1::Entry;
use reqwest::multipart;
use std::collections::BTreeMap;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
use url::Url;
impl KanidmClient {
@ -84,7 +86,34 @@ impl KanidmClient {
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn idm_oauth2_rs_revoke_key(
&self,
id: &str,
key_id: &str,
) -> Result<(), ClientError> {
self.perform_post_request(
&format!("/v1/oauth2/{}/_attr/{}", id, ATTR_KEY_ACTION_REVOKE),
vec![key_id.to_string()],
)
.await
}
pub async fn idm_oauth2_rs_rotate_keys(
&self,
id: &str,
rotate_at_time: OffsetDateTime,
) -> Result<(), ClientError> {
let rfc_3339_str = rotate_at_time.format(&Rfc3339).map_err(|_| {
ClientError::InvalidRequest("Unable to format rfc 3339 datetime".into())
})?;
self.perform_post_request(
&format!("/v1/oauth2/{}/_attr/{}", id, ATTR_KEY_ACTION_ROTATE),
vec![rfc_3339_str],
)
.await
}
pub async fn idm_oauth2_rs_update(
&self,
id: &str,
@ -92,8 +121,6 @@ impl KanidmClient {
displayname: Option<&str>,
landing: Option<&str>,
reset_secret: bool,
reset_token_key: bool,
reset_sign_key: bool,
) -> Result<(), ClientError> {
let mut update_oauth2_rs = Entry {
attrs: BTreeMap::new(),
@ -121,19 +148,6 @@ impl KanidmClient {
.attrs
.insert(ATTR_OAUTH2_RS_BASIC_SECRET.to_string(), Vec::new());
}
if reset_token_key {
update_oauth2_rs
.attrs
.insert(ATTR_OAUTH2_RS_TOKEN_KEY.to_string(), Vec::new());
}
if reset_sign_key {
update_oauth2_rs
.attrs
.insert(ATTR_ES256_PRIVATE_KEY_DER.to_string(), Vec::new());
update_oauth2_rs
.attrs
.insert(ATTR_RS256_PRIVATE_KEY_DER.to_string(), Vec::new());
}
self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs)
.await
}

View file

@ -89,6 +89,7 @@ pub enum Attribute {
KeyActionRotate,
KeyActionRevoke,
KeyActionImportJwsEs256,
KeyActionImportJwsRs256,
KeyInternalData,
KeyProvider,
LastModifiedCid,
@ -323,6 +324,7 @@ impl Attribute {
Attribute::KeyActionRotate => ATTR_KEY_ACTION_ROTATE,
Attribute::KeyActionRevoke => ATTR_KEY_ACTION_REVOKE,
Attribute::KeyActionImportJwsEs256 => ATTR_KEY_ACTION_IMPORT_JWS_ES256,
Attribute::KeyActionImportJwsRs256 => ATTR_KEY_ACTION_IMPORT_JWS_RS256,
Attribute::KeyInternalData => ATTR_KEY_INTERNAL_DATA,
Attribute::KeyProvider => ATTR_KEY_PROVIDER,
Attribute::LastModifiedCid => ATTR_LAST_MODIFIED_CID,
@ -510,6 +512,7 @@ impl Attribute {
ATTR_KEY_ACTION_ROTATE => Attribute::KeyActionRotate,
ATTR_KEY_ACTION_REVOKE => Attribute::KeyActionRevoke,
ATTR_KEY_ACTION_IMPORT_JWS_ES256 => Attribute::KeyActionImportJwsEs256,
ATTR_KEY_ACTION_IMPORT_JWS_RS256 => Attribute::KeyActionImportJwsRs256,
ATTR_KEY_INTERNAL_DATA => Attribute::KeyInternalData,
ATTR_KEY_PROVIDER => Attribute::KeyProvider,
ATTR_LAST_MODIFIED_CID => Attribute::LastModifiedCid,

View file

@ -133,6 +133,7 @@ pub const ATTR_JWS_ES256_PRIVATE_KEY: &str = "jws_es256_private_key";
pub const ATTR_KEY_ACTION_ROTATE: &str = "key_action_rotate";
pub const ATTR_KEY_ACTION_REVOKE: &str = "key_action_revoke";
pub const ATTR_KEY_ACTION_IMPORT_JWS_ES256: &str = "key_action_import_jws_es256";
pub const ATTR_KEY_ACTION_IMPORT_JWS_RS256: &str = "key_action_import_jws_rs256";
pub const ATTR_KEY_INTERNAL_DATA: &str = "key_internal_data";
pub const ATTR_KEY_PROVIDER: &str = "key_provider";
pub const ATTR_LAST_MODIFIED_CID: &str = "last_modified_cid";
@ -319,5 +320,6 @@ pub const ENTRYCLASS_KEY_PROVIDER: &str = "key_provider";
pub const ENTRYCLASS_KEY_PROVIDER_INTERNAL: &str = "key_provider_internal";
pub const ENTRYCLASS_KEY_OBJECT: &str = "key_object";
pub const ENTRYCLASS_KEY_OBJECT_JWT_ES256: &str = "key_object_jwt_es256";
pub const ENTRYCLASS_KEY_OBJECT_JWT_RS256: &str = "key_object_jwt_rs256";
pub const ENTRYCLASS_KEY_OBJECT_JWE_A128GCM: &str = "key_object_jwe_a128gcm";
pub const ENTRYCLASS_KEY_OBJECT_INTERNAL: &str = "key_object_internal";

View file

@ -270,6 +270,25 @@ pub enum OperationError {
KP0043KeyObjectJweA128GCMEncryption,
KP0044KeyObjectJwsPublicJwk,
KP0045KeyObjectImportJwsRs256DerInvalid,
KP0046KeyObjectSignerToVerifier,
KP0047KeyObjectPublicToDer,
KP0048KeyObjectJwtRs256Generation,
KP0049KeyObjectSignerToVerifier,
KP0050KeyObjectPrivateToDer,
KP0051KeyObjectPublicToDer,
KP0052KeyObjectJwsRs256DerInvalid,
KP0053KeyObjectSignerToVerifier,
KP0054KeyObjectJwsRs256DerInvalid,
KP0055KeyObjectJwsRs256DerInvalid,
KP0056KeyObjectJwsRs256Signature,
KP0057KeyObjectJwsNotAssociated,
KP0058KeyObjectJwsInvalid,
KP0059KeyObjectJwsKeyRevoked,
KP0060KeyObjectJwsPublicJwk,
KP0061KeyObjectNoActiveSigningKeys,
KP0062KeyProviderNoSuchKey,
// Plugins
PL0001GidOverlapsSystemRange,
@ -448,6 +467,26 @@ impl OperationError {
Self::KP0042KeyObjectNoActiveEncryptionKeys => None,
Self::KP0043KeyObjectJweA128GCMEncryption => None,
Self::KP0044KeyObjectJwsPublicJwk => None,
Self::KP0045KeyObjectImportJwsRs256DerInvalid => None,
Self::KP0046KeyObjectSignerToVerifier => None,
Self::KP0047KeyObjectPublicToDer => None,
Self::KP0048KeyObjectJwtRs256Generation => None,
Self::KP0049KeyObjectSignerToVerifier => None,
Self::KP0050KeyObjectPrivateToDer => None,
Self::KP0051KeyObjectPublicToDer => None,
Self::KP0052KeyObjectJwsRs256DerInvalid => None,
Self::KP0053KeyObjectSignerToVerifier => None,
Self::KP0054KeyObjectJwsRs256DerInvalid => None,
Self::KP0055KeyObjectJwsRs256DerInvalid => None,
Self::KP0056KeyObjectJwsRs256Signature => None,
Self::KP0057KeyObjectJwsNotAssociated => None,
Self::KP0058KeyObjectJwsInvalid => None,
Self::KP0059KeyObjectJwsKeyRevoked => None,
Self::KP0060KeyObjectJwsPublicJwk => None,
Self::KP0061KeyObjectNoActiveSigningKeys => None,
Self::KP0062KeyProviderNoSuchKey => None,
Self::KU001InitWhileSessionActive => Some("The session was active when the init function was called.".into()),
Self::KU002ContinueWhileSessionInActive => Some("Attempted to continue auth session while current session is inactive".into()),
Self::KU003PamAuthFailed => Some("Failed PAM account authentication step".into()),

View file

@ -13,9 +13,9 @@ tls_key = "/tmp/kanidm/key.pem"
# NOTE: this is overridden by KANIDM_LOG_LEVEL environment variable
# Defaults to "info"
#
log_level = "info"
# log_level = "info"
# log_level = "debug"
# log_level = "trace"
log_level = "trace"
# otel_grpc_url = "http://localhost:4317"

View file

@ -32,7 +32,6 @@ compact_jwt = { workspace = true, features = ["openssl", "hsm-crypto"] }
concread = { workspace = true }
dhat = { workspace = true, optional = true }
dyn-clone = { workspace = true }
fernet = { workspace = true, features = ["fernet_danger_timestamps"] }
# futures-util = { workspace = true }
hashbrown = { workspace = true }
idlset = { workspace = true }

View file

@ -692,6 +692,7 @@ pub enum DbValueImage {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum DbValueKeyUsage {
JwsEs256,
JwsRs256,
JweA128GCM,
}

View file

@ -40,6 +40,7 @@ pub enum EntryClass {
KeyProviderInternal,
KeyObject,
KeyObjectJwtEs256,
KeyObjectJwtRs256,
KeyObjectJweA128GCM,
KeyObjectInternal,
MemberOf,
@ -94,6 +95,7 @@ impl From<EntryClass> for &'static str {
EntryClass::KeyProviderInternal => ENTRYCLASS_KEY_PROVIDER_INTERNAL,
EntryClass::KeyObject => ENTRYCLASS_KEY_OBJECT,
EntryClass::KeyObjectJwtEs256 => ENTRYCLASS_KEY_OBJECT_JWT_ES256,
EntryClass::KeyObjectJwtRs256 => ENTRYCLASS_KEY_OBJECT_JWT_RS256,
EntryClass::KeyObjectJweA128GCM => ENTRYCLASS_KEY_OBJECT_JWE_A128GCM,
EntryClass::KeyObjectInternal => ENTRYCLASS_KEY_OBJECT_INTERNAL,
EntryClass::MemberOf => ENTRYCLASS_MEMBER_OF,

View file

@ -334,6 +334,10 @@ pub const UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENT_CLASS: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000189");
pub const UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVE_CLASS: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000190");
pub const UUID_SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_RS256: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000191");
pub const UUID_SCHEMA_CLASS_KEY_OBJECT_JWT_RS256: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000192");
// System and domain infos
// I'd like to strongly criticise william of the past for making poor choices about these allocations.

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,3 @@
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
use kanidm_lib_crypto::CryptoPolicy;
use compact_jwt::{Jwk, JwsCompact};
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
use concread::cowcell::CowCellReadTxn;
use concread::hashmap::HashMap;
use kanidm_proto::internal::{
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, ScimSyncToken,
UatPurpose, UserAuthToken,
};
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
use rand::prelude::*;
use tokio::sync::mpsc::{
unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
};
use tokio::sync::{Mutex, Semaphore};
use tracing::trace;
use url::Url;
use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
use zxcvbn::{zxcvbn, Score};
use super::event::ReadBackupCodeEvent;
use super::ldap::{LdapBoundToken, LdapSession};
use crate::credential::{softlock::CredSoftLock, Credential};
@ -38,9 +13,6 @@ use crate::idm::delayed::{
AuthSessionRecord, BackupCodeRemoval, DelayedAction, PasswordUpgrade, UnixPasswordUpgrade,
WebauthnCounterIncrement,
};
#[cfg(test)]
use crate::idm::event::PasswordChangeEvent;
use crate::idm::event::{AuthEvent, AuthEventStep, AuthResult};
use crate::idm::event::{
CredentialStatusEvent, LdapAuthEvent, LdapTokenAuthEvent, RadiusAuthTokenEvent,
@ -61,6 +33,31 @@ use crate::server::keys::KeyProvidersTransaction;
use crate::server::DomainInfo;
use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid};
use crate::value::{Session, SessionState};
use compact_jwt::{Jwk, JwsCompact};
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
use concread::cowcell::CowCellReadTxn;
use concread::hashmap::HashMap;
use kanidm_lib_crypto::CryptoPolicy;
use kanidm_proto::internal::{
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, ScimSyncToken,
UatPurpose, UserAuthToken,
};
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
use rand::prelude::*;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::{
unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
};
use tokio::sync::{Mutex, Semaphore};
use tracing::trace;
use url::Url;
use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
use zxcvbn::{zxcvbn, Score};
#[cfg(test)]
use crate::idm::event::PasswordChangeEvent;
pub(crate) type AuthSessionMutex = Arc<Mutex<AuthSession>>;
pub(crate) type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
@ -158,14 +155,12 @@ impl IdmServer {
let (audit_tx, audit_rx) = unbounded();
// Get the domain name, as the relying party id.
let (rp_id, rp_name, domain_level, oauth2rs_set, application_set) = {
let (rp_id, rp_name, application_set) = {
let mut qs_read = qs.read().await?;
(
qs_read.get_domain_name().to_string(),
qs_read.get_domain_display_name().to_string(),
qs_read.get_domain_version(),
// Add a read/reload of all oauth2 configurations.
qs_read.get_oauth2rs_set()?,
qs_read.get_applications_set()?,
)
};
@ -201,11 +196,10 @@ impl IdmServer {
OperationError::InvalidState
})?;
let oauth2rs = Oauth2ResourceServers::try_from((oauth2rs_set, origin_url, domain_level))
.map_err(|e| {
admin_error!("Failed to load oauth2 resource servers - {:?}", e);
e
})?;
let oauth2rs = Oauth2ResourceServers::new(origin_url).map_err(|err| {
error!(?err, "Failed to load oauth2 resource servers");
err
})?;
let applications = LdapApplications::try_from(application_set).map_err(|e| {
admin_error!("Failed to load ldap applications - {:?}", e);
@ -2121,6 +2115,12 @@ impl IdmServerProxyWriteTransaction<'_> {
#[instrument(level = "debug", skip_all)]
pub fn commit(mut self) -> Result<(), OperationError> {
// The problem we have here is that we need the qs_write layer to reload *first*
// so that things like schema and key objects are ready.
self.qs_write.reload()?;
// Now that's done, let's proceed.
if self.qs_write.get_changed_app() {
self.qs_write
.get_applications_set()
@ -2128,9 +2128,11 @@ impl IdmServerProxyWriteTransaction<'_> {
}
if self.qs_write.get_changed_oauth2() {
let domain_level = self.qs_write.get_domain_version();
self.qs_write
.get_oauth2rs_set()
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set, domain_level))?;
self.qs_write.get_oauth2rs_set().and_then(|oauth2rs_set| {
let key_providers = self.qs_write.get_key_providers();
self.oauth2rs
.reload(oauth2rs_set, key_providers, domain_level)
})?;
// Clear the flag to indicate we completed the reload.
self.qs_write.clear_changed_oauth2();
}

View file

@ -614,315 +614,7 @@ lazy_static! {
}
lazy_static! {
pub static ref IDM_ACP_OAUTH2_MANAGE_DL4: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
EntryClass::AccessControlCreate,
EntryClass::AccessControlDelete,
EntryClass::AccessControlModify,
EntryClass::AccessControlSearch
],
name: "idm_acp_hp_oauth2_manage_priv",
uuid: UUID_IDM_ACP_OAUTH2_MANAGE_V1,
description: "Builtin IDM Control for managing oauth2 resource server integrations.",
receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_OAUTH2_ADMINS]),
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
match_class_filter!(EntryClass::OAuth2ResourceServer),
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(),
])),
search_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
modify_removed_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
modify_present_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
create_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
create_classes: vec![
EntryClass::Object,
EntryClass::OAuth2ResourceServer,
EntryClass::OAuth2ResourceServerBasic,
EntryClass::OAuth2ResourceServerPublic,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_OAUTH2_MANAGE_DL5: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
EntryClass::AccessControlCreate,
EntryClass::AccessControlDelete,
EntryClass::AccessControlModify,
EntryClass::AccessControlSearch
],
name: "idm_acp_hp_oauth2_manage_priv",
uuid: UUID_IDM_ACP_OAUTH2_MANAGE_V1,
description: "Builtin IDM Control for managing oauth2 resource server integrations.",
receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_OAUTH2_ADMINS]),
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
match_class_filter!(EntryClass::OAuth2ResourceServer),
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(),
])),
search_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::Spn,
Attribute::OAuth2Session,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
modify_removed_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::OAuth2Session,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
modify_present_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
create_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::Name,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
],
create_classes: vec![
EntryClass::Object,
EntryClass::Account,
EntryClass::OAuth2ResourceServer,
EntryClass::OAuth2ResourceServerBasic,
EntryClass::OAuth2ResourceServerPublic,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_OAUTH2_MANAGE_DL7: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
EntryClass::AccessControlCreate,
EntryClass::AccessControlDelete,
EntryClass::AccessControlModify,
EntryClass::AccessControlSearch
],
name: "idm_acp_hp_oauth2_manage_priv",
uuid: UUID_IDM_ACP_OAUTH2_MANAGE_V1,
description: "Builtin IDM Control for managing oauth2 resource server integrations.",
receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_OAUTH2_ADMINS]),
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
match_class_filter!(EntryClass::OAuth2ResourceServer),
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(),
])),
search_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::Spn,
Attribute::OAuth2Session,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
],
modify_removed_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::OAuth2Session,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
],
modify_present_attrs: vec![
Attribute::Description,
Attribute::DisplayName,
Attribute::Name,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
],
create_attrs: vec![
Attribute::Class,
Attribute::Description,
Attribute::Name,
Attribute::DisplayName,
Attribute::OAuth2RsName,
Attribute::OAuth2RsOrigin,
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
Attribute::OAuth2RsClaimMap,
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
],
create_classes: vec![
EntryClass::Object,
EntryClass::Account,
EntryClass::OAuth2ResourceServer,
EntryClass::OAuth2ResourceServerBasic,
EntryClass::OAuth2ResourceServerPublic,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_OAUTH2_MANAGE_DL9: BuiltinAcp = BuiltinAcp {
pub static ref IDM_ACP_OAUTH2_MANAGE: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
@ -951,10 +643,7 @@ lazy_static! {
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
@ -962,6 +651,7 @@ lazy_static! {
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
Attribute::OAuth2DeviceFlowEnable,
Attribute::KeyInternalData,
],
modify_removed_attrs: vec![
Attribute::Description,
@ -973,10 +663,7 @@ lazy_static! {
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::OAuth2RsBasicSecret,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
Attribute::OAuth2AllowInsecureClientDisablePkce,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::OAuth2AllowLocalhostRedirect,
@ -984,6 +671,8 @@ lazy_static! {
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
Attribute::OAuth2DeviceFlowEnable,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
],
modify_present_attrs: vec![
Attribute::Description,
@ -1001,6 +690,8 @@ lazy_static! {
Attribute::Image,
Attribute::OAuth2StrictRedirectUri,
Attribute::OAuth2DeviceFlowEnable,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
],
create_attrs: vec![
Attribute::Class,
@ -1032,122 +723,6 @@ lazy_static! {
};
}
lazy_static! {
pub static ref IDM_ACP_DOMAIN_ADMIN_DL6: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
EntryClass::AccessControlModify,
EntryClass::AccessControlSearch
],
name: "idm_acp_domain_admin",
uuid: UUID_IDM_ACP_DOMAIN_ADMIN_V1,
description: "Builtin IDM Control for granting domain info administration locally",
receiver: BuiltinAcpReceiver::Group(vec![UUID_DOMAIN_ADMINS]),
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
ProtoFilter::Eq(
Attribute::Uuid.to_string(),
STR_UUID_DOMAIN_INFO.to_string()
),
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone()
])),
search_attrs: vec![
Attribute::Class,
Attribute::Name,
Attribute::Uuid,
Attribute::DomainDisplayName,
Attribute::DomainName,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::DomainSsid,
Attribute::DomainUuid,
// Grants read access to the key object.
// But this means we have to specify every type of key object?
// Future william problem ...
Attribute::KeyInternalData,
Attribute::LdapAllowUnixPwBind,
Attribute::Version,
],
modify_removed_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainSsid,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
],
modify_present_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::DomainSsid,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_DOMAIN_ADMIN_DL8: BuiltinAcp = BuiltinAcp {
classes: vec![
EntryClass::Object,
EntryClass::AccessControlProfile,
EntryClass::AccessControlModify,
EntryClass::AccessControlSearch
],
name: "idm_acp_domain_admin",
uuid: UUID_IDM_ACP_DOMAIN_ADMIN_V1,
description: "Builtin IDM Control for granting domain info administration locally",
receiver: BuiltinAcpReceiver::Group(vec![UUID_DOMAIN_ADMINS]),
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
ProtoFilter::Eq(
Attribute::Uuid.to_string(),
STR_UUID_DOMAIN_INFO.to_string()
),
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone()
])),
search_attrs: vec![
Attribute::Class,
Attribute::Name,
Attribute::Uuid,
Attribute::DomainDisplayName,
Attribute::DomainName,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::DomainSsid,
Attribute::DomainUuid,
Attribute::KeyInternalData,
Attribute::LdapAllowUnixPwBind,
Attribute::Version,
Attribute::Image,
],
modify_removed_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainSsid,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
Attribute::Image,
],
modify_present_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainLdapBasedn,
Attribute::LdapMaxQueryableAttrs,
Attribute::DomainSsid,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
Attribute::Image,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_DOMAIN_ADMIN_DL9: BuiltinAcp = BuiltinAcp {
classes: vec![

View file

@ -105,6 +105,7 @@ pub fn phase_1_schema_attrs() -> Vec<EntryInitNew> {
// DL10
SCHEMA_ATTR_DENIED_NAME_DL10.clone().into(),
SCHEMA_ATTR_LDAP_MAXIMUM_QUERYABLE_ATTRIBUTES.clone().into(),
SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_RS256_DL6.clone().into(),
]
}
@ -140,6 +141,7 @@ pub fn phase_2_schema_classes() -> Vec<EntryInitNew> {
SCHEMA_CLASS_OAUTH2_RS_DL9.clone().into(),
// DL10
SCHEMA_CLASS_DOMAIN_INFO_DL10.clone().into(),
SCHEMA_CLASS_KEY_OBJECT_JWT_RS256.clone().into(),
]
}
@ -260,8 +262,9 @@ pub fn phase_7_builtin_access_control_profiles() -> Vec<EntryInitNew> {
IDM_ACP_MAIL_SERVERS_DL8.clone().into(),
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone().into(),
// DL9
IDM_ACP_OAUTH2_MANAGE_DL9.clone().into(),
IDM_ACP_GROUP_MANAGE_DL9.clone().into(),
IDM_ACP_DOMAIN_ADMIN_DL9.clone().into(),
// DL10
IDM_ACP_OAUTH2_MANAGE.clone().into(),
]
}

View file

@ -657,6 +657,17 @@ pub static ref SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_ES256_DL6: SchemaAttribute = Sc
..Default::default()
};
pub static ref SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_RS256_DL6: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_RS256,
name: Attribute::KeyActionImportJwsRs256,
description: "".to_string(),
multivalue: true,
// Ephemeral action.
phantom: true,
syntax: SyntaxType::PrivateBinary,
..Default::default()
};
pub static ref SCHEMA_ATTR_PATCH_LEVEL_DL7: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_PATCH_LEVEL,
name: Attribute::PatchLevel,
@ -948,13 +959,12 @@ pub static ref SCHEMA_CLASS_SYSTEM_CONFIG: SchemaClass = SchemaClass {
pub static ref SCHEMA_CLASS_OAUTH2_RS_DL9: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_OAUTH2_RS,
name: EntryClass::OAuth2ResourceServer.into(),
description: "The class representing a configured OAuth2 Client".to_string(),
description: "The class epresenting a configured OAuth2 Client".to_string(),
systemmay: vec![
Attribute::Description,
Attribute::OAuth2RsScopeMap,
Attribute::OAuth2RsSupScopeMap,
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2JwtLegacyCryptoEnable,
Attribute::OAuth2PreferShortUsername,
Attribute::Image,
@ -963,11 +973,13 @@ pub static ref SCHEMA_CLASS_OAUTH2_RS_DL9: SchemaClass = SchemaClass {
Attribute::OAuth2RsOrigin,
Attribute::OAuth2StrictRedirectUri,
Attribute::OAuth2DeviceFlowEnable,
// Deprecated
Attribute::Rs256PrivateKeyDer,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
],
systemmust: vec![
Attribute::OAuth2RsOriginLanding,
Attribute::OAuth2RsTokenKey,
Attribute::Es256PrivateKeyDer,
],
..Default::default()
};
@ -1045,6 +1057,16 @@ pub static ref SCHEMA_CLASS_KEY_OBJECT_JWT_ES256_DL6: SchemaClass = SchemaClass
..Default::default()
};
pub static ref SCHEMA_CLASS_KEY_OBJECT_JWT_RS256: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_KEY_OBJECT_JWT_RS256,
name: EntryClass::KeyObjectJwtRs256.into(),
description: "A marker class indicating that this keyobject must provide jwt rs256 capability.".to_string(),
systemsupplements: vec![
EntryClass::KeyObject.into(),
],
..Default::default()
};
pub static ref SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM_DL6: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM,
name: EntryClass::KeyObjectJweA128GCM.into(),

View file

@ -135,11 +135,13 @@ impl KeyObjectManagement {
qs: &mut QueryServerWriteTransaction,
cand: &mut [Entry<EntryInvalid, T>],
) -> Result<(), OperationError> {
// Valid from right meow!
// New keys will be valid from right meow!
let valid_from = qs.get_curtime();
let txn_cid = qs.get_cid().clone();
let key_providers = qs.get_key_providers_mut();
// ====================================================================
// Transform any found KeyObjects and manage any related key operations
// for them
cand.iter_mut()
.filter(|entry| {
@ -172,6 +174,14 @@ impl KeyObjectManagement {
key_object.jws_es256_import(import_keys, valid_from, &txn_cid)?;
}
let maybe_import = entry.pop_ava(Attribute::KeyActionImportJwsRs256);
if let Some(import_keys) = maybe_import
.as_ref()
.and_then(|vs| vs.as_private_binary_set())
{
key_object.jws_rs256_import(import_keys, valid_from, &txn_cid)?;
}
// If revoke. This weird looking let dance is to ensure that the inner hexstring set
// lives long enough.
let maybe_revoked = entry.pop_ava(Attribute::KeyActionRevoke);
@ -208,6 +218,11 @@ impl KeyObjectManagement {
key_object.jws_es256_assert(Duration::ZERO, &txn_cid)?;
}
if entry.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtRs256.into())
{
key_object.jws_rs256_assert(Duration::ZERO, &txn_cid)?;
}
if entry
.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJweA128GCM.into())
{

View file

@ -18,10 +18,10 @@ mod domain;
pub(crate) mod dyngroup;
mod eckeygen;
pub(crate) mod gidnumber;
mod jwskeygen;
mod keyobject;
mod memberof;
mod namehistory;
mod oauth2;
mod refint;
mod session;
mod spn;
@ -230,15 +230,17 @@ impl Plugins {
) -> Result<(), OperationError> {
base::Base::pre_create_transform(qs, cand, ce)?;
valuedeny::ValueDeny::pre_create_transform(qs, cand, ce)?;
cred_import::CredImport::pre_create_transform(qs, cand, ce)?;
oauth2::OAuth2::pre_create_transform(qs, cand, ce)?;
eckeygen::EcdhKeyGen::pre_create_transform(qs, cand, ce)?;
keyobject::KeyObjectManagement::pre_create_transform(qs, cand, ce)?;
jwskeygen::JwsKeygen::pre_create_transform(qs, cand, ce)?;
cred_import::CredImport::pre_create_transform(qs, cand, ce)?;
gidnumber::GidNumber::pre_create_transform(qs, cand, ce)?;
domain::Domain::pre_create_transform(qs, cand, ce)?;
spn::Spn::pre_create_transform(qs, cand, ce)?;
default_values::DefaultValues::pre_create_transform(qs, cand, ce)?;
namehistory::NameHistory::pre_create_transform(qs, cand, ce)?;
eckeygen::EcdhKeyGen::pre_create_transform(qs, cand, ce)?;
// Should always be last
attrunique::AttrUnique::pre_create_transform(qs, cand, ce)
}
@ -271,16 +273,18 @@ impl Plugins {
) -> Result<(), OperationError> {
base::Base::pre_modify(qs, pre_cand, cand, me)?;
valuedeny::ValueDeny::pre_modify(qs, pre_cand, cand, me)?;
cred_import::CredImport::pre_modify(qs, pre_cand, cand, me)?;
jwskeygen::JwsKeygen::pre_modify(qs, pre_cand, cand, me)?;
oauth2::OAuth2::pre_modify(qs, pre_cand, cand, me)?;
eckeygen::EcdhKeyGen::pre_modify(qs, pre_cand, cand, me)?;
keyobject::KeyObjectManagement::pre_modify(qs, pre_cand, cand, me)?;
cred_import::CredImport::pre_modify(qs, pre_cand, cand, me)?;
gidnumber::GidNumber::pre_modify(qs, pre_cand, cand, me)?;
domain::Domain::pre_modify(qs, pre_cand, cand, me)?;
spn::Spn::pre_modify(qs, pre_cand, cand, me)?;
session::SessionConsistency::pre_modify(qs, pre_cand, cand, me)?;
default_values::DefaultValues::pre_modify(qs, pre_cand, cand, me)?;
namehistory::NameHistory::pre_modify(qs, pre_cand, cand, me)?;
eckeygen::EcdhKeyGen::pre_modify(qs, pre_cand, cand, me)?;
// attr unique should always be last
attrunique::AttrUnique::pre_modify(qs, pre_cand, cand, me)
}
@ -306,16 +310,18 @@ impl Plugins {
) -> Result<(), OperationError> {
base::Base::pre_batch_modify(qs, pre_cand, cand, me)?;
valuedeny::ValueDeny::pre_batch_modify(qs, pre_cand, cand, me)?;
cred_import::CredImport::pre_batch_modify(qs, pre_cand, cand, me)?;
jwskeygen::JwsKeygen::pre_batch_modify(qs, pre_cand, cand, me)?;
oauth2::OAuth2::pre_batch_modify(qs, pre_cand, cand, me)?;
eckeygen::EcdhKeyGen::pre_batch_modify(qs, pre_cand, cand, me)?;
keyobject::KeyObjectManagement::pre_batch_modify(qs, pre_cand, cand, me)?;
cred_import::CredImport::pre_batch_modify(qs, pre_cand, cand, me)?;
gidnumber::GidNumber::pre_batch_modify(qs, pre_cand, cand, me)?;
domain::Domain::pre_batch_modify(qs, pre_cand, cand, me)?;
spn::Spn::pre_batch_modify(qs, pre_cand, cand, me)?;
session::SessionConsistency::pre_batch_modify(qs, pre_cand, cand, me)?;
default_values::DefaultValues::pre_batch_modify(qs, pre_cand, cand, me)?;
namehistory::NameHistory::pre_batch_modify(qs, pre_cand, cand, me)?;
eckeygen::EcdhKeyGen::pre_batch_modify(qs, pre_cand, cand, me)?;
// attr unique should always be last
attrunique::AttrUnique::pre_batch_modify(qs, pre_cand, cand, me)
}

View file

@ -6,14 +6,14 @@ use crate::plugins::Plugin;
use crate::prelude::*;
use crate::utils::password_from_random;
pub struct JwsKeygen {}
pub struct OAuth2 {}
impl Plugin for JwsKeygen {
impl Plugin for OAuth2 {
fn id() -> &'static str {
"plugin_jws_keygen"
"plugin_oauth2"
}
#[instrument(level = "debug", name = "jwskeygen_pre_create_transform", skip_all)]
#[instrument(level = "debug", name = "oauth2_pre_create_transform", skip_all)]
fn pre_create_transform(
qs: &mut QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
@ -22,7 +22,7 @@ impl Plugin for JwsKeygen {
Self::modify_inner(qs, cand)
}
#[instrument(level = "debug", name = "jwskeygen_pre_modify", skip_all)]
#[instrument(level = "debug", name = "oauth2_pre_modify", skip_all)]
fn pre_modify(
qs: &mut QueryServerWriteTransaction,
_pre_cand: &[Arc<EntrySealedCommitted>],
@ -32,7 +32,7 @@ impl Plugin for JwsKeygen {
Self::modify_inner(qs, cand)
}
#[instrument(level = "debug", name = "jwskeygen_pre_batch_modify", skip_all)]
#[instrument(level = "debug", name = "oauth2_pre_batch_modify", skip_all)]
fn pre_batch_modify(
qs: &mut QueryServerWriteTransaction,
_pre_cand: &[Arc<EntrySealedCommitted>],
@ -43,52 +43,69 @@ impl Plugin for JwsKeygen {
}
}
impl JwsKeygen {
impl OAuth2 {
fn modify_inner<T: Clone>(
_qs: &mut QueryServerWriteTransaction,
qs: &mut QueryServerWriteTransaction,
cand: &mut [Entry<EntryInvalid, T>],
) -> Result<(), OperationError> {
cand.iter_mut().try_for_each(|e| {
if e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServerBasic.into()) &&
!e.attribute_pres(Attribute::OAuth2RsBasicSecret) {
security_info!("regenerating oauth2 basic secret");
let v = Value::SecretValue(password_from_random());
e.add_ava(Attribute::OAuth2RsBasicSecret, v);
}
let domain_level = qs.get_domain_version();
if e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into()) {
if !e.attribute_pres(Attribute::OAuth2RsTokenKey) {
security_info!("regenerating oauth2 token key");
let k = fernet::Fernet::generate_key();
let v = Value::new_secret_str(&k);
e.add_ava(Attribute::OAuth2RsTokenKey, v);
}
if !e.attribute_pres(Attribute::Es256PrivateKeyDer) {
security_info!("regenerating oauth2 es256 private key");
let der = JwsEs256Signer::generate_es256()
.and_then(|jws| jws.private_key_to_der())
.map_err(|e| {
admin_error!(err = ?e, "Unable to generate ES256 JwsSigner private key");
OperationError::CryptographyError
})?;
let v = Value::new_privatebinary(&der);
e.add_ava(Attribute::Es256PrivateKeyDer, v);
}
if e.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable).unwrap_or(false)
&& !e.attribute_pres(Attribute::Rs256PrivateKeyDer) {
security_info!("regenerating oauth2 legacy rs256 private key");
let der = JwsRs256Signer::generate_legacy_rs256()
.and_then(|jws| jws.private_key_to_der())
.map_err(|e| {
admin_error!(err = ?e, "Unable to generate Legacy RS256 JwsSigner private key");
OperationError::CryptographyError
})?;
let v = Value::new_privatebinary(&der);
e.add_ava(Attribute::Rs256PrivateKeyDer, v);
}
}
cand.iter_mut()
.filter(|entry| {
entry.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
})
.try_for_each(|entry| {
// Regenerate the basic secret, if needed
if entry.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServerBasic.into()) &&
!entry.attribute_pres(Attribute::OAuth2RsBasicSecret) {
security_info!("regenerating oauth2 basic secret");
let v = Value::SecretValue(password_from_random());
entry.add_ava(Attribute::OAuth2RsBasicSecret, v);
}
Ok(())
let has_rs256 = entry.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable).unwrap_or(false);
if domain_level >= DOMAIN_LEVEL_10 {
debug!("Generating OAuth2 Key Object");
// OAuth2 now requires a KeyObject, configure it now.
entry.add_ava(Attribute::Class, EntryClass::KeyObject.to_value());
entry.add_ava(Attribute::Class, EntryClass::KeyObjectJwtEs256.to_value());
entry.add_ava(Attribute::Class, EntryClass::KeyObjectJweA128GCM.to_value());
if has_rs256 {
entry.add_ava(Attribute::Class, EntryClass::KeyObjectJwtRs256.to_value());
}
} else {
if !entry.attribute_pres(Attribute::OAuth2RsTokenKey) {
security_info!("regenerating oauth2 token key");
let k = password_from_random();
let v = Value::new_secret_str(&k);
entry.add_ava(Attribute::OAuth2RsTokenKey, v);
}
if !entry.attribute_pres(Attribute::Es256PrivateKeyDer) {
security_info!("regenerating oauth2 es256 private key");
let der = JwsEs256Signer::generate_es256()
.and_then(|jws| jws.private_key_to_der())
.map_err(|e| {
admin_error!(err = ?e, "Unable to generate ES256 JwsSigner private key");
OperationError::CryptographyError
})?;
let v = Value::new_privatebinary(&der);
entry.add_ava(Attribute::Es256PrivateKeyDer, v);
}
if has_rs256 && !entry.attribute_pres(Attribute::Rs256PrivateKeyDer) {
security_info!("regenerating oauth2 legacy rs256 private key");
let der = JwsRs256Signer::generate_legacy_rs256()
.and_then(|jws| jws.private_key_to_der())
.map_err(|e| {
admin_error!(err = ?e, "Unable to generate Legacy RS256 JwsSigner private key");
OperationError::CryptographyError
})?;
let v = Value::new_privatebinary(&der);
entry.add_ava(Attribute::Rs256PrivateKeyDer, v);
}
}
Ok(())
})
}
}
@ -145,7 +162,6 @@ mod tests {
.internal_search_uuid(uuid)
.expect("failed to get oauth2 config");
assert!(e.attribute_pres(Attribute::OAuth2RsBasicSecret));
assert!(e.attribute_pres(Attribute::OAuth2RsTokenKey));
}
);
}
@ -186,8 +202,7 @@ mod tests {
(
Attribute::OAuth2RsBasicSecret,
Value::new_secret_str("12345")
),
(Attribute::OAuth2RsTokenKey, Value::new_secret_str("12345"))
)
);
let preload = vec![e];
@ -207,10 +222,8 @@ mod tests {
.internal_search_uuid(uuid)
.expect("failed to get oauth2 config");
assert!(e.attribute_pres(Attribute::OAuth2RsBasicSecret));
assert!(e.attribute_pres(Attribute::OAuth2RsTokenKey));
// Check the values are different.
assert!(e.get_ava_single_secret(Attribute::OAuth2RsBasicSecret) != Some("12345"));
assert!(e.get_ava_single_secret(Attribute::OAuth2RsTokenKey) != Some("12345"));
}
);
}

View file

@ -1,24 +1,22 @@
use super::object::{KeyObject, KeyObjectT};
use super::KeyId;
use crate::prelude::*;
use smolset::SmolSet;
use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
use crate::value::{KeyStatus, KeyUsage};
use crate::valueset::{KeyInternalData, ValueSetKeyInternal};
use compact_jwt::compact::{JweAlg, JweCompact, JweEnc};
use compact_jwt::crypto::{JweA128GCMEncipher, JweA128KWEncipher};
use compact_jwt::crypto::{
JweA128GCMEncipher, JweA128KWEncipher, JwsRs256Signer, JwsRs256Verifier,
};
use compact_jwt::jwe::Jwe;
use compact_jwt::traits::*;
use compact_jwt::{
JwaAlg, Jwk, Jws, JwsCompact, JwsEs256Signer, JwsEs256Verifier, JwsSigner, JwsSignerToVerifier,
JwaAlg, Jwk, JwkKeySet, Jws, JwsCompact, JwsEs256Signer, JwsEs256Verifier, JwsSigner,
JwsSignerToVerifier,
};
use smolset::SmolSet;
use std::collections::{BTreeMap, BTreeSet};
use std::ops::Bound::{Included, Unbounded};
use crate::value::{KeyStatus, KeyUsage};
use crate::valueset::{KeyInternalData, ValueSetKeyInternal};
use std::sync::Arc;
pub struct KeyProviderInternal {
uuid: Uuid,
@ -61,6 +59,7 @@ impl KeyProviderInternal {
uuid,
jws_es256: None,
jwe_a128gcm: None,
jws_rs256: None,
}))
}
@ -73,6 +72,7 @@ impl KeyProviderInternal {
debug!(?uuid, "Loading key object ...");
let mut jws_es256: Option<KeyObjectInternalJwtEs256> = None;
let mut jws_rs256: Option<KeyObjectInternalJwtRs256> = None;
let mut jwe_a128gcm: Option<KeyObjectInternalJweA128GCM> = None;
if let Some(key_internal_map) = entry
@ -104,6 +104,18 @@ impl KeyProviderInternal {
*valid_from,
)?;
}
KeyUsage::JwsRs256 => {
let jws_rs256_ref =
jws_rs256.get_or_insert_with(KeyObjectInternalJwtRs256::default);
jws_rs256_ref.load(
key_id,
*status,
status_cid.clone(),
der,
*valid_from,
)?;
}
KeyUsage::JweA128GCM => {
let jwe_a128gcm_ref =
jwe_a128gcm.get_or_insert_with(KeyObjectInternalJweA128GCM::default);
@ -125,6 +137,7 @@ impl KeyProviderInternal {
uuid,
jws_es256,
jwe_a128gcm,
jws_rs256,
})))
}
@ -194,7 +207,7 @@ impl KeyObjectInternalJweA128GCM {
fn assert_active(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError> {
if self.get_valid_cipher(valid_from).is_none() {
// This means there is no active signing key, so we need to create one.
warn!("no active jwe a128gcm found, creating a new one ...");
debug!("no active jwe a128gcm found, creating a new one ...");
self.new_active(valid_from, cid)
} else {
Ok(())
@ -422,7 +435,7 @@ impl KeyObjectInternalJwtEs256 {
fn assert_active(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError> {
if self.get_valid_signer(valid_from).is_none() {
// This means there is no active signing key, so we need to create one.
warn!("no active jwt es256 found, creating a new one ...");
debug!("no active jwt es256 found, creating a new one ...");
self.new_active(valid_from, cid)
} else {
Ok(())
@ -437,8 +450,8 @@ impl KeyObjectInternalJwtEs256 {
) -> Result<(), OperationError> {
let valid_from = valid_from.as_secs();
for der in import_keys {
let signer = JwsEs256Signer::from_es256_der(der).map_err(|err| {
for private_der in import_keys {
let signer = JwsEs256Signer::from_es256_der(private_der).map_err(|err| {
error!(?err, "Unable to load imported es256 DER signer");
OperationError::KP0028KeyObjectImportJwsEs256DerInvalid
})?;
@ -451,22 +464,19 @@ impl KeyObjectInternalJwtEs256 {
OperationError::KP0029KeyObjectSignerToVerifier
})?;
let public_der = verifier.public_key_to_der().map_err(|jwt_error| {
error!(?jwt_error, "Unable to convert public key to DER");
OperationError::KP0030KeyObjectPublicToDer
})?;
// We need to use the legacy KID for imported objects
let kid = signer.get_legacy_kid().to_string();
debug!(?kid, "imported key");
self.active.insert(valid_from, signer.clone());
self.all.insert(
kid,
InternalJwtEs256 {
valid_from,
status: InternalJwtEs256Status::Retained {
status: InternalJwtEs256Status::Valid {
verifier,
public_der,
private_der: private_der.clone(),
},
status_cid: cid.clone(),
},
@ -696,6 +706,25 @@ impl KeyObjectInternalJwtEs256 {
}
}
fn public_jwks(&self) -> JwkKeySet {
let keys = self
.all
.iter()
.filter_map(|(_, es256)| match &es256.status {
InternalJwtEs256Status::Valid { verifier, .. }
| InternalJwtEs256Status::Retained { verifier, .. } => verifier
.public_key_as_jwk()
.map_err(|err| {
error!(?err);
})
.ok(),
InternalJwtEs256Status::Revoked { .. } => None,
})
.collect::<Vec<_>>();
JwkKeySet { keys }
}
fn public_jwk(&self, key_id: &str) -> Result<Option<Jwk>, OperationError> {
if let Some(key_to_check) = self.all.get(key_id) {
match &key_to_check.status {
@ -728,11 +757,387 @@ impl KeyObjectInternalJwtEs256 {
}
}
#[derive(Clone)]
enum InternalJwtRs256Status {
Valid {
verifier: JwsRs256Verifier,
private_der: Vec<u8>,
},
Retained {
verifier: JwsRs256Verifier,
public_der: Vec<u8>,
},
Revoked {
untrusted_verifier: JwsRs256Verifier,
public_der: Vec<u8>,
},
}
#[derive(Clone)]
struct InternalJwtRs256 {
valid_from: u64,
status: InternalJwtRs256Status,
status_cid: Cid,
}
#[derive(Default, Clone)]
struct KeyObjectInternalJwtRs256 {
// active signing keys are in a BTreeMap indexed by their valid_from
// time so that we can retrieve the active key.
//
// We don't need to worry about manipulating this at runtime, since any expiry
// event will cause the keyObject to reload, which will reflect to this map.
active: BTreeMap<u64, JwsRs256Signer>,
// All keys are stored by their KeyId for fast lookup. Keys internally have a
// current status which is checked for signature validation.
all: BTreeMap<KeyId, InternalJwtRs256>,
}
impl KeyObjectInternalJwtRs256 {
fn get_valid_signer(&self, time: Duration) -> Option<&JwsRs256Signer> {
let ct_secs = time.as_secs();
self.active
.range((Unbounded, Included(ct_secs)))
.next_back()
.map(|(_time, signer)| signer)
}
fn assert_active(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError> {
if self.get_valid_signer(valid_from).is_none() {
// This means there is no active signing key, so we need to create one.
debug!("no active jwt rs256 found, creating a new one ...");
self.new_active(valid_from, cid)
} else {
Ok(())
}
}
fn import(
&mut self,
import_keys: &SmolSet<[Vec<u8>; 1]>,
valid_from: Duration,
cid: &Cid,
) -> Result<(), OperationError> {
let valid_from = valid_from.as_secs();
for private_der in import_keys {
let signer = JwsRs256Signer::from_rs256_der(private_der).map_err(|err| {
error!(?err, "Unable to load imported rs256 DER signer");
OperationError::KP0045KeyObjectImportJwsRs256DerInvalid
})?;
let verifier = signer.get_verifier().map_err(|jwt_error| {
error!(
?jwt_error,
"Unable to produce jwt rs256 verifier from signer"
);
OperationError::KP0046KeyObjectSignerToVerifier
})?;
// We need to use the legacy KID for imported objects
let kid = signer.get_legacy_kid().to_string();
debug!(?kid, "imported key");
self.active.insert(valid_from, signer.clone());
self.all.insert(
kid,
InternalJwtRs256 {
valid_from,
status: InternalJwtRs256Status::Valid {
verifier,
private_der: private_der.clone(),
},
status_cid: cid.clone(),
},
);
}
Ok(())
}
fn new_active(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError> {
let valid_from = valid_from.as_secs();
let signer = JwsRs256Signer::generate_legacy_rs256().map_err(|jwt_error| {
error!(?jwt_error, "Unable to generate new jwt rs256 signing key");
OperationError::KP0048KeyObjectJwtRs256Generation
})?;
let verifier = signer.get_verifier().map_err(|jwt_error| {
error!(
?jwt_error,
"Unable to produce jwt rs256 verifier from signer"
);
OperationError::KP0049KeyObjectSignerToVerifier
})?;
let private_der = signer.private_key_to_der().map_err(|jwt_error| {
error!(?jwt_error, "Unable to convert signing key to DER");
OperationError::KP0050KeyObjectPrivateToDer
})?;
self.active.insert(valid_from, signer.clone());
let kid = signer.get_kid().to_string();
self.all.insert(
kid,
InternalJwtRs256 {
valid_from,
status: InternalJwtRs256Status::Valid {
// signer,
verifier,
private_der,
},
status_cid: cid.clone(),
},
);
Ok(())
}
fn revoke(&mut self, revoke_key_id: &KeyId, cid: &Cid) -> Result<bool, OperationError> {
if let Some(key_to_revoke) = self.all.get_mut(revoke_key_id) {
let untrusted_verifier = match &key_to_revoke.status {
InternalJwtRs256Status::Valid { verifier, .. }
| InternalJwtRs256Status::Retained { verifier, .. } => verifier,
InternalJwtRs256Status::Revoked {
untrusted_verifier, ..
} => untrusted_verifier,
}
.clone();
let public_der = untrusted_verifier
.public_key_to_der()
.map_err(|jwt_error| {
error!(?jwt_error, "Unable to convert public key to DER");
OperationError::KP0051KeyObjectPublicToDer
})?;
key_to_revoke.status = InternalJwtRs256Status::Revoked {
untrusted_verifier,
public_der,
};
key_to_revoke.status_cid = cid.clone();
let valid_from = key_to_revoke.valid_from;
// Remove it from the active set.
self.active.remove(&valid_from);
Ok(true)
} else {
// We didn't revoke anything
Ok(false)
}
}
fn load(
&mut self,
id: &str,
status: KeyStatus,
status_cid: Cid,
der: &[u8],
valid_from: u64,
) -> Result<(), OperationError> {
let id: KeyId = id.to_string();
let status = match status {
KeyStatus::Valid => {
let signer = JwsRs256Signer::from_rs256_der(der).map_err(|err| {
error!(?err, ?id, "Unable to load rs256 DER signer");
OperationError::KP0052KeyObjectJwsRs256DerInvalid
})?;
let verifier = signer.get_verifier().map_err(|err| {
error!(?err, "Unable to retrieve verifier from signer");
OperationError::KP0053KeyObjectSignerToVerifier
})?;
self.active.insert(valid_from, signer);
InternalJwtRs256Status::Valid {
// signer,
verifier,
private_der: der.to_vec(),
}
}
KeyStatus::Retained => {
let verifier = JwsRs256Verifier::from_rs256_der(der).map_err(|err| {
error!(?err, ?id, "Unable to load rs256 DER verifier");
OperationError::KP0054KeyObjectJwsRs256DerInvalid
})?;
InternalJwtRs256Status::Retained {
verifier,
public_der: der.to_vec(),
}
}
KeyStatus::Revoked => {
let untrusted_verifier = JwsRs256Verifier::from_rs256_der(der).map_err(|err| {
error!(?err, ?id, "Unable to load rs256 DER revoked verifier");
OperationError::KP0055KeyObjectJwsRs256DerInvalid
})?;
InternalJwtRs256Status::Revoked {
untrusted_verifier,
public_der: der.to_vec(),
}
}
};
let internal_jwt = InternalJwtRs256 {
valid_from,
status,
status_cid,
};
self.all.insert(id, internal_jwt);
Ok(())
}
fn to_key_iter(&self) -> impl Iterator<Item = (KeyId, KeyInternalData)> + '_ {
self.all.iter().map(|(key_id, internal_jwt)| {
let usage = KeyUsage::JwsRs256;
let valid_from = internal_jwt.valid_from;
let status_cid = internal_jwt.status_cid.clone();
let (status, der) = match &internal_jwt.status {
InternalJwtRs256Status::Valid { private_der, .. } => {
(KeyStatus::Valid, private_der.clone())
}
InternalJwtRs256Status::Retained { public_der, .. } => {
(KeyStatus::Retained, public_der.clone())
}
InternalJwtRs256Status::Revoked { public_der, .. } => {
(KeyStatus::Revoked, public_der.clone())
}
};
(
key_id.clone(),
KeyInternalData {
usage,
valid_from,
der,
status,
status_cid,
},
)
})
}
fn sign<V: JwsSignable>(
&self,
jws: &V,
current_time: Duration,
) -> Result<V::Signed, OperationError> {
let Some(signing_key) = self.get_valid_signer(current_time) else {
error!("No signing keys available. This may indicate that no keys are valid yet!");
return Err(OperationError::KP0061KeyObjectNoActiveSigningKeys);
};
signing_key.sign(jws).map_err(|jwt_err| {
error!(?jwt_err, "Unable to sign jws");
OperationError::KP0056KeyObjectJwsRs256Signature
})
}
fn verify<V: JwsVerifiable>(&self, jwsc: &V) -> Result<V::Verified, OperationError> {
let internal_jws = jwsc
.kid()
.and_then(|kid| {
debug!(?kid);
self.all.get(kid)
})
.ok_or_else(|| {
error!("JWS is signed by a key that is not present in this KeyObject");
for pres_kid in self.all.keys() {
debug!(?pres_kid);
}
OperationError::KP0057KeyObjectJwsNotAssociated
})?;
match &internal_jws.status {
InternalJwtRs256Status::Valid { verifier, .. }
| InternalJwtRs256Status::Retained { verifier, .. } => {
verifier.verify(jwsc).map_err(|jwt_err| {
error!(?jwt_err, "Failed to verify jws");
OperationError::KP0058KeyObjectJwsInvalid
})
}
InternalJwtRs256Status::Revoked { .. } => {
error!("The key used to sign this JWS has been revoked.");
Err(OperationError::KP0059KeyObjectJwsKeyRevoked)
}
}
}
fn public_jwks(&self) -> JwkKeySet {
let keys = self
.all
.iter()
.filter_map(|(key_id, rs256)| {
error!(?key_id);
match &rs256.status {
InternalJwtRs256Status::Valid { verifier, .. }
| InternalJwtRs256Status::Retained { verifier, .. } => verifier
.public_key_as_jwk()
.map_err(|err| {
error!(?err);
})
.ok(),
InternalJwtRs256Status::Revoked { .. } => None,
}
})
.collect::<Vec<_>>();
JwkKeySet { keys }
}
fn public_jwk(&self, key_id: &str) -> Result<Option<Jwk>, OperationError> {
if let Some(key_to_check) = self.all.get(key_id) {
match &key_to_check.status {
InternalJwtRs256Status::Valid { verifier, .. }
| InternalJwtRs256Status::Retained { verifier, .. } => {
verifier.public_key_as_jwk().map(Some).map_err(|err| {
error!(?err, "Unable to construct public JWK.");
OperationError::KP0060KeyObjectJwsPublicJwk
})
}
InternalJwtRs256Status::Revoked { .. } => Ok(None),
}
} else {
Ok(None)
}
}
#[cfg(test)]
fn kid_status(&self, key_id: &KeyId) -> Result<Option<KeyStatus>, OperationError> {
if let Some(key_to_check) = self.all.get(key_id) {
let status = match &key_to_check.status {
InternalJwtRs256Status::Valid { .. } => KeyStatus::Valid,
InternalJwtRs256Status::Retained { .. } => KeyStatus::Retained,
InternalJwtRs256Status::Revoked { .. } => KeyStatus::Revoked,
};
Ok(Some(status))
} else {
Ok(None)
}
}
}
#[derive(Clone)]
pub struct KeyObjectInternal {
provider: Arc<KeyProviderInternal>,
uuid: Uuid,
jws_es256: Option<KeyObjectInternalJwtEs256>,
jws_rs256: Option<KeyObjectInternalJwtRs256>,
jwe_a128gcm: Option<KeyObjectInternalJweA128GCM>,
// If you add more types here you need to add these to rotate
// and revoke.
@ -769,6 +1174,10 @@ impl KeyObjectT for KeyObjectInternal {
jws_es256_object.new_active(rotation_time, cid)?;
}
if let Some(jws_rs256_object) = &mut self.jws_rs256 {
jws_rs256_object.new_active(rotation_time, cid)?;
}
if let Some(jwe_a128_gcm) = &mut self.jwe_a128gcm {
jwe_a128_gcm.new_active(rotation_time, cid)?;
}
@ -790,6 +1199,12 @@ impl KeyObjectT for KeyObjectInternal {
}
};
if let Some(jws_rs256_object) = &mut self.jws_rs256 {
if jws_rs256_object.revoke(revoke_key_id, cid)? {
has_revoked = true;
}
};
if let Some(jwe_a128_gcm) = &mut self.jwe_a128gcm {
if jwe_a128_gcm.revoke(revoke_key_id, cid)? {
has_revoked = true;
@ -831,6 +1246,14 @@ impl KeyObjectT for KeyObjectInternal {
Err(OperationError::KP0018KeyProviderNoSuchKey)
}
}
JwaAlg::RS256 => {
if let Some(jws_rs256_object) = &self.jws_rs256 {
jws_rs256_object.verify(jwsc)
} else {
error!(provider_uuid = ?self.uuid, "jwt rs256 not available on this provider");
Err(OperationError::KP0018KeyProviderNoSuchKey)
}
}
unsupported_alg => {
// unsupported rn.
error!(provider_uuid = ?self.uuid, ?unsupported_alg, "algorithm not available on this provider");
@ -839,12 +1262,26 @@ impl KeyObjectT for KeyObjectInternal {
}
}
fn jws_public_jwk(&self, kid: &str) -> Result<Option<Jwk>, OperationError> {
fn jws_es256_jwks(&self) -> Option<JwkKeySet> {
self.jws_es256
.as_ref()
.map(|jws_es256_object| jws_es256_object.public_jwks())
}
fn jws_public_jwk(&self, key_id: &str) -> Result<Option<Jwk>, OperationError> {
if let Some(jws_es256_object) = &self.jws_es256 {
jws_es256_object.public_jwk(kid)
} else {
Ok(None)
if let Some(status) = jws_es256_object.public_jwk(key_id)? {
return Ok(Some(status));
}
}
if let Some(jws_rs256_object) = &self.jws_rs256 {
if let Some(status) = jws_rs256_object.public_jwk(key_id)? {
return Ok(Some(status));
}
}
Ok(None)
}
fn jws_es256_import(
@ -921,6 +1358,12 @@ impl KeyObjectT for KeyObjectInternal {
}
}
if let Some(jws_rs256_object) = &self.jws_rs256 {
if let Some(status) = jws_rs256_object.kid_status(key_id)? {
return Ok(Some(status));
}
}
Ok(None)
}
@ -933,6 +1376,11 @@ impl KeyObjectT for KeyObjectInternal {
self.jwe_a128gcm
.iter()
.flat_map(|jwe_a128gcm| jwe_a128gcm.to_key_iter()),
)
.chain(
self.jws_rs256
.iter()
.flat_map(|jws_rs256| jws_rs256.to_key_iter()),
);
let key_vs = ValueSetKeyInternal::from_key_iter(key_iter)? as ValueSet;
@ -948,6 +1396,46 @@ impl KeyObjectT for KeyObjectInternal {
(Attribute::KeyInternalData, key_vs),
])
}
fn jws_rs256_import(
&mut self,
import_keys: &SmolSet<[Vec<u8>; 1]>,
valid_from: Duration,
cid: &Cid,
) -> Result<(), OperationError> {
let koi = self
.jws_rs256
.get_or_insert_with(KeyObjectInternalJwtRs256::default);
koi.import(import_keys, valid_from, cid)
}
fn jws_rs256_assert(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError> {
let koi = self
.jws_rs256
.get_or_insert_with(KeyObjectInternalJwtRs256::default);
koi.assert_active(valid_from, cid)
}
fn jws_rs256_sign(
&self,
jws: &Jws,
current_time: Duration,
) -> Result<JwsCompact, OperationError> {
if let Some(jws_rs256_object) = &self.jws_rs256 {
jws_rs256_object.sign(jws, current_time)
} else {
error!(provider_uuid = ?self.uuid, "jwt rs256 not available on this provider");
Err(OperationError::KP0062KeyProviderNoSuchKey)
}
}
fn jws_rs256_jwks(&self) -> Option<JwkKeySet> {
self.jws_rs256
.as_ref()
.map(|jws_rs256_object| jws_rs256_object.public_jwks())
}
}
#[cfg(test)]

View file

@ -1,6 +1,6 @@
use crate::prelude::*;
use compact_jwt::{compact::JweCompact, jwe::Jwe};
use compact_jwt::{Jwk, Jws, JwsCompact};
use compact_jwt::{Jwk, JwkKeySet, Jws, JwsCompact};
use smolset::SmolSet;
use std::collections::BTreeSet;
use uuid::Uuid;
@ -29,6 +29,25 @@ pub trait KeyObjectT {
current_time: Duration,
) -> Result<JwsCompact, OperationError>;
fn jws_es256_jwks(&self) -> Option<JwkKeySet>;
fn jws_rs256_import(
&mut self,
import_keys: &SmolSet<[Vec<u8>; 1]>,
valid_from: Duration,
cid: &Cid,
) -> Result<(), OperationError>;
fn jws_rs256_assert(&mut self, valid_from: Duration, cid: &Cid) -> Result<(), OperationError>;
fn jws_rs256_sign(
&self,
jws: &Jws,
current_time: Duration,
) -> Result<JwsCompact, OperationError>;
fn jws_rs256_jwks(&self) -> Option<JwkKeySet>;
fn jws_verify(&self, jwsc: &JwsCompact) -> Result<Jws, OperationError>;
fn jws_public_jwk(&self, kid: &str) -> Result<Option<Jwk>, OperationError>;

View file

@ -532,6 +532,78 @@ impl QueryServerWriteTransaction<'_> {
self.reload()?;
// =========== OAuth2 Cryptography Migration ==============
debug!("START OAUTH2 MIGRATION");
// Load all the OAuth2 providers.
let all_oauth2_rs_entries = self.internal_search(filter!(f_eq(
Attribute::Class,
EntryClass::OAuth2ResourceServer.into()
)))?;
if !all_oauth2_rs_entries.is_empty() {
let entry_iter = all_oauth2_rs_entries.iter().map(|tgt_entry| {
let entry_uuid = tgt_entry.get_uuid();
let mut modlist = ModifyList::new_list(vec![
Modify::Present(Attribute::Class, EntryClass::KeyObject.to_value()),
Modify::Present(Attribute::Class, EntryClass::KeyObjectJwtEs256.to_value()),
Modify::Present(Attribute::Class, EntryClass::KeyObjectJweA128GCM.to_value()),
// Delete the fernet key, rs256 if any, and the es256 key
Modify::Purged(Attribute::OAuth2RsTokenKey),
Modify::Purged(Attribute::Es256PrivateKeyDer),
Modify::Purged(Attribute::Rs256PrivateKeyDer),
]);
trace!(?tgt_entry);
// Import the ES256 Key
if let Some(es256_private_der) =
tgt_entry.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
{
modlist.push_mod(Modify::Present(
Attribute::KeyActionImportJwsEs256,
Value::PrivateBinary(es256_private_der.to_vec()),
))
} else {
warn!("Unable to migrate es256 key");
}
let has_rs256 = tgt_entry
.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable)
.unwrap_or(false);
// If there is an rs256 key, import it.
// Import the RS256 Key
if has_rs256 {
modlist.push_mod(Modify::Present(
Attribute::Class,
EntryClass::KeyObjectJwtEs256.to_value(),
));
if let Some(rs256_private_der) =
tgt_entry.get_ava_single_private_binary(Attribute::Rs256PrivateKeyDer)
{
modlist.push_mod(Modify::Present(
Attribute::KeyActionImportJwsRs256,
Value::PrivateBinary(rs256_private_der.to_vec()),
))
} else {
warn!("Unable to migrate rs256 key");
}
}
(entry_uuid, modlist)
});
self.internal_batch_modify(entry_iter)?;
}
// Reload for new keys, and updated oauth2
self.reload()?;
// Done!
Ok(())
}

View file

@ -1251,6 +1251,7 @@ pub struct Oauth2Session {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeyUsage {
JwsEs256,
JwsRs256,
JweA128GCM,
}
@ -1261,6 +1262,7 @@ impl fmt::Display for KeyUsage {
"{}",
match self {
KeyUsage::JwsEs256 => "jws_es256",
KeyUsage::JwsRs256 => "jws_rs256",
KeyUsage::JweA128GCM => "jwe_a128gcm",
}
)

View file

@ -83,6 +83,7 @@ impl ValueSetKeyInternal {
let id: KeyId = id;
let usage = match usage {
DbValueKeyUsage::JwsEs256 => KeyUsage::JwsEs256,
DbValueKeyUsage::JwsRs256 => KeyUsage::JwsRs256,
DbValueKeyUsage::JweA128GCM => KeyUsage::JweA128GCM,
};
let status_cid = status_cid.into();
@ -131,6 +132,7 @@ impl ValueSetKeyInternal {
let id: String = id.clone();
let usage = match usage {
KeyUsage::JwsEs256 => DbValueKeyUsage::JwsEs256,
KeyUsage::JwsRs256 => DbValueKeyUsage::JwsRs256,
KeyUsage::JweA128GCM => DbValueKeyUsage::JweA128GCM,
};
let status_cid = status_cid.into();

View file

@ -1,9 +1,6 @@
#![deny(warnings)]
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryFrom;
use std::str::FromStr;
use compact_jwt::{JwkKeySet, JwsEs256Verifier, JwsVerifier, OidcToken, OidcUnverified};
use kanidm_client::{http::header, KanidmClient, StatusCode};
use kanidm_proto::constants::uri::{OAUTH2_AUTHORISE, OAUTH2_AUTHORISE_PERMIT};
use kanidm_proto::constants::*;
use kanidm_proto::internal::Oauth2ClaimMapJoin;
@ -14,18 +11,20 @@ use kanidm_proto::oauth2::{
};
use kanidmd_lib::constants::NAME_IDM_ALL_ACCOUNTS;
use kanidmd_lib::prelude::Attribute;
use oauth2_ext::PkceCodeChallenge;
use reqwest::header::{HeaderValue, CONTENT_TYPE};
use uri::{OAUTH2_TOKEN_ENDPOINT, OAUTH2_TOKEN_INTROSPECT_ENDPOINT, OAUTH2_TOKEN_REVOKE_ENDPOINT};
use url::{form_urlencoded::parse as query_parse, Url};
use kanidm_client::{http::header, KanidmClient, StatusCode};
use kanidmd_testkit::{
assert_no_cache, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, NOT_ADMIN_TEST_EMAIL,
NOT_ADMIN_TEST_PASSWORD, NOT_ADMIN_TEST_USERNAME, TEST_INTEGRATION_RS_DISPLAY,
TEST_INTEGRATION_RS_GROUP_ALL, TEST_INTEGRATION_RS_ID, TEST_INTEGRATION_RS_REDIRECT_URL,
TEST_INTEGRATION_RS_URL,
};
use oauth2_ext::PkceCodeChallenge;
use reqwest::header::{HeaderValue, CONTENT_TYPE};
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryFrom;
use std::str::FromStr;
use time::OffsetDateTime;
use uri::{OAUTH2_TOKEN_ENDPOINT, OAUTH2_TOKEN_INTROSPECT_ENDPOINT, OAUTH2_TOKEN_REVOKE_ENDPOINT};
use url::{form_urlencoded::parse as query_parse, Url};
/// Tests an OAuth 2.0 / OpenID confidential client Authorisation Client flow.
///
@ -91,10 +90,15 @@ async fn test_oauth2_openid_basic_flow_impl(
.expect("Failed to configure account password");
rsclient
.idm_oauth2_rs_update(TEST_INTEGRATION_RS_ID, None, None, None, true, true, true)
.idm_oauth2_rs_update(TEST_INTEGRATION_RS_ID, None, None, None, true)
.await
.expect("Failed to update oauth2 config");
rsclient
.idm_oauth2_rs_rotate_keys(TEST_INTEGRATION_RS_ID, OffsetDateTime::now_utc())
.await
.expect("Failed to rotate oauth2 keys");
rsclient
.idm_oauth2_rs_update_scope_map(
TEST_INTEGRATION_RS_ID,
@ -621,7 +625,7 @@ async fn test_oauth2_openid_public_flow_impl(
.expect("Failed to configure account password");
rsclient
.idm_oauth2_rs_update(TEST_INTEGRATION_RS_ID, None, None, None, true, true, true)
.idm_oauth2_rs_update(TEST_INTEGRATION_RS_ID, None, None, None, true)
.await
.expect("Failed to update oauth2 config");

View file

@ -1,9 +1,7 @@
#![deny(warnings)]
use std::path::Path;
use std::time::SystemTime;
use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifier};
use kanidm_client::{ClientError, KanidmClient};
use kanidm_proto::constants::{ATTR_GIDNUMBER, KSESSIONID};
use kanidm_proto::internal::{
ApiToken, CURegState, Filter, ImageValue, Modify, ModifyList, UatPurpose, UserAuthToken,
};
@ -13,19 +11,16 @@ use kanidm_proto::v1::{
};
use kanidmd_lib::constants::{NAME_IDM_ADMINS, NAME_SYSTEM_ADMINS};
use kanidmd_lib::credential::totp::Totp;
use kanidmd_lib::prelude::Attribute;
use tracing::{debug, trace};
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
use std::path::Path;
use std::str::FromStr;
use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifier};
use std::time::SystemTime;
use time::OffsetDateTime;
use tracing::{debug, trace};
use webauthn_authenticator_rs::softpasskey::SoftPasskey;
use webauthn_authenticator_rs::WebauthnAuthenticator;
use kanidm_client::{ClientError, KanidmClient};
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
const UNIX_TEST_PASSWORD: &str = "unix test user password";
#[kanidmd_testkit::test]
@ -851,8 +846,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: &KanidmClient) {
assert_eq!(initial_configs.len(), 1);
// Get the value. Assert we have oauth2_rs_basic_secret,
// but can NOT see the token_secret.
// Get the value. Assert we have oauth2_rs_basic_secret.
let oauth2_config = rsclient
.idm_oauth2_rs_get("test_integration")
.await
@ -866,10 +860,6 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: &KanidmClient) {
assert!(oauth2_config
.attrs
.contains_key(Attribute::OAuth2RsBasicSecret.as_str()));
// This is present, but redacted.
assert!(oauth2_config
.attrs
.contains_key(Attribute::OAuth2RsTokenKey.as_str()));
// Mod delete the secret/key and check them again.
// Check we can patch the oauth2_rs_name / oauth2_rs_origin
@ -880,12 +870,15 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: &KanidmClient) {
Some("Test Integration"),
Some("https://new_demo.example.com"),
true,
true,
true,
)
.await
.expect("Failed to update config");
rsclient
.idm_oauth2_rs_rotate_keys("test_integration", OffsetDateTime::now_utc())
.await
.expect("Failed to rotate oauth2 keys");
let oauth2_config_updated = rsclient
.idm_oauth2_rs_get("test_integration")
.await

View file

@ -73,6 +73,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
uuid = { workspace = true }
url = { workspace = true }
time = { workspace = true }
## See src/cli/webauthn/mod.rs for which features are
## required for which target_os here

View file

@ -1,12 +1,11 @@
use crate::common::OpType;
use crate::Oauth2ClaimMapJoin;
use crate::{handle_client_error, Oauth2Opt, OutputMode};
use anyhow::{Context, Error};
use kanidm_proto::internal::{ImageValue, Oauth2ClaimMapJoin as ProtoOauth2ClaimMapJoin};
use std::fs::read;
use std::process::exit;
use crate::Oauth2ClaimMapJoin;
use kanidm_proto::internal::{ImageValue, Oauth2ClaimMapJoin as ProtoOauth2ClaimMapJoin};
impl Oauth2Opt {
pub fn debug(&self) -> bool {
match self {
@ -47,7 +46,9 @@ impl Oauth2Opt {
| Oauth2Opt::EnableStrictRedirectUri { copt, .. }
| Oauth2Opt::DisableStrictRedirectUri { copt, .. }
| Oauth2Opt::AddOrigin { copt, .. }
| Oauth2Opt::RemoveOrigin { copt, .. } => copt.debug,
| Oauth2Opt::RemoveOrigin { copt, .. }
| Oauth2Opt::RotateCryptographicKeys { copt, .. }
| Oauth2Opt::RevokeCryptographicKey { copt, .. } => copt.debug,
}
}
@ -196,7 +197,7 @@ impl Oauth2Opt {
Oauth2Opt::ResetSecrets(cbopt) => {
let client = cbopt.copt.to_client(OpType::Write).await;
match client
.idm_oauth2_rs_update(cbopt.name.as_str(), None, None, None, true, true, true)
.idm_oauth2_rs_update(cbopt.name.as_str(), None, None, None, true)
.await
{
Ok(_) => println!("Success"),
@ -238,8 +239,6 @@ impl Oauth2Opt {
Some(cbopt.displayname.as_str()),
None,
false,
false,
false,
)
.await
{
@ -256,8 +255,6 @@ impl Oauth2Opt {
None,
None,
false,
false,
false,
)
.await
{
@ -268,15 +265,7 @@ impl Oauth2Opt {
Oauth2Opt::SetLandingUrl { nopt, url } => {
let client = nopt.copt.to_client(OpType::Write).await;
match client
.idm_oauth2_rs_update(
nopt.name.as_str(),
None,
None,
Some(url.as_str()),
false,
false,
false,
)
.idm_oauth2_rs_update(nopt.name.as_str(), None, None, Some(url.as_str()), false)
.await
{
Ok(_) => println!("Success"),
@ -522,6 +511,30 @@ impl Oauth2Opt {
Err(e) => handle_client_error(e, copt.output_mode),
}
}
Oauth2Opt::RotateCryptographicKeys {
copt,
name,
rotate_at,
} => {
let client = copt.to_client(OpType::Write).await;
match client
.idm_oauth2_rs_rotate_keys(name.as_str(), *rotate_at)
.await
{
Ok(_) => println!("Success"),
Err(e) => handle_client_error(e, copt.output_mode),
}
}
Oauth2Opt::RevokeCryptographicKey { copt, name, key_id } => {
let client = copt.to_client(OpType::Write).await;
match client
.idm_oauth2_rs_revoke_key(name.as_str(), key_id.as_str())
.await
{
Ok(_) => println!("Success"),
Err(e) => handle_client_error(e, copt.output_mode),
}
}
}
}
}

View file

@ -1,6 +1,12 @@
use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
use kanidm_proto::internal::ImageType;
use std::fmt;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
fn parse_rfc3339(input: &str) -> Result<OffsetDateTime, time::error::Parse > {
OffsetDateTime::parse(input, &Rfc3339)
}
#[derive(Debug, Args)]
pub struct Named {
@ -1131,8 +1137,9 @@ pub enum Oauth2Opt {
group: String,
},
#[clap(name = "reset-secrets")]
/// Reset the secrets associated to this client
#[clap(name = "reset-basic-secret")]
/// Reset the client basic secret. You will need to update your client after
/// executing this.
ResetSecrets(Named),
#[clap(name = "show-basic-secret")]
/// Show the associated basic secret for this client
@ -1143,7 +1150,7 @@ pub enum Oauth2Opt {
/// Set a new display name for a client
#[clap(name = "set-displayname")]
SetDisplayname(Oauth2SetDisplayname),
/// Set a new name for this client. You may need to update
/// Set a new name for this client. You will need to update
/// your integrated applications after this so that they continue to
/// function correctly.
#[clap(name = "set-name")]
@ -1256,6 +1263,28 @@ pub enum Oauth2Opt {
#[cfg(feature = "dev-oauth2-device-flow")]
/// Disable OAuth2 Device Flow authentication
DeviceFlowDisable(Named),
/// Rotate the signing and encryption keys used by this client. The rotation
/// will occur at the specified time, or immediately if the time is in the past.
/// Past signatures will continue to operate even after a rotation occurs. If you
/// have concerns a key is compromised, then you should revoke it instead.
#[clap(name = "rotate-cryptographic-keys")]
RotateCryptographicKeys {
#[clap(flatten)]
copt: CommonOpt,
name: String,
#[clap(value_parser = parse_rfc3339)]
rotate_at: OffsetDateTime,
},
/// Revoke the signing and encryption keys used by this client. This will immediately
/// trigger a rotation of the key in question, and signtatures or tokens issued by
/// the revoked key will not be considered valid.
#[clap(name = "revoke-cryptographic-key")]
RevokeCryptographicKey {
#[clap(flatten)]
copt: CommonOpt,
name: String,
key_id: String,
},
}
#[derive(Args, Debug)]