mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-20 16:03:55 +02:00
Merge 1d2a61f86a
into be4818e121
This commit is contained in:
commit
db69bf5a0f
Cargo.lockCargo.toml
libs/client/src
proto/src
server
daemon
lib
testkit/tests/testkit
tools/cli
289
Cargo.lock
generated
289
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -692,6 +692,7 @@ pub enum DbValueImage {
|
|||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum DbValueKeyUsage {
|
||||
JwsEs256,
|
||||
JwsRs256,
|
||||
JweA128GCM,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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![
|
||||
|
|
|
@ -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(),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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)]
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Reference in a new issue