mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-03 07:35:05 +02:00
Drop fernet in favour of JWE
This drops the use of fernet from OAuth2 in favour of JWE. To achieve this cleanly, we swap OAuth2 to using our internel key object handler so that in future we can consider the use of pkcs11 devices. This also makes it easier in general to handle any future cryptographic changes.
This commit is contained in:
parent
be4818e121
commit
7236bee837
Cargo.lockCargo.toml
libs/client/src
proto/src
server
Dockerfile
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()),
|
||||
|
|
|
@ -65,6 +65,8 @@ RUN <<EOF
|
|||
ls -Rla /out/libs-root
|
||||
EOF
|
||||
|
||||
RUN ls /usr/src/kanidm/target/release/kanidmd
|
||||
|
||||
# ======================
|
||||
|
||||
FROM scratch
|
||||
|
|
|
@ -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