mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-22 20:26:30 +01:00
Rework deps (#1079)
This commit is contained in:
parent
8e0238ab97
commit
821b2c05c4
11
.rustfmt.toml
Normal file
11
.rustfmt.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
reorder_imports = true
|
||||
|
||||
## Requires nightly
|
||||
# imports_granularity = "Module"
|
||||
# group_imports = "StdExternalCrate"
|
||||
# format_code_in_doc_comments = true
|
||||
# format_macro_bodies = true
|
||||
# reorder_impl_items = true
|
||||
|
||||
|
255
Cargo.lock
generated
255
Cargo.lock
generated
|
@ -82,6 +82,21 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc-no-stdlib"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||
|
||||
[[package]]
|
||||
name = "alloc-stdlib"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
|
@ -186,6 +201,7 @@ version = "0.3.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"flate2",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
|
@ -578,6 +594,27 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "3.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
|
@ -602,6 +639,12 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -753,6 +796,12 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "compact_jwt"
|
||||
version = "0.2.8"
|
||||
|
@ -850,9 +899,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
||||
checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time 0.3.14",
|
||||
|
@ -865,7 +914,7 @@ version = "0.16.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd"
|
||||
dependencies = [
|
||||
"cookie 0.16.0",
|
||||
"cookie 0.16.1",
|
||||
"idna 0.2.3",
|
||||
"log",
|
||||
"publicsuffix",
|
||||
|
@ -1991,6 +2040,19 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
|
@ -2061,6 +2123,20 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
|
@ -2174,7 +2250,7 @@ dependencies = [
|
|||
"rusqlite",
|
||||
"saffron",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_cbor_2",
|
||||
"serde_json",
|
||||
"sketching",
|
||||
"smartstring",
|
||||
|
@ -2664,6 +2740,28 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
|
@ -2724,6 +2822,7 @@ dependencies = [
|
|||
"getrandom 0.2.7",
|
||||
"http",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
|
@ -2773,9 +2872,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.41"
|
||||
version = "0.10.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
|
||||
checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
|
@ -2805,9 +2904,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.75"
|
||||
version = "0.9.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
|
||||
checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
|
@ -2919,6 +3018,24 @@ version = "2.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phonenumber"
|
||||
version = "0.3.1+8.12.9"
|
||||
|
@ -3085,9 +3202,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -3124,6 +3241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f"
|
||||
dependencies = [
|
||||
"checked_int_cast",
|
||||
"image",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3341,7 +3459,7 @@ checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
|
|||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"bytes",
|
||||
"cookie 0.16.0",
|
||||
"cookie 0.16.1",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
|
@ -3350,6 +3468,7 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
|
@ -3360,19 +3479,38 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite 0.2.9",
|
||||
"proc-macro-hack",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "route-recognizer"
|
||||
version = "0.2.0"
|
||||
|
@ -3443,6 +3581,27 @@ dependencies = [
|
|||
"nom 7.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -3511,7 +3670,6 @@ version = "1.1.0-alpha.9"
|
|||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"base64 0.13.0",
|
||||
"compact_jwt",
|
||||
"futures",
|
||||
"futures-util",
|
||||
|
@ -3541,6 +3699,16 @@ dependencies = [
|
|||
"webauthn-authenticator-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.7.0"
|
||||
|
@ -3800,9 +3968,15 @@ dependencies = [
|
|||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "sketching"
|
||||
version = "0.1.0"
|
||||
version = "1.1.0-alpha.9"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"num_enum",
|
||||
|
@ -3861,6 +4035,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "sptr"
|
||||
version = "0.3.2"
|
||||
|
@ -3965,9 +4145,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.100"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4027,18 +4207,18 @@ checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
|
||||
checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
|
||||
checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4086,6 +4266,7 @@ dependencies = [
|
|||
"async-compression",
|
||||
"futures-lite",
|
||||
"http-types",
|
||||
"phf",
|
||||
"regex",
|
||||
"tide",
|
||||
]
|
||||
|
@ -4273,6 +4454,17 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.4"
|
||||
|
@ -4441,6 +4633,12 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
@ -4774,6 +4972,25 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-ffi"
|
||||
version = "0.1.2"
|
||||
|
|
122
Cargo.toml
122
Cargo.toml
|
@ -17,20 +17,131 @@ members = [
|
|||
"kanidmd/score",
|
||||
"orca",
|
||||
"profiles",
|
||||
"sketching",
|
||||
"sketching"
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"kanidm_unix_int/pam_tester"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = [
|
||||
"William Brown <william@blackhats.net.au>",
|
||||
"James Hodgkinson <james@terminaloutcomes.com>",
|
||||
]
|
||||
rust-version = "1.64"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-std = { version = "^1.12.0", features = ["tokio1"] }
|
||||
async-trait = "^0.1.57"
|
||||
base32 = "^0.4.0"
|
||||
base64 = "^0.13.0"
|
||||
base64urlsafedata = "0.1.0"
|
||||
bytes = "^1.1.0"
|
||||
clap = { version = "^3.2", features = ["derive"] }
|
||||
clap_complete = "^3.2.5"
|
||||
# Forced by saffron
|
||||
chrono = "^0.4.20"
|
||||
compact_jwt = "^0.2.3"
|
||||
# compact_jwt = { path = "../compact_jwt" }
|
||||
concread = "^0.4.0"
|
||||
# concread = { path = "../concread" }
|
||||
crossbeam = "0.8.1"
|
||||
criterion = "^0.4.0"
|
||||
csv = "1.1.6"
|
||||
dialoguer = "0.10.1"
|
||||
dyn-clone = "^1.0.9"
|
||||
fernet = "^0.2.0"
|
||||
filetime = "^0.2.17"
|
||||
futures = "^0.3.21"
|
||||
futures-util = "^0.3.21"
|
||||
gloo = "^0.8.0"
|
||||
gloo-net = "0.2.4"
|
||||
hashbrown = { version = "0.12.3", features = ["serde", "inline-more", "ahash"] }
|
||||
http-types = "^2.12.0"
|
||||
idlset = "^0.2.4"
|
||||
# idlset = { path = "../idlset" }
|
||||
# ldap3_server = { path = "../ldap3_server" }
|
||||
js-sys = "^0.3.58"
|
||||
# RENAME THIS
|
||||
kanidm = { path = "./kanidmd/idm" }
|
||||
kanidm_client = { path = "./kanidm_client" }
|
||||
kanidm_proto = { path = "./kanidm_proto" }
|
||||
kanidm_unix_int = { path = "./kanidm_unix_int" }
|
||||
last-git-commit = "0.2.0"
|
||||
# REMOVE this
|
||||
lazy_static = "^1.4.0"
|
||||
ldap3_proto = "^0.2.3"
|
||||
libc = "^0.2.127"
|
||||
libnss = "^0.4.0"
|
||||
libsqlite3-sys = "^0.25.0"
|
||||
lru = "^0.8.0"
|
||||
mathru = "^0.13.0"
|
||||
num_enum = "^0.5.7"
|
||||
oauth2_ext = { version = "^4.1.0", package = "oauth2" }
|
||||
openssl = "^0.10.41"
|
||||
paste = "^1.0.9"
|
||||
pkg-config = "^0.3.25"
|
||||
profiles = { path = "./profiles" }
|
||||
qrcode = "^0.12.0"
|
||||
r2d2 = "^0.8.9"
|
||||
r2d2_sqlite = "^0.21.0"
|
||||
rand = "^0.8.5"
|
||||
# try to remove this
|
||||
rayon = "^1.5.3"
|
||||
regex = "1.5.6"
|
||||
reqwest = "0.11.11"
|
||||
rpassword = "^7.0.0"
|
||||
rusqlite = "^0.28.0"
|
||||
saffron = "^0.1.0"
|
||||
# Rename this!
|
||||
score = { path = "./kanidmd/score" }
|
||||
serde = "^1.0.142"
|
||||
serde_cbor = { version = "0.12.0-dev", package = "serde_cbor_2" }
|
||||
serde_json = "^1.0.83"
|
||||
serde-wasm-bindgen = "0.4"
|
||||
shellexpand = "^2.1.2"
|
||||
sketching = { path = "./sketching" }
|
||||
smartstring = "^1.0.1"
|
||||
smolset = "^1.3.1"
|
||||
sshkeys = "^0.3.1"
|
||||
|
||||
tide = "^0.16.0"
|
||||
tide-compress = "0.10.6"
|
||||
tide-openssl = "^0.1.1"
|
||||
|
||||
# Unable to increase version due to removing ability to detect
|
||||
# local platform time.
|
||||
time = "=0.2.27"
|
||||
|
||||
tikv-jemallocator = "0.5"
|
||||
|
||||
tokio = "^1.21.1"
|
||||
tokio-openssl = "^0.6.3"
|
||||
tokio-util = "^0.7.4"
|
||||
|
||||
toml = "^0.5.9"
|
||||
touch = "^0.0.1"
|
||||
tracing = { version = "^0.1.35", features = ["max_level_trace", "release_max_level_debug"] }
|
||||
tracing-subscriber = { version = "^0.3.14", features = ["env-filter"] }
|
||||
|
||||
# tracing-forest = { path = "/Users/william/development/tracing-forest/tracing-forest" }
|
||||
tracing-forest = { git = "https://github.com/QnnOkabayashi/tracing-forest.git", rev = "48d78f7294ceee47a22eee5c80964143c4fb3fe1" }
|
||||
|
||||
url = "^2.3.1"
|
||||
urlencoding = "2.1.2"
|
||||
users = "^0.11.0"
|
||||
uuid = "^1.1.2"
|
||||
|
||||
validator = "^0.16.0"
|
||||
|
||||
wasm-bindgen = "^0.2.81"
|
||||
wasm-bindgen-futures = "^0.4.30"
|
||||
wasm-bindgen-test = "0.3.33"
|
||||
|
||||
webauthn-authenticator-rs = "0.4.7"
|
||||
webauthn-rs = "0.4.7"
|
||||
|
@ -40,6 +151,13 @@ webauthn-rs-proto = "0.4.7"
|
|||
# webauthn-rs = { path = "../webauthn-rs/webauthn-rs" }
|
||||
# webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" }
|
||||
# webauthn-rs-proto = { path = "../webauthn-rs/webauthn-rs-proto" }
|
||||
web-sys = "^0.3.60"
|
||||
whoami = "^1.2.3"
|
||||
|
||||
yew = "^0.19.3"
|
||||
yew-agent = "^0.1.0"
|
||||
yew-router = "^0.16.0"
|
||||
zxcvbn = "^2.2.1"
|
||||
|
||||
# enshrinken the WASMs
|
||||
[profile.release.package.kanidmd_web_ui]
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
[package]
|
||||
name = "kanidm_client"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.64"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Client Library"
|
||||
documentation = "https://docs.rs/kanidm_client/latest/kanidm_client/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tracing = "^0.1.35"
|
||||
reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] }
|
||||
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" }
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
serde_json = "^1.0.83"
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
tokio = { version = "^1.21.1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
|
||||
toml = "^0.5.9"
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4"] }
|
||||
url = { version = "^2.3.1", features = ["serde"] }
|
||||
tracing.workspace = true
|
||||
reqwest = { workspace = true, features=["cookies", "json", "native-tls"] }
|
||||
kanidm_proto.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
time = { workspace = true, features = ["serde", "std"] }
|
||||
tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] }
|
||||
toml.workspace = true
|
||||
uuid = { workspace = true, features = ["serde", "v4"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
webauthn-rs-proto = { workspace = true, features = ["wasm"] }
|
||||
|
||||
|
|
|
@ -13,32 +13,26 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::error::Error as SerdeJsonError;
|
||||
use std::collections::{BTreeMap, BTreeSet as Set};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
#[cfg(target_family = "unix")] // not needed for windows builds
|
||||
use std::fs::{metadata, Metadata};
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Read;
|
||||
|
||||
use std::io::{ErrorKind, Read};
|
||||
#[cfg(target_family = "unix")] // not needed for windows builds
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet as Set;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::*;
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
pub use reqwest::StatusCode;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::error::Error as SerdeJsonError;
|
||||
use tokio::sync::RwLock;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use reqwest::StatusCode;
|
||||
|
||||
use kanidm_proto::v1::*;
|
||||
use webauthn_rs_proto::{
|
||||
PublicKeyCredential, RegisterPublicKeyCredential, RequestChallengeResponse,
|
||||
};
|
||||
|
@ -1767,6 +1761,7 @@ impl KanidmClient {
|
|||
self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_oauth2_rs_prefer_spn_username(&self, id: &str) -> Result<(), ClientError> {
|
||||
let mut update_oauth2_rs = Entry {
|
||||
attrs: BTreeMap::new(),
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use crate::ClientError;
|
||||
use crate::KanidmClient;
|
||||
use kanidm_proto::v1::AccountUnixExtend;
|
||||
use kanidm_proto::v1::CredentialStatus;
|
||||
use kanidm_proto::v1::Entry;
|
||||
use kanidm_proto::v1::SingleStringRequest;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use kanidm_proto::v1::{AccountUnixExtend, CredentialStatus, Entry, SingleStringRequest};
|
||||
|
||||
use crate::{ClientError, KanidmClient};
|
||||
|
||||
impl KanidmClient {
|
||||
pub async fn idm_person_account_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/person").await
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use crate::ClientError;
|
||||
use crate::KanidmClient;
|
||||
use kanidm_proto::v1::AccountUnixExtend;
|
||||
use kanidm_proto::v1::CredentialStatus;
|
||||
use kanidm_proto::v1::Entry;
|
||||
use kanidm_proto::v1::{ApiToken, ApiTokenGenerate};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use kanidm_proto::v1::{AccountUnixExtend, ApiToken, ApiTokenGenerate, CredentialStatus, Entry};
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{ClientError, KanidmClient};
|
||||
|
||||
impl KanidmClient {
|
||||
pub async fn idm_service_account_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/service_account").await
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
[package]
|
||||
name = "kanidm_proto"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.64"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Protocol Bindings for serde"
|
||||
documentation = "https://docs.rs/kanidm_proto/latest/kanidm_proto/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
wasm = ["webauthn-rs-proto/wasm"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
base32 = "^0.4.0"
|
||||
base64urlsafedata = "0.1.0"
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
serde_json = "^1.0.83"
|
||||
# Can not upgrade due to breaking timezone apis.
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
url = { version = "^2.3.1", features = ["serde"] }
|
||||
urlencoding = "2.1.2"
|
||||
uuid = { version = "^1.1.2", features = ["serde"] }
|
||||
base32.workspace = true
|
||||
base64urlsafedata.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
time = { workspace = true, features = ["serde", "std"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
urlencoding.workspace = true
|
||||
uuid = { workspace = true, features = ["serde"] }
|
||||
webauthn-rs-proto.workspace = true
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
last-git-commit = "0.2.0"
|
||||
last-git-commit.workspace = true
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// User-facing output things
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This is used in user-facing CLIs to set the formatting for output,
|
||||
/// and defaults to text.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -21,6 +22,7 @@ impl Default for ConsoleOutputMode {
|
|||
|
||||
impl FromStr for ConsoleOutputMode {
|
||||
type Err = &'static str;
|
||||
|
||||
/// This can be safely unwrap'd because it'll always return a default of text
|
||||
/// ```
|
||||
/// use kanidm_proto::messages::ConsoleOutputMode;
|
||||
|
@ -141,7 +143,6 @@ impl Default for AccountChangeMessage {
|
|||
/// msg.output_mode = ConsoleOutputMode::JSON;
|
||||
/// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\",\"src_user\":\"Kani\",\"dest_user\":\"Krabby\"}";
|
||||
/// assert_eq!(format!("{}", msg), expected_result);
|
||||
///
|
||||
/// ```
|
||||
impl fmt::Display for AccountChangeMessage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -149,7 +150,7 @@ impl fmt::Display for AccountChangeMessage {
|
|||
ConsoleOutputMode::JSON => write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string(self).unwrap_or(format!("{:?}", self)) // if it fails to JSON serialize, just debug-dump it
|
||||
serde_json::to_string(self).unwrap_or(format!("{:?}", self)) /* if it fails to JSON serialize, just debug-dump it */
|
||||
),
|
||||
ConsoleOutputMode::Text => write!(
|
||||
f,
|
||||
|
@ -182,20 +183,20 @@ impl Default for BasicMessage {
|
|||
|
||||
/// This outputs in either JSON or Text depending on the output_mode setting
|
||||
/// ```
|
||||
/// use std::fmt::format;
|
||||
/// use kanidm_proto::messages::*;
|
||||
/// use std::fmt::format;
|
||||
/// let mut msg = BasicMessage::default();
|
||||
/// msg.action=String::from("cake_eating");
|
||||
/// msg.result=String::from("It was amazing");
|
||||
/// msg.action = String::from("cake_eating");
|
||||
/// msg.result = String::from("It was amazing");
|
||||
/// assert_eq!(msg.status, MessageStatus::Success);
|
||||
///
|
||||
/// let expected_result = "success - cake_eating: It was amazing";
|
||||
/// assert_eq!(format!("{}", msg), expected_result);
|
||||
///
|
||||
/// msg.output_mode = ConsoleOutputMode::JSON;
|
||||
/// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\"}";
|
||||
/// let expected_result =
|
||||
/// "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\"}";
|
||||
/// assert_eq!(format!("{}", msg), expected_result);
|
||||
///
|
||||
/// ```
|
||||
impl fmt::Display for BasicMessage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -203,7 +204,7 @@ impl fmt::Display for BasicMessage {
|
|||
ConsoleOutputMode::JSON => write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string(self).unwrap_or(format!("{:?}", self)) // if it fails to JSON serialize, just debug-dump it
|
||||
serde_json::to_string(self).unwrap_or(format!("{:?}", self)) /* if it fails to JSON serialize, just debug-dump it */
|
||||
),
|
||||
ConsoleOutputMode::Text => {
|
||||
write!(f, "{} - {}: {}", self.status, self.action, self.result,)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use base64urlsafedata::Base64UrlSafeData;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs_proto::{
|
||||
CreationChallengeResponse, PublicKeyCredential, RegisterPublicKeyCredential,
|
||||
|
@ -1068,8 +1068,7 @@ impl SingleStringRequest {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::v1::Filter as ProtoFilter;
|
||||
use crate::v1::{TotpAlgo, TotpSecret};
|
||||
use crate::v1::{Filter as ProtoFilter, TotpAlgo, TotpSecret};
|
||||
|
||||
#[test]
|
||||
fn test_protofilter_simple() {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "kanidm_tools"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.64"
|
||||
edition = "2021"
|
||||
default-run = "kanidm"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Client Tools"
|
||||
documentation = "https://docs.rs/kanidm_tools/latest/kanidm_tools/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "kanidm_cli"
|
||||
|
@ -29,28 +30,28 @@ name = "kanidm_badlist_preprocess"
|
|||
path = "src/badlist_preprocess.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||
compact_jwt = "^0.2.3"
|
||||
dialoguer = "^0.10.1"
|
||||
libc = "^0.2.127"
|
||||
kanidm_client = { path = "../kanidm_client", version = "1.1.0-alpha.8" }
|
||||
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" }
|
||||
qrcode = { version = "^0.12.0", default-features = false }
|
||||
rayon = "^1.5.3"
|
||||
rpassword = "^7.0.0"
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
serde_json = "^1.0.83"
|
||||
shellexpand = "^2.1.2"
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
tracing = "^0.1.35"
|
||||
tracing-subscriber = { version = "^0.3.14", features = ["env-filter", "fmt"] }
|
||||
tokio = { version = "^1.21.1", features = ["rt", "macros"] }
|
||||
url = { version = "^2.3.1", features = ["serde"] }
|
||||
uuid = "^1.1.2"
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
compact_jwt.workspace = true
|
||||
dialoguer.workspace = true
|
||||
libc.workspace = true
|
||||
kanidm_client.workspace = true
|
||||
kanidm_proto.workspace = true
|
||||
qrcode = { workspace = true, default-features = false }
|
||||
rayon.workspace = true
|
||||
rpassword.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
shellexpand.workspace = true
|
||||
time = { workspace = true, features = ["serde", "std"] }
|
||||
tracing.workspace = true
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
|
||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
uuid.workspace = true
|
||||
webauthn-authenticator-rs = { workspace = true, features = ["u2fhid"] }
|
||||
zxcvbn = "^2.2.1"
|
||||
zxcvbn.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "^3.2", features = ["derive"] }
|
||||
clap_complete = { version = "^3.2.5"}
|
||||
uuid = "^1.1.2"
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete.workspace = true
|
||||
uuid.workspace = true
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use uuid::Uuid;
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use clap_complete::{generate_to, Shell};
|
||||
use uuid::Uuid;
|
||||
|
||||
include!("src/opt/ssh_authorizedkeys.rs");
|
||||
include!("src/opt/badlist_preprocess.rs");
|
||||
|
|
|
@ -14,9 +14,8 @@ use std::io::BufWriter;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use kanidm_proto::v1::Modify;
|
||||
|
||||
use clap::Parser;
|
||||
use kanidm_proto::v1::Modify;
|
||||
use rayon::prelude::*;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
use crate::session::read_tokens;
|
||||
use crate::CommonOpt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use dialoguer::{theme::ColorfulTheme, Select};
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use dialoguer::Select;
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME};
|
||||
use kanidm_proto::v1::UserAuthToken;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::session::read_tokens;
|
||||
use crate::CommonOpt;
|
||||
|
||||
impl CommonOpt {
|
||||
pub fn to_unauth_client(&self) -> KanidmClient {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
extern crate tracing;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
include!("../opt/kanidm.rs");
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
use clap::Parser;
|
||||
use kanidm_cli::KanidmClientParser;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
use crate::password_prompt;
|
||||
use crate::{
|
||||
AccountCredential, AccountRadius, AccountSsh, AccountValidity, PersonOpt, PersonPosix,
|
||||
};
|
||||
use dialoguer::{theme::ColorfulTheme, Select};
|
||||
use dialoguer::{Confirm, Input, Password};
|
||||
use std::fmt::{self, Debug};
|
||||
use std::str::FromStr;
|
||||
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use dialoguer::{Confirm, Input, Password, Select};
|
||||
use kanidm_client::ClientError::Http as ClientErrorHttp;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use kanidm_proto::v1::OperationError::PasswordQuality;
|
||||
use kanidm_proto::v1::{CUIntentToken, CURegState, CUSessionToken, CUStatus, TotpSecret};
|
||||
use qrcode::{render::unicode, QrCode};
|
||||
use std::fmt::{self, Debug};
|
||||
use std::str::FromStr;
|
||||
use qrcode::render::unicode;
|
||||
use qrcode::QrCode;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
use webauthn_authenticator_rs::u2fhid::U2FHid;
|
||||
use webauthn_authenticator_rs::WebauthnAuthenticator;
|
||||
|
||||
use webauthn_authenticator_rs::{u2fhid::U2FHid, WebauthnAuthenticator};
|
||||
use crate::{
|
||||
password_prompt, AccountCredential, AccountRadius, AccountSsh, AccountValidity, PersonOpt,
|
||||
PersonPosix,
|
||||
};
|
||||
|
||||
impl PersonOpt {
|
||||
pub fn debug(&self) -> bool {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use crate::RawOpt;
|
||||
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::RawOpt;
|
||||
|
||||
fn read_file<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T, Box<dyn Error>> {
|
||||
let f = File::open(path)?;
|
||||
let r = BufReader::new(f);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::{
|
||||
AccountSsh, AccountValidity, ServiceAccountApiToken, ServiceAccountCredential,
|
||||
ServiceAccountOpt, ServiceAccountPosix,
|
||||
};
|
||||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
impl ServiceAccountOpt {
|
||||
pub fn debug(&self) -> bool {
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
use crate::common::prompt_for_username_get_username;
|
||||
use crate::{LoginOpt, LogoutOpt, SessionOpt};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{create_dir, File};
|
||||
use std::io::{self, BufReader, BufWriter, ErrorKind, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use compact_jwt::JwsUnverified;
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use dialoguer::Select;
|
||||
use kanidm_client::{ClientError, KanidmClient};
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState, UserAuthToken};
|
||||
#[cfg(target_family = "unix")]
|
||||
use libc::umask;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{create_dir, File};
|
||||
use std::io::ErrorKind;
|
||||
use std::io::{self, BufReader, BufWriter, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use webauthn_authenticator_rs::{
|
||||
prelude::RequestChallengeResponse, u2fhid::U2FHid, WebauthnAuthenticator,
|
||||
};
|
||||
use webauthn_authenticator_rs::prelude::RequestChallengeResponse;
|
||||
use webauthn_authenticator_rs::u2fhid::U2FHid;
|
||||
use webauthn_authenticator_rs::WebauthnAuthenticator;
|
||||
|
||||
use dialoguer::{theme::ColorfulTheme, Select};
|
||||
|
||||
use compact_jwt::JwsUnverified;
|
||||
use crate::common::prompt_for_username_get_username;
|
||||
use crate::{LoginOpt, LogoutOpt, SessionOpt};
|
||||
|
||||
static TOKEN_DIR: &str = "~/.cache";
|
||||
static TOKEN_PATH: &str = "~/.cache/kanidm_tokens";
|
||||
|
|
|
@ -12,7 +12,6 @@ use std::path::PathBuf;
|
|||
|
||||
use clap::Parser;
|
||||
use kanidm_client::{ClientError, KanidmClientBuilder};
|
||||
|
||||
use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME};
|
||||
use tracing::{debug, error};
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
[package]
|
||||
name = "kanidm_unix_int"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.64"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Unix Integration Clients"
|
||||
documentation = "https://docs.rs/kanidm/latest/kanidm/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "kanidm_unix_common"
|
||||
|
@ -43,46 +44,42 @@ name = "kanidm_test_auth"
|
|||
path = "src/test_auth.rs"
|
||||
|
||||
[dependencies]
|
||||
kanidm_client = { path = "../kanidm_client" }
|
||||
kanidm_proto = { path = "../kanidm_proto" }
|
||||
kanidm = { path = "../kanidmd/idm" }
|
||||
bytes.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
futures.workspace = true
|
||||
libc.workspace = true
|
||||
libsqlite3-sys.workspace = true
|
||||
lru.workspace = true
|
||||
kanidm_client.workspace = true
|
||||
kanidm_proto.workspace = true
|
||||
# This is just used for password hashing and tests, so we could
|
||||
# clean this up
|
||||
kanidm.workspace = true
|
||||
|
||||
tracing = "^0.1.35"
|
||||
sketching = { path = "../sketching" }
|
||||
r2d2.workspace = true
|
||||
r2d2_sqlite.workspace = true
|
||||
rpassword.workspace = true
|
||||
rusqlite.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
sketching.workspace = true
|
||||
|
||||
toml = "^0.5.9"
|
||||
rpassword = "^7.0.0"
|
||||
tokio = { version = "^1.21.1", features = ["rt", "macros", "sync", "time", "net", "io-util"] }
|
||||
tokio-util = { version = "^0.7.4", features = ["codec"] }
|
||||
|
||||
futures = "^0.3.21"
|
||||
bytes = "^1.1.0"
|
||||
|
||||
libc = "^0.2.127"
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
serde_json = "^1.0.83"
|
||||
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||
|
||||
libsqlite3-sys = "0.25.0"
|
||||
rusqlite = "^0.28.0"
|
||||
r2d2 = "^0.8.10"
|
||||
r2d2_sqlite = "^0.21.0"
|
||||
|
||||
reqwest = "^0.11.11"
|
||||
|
||||
users = "^0.11.0"
|
||||
|
||||
lru = "^0.8.0"
|
||||
toml.workspace = true
|
||||
tokio = { workspace = true, features = ["rt", "macros", "sync", "time", "net", "io-util"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
tracing.workspace = true
|
||||
reqwest.workspace = true
|
||||
users.workspace = true
|
||||
|
||||
[features]
|
||||
# default = [ "libsqlite3-sys/bundled" ]
|
||||
|
||||
[dev-dependencies]
|
||||
# kanidm = { path = "../kanidmd/idm" }
|
||||
score = { path = "../kanidmd/score" }
|
||||
score.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "^3.2", features = ["derive"] }
|
||||
clap_complete = "^3.2.5"
|
||||
profiles = { path = "../profiles" }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete.workspace = true
|
||||
profiles.workspace = true
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#![allow(dead_code)]
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{IntoApp, Parser};
|
||||
use clap_complete::{generate_to, Shell};
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
include!("src/opt/ssh_authorizedkeys.rs");
|
||||
include!("src/opt/cache_invalidate.rs");
|
||||
include!("src/opt/cache_clear.rs");
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
[package]
|
||||
name = "nss_kanidm"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "nss_kanidm"
|
||||
|
@ -11,9 +15,9 @@ crate-type = [ "cdylib" ]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
kanidm_unix_int = { path = "../" }
|
||||
libnss = "^0.4.0"
|
||||
libc = "^0.2.127"
|
||||
paste = "^1.0.9"
|
||||
lazy_static = "^1.4.0"
|
||||
kanidm_unix_int.workspace = true
|
||||
libnss.workspace = true
|
||||
libc.workspace = true
|
||||
paste.workspace = true
|
||||
lazy_static.workspace = true
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ use kanidm_unix_common::client_sync::call_daemon_blocking;
|
|||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, NssGroup, NssUser};
|
||||
|
||||
use libnss::group::{Group, GroupHooks};
|
||||
use libnss::interop::Response;
|
||||
use libnss::passwd::{Passwd, PasswdHooks};
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
[package]
|
||||
name = "pam_kanidm"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
links = "pam"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "pam_kanidm"
|
||||
crate-type = [ "cdylib" ]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
kanidm_unix_int = { path = "../" }
|
||||
libc = "^0.2.127"
|
||||
kanidm_unix_int.workspace = true
|
||||
libc.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
pkg-config = "^0.3.25"
|
||||
pkg-config.workspace = true
|
||||
|
|
|
@ -14,19 +14,20 @@
|
|||
// extern crate libc;
|
||||
|
||||
mod pam;
|
||||
use crate::pam::constants::*;
|
||||
use crate::pam::conv::PamConv;
|
||||
use crate::pam::module::{PamHandle, PamHooks};
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::CStr;
|
||||
|
||||
// use std::os::raw::c_char;
|
||||
use kanidm_unix_common::client_sync::call_daemon_blocking;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse};
|
||||
|
||||
use crate::pam::constants::*;
|
||||
use crate::pam::conv::PamConv;
|
||||
use crate::pam::module::{PamHandle, PamHooks};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Options {
|
||||
debug: bool,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use libc::{c_char, c_int};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr;
|
||||
|
||||
use crate::pam::constants::PamResultCode;
|
||||
use crate::pam::constants::*;
|
||||
use libc::{c_char, c_int};
|
||||
|
||||
use crate::pam::constants::{PamResultCode, *};
|
||||
use crate::pam::module::{PamItem, PamResult};
|
||||
|
||||
#[allow(missing_copy_implementations)]
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
/// Here is full example of a PAM module that would authenticate and authorize everybody:
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use] extern crate pam;
|
||||
/// #[macro_use]
|
||||
/// extern crate pam;
|
||||
///
|
||||
/// use pam::module::{PamHooks, PamHandle};
|
||||
/// use pam::constants::{PamResultCode, PamFlag};
|
||||
/// use pam::constants::{PamFlag, PamResultCode};
|
||||
/// use pam::module::{PamHandle, PamHooks};
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// # fn main() {}
|
||||
|
@ -18,15 +19,15 @@
|
|||
/// pam_hooks!(MyPamModule);
|
||||
///
|
||||
/// impl PamHooks for MyPamModule {
|
||||
/// fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||
/// println!("Everybody is authenticated!");
|
||||
/// PamResultCode::PAM_SUCCESS
|
||||
/// }
|
||||
/// fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||
/// println!("Everybody is authenticated!");
|
||||
/// PamResultCode::PAM_SUCCESS
|
||||
/// }
|
||||
///
|
||||
/// fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||
/// println!("Everybody is authorized!");
|
||||
/// PamResultCode::PAM_SUCCESS
|
||||
/// }
|
||||
/// fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||
/// println!("Everybody is authorized!");
|
||||
/// PamResultCode::PAM_SUCCESS
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
|
@ -36,6 +37,7 @@ macro_rules! pam_hooks {
|
|||
mod pam_hooks_scope {
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use $crate::pam::constants::{PamFlag, PamResultCode};
|
||||
use $crate::pam::module::{PamHandle, PamHooks};
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Functions for use in pam modules.
|
||||
|
||||
use libc::c_char;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use libc::c_char;
|
||||
|
||||
use crate::pam::constants::{PamFlag, PamItemType, PamResultCode, PAM_AUTHTOK};
|
||||
|
||||
/// Opaque type, used as a pointer when making pam API calls.
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
[package]
|
||||
name = "pam_tester"
|
||||
version = "0.1.2"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pam = "^0.7.0"
|
||||
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
use crate::db::Db;
|
||||
use crate::unix_config::{HomeAttr, UidAttr};
|
||||
use crate::unix_proto::{HomeDirectoryInfo, NssGroup, NssUser};
|
||||
use kanidm_client::ClientError;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
|
||||
use lru::LruCache;
|
||||
use reqwest::StatusCode;
|
||||
use std::collections::BTreeSet;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::path::Path;
|
||||
use std::string::ToString;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use kanidm_client::{ClientError, KanidmClient};
|
||||
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
|
||||
use lru::LruCache;
|
||||
use reqwest::StatusCode;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
|
||||
use crate::db::Db;
|
||||
use crate::unix_config::{HomeAttr, UidAttr};
|
||||
use crate::unix_proto::{HomeDirectoryInfo, NssGroup, NssUser};
|
||||
|
||||
const NXCACHE_SIZE: usize = 2048;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
extern crate tracing;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use futures::executor::block_on;
|
||||
|
||||
use kanidm_unix_common::client::call_daemon;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
extern crate tracing;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use futures::executor::block_on;
|
||||
|
||||
use kanidm_unix_common::client::call_daemon;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use bytes::{BufMut, BytesMut};
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
use std::error::Error;
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use tokio::net::UnixStream;
|
||||
// use tokio::runtime::Builder;
|
||||
use tokio_util::codec::Framed;
|
||||
|
@ -14,8 +13,8 @@ use crate::unix_proto::{ClientRequest, ClientResponse};
|
|||
struct ClientCodec;
|
||||
|
||||
impl Decoder for ClientCodec {
|
||||
type Item = ClientResponse;
|
||||
type Error = IoError;
|
||||
type Item = ClientResponse;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_json::from_slice::<ClientResponse>(&src) {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::error::Error;
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{Error as IoError, ErrorKind, Read, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::unix_proto::{ClientRequest, ClientResponse};
|
||||
|
|
|
@ -10,10 +10,18 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs::metadata;
|
||||
use std::io;
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use kanidm::utils::file_permissions_readonly;
|
||||
use kanidm_client::KanidmClientBuilder;
|
||||
use kanidm_proto::constants::DEFAULT_CLIENT_CONFIG_PATH;
|
||||
|
@ -22,22 +30,14 @@ use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
|||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, TaskRequest, TaskResponse};
|
||||
use libc::umask;
|
||||
use sketching::tracing_forest::{self, traits::*, util::*};
|
||||
use std::error::Error;
|
||||
use std::fs::metadata;
|
||||
use std::io;
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use sketching::tracing_forest::traits::*;
|
||||
use sketching::tracing_forest::util::*;
|
||||
use sketching::tracing_forest::{self};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time;
|
||||
use tokio_util::codec::Framed;
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
|
||||
//=== the codec
|
||||
|
@ -47,8 +47,8 @@ type AsyncTaskRequest = (TaskRequest, oneshot::Sender<()>);
|
|||
struct ClientCodec;
|
||||
|
||||
impl Decoder for ClientCodec {
|
||||
type Item = ClientRequest;
|
||||
type Error = io::Error;
|
||||
type Item = ClientRequest;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_json::from_slice::<ClientRequest>(&src) {
|
||||
|
@ -85,8 +85,8 @@ impl ClientCodec {
|
|||
struct TaskCodec;
|
||||
|
||||
impl Decoder for TaskCodec {
|
||||
type Item = TaskResponse;
|
||||
type Error = io::Error;
|
||||
type Item = TaskResponse;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_json::from_slice::<TaskResponse>(&src) {
|
||||
|
|
|
@ -13,12 +13,10 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
// use futures::executor::block_on;
|
||||
|
||||
use kanidm_unix_common::client_sync::call_daemon_blocking;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
||||
use libc::umask;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::cache::Id;
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
||||
use kanidm::be::dbvalue::DbPasswordV1;
|
||||
use kanidm::credential::policy::CryptoPolicy;
|
||||
use kanidm::credential::Password;
|
||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
||||
use libc::umask;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
||||
use crate::cache::Id;
|
||||
|
||||
pub struct Db {
|
||||
pool: Pool<SqliteConnectionManager>,
|
||||
|
@ -732,9 +732,10 @@ impl<'a> Drop for DbTxn<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
||||
|
||||
use super::Db;
|
||||
use crate::cache::Id;
|
||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
||||
|
||||
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
||||
const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test";
|
||||
|
|
|
@ -13,11 +13,10 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::executor::block_on;
|
||||
|
||||
use kanidm_unix_common::client::call_daemon;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
|
|
|
@ -10,35 +10,31 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
use users::{get_effective_gid, get_effective_uid};
|
||||
|
||||
use std::os::unix::fs::symlink;
|
||||
|
||||
use libc::{lchown, umask};
|
||||
use std::ffi::CString;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
use sketching::tracing_forest::{self, traits::*, util::*};
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::time;
|
||||
use tokio_util::codec::Framed;
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
use std::{fs, io};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
use kanidm_unix_common::unix_proto::{HomeDirectoryInfo, TaskRequest, TaskResponse};
|
||||
use libc::{lchown, umask};
|
||||
use sketching::tracing_forest::traits::*;
|
||||
use sketching::tracing_forest::util::*;
|
||||
use sketching::tracing_forest::{self};
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::time;
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
use users::{get_effective_gid, get_effective_uid};
|
||||
|
||||
struct TaskCodec;
|
||||
|
||||
impl Decoder for TaskCodec {
|
||||
type Item = TaskRequest;
|
||||
type Error = io::Error;
|
||||
type Item = TaskRequest;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_json::from_slice::<TaskRequest>(&src) {
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
extern crate tracing;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use futures::executor::block_on;
|
||||
|
||||
use kanidm_unix_common::client::call_daemon;
|
||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use crate::constants::{
|
||||
DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_GID_ATTR_MAP,
|
||||
DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_SHELL, DEFAULT_SOCK_PATH,
|
||||
DEFAULT_TASK_SOCK_PATH, DEFAULT_UID_ATTR_MAP,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::{ErrorKind, Read};
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::constants::{
|
||||
DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_GID_ATTR_MAP,
|
||||
DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_SHELL, DEFAULT_SOCK_PATH,
|
||||
DEFAULT_TASK_SOCK_PATH, DEFAULT_UID_ATTR_MAP,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ConfigInt {
|
||||
db_path: Option<String>,
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
#![deny(warnings)]
|
||||
use std::future::Future;
|
||||
use std::net::TcpStream;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm::audit::LogLevel;
|
||||
use kanidm::config::{Configuration, IntegrationTestConfig, ServerRole};
|
||||
use score::create_server_core;
|
||||
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
use kanidm_unix_common::cache::{CacheLayer, Id};
|
||||
use kanidm_unix_common::constants::{
|
||||
DEFAULT_GID_ATTR_MAP, DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX,
|
||||
DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP,
|
||||
};
|
||||
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use score::create_server_core;
|
||||
use tokio::task;
|
||||
|
||||
static PORT_ALLOC: AtomicU16 = AtomicU16::new(28080);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
[package]
|
||||
name = "daemon"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Server Daemon"
|
||||
documentation = "https://docs.rs/kanidm/latest/kanidm/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -17,25 +18,25 @@ name = "kanidmd"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
kanidm = { path = "../idm" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
score = { path = "../score" }
|
||||
sketching = { path = "../../sketching" }
|
||||
kanidm.workspace = true
|
||||
kanidm_proto.workspace = true
|
||||
score.workspace = true
|
||||
sketching.workspace = true
|
||||
|
||||
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
tokio = { version = "^1.21.1", features = ["rt-multi-thread", "macros", "signal"] }
|
||||
toml = "0.5.9"
|
||||
clap = { workspace = true, features = ["env"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
|
||||
toml.workspace = true
|
||||
|
||||
[target.'cfg(target_family = "windows")'.dependencies]
|
||||
whoami = "^1.2.3"
|
||||
whoami.workspace = true
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
||||
users = "^0.11.0"
|
||||
tikv-jemallocator = "0.5"
|
||||
users.workspace = true
|
||||
tikv-jemallocator.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
clap = { version = "^3.2", features = ["derive"] }
|
||||
clap_complete = "^3.2.5"
|
||||
profiles = { path = "../../profiles" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete.workspace = true
|
||||
profiles.workspace = true
|
||||
|
|
|
@ -5,7 +5,6 @@ use std::path::PathBuf;
|
|||
|
||||
use clap::{Args, IntoApp, Parser, Subcommand};
|
||||
use clap_complete::{generate_to, Shell};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
include!("../idm/src/audit_loglevel.rs");
|
||||
|
|
|
@ -14,25 +14,15 @@
|
|||
#[global_allocator]
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[cfg(not(target_family = "windows"))] // not needed for windows builds
|
||||
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
#[cfg(target_family = "windows")] // for windows builds
|
||||
use whoami;
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::fs::{metadata, File, Metadata};
|
||||
|
||||
use std::io::Read;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
|
||||
use sketching::tracing_forest::{self, traits::*, util::*};
|
||||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use kanidm::audit::LogLevel;
|
||||
use kanidm::config::{Configuration, OnlineBackup, ServerRole};
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
|
@ -43,8 +33,14 @@ use score::{
|
|||
domain_rename_core, recover_account_core, reindex_server_core, restore_server_core,
|
||||
vacuum_server_core, verify_server_core,
|
||||
};
|
||||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use serde::Deserialize;
|
||||
use sketching::tracing_forest::traits::*;
|
||||
use sketching::tracing_forest::util::*;
|
||||
use sketching::tracing_forest::{self};
|
||||
#[cfg(not(target_family = "windows"))] // not needed for windows builds
|
||||
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
#[cfg(target_family = "windows")] // for windows builds
|
||||
use whoami;
|
||||
|
||||
include!("./opt.rs");
|
||||
|
||||
|
|
|
@ -1,98 +1,91 @@
|
|||
[package]
|
||||
name = "kanidm"
|
||||
version = "1.1.0-alpha.9"
|
||||
authors = ["William Brown <william@blackhats.net.au>"]
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
description = "Kanidm Server Library and Binary"
|
||||
documentation = "https://docs.rs/kanidm/latest/kanidm/"
|
||||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "kanidm"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "scaling_10k"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
async-std = { version = "^1.12.0", features = ["tokio1"] }
|
||||
async-trait = "^0.1.57"
|
||||
base64 = "^0.13.0"
|
||||
base64urlsafedata = "0.1.0"
|
||||
chrono = "^0.4.20"
|
||||
compact_jwt = "^0.2.3"
|
||||
concread = "^0.4.0"
|
||||
dyn-clone = "^1.0.9"
|
||||
fernet = { version = "^0.2.0", features = ["fernet_danger_timestamps"] }
|
||||
filetime = "^0.2.17"
|
||||
futures = "^0.3.21"
|
||||
futures-util = "^0.3.21"
|
||||
hashbrown = { version = "0.12.3", features = ["serde", "inline-more", "ahash"] }
|
||||
idlset = { version = "^0.2.4" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
lazy_static = "^1.4.0"
|
||||
ldap3_proto = "^0.2.3"
|
||||
libc = "^0.2.127"
|
||||
libsqlite3-sys = "^0.25.0"
|
||||
num_enum = "^0.5.7"
|
||||
openssl = "^0.10.41"
|
||||
r2d2 = "^0.8.9"
|
||||
r2d2_sqlite = "^0.21.0"
|
||||
rand = "^0.8.5"
|
||||
regex = "^1.5.6"
|
||||
saffron = "^0.1.0"
|
||||
serde = { version = "^1.0.142", features = ["derive"] }
|
||||
serde_cbor = "^0.11.2"
|
||||
serde_json = "^1.0.83"
|
||||
smartstring = { version = "^1.0.1", features = ["serde"] }
|
||||
smolset = "^1.3.1"
|
||||
sshkeys = "^0.3.1"
|
||||
tide = "^0.16.0"
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
tokio = { version = "^1.21.1", features = ["net", "sync", "time"] }
|
||||
tokio-util = { version = "^0.7.4", features = ["codec"] }
|
||||
toml = "^0.5.9"
|
||||
touch = "^0.0.1"
|
||||
async-std.workspace = true
|
||||
async-trait.workspace = true
|
||||
base64.workspace = true
|
||||
base64urlsafedata.workspace = true
|
||||
chrono.workspace = true
|
||||
compact_jwt.workspace = true
|
||||
concread.workspace = true
|
||||
dyn-clone.workspace = true
|
||||
fernet = { workspace = true, features = ["fernet_danger_timestamps"] }
|
||||
filetime.workspace = true
|
||||
futures.workspace = true
|
||||
futures-util.workspace = true
|
||||
hashbrown.workspace = true
|
||||
idlset.workspace = true
|
||||
kanidm_proto.workspace = true
|
||||
lazy_static.workspace = true
|
||||
ldap3_proto.workspace = true
|
||||
libc.workspace = true
|
||||
libsqlite3-sys.workspace = true
|
||||
num_enum.workspace = true
|
||||
openssl.workspace = true
|
||||
r2d2.workspace = true
|
||||
r2d2_sqlite.workspace = true
|
||||
rand.workspace = true
|
||||
regex.workspace = true
|
||||
saffron.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_cbor.workspace = true
|
||||
serde_json.workspace = true
|
||||
sketching.workspace = true
|
||||
smartstring = { workspace = true, features = ["serde"] }
|
||||
smolset.workspace = true
|
||||
sshkeys.workspace = true
|
||||
tide.workspace = true
|
||||
time = { workspace = true, features = ["serde", "std"] }
|
||||
tokio = { workspace = true, features = ["net", "sync", "time"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
toml.workspace = true
|
||||
touch.workspace = true
|
||||
|
||||
sketching = { path = "../../sketching" }
|
||||
tracing = { version = "^0.1.35", features = ["attributes"] }
|
||||
tracing = { workspace = true, features = ["attributes"] }
|
||||
|
||||
url = { version = "^2.3.1", features = ["serde"] }
|
||||
urlencoding = "2.1.2"
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
validator = { version = "^0.16.0", features = ["phone"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
urlencoding.workspace = true
|
||||
uuid = { workspace = true, features = ["serde", "v4" ] }
|
||||
validator = { workspace = true, features = ["phone"] }
|
||||
webauthn-rs = { workspace = true, features = ["resident-key-support", "preview-features", "danger-credential-internals"] }
|
||||
webauthn-rs-core.workspace = true
|
||||
zxcvbn = "^2.2.1"
|
||||
zxcvbn.workspace = true
|
||||
|
||||
# because windows really can't build without the bundled one
|
||||
[target.'cfg(target_family = "windows")'.dependencies.rusqlite]
|
||||
version = "^0.28.0"
|
||||
features = ["bundled"]
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies.rusqlite]
|
||||
version = "^0.28.0"
|
||||
|
||||
[target.'cfg(target_family = "windows")'.dependencies]
|
||||
whoami = "^1.2.3"
|
||||
|
||||
rusqlite = { workspace = true, features = ["bundled"] }
|
||||
whoami.workspace = true
|
||||
|
||||
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
||||
users = "^0.11.0"
|
||||
|
||||
rusqlite.workspace = true
|
||||
users.workspace = true
|
||||
|
||||
[features]
|
||||
# default = [ "libsqlite3-sys/bundled", "openssl/vendored" ]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "^0.4.0", features = ["html_reports"] }
|
||||
# For testing webauthn
|
||||
criterion = { workspace = true, features = ["html_reports"] }
|
||||
webauthn-authenticator-rs.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
profiles = { path = "../../profiles" }
|
||||
|
||||
[[bench]]
|
||||
name = "scaling_10k"
|
||||
harness = false
|
||||
profiles.workspace = true
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use async_std::task;
|
||||
use criterion::{
|
||||
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
|
||||
};
|
||||
|
||||
use kanidm;
|
||||
use kanidm::entry::{Entry, EntryInit, EntryNew};
|
||||
use kanidm::entry_init;
|
||||
|
@ -11,9 +13,6 @@ use kanidm::server::QueryServer;
|
|||
use kanidm::utils::duration_from_epoch_now;
|
||||
use kanidm::value::Value;
|
||||
|
||||
use async_std::task;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub fn scaling_user_create_single(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("user_create_single");
|
||||
group.sample_size(10);
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
include!("./audit_loglevel.rs");
|
||||
|
||||
pub const AUDIT_LINE_SIZE: usize = 512;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use crate::be::dbvalue::{DbValueEmailAddressV1, DbValuePhoneNumberV1, DbValueSetV2, DbValueV1};
|
||||
use crate::prelude::OperationError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::be::dbvalue::{DbValueEmailAddressV1, DbValuePhoneNumberV1, DbValueSetV2, DbValueV1};
|
||||
use crate::prelude::OperationError;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DbEntryV1 {
|
||||
pub attrs: BTreeMap<AttrString, Vec<DbValueV1>>,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::{
|
||||
DeviceKey as DeviceKeyV4, Passkey as PasskeyV4, SecurityKey as SecurityKeyV4,
|
||||
};
|
||||
use webauthn_rs_core::proto::{COSEKey, UserVerificationPolicy};
|
||||
|
||||
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
use webauthn_rs::prelude::SecurityKey as SecurityKeyV4;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DbCidV1 {
|
||||
#[serde(rename = "d")]
|
||||
|
@ -556,11 +556,11 @@ impl DbValueSetV2 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DbCred;
|
||||
use super::{DbBackupCodeV1, DbPasswordV1, DbTotpV1, DbWebauthnV1};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{DbBackupCodeV1, DbCred, DbPasswordV1, DbTotpV1, DbWebauthnV1};
|
||||
|
||||
fn dbcred_type_default_pw() -> DbCredTypeV1 {
|
||||
DbCredTypeV1::Pw
|
||||
}
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::convert::TryInto;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn, ARCacheWriteTxn};
|
||||
use concread::cowcell::*;
|
||||
use hashbrown::HashMap;
|
||||
use idlset::v2::IDLBitRange;
|
||||
use idlset::AndNot;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::be::idl_sqlite::{
|
||||
IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction,
|
||||
};
|
||||
|
@ -6,24 +21,8 @@ use crate::be::idxkey::{
|
|||
};
|
||||
use crate::be::{BackendConfig, IdList, IdRawEntry};
|
||||
use crate::entry::{Entry, EntryCommitted, EntrySealed};
|
||||
use crate::value::IndexType;
|
||||
use crate::value::Value;
|
||||
|
||||
use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn, ARCacheWriteTxn};
|
||||
use concread::cowcell::*;
|
||||
use idlset::{v2::IDLBitRange, AndNot};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::convert::TryInto;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::prelude::*;
|
||||
use tracing::trace;
|
||||
use crate::value::{IndexType, Value};
|
||||
|
||||
// use std::borrow::Borrow;
|
||||
|
||||
|
@ -82,58 +81,56 @@ macro_rules! get_identry {
|
|||
$idl:expr,
|
||||
$is_read_op:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::get_identry", {
|
||||
let mut result: Vec<Arc<EntrySealedCommitted>> = Vec::new();
|
||||
match $idl {
|
||||
IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => {
|
||||
let mut nidl = IDLBitRange::new();
|
||||
let mut result: Vec<Arc<EntrySealedCommitted>> = Vec::new();
|
||||
match $idl {
|
||||
IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => {
|
||||
let mut nidl = IDLBitRange::new();
|
||||
|
||||
idli.into_iter().for_each(|i| {
|
||||
// For all the id's in idl.
|
||||
// is it in the cache?
|
||||
match $self.entry_cache.get(&i) {
|
||||
Some(eref) => result.push(eref.clone()),
|
||||
None => unsafe { nidl.push_id(i) },
|
||||
}
|
||||
idli.into_iter().for_each(|i| {
|
||||
// For all the id's in idl.
|
||||
// is it in the cache?
|
||||
match $self.entry_cache.get(&i) {
|
||||
Some(eref) => result.push(eref.clone()),
|
||||
None => unsafe { nidl.push_id(i) },
|
||||
}
|
||||
});
|
||||
|
||||
if !nidl.is_empty() {
|
||||
// Now, get anything from nidl that is needed.
|
||||
let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
|
||||
// Clone everything from db_result into the cache.
|
||||
if $is_read_op {
|
||||
db_result.iter().for_each(|e| {
|
||||
$self.entry_cache.insert(e.get_id(), e.clone());
|
||||
});
|
||||
}
|
||||
// Merge the two vecs
|
||||
result.append(&mut db_result);
|
||||
}
|
||||
}
|
||||
IdList::AllIds => {
|
||||
// VERY similar to above, but we skip adding the entries to the cache
|
||||
// on miss to prevent scan/invalidation attacks.
|
||||
let idli = (*$self.allids).clone();
|
||||
let mut nidl = IDLBitRange::new();
|
||||
|
||||
(&idli)
|
||||
.into_iter()
|
||||
.for_each(|i| match $self.entry_cache.get(&i) {
|
||||
Some(eref) => result.push(eref.clone()),
|
||||
None => unsafe { nidl.push_id(i) },
|
||||
});
|
||||
|
||||
if !nidl.is_empty() {
|
||||
// Now, get anything from nidl that is needed.
|
||||
let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
|
||||
// Clone everything from db_result into the cache.
|
||||
if $is_read_op {
|
||||
db_result.iter().for_each(|e| {
|
||||
$self.entry_cache.insert(e.get_id(), e.clone());
|
||||
});
|
||||
}
|
||||
// Merge the two vecs
|
||||
result.append(&mut db_result);
|
||||
}
|
||||
if !nidl.is_empty() {
|
||||
// Now, get anything from nidl that is needed.
|
||||
let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
|
||||
// Merge the two vecs
|
||||
result.append(&mut db_result);
|
||||
}
|
||||
IdList::AllIds => {
|
||||
// VERY similar to above, but we skip adding the entries to the cache
|
||||
// on miss to prevent scan/invalidation attacks.
|
||||
let idli = (*$self.allids).clone();
|
||||
let mut nidl = IDLBitRange::new();
|
||||
|
||||
(&idli)
|
||||
.into_iter()
|
||||
.for_each(|i| match $self.entry_cache.get(&i) {
|
||||
Some(eref) => result.push(eref.clone()),
|
||||
None => unsafe { nidl.push_id(i) },
|
||||
});
|
||||
|
||||
if !nidl.is_empty() {
|
||||
// Now, get anything from nidl that is needed.
|
||||
let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
|
||||
// Merge the two vecs
|
||||
result.append(&mut db_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Return
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
};
|
||||
// Return
|
||||
Ok(result)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -165,7 +162,6 @@ macro_rules! get_idl {
|
|||
$itype:expr,
|
||||
$idx_key:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::get_idl", {
|
||||
// SEE ALSO #259: Find a way to implement borrow for this properly.
|
||||
// I don't think this is possible. When we make this dyn, the arc
|
||||
// needs the dyn trait to be sized so that it *could* claim a clone
|
||||
|
@ -188,10 +184,9 @@ macro_rules! get_idl {
|
|||
// If hit, continue.
|
||||
if let Some(ref data) = cache_r {
|
||||
trace!(
|
||||
%data,
|
||||
"Got cached idl for index {:?} {:?}",
|
||||
$itype,
|
||||
$attr,
|
||||
cached_index = ?$itype,
|
||||
attr = ?$attr,
|
||||
idl = %data,
|
||||
);
|
||||
return Ok(Some(data.as_ref().clone()));
|
||||
}
|
||||
|
@ -206,7 +201,6 @@ macro_rules! get_idl {
|
|||
$self.idl_cache.insert(ncache_key, Box::new(idl.clone()))
|
||||
}
|
||||
Ok(db_r)
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -215,24 +209,22 @@ macro_rules! name2uuid {
|
|||
$self:expr,
|
||||
$name:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::name2uuid", {
|
||||
let cache_key = NameCacheKey::Name2Uuid($name.to_string());
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::U(uuid)) = cache_r {
|
||||
trace!(?uuid, "Got cached name2uuid");
|
||||
return Ok(Some(uuid.clone()));
|
||||
} else {
|
||||
trace!("Cache miss uuid for name2uuid");
|
||||
}
|
||||
let cache_key = NameCacheKey::Name2Uuid($name.to_string());
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::U(uuid)) = cache_r {
|
||||
trace!(?uuid, "Got cached name2uuid");
|
||||
return Ok(Some(uuid.clone()));
|
||||
} else {
|
||||
trace!("Cache miss uuid for name2uuid");
|
||||
}
|
||||
|
||||
let db_r = $self.db.name2uuid($name)?;
|
||||
if let Some(uuid) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::U(uuid.clone()))
|
||||
}
|
||||
Ok(db_r)
|
||||
})
|
||||
let db_r = $self.db.name2uuid($name)?;
|
||||
if let Some(uuid) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::U(uuid.clone()))
|
||||
}
|
||||
Ok(db_r)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -241,24 +233,22 @@ macro_rules! uuid2spn {
|
|||
$self:expr,
|
||||
$uuid:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::uuid2spn", {
|
||||
let cache_key = NameCacheKey::Uuid2Spn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::S(ref spn)) = cache_r {
|
||||
trace!(?spn, "Got cached uuid2spn");
|
||||
return Ok(Some(spn.as_ref().clone()));
|
||||
} else {
|
||||
trace!("Cache miss spn for uuid2spn");
|
||||
}
|
||||
let cache_key = NameCacheKey::Uuid2Spn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::S(ref spn)) = cache_r {
|
||||
trace!(?spn, "Got cached uuid2spn");
|
||||
return Ok(Some(spn.as_ref().clone()));
|
||||
} else {
|
||||
trace!("Cache miss spn for uuid2spn");
|
||||
}
|
||||
|
||||
let db_r = $self.db.uuid2spn($uuid)?;
|
||||
if let Some(ref data) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::S(Box::new(data.clone())))
|
||||
}
|
||||
Ok(db_r)
|
||||
})
|
||||
let db_r = $self.db.uuid2spn($uuid)?;
|
||||
if let Some(ref data) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::S(Box::new(data.clone())))
|
||||
}
|
||||
Ok(db_r)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -267,23 +257,21 @@ macro_rules! uuid2rdn {
|
|||
$self:expr,
|
||||
$uuid:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::uuid2rdn", {
|
||||
let cache_key = NameCacheKey::Uuid2Rdn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::R(ref rdn)) = cache_r {
|
||||
return Ok(Some(rdn.clone()));
|
||||
} else {
|
||||
trace!("Cache miss rdn for uuid2rdn");
|
||||
}
|
||||
let cache_key = NameCacheKey::Uuid2Rdn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::R(ref rdn)) = cache_r {
|
||||
return Ok(Some(rdn.clone()));
|
||||
} else {
|
||||
trace!("Cache miss rdn for uuid2rdn");
|
||||
}
|
||||
|
||||
let db_r = $self.db.uuid2rdn($uuid)?;
|
||||
if let Some(ref data) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::R(data.clone()))
|
||||
}
|
||||
Ok(db_r)
|
||||
})
|
||||
let db_r = $self.db.uuid2rdn($uuid)?;
|
||||
if let Some(ref data) = db_r {
|
||||
$self
|
||||
.name_cache
|
||||
.insert(cache_key, NameCacheValue::R(data.clone()))
|
||||
}
|
||||
Ok(db_r)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -528,88 +516,83 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
||||
#[instrument(level = "debug", name = "idl_arc_sqlite::commit", skip_all)]
|
||||
pub fn commit(self) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::commit", {
|
||||
let IdlArcSqliteWriteTransaction {
|
||||
db,
|
||||
mut entry_cache,
|
||||
mut idl_cache,
|
||||
mut name_cache,
|
||||
op_ts_max,
|
||||
allids,
|
||||
maxid,
|
||||
} = self;
|
||||
let IdlArcSqliteWriteTransaction {
|
||||
db,
|
||||
mut entry_cache,
|
||||
mut idl_cache,
|
||||
mut name_cache,
|
||||
op_ts_max,
|
||||
allids,
|
||||
maxid,
|
||||
} = self;
|
||||
|
||||
// Write any dirty items to the disk.
|
||||
spanned!("be::idl_arc_sqlite::commit<entry>", {
|
||||
entry_cache
|
||||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| match v {
|
||||
Some(e) => db.write_identry(e),
|
||||
None => db.delete_identry(*k),
|
||||
})
|
||||
// Write any dirty items to the disk.
|
||||
entry_cache
|
||||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| match v {
|
||||
Some(e) => db.write_identry(e),
|
||||
None => db.delete_identry(*k),
|
||||
})
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to sync entry cache to sqlite");
|
||||
e
|
||||
})?;
|
||||
|
||||
spanned!("be::idl_arc_sqlite::commit<idl>", {
|
||||
idl_cache.iter_mut_mark_clean().try_for_each(|(k, v)| {
|
||||
match v {
|
||||
Some(idl) => db.write_idl(k.a.as_str(), k.i, k.k.as_str(), idl),
|
||||
#[allow(clippy::unreachable)]
|
||||
None => {
|
||||
// Due to how we remove items, we always write an empty idl
|
||||
// to the cache, so this should never be none.
|
||||
//
|
||||
// If it is none, this means we have memory corruption so we MUST
|
||||
// panic.
|
||||
// Why is `v` the `Option` type then?
|
||||
unreachable!();
|
||||
}
|
||||
idl_cache
|
||||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| {
|
||||
match v {
|
||||
Some(idl) => db.write_idl(k.a.as_str(), k.i, k.k.as_str(), idl),
|
||||
#[allow(clippy::unreachable)]
|
||||
None => {
|
||||
// Due to how we remove items, we always write an empty idl
|
||||
// to the cache, so this should never be none.
|
||||
//
|
||||
// If it is none, this means we have memory corruption so we MUST
|
||||
// panic.
|
||||
// Why is `v` the `Option` type then?
|
||||
unreachable!();
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to sync idl cache to sqlite");
|
||||
e
|
||||
})?;
|
||||
|
||||
spanned!("be::idl_arc_sqlite::commit<names>", {
|
||||
name_cache
|
||||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| match (k, v) {
|
||||
(NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => {
|
||||
db.write_name2uuid_add(k, *v)
|
||||
}
|
||||
(NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k),
|
||||
(NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => {
|
||||
db.write_uuid2spn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None),
|
||||
(NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => {
|
||||
db.write_uuid2rdn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
|
||||
name_cache
|
||||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| match (k, v) {
|
||||
(NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => {
|
||||
db.write_name2uuid_add(k, *v)
|
||||
}
|
||||
(NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k),
|
||||
(NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => {
|
||||
db.write_uuid2spn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None),
|
||||
(NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => {
|
||||
db.write_uuid2rdn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
|
||||
|
||||
_ => Err(OperationError::InvalidCacheState),
|
||||
})
|
||||
_ => Err(OperationError::InvalidCacheState),
|
||||
})
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to sync name cache to sqlite");
|
||||
e
|
||||
})?;
|
||||
|
||||
// Undo the caches in the reverse order.
|
||||
db.commit().map(|()| {
|
||||
op_ts_max.commit();
|
||||
name_cache.commit();
|
||||
idl_cache.commit();
|
||||
entry_cache.commit();
|
||||
allids.commit();
|
||||
maxid.commit();
|
||||
})
|
||||
// Undo the caches in the reverse order.
|
||||
db.commit().map(|()| {
|
||||
op_ts_max.commit();
|
||||
name_cache.commit();
|
||||
idl_cache.commit();
|
||||
entry_cache.commit();
|
||||
allids.commit();
|
||||
maxid.commit();
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -626,18 +609,16 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
where
|
||||
I: Iterator<Item = &'b Entry<EntrySealed, EntryCommitted>>,
|
||||
{
|
||||
spanned!("be::idl_arc_sqlite::write_identries", {
|
||||
entries.try_for_each(|e| {
|
||||
trace!("Inserting {:?} to cache", e.get_id());
|
||||
if e.get_id() == 0 {
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).insert_id(e.get_id());
|
||||
self.entry_cache
|
||||
.insert_dirty(e.get_id(), Arc::new(e.clone()));
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
entries.try_for_each(|e| {
|
||||
trace!("Inserting {:?} to cache", e.get_id());
|
||||
if e.get_id() == 0 {
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).insert_id(e.get_id());
|
||||
self.entry_cache
|
||||
.insert_dirty(e.get_id(), Arc::new(e.clone()));
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -661,17 +642,15 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
where
|
||||
I: Iterator<Item = u64>,
|
||||
{
|
||||
spanned!("be::idl_arc_sqlite::delete_identry", {
|
||||
idl.try_for_each(|i| {
|
||||
trace!("Removing {:?} from cache", i);
|
||||
if i == 0 {
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).remove_id(i);
|
||||
self.entry_cache.remove_dirty(i);
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
idl.try_for_each(|i| {
|
||||
trace!("Removing {:?} from cache", i);
|
||||
if i == 0 {
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).remove_id(i);
|
||||
self.entry_cache.remove_dirty(i);
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -682,32 +661,30 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
idx_key: &str,
|
||||
idl: &IDLBitRange,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_idl", {
|
||||
let cache_key = IdlCacheKey {
|
||||
a: attr.into(),
|
||||
i: itype,
|
||||
k: idx_key.into(),
|
||||
};
|
||||
// On idl == 0 the db will remove this, and synthesise an empty IdList on a miss
|
||||
// but we can cache this as a new empty IdList instead, so that we can avoid the
|
||||
// db lookup on this idl.
|
||||
if idl.is_empty() {
|
||||
self.idl_cache
|
||||
.insert_dirty(cache_key, Box::new(IDLBitRange::new()));
|
||||
} else {
|
||||
self.idl_cache
|
||||
.insert_dirty(cache_key, Box::new(idl.clone()));
|
||||
}
|
||||
// self.db.write_idl(audit, attr, itype, idx_key, idl)
|
||||
Ok(())
|
||||
})
|
||||
let cache_key = IdlCacheKey {
|
||||
a: attr.into(),
|
||||
i: itype,
|
||||
k: idx_key.into(),
|
||||
};
|
||||
// On idl == 0 the db will remove this, and synthesise an empty IdList on a miss
|
||||
// but we can cache this as a new empty IdList instead, so that we can avoid the
|
||||
// db lookup on this idl.
|
||||
if idl.is_empty() {
|
||||
self.idl_cache
|
||||
.insert_dirty(cache_key, Box::new(IDLBitRange::new()));
|
||||
} else {
|
||||
self.idl_cache
|
||||
.insert_dirty(cache_key, Box::new(idl.clone()));
|
||||
}
|
||||
// self.db.write_idl(audit, attr, itype, idx_key, idl)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn optimise_dirty_idls(&mut self) {
|
||||
self.idl_cache.iter_mut_dirty().for_each(|(k, maybe_idl)| {
|
||||
if let Some(idl) = maybe_idl {
|
||||
if idl.maybe_compress() {
|
||||
filter_trace!(?k, "Compressed idl");
|
||||
trace!(?k, "Compressed idl");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -971,27 +948,21 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
uuid: Uuid,
|
||||
add: BTreeSet<String>,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_name2uuid_add", {
|
||||
add.into_iter().for_each(|k| {
|
||||
let cache_key = NameCacheKey::Name2Uuid(k);
|
||||
let cache_value = NameCacheValue::U(uuid);
|
||||
self.name_cache.insert_dirty(cache_key, cache_value)
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
add.into_iter().for_each(|k| {
|
||||
let cache_key = NameCacheKey::Name2Uuid(k);
|
||||
let cache_value = NameCacheValue::U(uuid);
|
||||
self.name_cache.insert_dirty(cache_key, cache_value)
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_name2uuid_rem(&mut self, rem: BTreeSet<String>) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_name2uuid_rem", {
|
||||
// self.db.write_name2uuid_rem(audit, &rem).and_then(|_| {
|
||||
rem.into_iter().for_each(|k| {
|
||||
// why not just a for loop here...
|
||||
let cache_key = NameCacheKey::Name2Uuid(k);
|
||||
self.name_cache.remove_dirty(cache_key)
|
||||
});
|
||||
Ok(())
|
||||
// })
|
||||
})
|
||||
rem.into_iter().for_each(|k| {
|
||||
// why not just a for loop here...
|
||||
let cache_key = NameCacheKey::Name2Uuid(k);
|
||||
self.name_cache.remove_dirty(cache_key)
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_uuid2spn(&self) -> Result<(), OperationError> {
|
||||
|
@ -999,16 +970,14 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
pub fn write_uuid2spn(&mut self, uuid: Uuid, k: Option<Value>) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_uuid2spn", {
|
||||
let cache_key = NameCacheKey::Uuid2Spn(uuid);
|
||||
match k {
|
||||
Some(v) => self
|
||||
.name_cache
|
||||
.insert_dirty(cache_key, NameCacheValue::S(Box::new(v))),
|
||||
None => self.name_cache.remove_dirty(cache_key),
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
let cache_key = NameCacheKey::Uuid2Spn(uuid);
|
||||
match k {
|
||||
Some(v) => self
|
||||
.name_cache
|
||||
.insert_dirty(cache_key, NameCacheValue::S(Box::new(v))),
|
||||
None => self.name_cache.remove_dirty(cache_key),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_uuid2rdn(&self) -> Result<(), OperationError> {
|
||||
|
@ -1016,16 +985,14 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
pub fn write_uuid2rdn(&mut self, uuid: Uuid, k: Option<String>) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_uuid2rdn", {
|
||||
let cache_key = NameCacheKey::Uuid2Rdn(uuid);
|
||||
match k {
|
||||
Some(s) => self
|
||||
.name_cache
|
||||
.insert_dirty(cache_key, NameCacheValue::R(s)),
|
||||
None => self.name_cache.remove_dirty(cache_key),
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
let cache_key = NameCacheKey::Uuid2Rdn(uuid);
|
||||
match k {
|
||||
Some(s) => self
|
||||
.name_cache
|
||||
.insert_dirty(cache_key, NameCacheValue::R(s)),
|
||||
None => self.name_cache.remove_dirty(cache_key),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_idx(&self, attr: &str, itype: IndexType) -> Result<(), OperationError> {
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
use crate::be::dbentry::DbEntry;
|
||||
use crate::be::dbentry::DbIdentSpn;
|
||||
use crate::be::{BackendConfig, IdList, IdRawEntry, IdxKey, IdxSlope};
|
||||
use crate::entry::{Entry, EntryCommitted, EntrySealed};
|
||||
use crate::prelude::*;
|
||||
use crate::value::{IndexType, Value};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
// use crate::valueset;
|
||||
use hashbrown::HashMap;
|
||||
use idlset::v2::IDLBitRange;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::Connection;
|
||||
use rusqlite::OpenFlags;
|
||||
use rusqlite::OptionalExtension;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tracing::trace;
|
||||
use rusqlite::{Connection, OpenFlags, OptionalExtension};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::be::dbentry::{DbEntry, DbIdentSpn};
|
||||
use crate::be::{BackendConfig, IdList, IdRawEntry, IdxKey, IdxSlope};
|
||||
use crate::entry::{Entry, EntryCommitted, EntrySealed};
|
||||
use crate::prelude::*;
|
||||
use crate::value::{IndexType, Value};
|
||||
|
||||
// use uuid::Uuid;
|
||||
|
||||
const DBV_ID2ENTRY: &str = "id2entry";
|
||||
|
@ -117,12 +115,10 @@ pub trait IdlSqliteTransaction {
|
|||
fn get_conn(&self) -> &r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
|
||||
|
||||
fn get_identry(&self, idl: &IdList) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
|
||||
spanned!("be::idl_sqlite::get_identry", {
|
||||
self.get_identry_raw(idl)?
|
||||
.into_iter()
|
||||
.map(|ide| ide.into_entry().map(Arc::new))
|
||||
.collect()
|
||||
})
|
||||
self.get_identry_raw(idl)?
|
||||
.into_iter()
|
||||
.map(|ide| ide.into_entry().map(Arc::new))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError> {
|
||||
|
@ -220,112 +216,104 @@ pub trait IdlSqliteTransaction {
|
|||
itype: IndexType,
|
||||
idx_key: &str,
|
||||
) -> Result<Option<IDLBitRange>, OperationError> {
|
||||
spanned!("be::idl_sqlite::get_idl", {
|
||||
if !(self.exists_idx(attr, itype)?) {
|
||||
filter_error!(
|
||||
"IdlSqliteTransaction: Index {:?} {:?} not found",
|
||||
itype,
|
||||
attr
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
// The table exists - lets now get the actual index itself.
|
||||
|
||||
let query = format!(
|
||||
"SELECT idl FROM idx_{}_{} WHERE key = :idx_key",
|
||||
itype.as_idx_str(),
|
||||
if !(self.exists_idx(attr, itype)?) {
|
||||
filter_error!(
|
||||
"IdlSqliteTransaction: Index {:?} {:?} not found",
|
||||
itype,
|
||||
attr
|
||||
);
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare(query.as_str())
|
||||
.map_err(sqlite_error)?;
|
||||
let idl_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":idx_key", &idx_key)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
return Ok(None);
|
||||
}
|
||||
// The table exists - lets now get the actual index itself.
|
||||
|
||||
let idl = match idl_raw {
|
||||
Some(d) => serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?,
|
||||
// We don't have this value, it must be empty (or we
|
||||
// have a corrupted index .....
|
||||
None => IDLBitRange::new(),
|
||||
};
|
||||
trace!(%idl, "Got idl for index {:?} {:?}", itype, attr);
|
||||
let query = format!(
|
||||
"SELECT idl FROM idx_{}_{} WHERE key = :idx_key",
|
||||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare(query.as_str())
|
||||
.map_err(sqlite_error)?;
|
||||
let idl_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":idx_key", &idx_key)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
Ok(Some(idl))
|
||||
})
|
||||
let idl = match idl_raw {
|
||||
Some(d) => serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?,
|
||||
// We don't have this value, it must be empty (or we
|
||||
// have a corrupted index .....
|
||||
None => IDLBitRange::new(),
|
||||
};
|
||||
|
||||
trace!(
|
||||
miss_index = ?itype,
|
||||
attr = ?attr,
|
||||
idl = %idl,
|
||||
);
|
||||
|
||||
Ok(Some(idl))
|
||||
}
|
||||
|
||||
fn name2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError> {
|
||||
spanned!("be::idl_sqlite::name2uuid", {
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT uuid FROM idx_name2uuid WHERE name = :name")
|
||||
.map_err(sqlite_error)?;
|
||||
let uuid_raw: Option<String> = stmt
|
||||
.query_row(&[(":name", &name)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT uuid FROM idx_name2uuid WHERE name = :name")
|
||||
.map_err(sqlite_error)?;
|
||||
let uuid_raw: Option<String> = stmt
|
||||
.query_row(&[(":name", &name)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
let uuid = uuid_raw.as_ref().and_then(|u| Uuid::parse_str(u).ok());
|
||||
trace!(%name, ?uuid, "Got uuid for index");
|
||||
let uuid = uuid_raw.as_ref().and_then(|u| Uuid::parse_str(u).ok());
|
||||
|
||||
Ok(uuid)
|
||||
})
|
||||
Ok(uuid)
|
||||
}
|
||||
|
||||
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
spanned!("be::idl_sqlite::uuid2spn", {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT spn FROM idx_uuid2spn WHERE uuid = :uuid")
|
||||
.map_err(sqlite_error)?;
|
||||
let spn_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT spn FROM idx_uuid2spn WHERE uuid = :uuid")
|
||||
.map_err(sqlite_error)?;
|
||||
let spn_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
let spn: Option<Value> = match spn_raw {
|
||||
Some(d) => {
|
||||
let dbv: DbIdentSpn =
|
||||
serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?;
|
||||
let spn: Option<Value> = match spn_raw {
|
||||
Some(d) => {
|
||||
let dbv: DbIdentSpn =
|
||||
serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?;
|
||||
|
||||
Some(Value::from(dbv))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
Some(Value::from(dbv))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
trace!(?uuid, ?spn, "Got spn for uuid");
|
||||
|
||||
Ok(spn)
|
||||
})
|
||||
Ok(spn)
|
||||
}
|
||||
|
||||
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
|
||||
spanned!("be::idl_sqlite::uuid2rdn", {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT rdn FROM idx_uuid2rdn WHERE uuid = :uuid")
|
||||
.map_err(sqlite_error)?;
|
||||
let rdn: Option<String> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT rdn FROM idx_uuid2rdn WHERE uuid = :uuid")
|
||||
.map_err(sqlite_error)?;
|
||||
let rdn: Option<String> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
trace!(?uuid, ?rdn, "Got rdn for uuid");
|
||||
|
||||
Ok(rdn)
|
||||
})
|
||||
Ok(rdn)
|
||||
}
|
||||
|
||||
fn get_db_s_uuid(&self) -> Result<Option<Uuid>, OperationError> {
|
||||
|
@ -422,8 +410,8 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "idl_sqlite::get_allids", skip_all)]
|
||||
fn get_allids(&self) -> Result<IDLBitRange, OperationError> {
|
||||
trace!("Building allids...");
|
||||
let mut stmt = self
|
||||
.get_conn()
|
||||
.prepare("SELECT id FROM id2entry")
|
||||
|
@ -604,20 +592,18 @@ impl IdlSqliteWriteTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "idl_sqlite::commit", skip_all)]
|
||||
pub fn commit(mut self) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_sqlite::commit", {
|
||||
trace!("Commiting BE WR txn");
|
||||
assert!(!self.committed);
|
||||
self.committed = true;
|
||||
assert!(!self.committed);
|
||||
self.committed = true;
|
||||
|
||||
self.conn
|
||||
.execute("COMMIT TRANSACTION", [])
|
||||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "CRITICAL: failed to commit sqlite txn");
|
||||
OperationError::BackendEngine
|
||||
})
|
||||
})
|
||||
self.conn
|
||||
.execute("COMMIT TRANSACTION", [])
|
||||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "CRITICAL: failed to commit sqlite txn");
|
||||
OperationError::BackendEngine
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_id2entry_max_id(&self) -> Result<u64, OperationError> {
|
||||
|
@ -770,46 +756,42 @@ impl IdlSqliteWriteTransaction {
|
|||
idx_key: &str,
|
||||
idl: &IDLBitRange,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_sqlite::write_idl", {
|
||||
if idl.is_empty() {
|
||||
trace!(?idl, "purging idl");
|
||||
// delete it
|
||||
// Delete this idx_key from the table.
|
||||
let query = format!(
|
||||
"DELETE FROM idx_{}_{} WHERE key = :key",
|
||||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
if idl.is_empty() {
|
||||
// delete it
|
||||
// Delete this idx_key from the table.
|
||||
let query = format!(
|
||||
"DELETE FROM idx_{}_{} WHERE key = :key",
|
||||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
|
||||
self.conn
|
||||
.prepare(query.as_str())
|
||||
.and_then(|mut stmt| stmt.execute(&[(":key", &idx_key)]))
|
||||
.map_err(sqlite_error)
|
||||
} else {
|
||||
trace!(?idl, "writing idl");
|
||||
// Serialise the IdList to Vec<u8>
|
||||
let idl_raw = serde_json::to_vec(idl).map_err(serde_json_error)?;
|
||||
self.conn
|
||||
.prepare(query.as_str())
|
||||
.and_then(|mut stmt| stmt.execute(&[(":key", &idx_key)]))
|
||||
.map_err(sqlite_error)
|
||||
} else {
|
||||
// Serialise the IdList to Vec<u8>
|
||||
let idl_raw = serde_json::to_vec(idl).map_err(serde_json_error)?;
|
||||
|
||||
// update or create it.
|
||||
let query = format!(
|
||||
"INSERT OR REPLACE INTO idx_{}_{} (key, idl) VALUES(:key, :idl)",
|
||||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
// update or create it.
|
||||
let query = format!(
|
||||
"INSERT OR REPLACE INTO idx_{}_{} (key, idl) VALUES(:key, :idl)",
|
||||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
|
||||
self.conn
|
||||
.prepare(query.as_str())
|
||||
.and_then(|mut stmt| {
|
||||
stmt.execute(named_params! {
|
||||
":key": &idx_key,
|
||||
":idl": &idl_raw
|
||||
})
|
||||
self.conn
|
||||
.prepare(query.as_str())
|
||||
.and_then(|mut stmt| {
|
||||
stmt.execute(named_params! {
|
||||
":key": &idx_key,
|
||||
":idl": &idl_raw
|
||||
})
|
||||
.map_err(sqlite_error)
|
||||
}
|
||||
// Get rid of the sqlite rows usize
|
||||
.map(|_| ())
|
||||
})
|
||||
})
|
||||
.map_err(sqlite_error)
|
||||
}
|
||||
// Get rid of the sqlite rows usize
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn create_name2uuid(&self) -> Result<(), OperationError> {
|
||||
|
@ -944,7 +926,7 @@ impl IdlSqliteWriteTransaction {
|
|||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
trace!(idx = %idx_stmt, "Creating index");
|
||||
trace!(idx = %idx_stmt, "creating index");
|
||||
|
||||
self.conn
|
||||
.execute(idx_stmt.as_str(), [])
|
||||
|
@ -1034,7 +1016,6 @@ impl IdlSqliteWriteTransaction {
|
|||
}
|
||||
|
||||
pub unsafe fn purge_id2entry(&self) -> Result<(), OperationError> {
|
||||
trace!("purge id2entry ...");
|
||||
self.conn
|
||||
.execute("DELETE FROM id2entry", [])
|
||||
.map(|_| ())
|
||||
|
@ -1175,7 +1156,6 @@ impl IdlSqliteWriteTransaction {
|
|||
|
||||
// If the table is empty, populate the versions as 0.
|
||||
let mut dbv_id2entry = self.get_db_version_key(DBV_ID2ENTRY);
|
||||
trace!(initial = %dbv_id2entry, "dbv_id2entry");
|
||||
|
||||
// Check db_version here.
|
||||
// * if 0 -> create v1.
|
||||
|
@ -1374,13 +1354,12 @@ impl IdlSqlite {
|
|||
}
|
||||
|
||||
pub(crate) fn get_allids_count(&self) -> Result<u64, OperationError> {
|
||||
trace!("Counting allids...");
|
||||
#[allow(clippy::expect_used)]
|
||||
self.pool
|
||||
.try_get()
|
||||
.expect("Unable to get connection from pool!!!")
|
||||
.query_row("select count(id) from id2entry", [], |row| row.get(0))
|
||||
.map_err(sqlite_error) // this was initially `ltrace`, but I think that was a mistake so I replaced it anyways.
|
||||
.map_err(sqlite_error)
|
||||
}
|
||||
|
||||
pub fn read(&self) -> IdlSqliteReadTransaction {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::value::IndexType;
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use smartstring::alias::String as AttrString;
|
||||
|
||||
use crate::value::IndexType;
|
||||
|
||||
pub type IdxSlope = u8;
|
||||
|
||||
// Huge props to https://github.com/sunshowers/borrow-complex-key-example/blob/master/src/lib.rs
|
||||
|
|
|
@ -4,35 +4,32 @@
|
|||
//! is to persist content safely to disk, load that content, and execute queries
|
||||
//! utilising indexes in the most effective way possible.
|
||||
|
||||
use std::fs;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::value::IndexType;
|
||||
use hashbrown::HashMap as Map;
|
||||
use hashbrown::HashSet;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fs;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use concread::cowcell::*;
|
||||
use hashbrown::{HashMap as Map, HashSet};
|
||||
use idlset::v2::IDLBitRange;
|
||||
use idlset::AndNot;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use tracing::{trace, trace_span};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::be::dbentry::{DbBackup, DbEntry};
|
||||
use crate::entry::{Entry, EntryCommitted, EntryNew, EntrySealed};
|
||||
use crate::filter::{Filter, FilterPlan, FilterResolved, FilterValidResolved};
|
||||
use crate::identity::Limits;
|
||||
use crate::value::Value;
|
||||
use concread::cowcell::*;
|
||||
use idlset::v2::IDLBitRange;
|
||||
use idlset::AndNot;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::ops::DerefMut;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use crate::repl::ruv::{
|
||||
ReplicationUpdateVector, ReplicationUpdateVectorReadTransaction,
|
||||
ReplicationUpdateVectorTransaction, ReplicationUpdateVectorWriteTransaction,
|
||||
};
|
||||
use crate::value::{IndexType, Value};
|
||||
|
||||
pub mod dbentry;
|
||||
pub mod dbvalue;
|
||||
|
@ -41,12 +38,10 @@ mod idl_sqlite;
|
|||
pub(crate) mod idxkey;
|
||||
|
||||
pub(crate) use self::idxkey::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxSlope};
|
||||
|
||||
use crate::be::idl_arc_sqlite::{
|
||||
IdlArcSqlite, IdlArcSqliteReadTransaction, IdlArcSqliteTransaction,
|
||||
IdlArcSqliteWriteTransaction,
|
||||
};
|
||||
|
||||
// Re-export this
|
||||
pub use crate::be::idl_sqlite::FsType;
|
||||
|
||||
|
@ -175,6 +170,7 @@ pub trait BackendTransaction {
|
|||
/// Recursively apply a filter, transforming into IdList's on the way. This builds a query
|
||||
/// execution log, so that it can be examined how an operation proceeded.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
#[instrument(level = "debug", name = "be::filter2idl", skip_all)]
|
||||
fn filter2idl(
|
||||
&self,
|
||||
filt: &FilterResolved,
|
||||
|
@ -534,6 +530,7 @@ pub trait BackendTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "be::search", skip_all)]
|
||||
fn search(
|
||||
&self,
|
||||
erl: &Limits,
|
||||
|
@ -543,165 +540,150 @@ pub trait BackendTransaction {
|
|||
// Unlike DS, even if we don't get the index back, we can just pass
|
||||
// to the in-memory filter test and be done.
|
||||
|
||||
spanned!("be::search", {
|
||||
filter_trace!(?filt, "filter optimized");
|
||||
debug!(filter_optimised = ?filt);
|
||||
|
||||
let (idl, fplan) = trace_span!("be::search -> filter2idl").in_scope(|| {
|
||||
spanned!("be::search -> filter2idl", {
|
||||
self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD)
|
||||
})
|
||||
})?;
|
||||
let (idl, fplan) = trace_span!("be::search -> filter2idl")
|
||||
.in_scope(|| self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD))?;
|
||||
|
||||
filter_trace!(?fplan, "filter executed plan");
|
||||
debug!(filter_executed_plan = ?fplan);
|
||||
|
||||
match &idl {
|
||||
IdList::AllIds => {
|
||||
if !erl.unindexed_allow {
|
||||
admin_error!(
|
||||
"filter (search) is fully unindexed, and not allowed by resource limits"
|
||||
);
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
match &idl {
|
||||
IdList::AllIds => {
|
||||
if !erl.unindexed_allow {
|
||||
admin_error!(
|
||||
"filter (search) is fully unindexed, and not allowed by resource limits"
|
||||
);
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
IdList::Partial(idl_br) => {
|
||||
// if idl_br.len() > erl.search_max_filter_test {
|
||||
if !idl_br.below_threshold(erl.search_max_filter_test) {
|
||||
admin_error!("filter (search) is partial indexed and greater than search_max_filter_test allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
}
|
||||
IdList::PartialThreshold(_) => {
|
||||
// Since we opted for this, this is not the fault
|
||||
// of the user and we should not penalise them by limiting on partial.
|
||||
}
|
||||
IdList::Indexed(idl_br) => {
|
||||
// We know this is resolved here, so we can attempt the limit
|
||||
// check. This has to fold the whole index, but you know, class=pres is
|
||||
// indexed ...
|
||||
// if idl_br.len() > erl.search_max_results {
|
||||
if !idl_br.below_threshold(erl.search_max_results) {
|
||||
admin_error!("filter (search) is indexed and greater than search_max_results allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
let entries_filtered = match idl {
|
||||
IdList::AllIds => trace_span!("be::search<entry::ftest::allids>").in_scope(|| {
|
||||
spanned!("be::search<entry::ftest::allids>", {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
})
|
||||
}),
|
||||
IdList::Partial(_) => {
|
||||
trace_span!("be::search<entry::ftest::partial>").in_scope(|| {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
IdList::PartialThreshold(_) => trace_span!("be::search<entry::ftest::thresh>")
|
||||
.in_scope(|| {
|
||||
spanned!("be::search<entry::ftest::thresh>", {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
})
|
||||
}),
|
||||
// Since the index fully resolved, we can shortcut the filter test step here!
|
||||
IdList::Indexed(_) => {
|
||||
filter_trace!("filter (search) was fully indexed 👏");
|
||||
entries
|
||||
}
|
||||
};
|
||||
|
||||
// If the idl was not indexed, apply the resource limit now. Avoid the needless match since the
|
||||
// if statement is quick.
|
||||
if entries_filtered.len() > erl.search_max_results {
|
||||
admin_error!("filter (search) is resolved and greater than search_max_results allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
IdList::Partial(idl_br) => {
|
||||
// if idl_br.len() > erl.search_max_filter_test {
|
||||
if !idl_br.below_threshold(erl.search_max_filter_test) {
|
||||
admin_error!("filter (search) is partial indexed and greater than search_max_filter_test allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
}
|
||||
IdList::PartialThreshold(_) => {
|
||||
// Since we opted for this, this is not the fault
|
||||
// of the user and we should not penalise them by limiting on partial.
|
||||
}
|
||||
IdList::Indexed(idl_br) => {
|
||||
// We know this is resolved here, so we can attempt the limit
|
||||
// check. This has to fold the whole index, but you know, class=pres is
|
||||
// indexed ...
|
||||
// if idl_br.len() > erl.search_max_results {
|
||||
if !idl_br.below_threshold(erl.search_max_results) {
|
||||
admin_error!("filter (search) is indexed and greater than search_max_results allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(entries_filtered)
|
||||
})
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
let entries_filtered = match idl {
|
||||
IdList::AllIds => trace_span!("be::search<entry::ftest::allids>").in_scope(|| {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
}),
|
||||
IdList::Partial(_) => trace_span!("be::search<entry::ftest::partial>").in_scope(|| {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
}),
|
||||
IdList::PartialThreshold(_) => trace_span!("be::search<entry::ftest::thresh>")
|
||||
.in_scope(|| {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
}),
|
||||
// Since the index fully resolved, we can shortcut the filter test step here!
|
||||
IdList::Indexed(_) => {
|
||||
filter_trace!("filter (search) was fully indexed 👏");
|
||||
entries
|
||||
}
|
||||
};
|
||||
|
||||
// If the idl was not indexed, apply the resource limit now. Avoid the needless match since the
|
||||
// if statement is quick.
|
||||
if entries_filtered.len() > erl.search_max_results {
|
||||
admin_error!("filter (search) is resolved and greater than search_max_results allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
|
||||
Ok(entries_filtered)
|
||||
}
|
||||
|
||||
/// Given a filter, assert some condition exists.
|
||||
/// Basically, this is a specialised case of search, where we don't need to
|
||||
/// load any candidates if they match. This is heavily used in uuid
|
||||
/// refint and attr uniqueness.
|
||||
#[instrument(level = "debug", name = "be::exists", skip_all)]
|
||||
fn exists(
|
||||
&self,
|
||||
erl: &Limits,
|
||||
filt: &Filter<FilterValidResolved>,
|
||||
) -> Result<bool, OperationError> {
|
||||
let _entered = trace_span!("be::exists").entered();
|
||||
spanned!("be::exists", {
|
||||
filter_trace!(?filt, "filter optimised");
|
||||
debug!(filter_optimised = ?filt);
|
||||
|
||||
// Using the indexes, resolve the IdList here, or AllIds.
|
||||
// Also get if the filter was 100% resolved or not.
|
||||
let (idl, fplan) = spanned!("be::exists -> filter2idl", {
|
||||
spanned!("be::exists -> filter2idl", {
|
||||
self.filter2idl(filt.to_inner(), FILTER_EXISTS_TEST_THRESHOLD)
|
||||
})
|
||||
})?;
|
||||
// Using the indexes, resolve the IdList here, or AllIds.
|
||||
// Also get if the filter was 100% resolved or not.
|
||||
let (idl, fplan) = self.filter2idl(filt.to_inner(), FILTER_EXISTS_TEST_THRESHOLD)?;
|
||||
|
||||
filter_trace!(?fplan, "filter executed plan");
|
||||
debug!(filter_executed_plan = ?fplan);
|
||||
|
||||
// Apply limits to the IdList.
|
||||
match &idl {
|
||||
IdList::AllIds => {
|
||||
if !erl.unindexed_allow {
|
||||
admin_error!("filter (exists) is fully unindexed, and not allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
// Apply limits to the IdList.
|
||||
match &idl {
|
||||
IdList::AllIds => {
|
||||
if !erl.unindexed_allow {
|
||||
admin_error!(
|
||||
"filter (exists) is fully unindexed, and not allowed by resource limits"
|
||||
);
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
IdList::Partial(idl_br) => {
|
||||
if !idl_br.below_threshold(erl.search_max_filter_test) {
|
||||
admin_error!("filter (exists) is partial indexed and greater than search_max_filter_test allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
}
|
||||
IdList::PartialThreshold(_) => {
|
||||
// Since we opted for this, this is not the fault
|
||||
// of the user and we should not penalise them.
|
||||
}
|
||||
IdList::Indexed(_) => {}
|
||||
}
|
||||
|
||||
// Now, check the idl -- if it's fully resolved, we can skip this because the query
|
||||
// was fully indexed.
|
||||
match &idl {
|
||||
IdList::Indexed(idl) => Ok(!idl.is_empty()),
|
||||
_ => {
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
// if not 100% resolved query, apply the filter test.
|
||||
let entries_filtered: Vec<_> =
|
||||
spanned!("be::exists -> entry_match_no_index", {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
});
|
||||
|
||||
Ok(!entries_filtered.is_empty())
|
||||
IdList::Partial(idl_br) => {
|
||||
if !idl_br.below_threshold(erl.search_max_filter_test) {
|
||||
admin_error!("filter (exists) is partial indexed and greater than search_max_filter_test allowed by resource limits");
|
||||
return Err(OperationError::ResourceLimit);
|
||||
}
|
||||
} // end match idl
|
||||
}) // end spanned
|
||||
}
|
||||
IdList::PartialThreshold(_) => {
|
||||
// Since we opted for this, this is not the fault
|
||||
// of the user and we should not penalise them.
|
||||
}
|
||||
IdList::Indexed(_) => {}
|
||||
}
|
||||
|
||||
// Now, check the idl -- if it's fully resolved, we can skip this because the query
|
||||
// was fully indexed.
|
||||
match &idl {
|
||||
IdList::Indexed(idl) => Ok(!idl.is_empty()),
|
||||
_ => {
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
// if not 100% resolved query, apply the filter test.
|
||||
let entries_filtered: Vec<_> =
|
||||
trace_span!("be::exists<entry::ftest>").in_scope(|| {
|
||||
entries
|
||||
.into_iter()
|
||||
.filter(|e| e.entry_match_no_index(filt))
|
||||
.collect()
|
||||
});
|
||||
|
||||
Ok(!entries_filtered.is_empty())
|
||||
}
|
||||
} // end match idl
|
||||
}
|
||||
|
||||
fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
|
||||
|
@ -878,6 +860,7 @@ pub trait BackendTransaction {
|
|||
|
||||
impl<'a> BackendTransaction for BackendReadTransaction<'a> {
|
||||
type IdlLayerType = IdlArcSqliteReadTransaction<'a>;
|
||||
type RuvType = ReplicationUpdateVectorReadTransaction<'a>;
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
fn get_idlayer(&self) -> &mut IdlArcSqliteReadTransaction<'a> {
|
||||
|
@ -895,8 +878,6 @@ impl<'a> BackendTransaction for BackendReadTransaction<'a> {
|
|||
unsafe { &mut (*self.idlayer.get()) }
|
||||
}
|
||||
|
||||
type RuvType = ReplicationUpdateVectorReadTransaction<'a>;
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
fn get_ruv(&self) -> &mut ReplicationUpdateVectorReadTransaction<'a> {
|
||||
unsafe { &mut (*self.ruv.get()) }
|
||||
|
@ -930,14 +911,13 @@ impl<'a> BackendReadTransaction<'a> {
|
|||
|
||||
impl<'a> BackendTransaction for BackendWriteTransaction<'a> {
|
||||
type IdlLayerType = IdlArcSqliteWriteTransaction<'a>;
|
||||
type RuvType = ReplicationUpdateVectorWriteTransaction<'a>;
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
fn get_idlayer(&self) -> &mut IdlArcSqliteWriteTransaction<'a> {
|
||||
unsafe { &mut (*self.idlayer.get()) }
|
||||
}
|
||||
|
||||
type RuvType = ReplicationUpdateVectorWriteTransaction<'a>;
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
fn get_ruv(&self) -> &mut ReplicationUpdateVectorWriteTransaction<'a> {
|
||||
unsafe { &mut (*self.ruv.get()) }
|
||||
|
@ -949,181 +929,179 @@ impl<'a> BackendTransaction for BackendWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
impl<'a> BackendWriteTransaction<'a> {
|
||||
#[instrument(level = "debug", name = "be::create", skip_all)]
|
||||
pub fn create(
|
||||
&self,
|
||||
cid: &Cid,
|
||||
entries: Vec<Entry<EntrySealed, EntryNew>>,
|
||||
) -> Result<Vec<Entry<EntrySealed, EntryCommitted>>, OperationError> {
|
||||
spanned!("be::create", {
|
||||
if entries.is_empty() {
|
||||
admin_error!("No entries provided to BE to create, invalid server call!");
|
||||
return Err(OperationError::EmptyRequest);
|
||||
if entries.is_empty() {
|
||||
admin_error!("No entries provided to BE to create, invalid server call!");
|
||||
return Err(OperationError::EmptyRequest);
|
||||
}
|
||||
|
||||
// Check that every entry has a change associated
|
||||
// that matches the cid?
|
||||
entries.iter().try_for_each(|e| {
|
||||
if e.get_changelog().contains_tail_cid(cid) {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!(
|
||||
"Entry changelog does not contain a change related to this transaction"
|
||||
);
|
||||
Err(OperationError::ReplEntryNotChanged)
|
||||
}
|
||||
})?;
|
||||
|
||||
// Check that every entry has a change associated
|
||||
// that matches the cid?
|
||||
entries.iter().try_for_each(|e| {
|
||||
if e.get_changelog().contains_tail_cid(cid) {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!(
|
||||
"Entry changelog does not contain a change related to this transaction"
|
||||
);
|
||||
Err(OperationError::ReplEntryNotChanged)
|
||||
}
|
||||
})?;
|
||||
let idlayer = self.get_idlayer();
|
||||
// Now, assign id's to all the new entries.
|
||||
|
||||
let idlayer = self.get_idlayer();
|
||||
// Now, assign id's to all the new entries.
|
||||
let mut id_max = idlayer.get_id2entry_max_id()?;
|
||||
let c_entries: Vec<_> = entries
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
id_max += 1;
|
||||
e.into_sealed_committed_id(id_max)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut id_max = idlayer.get_id2entry_max_id()?;
|
||||
let c_entries: Vec<_> = entries
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
id_max += 1;
|
||||
e.into_sealed_committed_id(id_max)
|
||||
})
|
||||
.collect();
|
||||
// All good, lets update the RUV.
|
||||
// This auto compresses.
|
||||
let ruv_idl = IDLBitRange::from_iter(c_entries.iter().map(|e| e.get_id()));
|
||||
|
||||
// All good, lets update the RUV.
|
||||
// This auto compresses.
|
||||
let ruv_idl = IDLBitRange::from_iter(c_entries.iter().map(|e| e.get_id()));
|
||||
self.get_ruv().insert_change(cid, ruv_idl)?;
|
||||
|
||||
self.get_ruv().insert_change(cid, ruv_idl)?;
|
||||
idlayer.write_identries(c_entries.iter())?;
|
||||
|
||||
idlayer.write_identries(c_entries.iter())?;
|
||||
idlayer.set_id2entry_max_id(id_max);
|
||||
|
||||
idlayer.set_id2entry_max_id(id_max);
|
||||
// Now update the indexes as required.
|
||||
for e in c_entries.iter() {
|
||||
self.entry_index(None, Some(e))?
|
||||
}
|
||||
|
||||
// Now update the indexes as required.
|
||||
for e in c_entries.iter() {
|
||||
self.entry_index(None, Some(e))?
|
||||
}
|
||||
|
||||
Ok(c_entries)
|
||||
})
|
||||
Ok(c_entries)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "be::modify", skip_all)]
|
||||
pub fn modify(
|
||||
&self,
|
||||
cid: &Cid,
|
||||
pre_entries: &[Arc<EntrySealedCommitted>],
|
||||
post_entries: &[EntrySealedCommitted],
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("be::modify", {
|
||||
if post_entries.is_empty() || pre_entries.is_empty() {
|
||||
admin_error!("No entries provided to BE to modify, invalid server call!");
|
||||
return Err(OperationError::EmptyRequest);
|
||||
if post_entries.is_empty() || pre_entries.is_empty() {
|
||||
admin_error!("No entries provided to BE to modify, invalid server call!");
|
||||
return Err(OperationError::EmptyRequest);
|
||||
}
|
||||
|
||||
assert!(post_entries.len() == pre_entries.len());
|
||||
|
||||
post_entries.iter().try_for_each(|e| {
|
||||
if e.get_changelog().contains_tail_cid(cid) {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!(
|
||||
"Entry changelog does not contain a change related to this transaction"
|
||||
);
|
||||
Err(OperationError::ReplEntryNotChanged)
|
||||
}
|
||||
})?;
|
||||
|
||||
assert!(post_entries.len() == pre_entries.len());
|
||||
// All good, lets update the RUV.
|
||||
// This auto compresses.
|
||||
let ruv_idl = IDLBitRange::from_iter(post_entries.iter().map(|e| e.get_id()));
|
||||
self.get_ruv().insert_change(cid, ruv_idl)?;
|
||||
|
||||
post_entries.iter().try_for_each(|e| {
|
||||
if e.get_changelog().contains_tail_cid(cid) {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!(
|
||||
"Entry changelog does not contain a change related to this transaction"
|
||||
);
|
||||
Err(OperationError::ReplEntryNotChanged)
|
||||
}
|
||||
})?;
|
||||
// Now, given the list of id's, update them
|
||||
self.get_idlayer().write_identries(post_entries.iter())?;
|
||||
|
||||
// All good, lets update the RUV.
|
||||
// This auto compresses.
|
||||
let ruv_idl = IDLBitRange::from_iter(post_entries.iter().map(|e| e.get_id()));
|
||||
self.get_ruv().insert_change(cid, ruv_idl)?;
|
||||
|
||||
// Now, given the list of id's, update them
|
||||
self.get_idlayer().write_identries(post_entries.iter())?;
|
||||
|
||||
// Finally, we now reindex all the changed entries. We do this by iterating and zipping
|
||||
// over the set, because we know the list is in the same order.
|
||||
pre_entries
|
||||
.iter()
|
||||
.zip(post_entries.iter())
|
||||
.try_for_each(|(pre, post)| self.entry_index(Some(pre.as_ref()), Some(post)))
|
||||
})
|
||||
// Finally, we now reindex all the changed entries. We do this by iterating and zipping
|
||||
// over the set, because we know the list is in the same order.
|
||||
pre_entries
|
||||
.iter()
|
||||
.zip(post_entries.iter())
|
||||
.try_for_each(|(pre, post)| self.entry_index(Some(pre.as_ref()), Some(post)))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "be::reap_tombstones", skip_all)]
|
||||
pub fn reap_tombstones(&self, cid: &Cid) -> Result<usize, OperationError> {
|
||||
spanned!("be::reap_tombstones", {
|
||||
// We plan to clear the RUV up to this cid. So we need to build an IDL
|
||||
// of all the entries we need to examine.
|
||||
let idl = self.get_ruv().trim_up_to(cid).map_err(|e| {
|
||||
admin_error!(?e, "failed to trim RUV to {:?}", cid);
|
||||
// We plan to clear the RUV up to this cid. So we need to build an IDL
|
||||
// of all the entries we need to examine.
|
||||
let idl = self.get_ruv().trim_up_to(cid).map_err(|e| {
|
||||
admin_error!(?e, "failed to trim RUV to {:?}", cid);
|
||||
e
|
||||
})?;
|
||||
|
||||
let entries = self
|
||||
.get_idlayer()
|
||||
.get_identry(&IdList::Indexed(idl))
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
let entries = self
|
||||
.get_idlayer()
|
||||
.get_identry(&IdList::Indexed(idl))
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
if entries.is_empty() {
|
||||
admin_info!("No entries affected - reap_tombstones operation success");
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if entries.is_empty() {
|
||||
admin_info!("No entries affected - reap_tombstones operation success");
|
||||
return Ok(0);
|
||||
}
|
||||
// Now that we have a list of entries we need to partition them into
|
||||
// two sets. The entries that are tombstoned and ready to reap_tombstones, and
|
||||
// the entries that need to have their change logs trimmed.
|
||||
|
||||
// Now that we have a list of entries we need to partition them into
|
||||
// two sets. The entries that are tombstoned and ready to reap_tombstones, and
|
||||
// the entries that need to have their change logs trimmed.
|
||||
// First we trim changelogs. Go through each entry, and trim the CL, and write it back.
|
||||
let mut entries: Vec<_> = entries.iter().map(|er| er.as_ref().clone()).collect();
|
||||
|
||||
// First we trim changelogs. Go through each entry, and trim the CL, and write it back.
|
||||
let mut entries: Vec<_> = entries.iter().map(|er| er.as_ref().clone()).collect();
|
||||
entries
|
||||
.iter_mut()
|
||||
.try_for_each(|e| e.get_changelog_mut().trim_up_to(cid))?;
|
||||
|
||||
entries
|
||||
.iter_mut()
|
||||
.try_for_each(|e| e.get_changelog_mut().trim_up_to(cid))?;
|
||||
// Write down the cl trims
|
||||
self.get_idlayer().write_identries(entries.iter())?;
|
||||
|
||||
// Write down the cl trims
|
||||
self.get_idlayer().write_identries(entries.iter())?;
|
||||
let (tombstones, leftover): (Vec<_>, Vec<_>) = entries
|
||||
.into_iter()
|
||||
.partition(|e| e.get_changelog().can_delete());
|
||||
|
||||
let (tombstones, leftover): (Vec<_>, Vec<_>) = entries
|
||||
.into_iter()
|
||||
.partition(|e| e.get_changelog().can_delete());
|
||||
// Assert that anything leftover still either is *alive* OR is a tombstone
|
||||
// and has entries in the RUV!
|
||||
let ruv_idls = self.get_ruv().ruv_idls();
|
||||
|
||||
// Assert that anything leftover still either is *alive* OR is a tombstone
|
||||
// and has entries in the RUV!
|
||||
let ruv_idls = self.get_ruv().ruv_idls();
|
||||
if !leftover
|
||||
.iter()
|
||||
.all(|e| e.get_changelog().is_live() || ruv_idls.contains(e.get_id()))
|
||||
{
|
||||
admin_error!("Left over entries may be orphaned due to missing RUV entries");
|
||||
return Err(OperationError::ReplInvalidRUVState);
|
||||
}
|
||||
|
||||
if !leftover
|
||||
.iter()
|
||||
.all(|e| e.get_changelog().is_live() || ruv_idls.contains(e.get_id()))
|
||||
{
|
||||
admin_error!("Left over entries may be orphaned due to missing RUV entries");
|
||||
return Err(OperationError::ReplInvalidRUVState);
|
||||
}
|
||||
// Now setup to reap_tombstones the tombstones. Remember, in the post cleanup, it's could
|
||||
// now have been trimmed to a point we can purge them!
|
||||
|
||||
// Now setup to reap_tombstones the tombstones. Remember, in the post cleanup, it's could
|
||||
// now have been trimmed to a point we can purge them!
|
||||
// Assert the id's exist on the entry.
|
||||
let id_list: IDLBitRange = tombstones.iter().map(|e| e.get_id()).collect();
|
||||
|
||||
// Assert the id's exist on the entry.
|
||||
let id_list: IDLBitRange = tombstones.iter().map(|e| e.get_id()).collect();
|
||||
// Ensure nothing here exists in the RUV index, else it means
|
||||
// we didn't trim properly, or some other state violation has occured.
|
||||
if !((&ruv_idls & &id_list).is_empty()) {
|
||||
admin_error!("RUV still contains entries that are going to be removed.");
|
||||
return Err(OperationError::ReplInvalidRUVState);
|
||||
}
|
||||
|
||||
// Ensure nothing here exists in the RUV index, else it means
|
||||
// we didn't trim properly, or some other state violation has occured.
|
||||
if !((&ruv_idls & &id_list).is_empty()) {
|
||||
admin_error!("RUV still contains entries that are going to be removed.");
|
||||
return Err(OperationError::ReplInvalidRUVState);
|
||||
}
|
||||
// Now, given the list of id's, reap_tombstones them.
|
||||
let sz = id_list.len();
|
||||
self.get_idlayer().delete_identry(id_list.into_iter())?;
|
||||
|
||||
// Now, given the list of id's, reap_tombstones them.
|
||||
let sz = id_list.len();
|
||||
self.get_idlayer().delete_identry(id_list.into_iter())?;
|
||||
// Finally, purge the indexes from the entries we removed.
|
||||
tombstones
|
||||
.iter()
|
||||
.try_for_each(|e| self.entry_index(Some(e), None))?;
|
||||
|
||||
// Finally, purge the indexes from the entries we removed.
|
||||
tombstones
|
||||
.iter()
|
||||
.try_for_each(|e| self.entry_index(Some(e), None))?;
|
||||
|
||||
Ok(sz)
|
||||
})
|
||||
Ok(sz)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "be::update_idxmeta", skip_all)]
|
||||
pub fn update_idxmeta(&mut self, idxkeys: Vec<IdxKey>) -> Result<(), OperationError> {
|
||||
if self.is_idx_slopeyness_generated()? {
|
||||
trace!("Indexing slopes available");
|
||||
|
@ -1522,25 +1500,24 @@ impl<'a> BackendWriteTransaction<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "be::ruv_rebuild", skip_all)]
|
||||
pub fn ruv_rebuild(&mut self) -> Result<(), OperationError> {
|
||||
// Rebuild the ruv!
|
||||
spanned!("server::ruv_rebuild", {
|
||||
// For now this has to read from all the entries in the DB, but in the future
|
||||
// we'll actually store this properly (?). If it turns out this is really fast
|
||||
// we may just rebuild this always on startup.
|
||||
// For now this has to read from all the entries in the DB, but in the future
|
||||
// we'll actually store this properly (?). If it turns out this is really fast
|
||||
// we may just rebuild this always on startup.
|
||||
|
||||
// NOTE: An important detail is that we don't rely on indexes here!
|
||||
// NOTE: An important detail is that we don't rely on indexes here!
|
||||
|
||||
let idl = IdList::AllIds;
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
let idl = IdList::AllIds;
|
||||
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
|
||||
admin_error!(?e, "get_identry failed");
|
||||
e
|
||||
})?;
|
||||
|
||||
self.get_ruv().rebuild(&entries)?;
|
||||
self.get_ruv().rebuild(&entries)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Result<(), OperationError> {
|
||||
|
@ -1639,6 +1616,7 @@ fn get_idx_slope_default(ikey: &IdxKey) -> IdxSlope {
|
|||
|
||||
// In the future this will do the routing between the chosen backends etc.
|
||||
impl Backend {
|
||||
#[instrument(level = "debug", name = "be::new", skip_all)]
|
||||
pub fn new(
|
||||
mut cfg: BackendConfig,
|
||||
// path: &str,
|
||||
|
@ -1675,40 +1653,38 @@ impl Backend {
|
|||
let ruv = Arc::new(ReplicationUpdateVector::default());
|
||||
|
||||
// this has a ::memory() type, but will path == "" work?
|
||||
spanned!("be::new", {
|
||||
let idlayer = Arc::new(IdlArcSqlite::new(&cfg, vacuum)?);
|
||||
let be = Backend {
|
||||
cfg,
|
||||
idlayer,
|
||||
ruv,
|
||||
idxmeta: Arc::new(CowCell::new(IdxMeta::new(idxkeys))),
|
||||
};
|
||||
let idlayer = Arc::new(IdlArcSqlite::new(&cfg, vacuum)?);
|
||||
let be = Backend {
|
||||
cfg,
|
||||
idlayer,
|
||||
ruv,
|
||||
idxmeta: Arc::new(CowCell::new(IdxMeta::new(idxkeys))),
|
||||
};
|
||||
|
||||
// Now complete our setup with a txn
|
||||
// In this case we can use an empty idx meta because we don't
|
||||
// access any parts of
|
||||
// the indexing subsystem here.
|
||||
let mut idl_write = be.idlayer.write();
|
||||
idl_write
|
||||
.setup()
|
||||
.and_then(|_| idl_write.commit())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to setup idlayer");
|
||||
e
|
||||
})?;
|
||||
// Now complete our setup with a txn
|
||||
// In this case we can use an empty idx meta because we don't
|
||||
// access any parts of
|
||||
// the indexing subsystem here.
|
||||
let mut idl_write = be.idlayer.write();
|
||||
idl_write
|
||||
.setup()
|
||||
.and_then(|_| idl_write.commit())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to setup idlayer");
|
||||
e
|
||||
})?;
|
||||
|
||||
// Now rebuild the ruv.
|
||||
let mut be_write = be.write();
|
||||
be_write
|
||||
.ruv_rebuild()
|
||||
.and_then(|_| be_write.commit())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to reload ruv");
|
||||
e
|
||||
})?;
|
||||
// Now rebuild the ruv.
|
||||
let mut be_write = be.write();
|
||||
be_write
|
||||
.ruv_rebuild()
|
||||
.and_then(|_| be_write.commit())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Failed to reload ruv");
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok(be)
|
||||
})
|
||||
Ok(be)
|
||||
}
|
||||
|
||||
pub fn get_pool_size(&self) -> u32 {
|
||||
|
@ -1762,22 +1738,23 @@ impl Backend {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use idlset::v2::IDLBitRange;
|
||||
use std::fs;
|
||||
use std::iter::FromIterator;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use idlset::v2::IDLBitRange;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::super::entry::{Entry, EntryInit, EntryNew};
|
||||
use super::{
|
||||
Backend, BackendConfig, BackendTransaction, BackendWriteTransaction, IdList, OperationError,
|
||||
Backend, BackendConfig, BackendTransaction, BackendWriteTransaction, DbBackup, IdList,
|
||||
IdxKey, OperationError,
|
||||
};
|
||||
use super::{DbBackup, IdxKey};
|
||||
use crate::identity::Limits;
|
||||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use crate::value::{IndexType, PartialValue, Value};
|
||||
use std::time::Duration;
|
||||
|
||||
lazy_static! {
|
||||
static ref CID_ZERO: Cid = unsafe { Cid::new_zero() };
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
//! These components should be "per server". Any "per domain" config should be in the system
|
||||
//! or domain entries that are able to be replicated.
|
||||
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use kanidm_proto::messages::ConsoleOutputMode;
|
||||
use rand::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct IntegrationTestConfig {
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
use crate::be::dbvalue::DbBackupCodeV1;
|
||||
use crate::be::dbvalue::{DbCred, DbPasswordV1};
|
||||
use hashbrown::HashMap as Map;
|
||||
use hashbrown::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use hashbrown::{HashMap as Map, HashSet};
|
||||
use kanidm_proto::v1::{BackupCodesView, CredentialDetail, CredentialDetailType, OperationError};
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkcs5::pbkdf2_hmac;
|
||||
use openssl::sha::Sha512;
|
||||
use rand::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::{Duration, Instant};
|
||||
use uuid::Uuid;
|
||||
|
||||
use webauthn_rs_core::proto::Credential as WebauthnCredential;
|
||||
use webauthn_rs_core::proto::CredentialV3;
|
||||
|
||||
use webauthn_rs::prelude::{AuthenticationResult, Passkey, SecurityKey};
|
||||
use webauthn_rs_core::proto::{Credential as WebauthnCredential, CredentialV3};
|
||||
|
||||
use crate::be::dbvalue::{DbBackupCodeV1, DbCred, DbPasswordV1};
|
||||
|
||||
pub mod policy;
|
||||
pub mod softlock;
|
||||
|
@ -234,12 +231,15 @@ impl BackupCodes {
|
|||
pub fn new(code_set: HashSet<String>) -> Self {
|
||||
BackupCodes { code_set }
|
||||
}
|
||||
|
||||
pub fn verify(&self, code_chal: &str) -> bool {
|
||||
self.code_set.contains(code_chal)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, code_chal: &str) -> bool {
|
||||
self.code_set.remove(code_chal)
|
||||
}
|
||||
|
||||
pub fn to_dbbackupcodev1(&self) -> DbBackupCodeV1 {
|
||||
DbBackupCodeV1 {
|
||||
code_set: self.code_set.clone(),
|
||||
|
@ -892,9 +892,10 @@ impl CredentialType {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_credential_simple() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::Password;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::Password;
|
||||
|
||||
const PBKDF2_MIN_NIST_COST: u64 = 10000;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use crate::be::dbvalue::{DbTotpAlgoV1, DbTotpV1};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use kanidm_proto::v1::{TotpAlgo as ProtoTotpAlgo, TotpSecret as ProtoTotp};
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::PKey;
|
||||
use openssl::sign::Signer;
|
||||
use rand::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use kanidm_proto::v1::TotpAlgo as ProtoTotpAlgo;
|
||||
use kanidm_proto::v1::TotpSecret as ProtoTotp;
|
||||
use crate::be::dbvalue::{DbTotpAlgoV1, DbTotpV1};
|
||||
|
||||
// This is 64 bits of entropy, as the examples in https://tools.ietf.org/html/rfc6238 show.
|
||||
const SECRET_SIZE_BYTES: usize = 8;
|
||||
|
@ -192,9 +191,10 @@ impl Totp {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::credential::totp::{Totp, TotpAlgo, TotpError, TOTP_DEFAULT_STEP};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::credential::totp::{Totp, TotpAlgo, TotpError, TOTP_DEFAULT_STEP};
|
||||
|
||||
#[test]
|
||||
fn hotp_basic() {
|
||||
let otp_sha1 = Totp::new(vec![0], 30, TotpAlgo::Sha1);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! This module contains cryptographic setup code, a long with what policy
|
||||
//! and ciphers we accept.
|
||||
|
||||
use crate::config::Configuration;
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||
|
||||
use crate::config::Configuration;
|
||||
|
||||
/// From the server configuration, generate an OpenSSL acceptor that we can use
|
||||
/// to build our sockets for https/ldaps.
|
||||
pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> {
|
||||
|
|
|
@ -24,6 +24,26 @@
|
|||
//! [`filter`]: ../filter/index.html
|
||||
//! [`schema`]: ../schema/index.html
|
||||
|
||||
use std::cmp::Ordering;
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use compact_jwt::JwsSigner;
|
||||
use hashbrown::HashMap;
|
||||
use kanidm_proto::v1::{
|
||||
ConsistencyError, Entry as ProtoEntry, Filter as ProtoFilter, OperationError, SchemaError,
|
||||
};
|
||||
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::{DeviceKey as DeviceKeyV4, Passkey as PasskeyV4};
|
||||
|
||||
use crate::be::dbentry::{DbEntry, DbEntryV2, DbEntryVers};
|
||||
use crate::be::dbvalue::DbValueSetV2;
|
||||
use crate::be::{IdxKey, IdxSlope};
|
||||
use crate::credential::Credential;
|
||||
use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
|
||||
use crate::ldap::ldap_vattr_map;
|
||||
|
@ -32,33 +52,8 @@ use crate::prelude::*;
|
|||
use crate::repl::cid::Cid;
|
||||
use crate::repl::entry::EntryChangelog;
|
||||
use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
|
||||
use crate::value::{IndexType, SyntaxType};
|
||||
use crate::value::{IntentTokenState, PartialValue, Session, Value};
|
||||
use crate::value::{IndexType, IntentTokenState, PartialValue, Session, SyntaxType, Value};
|
||||
use crate::valueset::{self, ValueSet};
|
||||
use kanidm_proto::v1::Entry as ProtoEntry;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError, SchemaError};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::be::dbentry::{DbEntry, DbEntryV2, DbEntryVers};
|
||||
use crate::be::dbvalue::DbValueSetV2;
|
||||
use crate::be::{IdxKey, IdxSlope};
|
||||
|
||||
use compact_jwt::JwsSigner;
|
||||
use hashbrown::HashMap;
|
||||
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap as Map;
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
|
||||
// use std::convert::TryFrom;
|
||||
// use std::str::FromStr;
|
||||
|
@ -222,7 +217,6 @@ pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
|
|||
/// [`schema`]: ../schema/index.html
|
||||
/// [`access`]: ../access/index.html
|
||||
/// [`event`]: ../event/index.html
|
||||
///
|
||||
pub struct Entry<VALID, STATE> {
|
||||
valid: VALID,
|
||||
state: STATE,
|
||||
|
@ -323,29 +317,28 @@ impl Entry<EntryInit, EntryNew> {
|
|||
|
||||
/// Given a proto entry in JSON formed as a serialised string, processed that string
|
||||
/// into an Entry.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn from_proto_entry_str(
|
||||
es: &str,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("from_proto_entry_str", {
|
||||
if cfg!(test) {
|
||||
if es.len() > 256 {
|
||||
let (dsp_es, _) = es.split_at(255);
|
||||
trace!("Parsing -> {}...", dsp_es);
|
||||
} else {
|
||||
trace!("Parsing -> {}", es);
|
||||
}
|
||||
if cfg!(test) {
|
||||
if es.len() > 256 {
|
||||
let (dsp_es, _) = es.split_at(255);
|
||||
trace!("Parsing -> {}...", dsp_es);
|
||||
} else {
|
||||
trace!("Parsing -> {}", es);
|
||||
}
|
||||
// str -> Proto entry
|
||||
let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
|
||||
// We probably shouldn't print ES here because that would allow users
|
||||
// to inject content into our logs :)
|
||||
admin_error!(?e, "SerdeJson Failure");
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
// now call from_proto_entry
|
||||
Self::from_proto_entry(&pe, qs)
|
||||
})
|
||||
}
|
||||
// str -> Proto entry
|
||||
let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
|
||||
// We probably shouldn't print ES here because that would allow users
|
||||
// to inject content into our logs :)
|
||||
admin_error!(?e, "SerdeJson Failure");
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
// now call from_proto_entry
|
||||
Self::from_proto_entry(&pe, qs)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2471,13 +2464,15 @@ impl From<&SchemaClass> for Entry<EntryInit, EntryNew> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet as Set;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use smartstring::alias::String as AttrString;
|
||||
|
||||
use crate::be::{IdxKey, IdxSlope};
|
||||
use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
|
||||
use crate::modify::{Modify, ModifyList};
|
||||
use crate::value::{IndexType, PartialValue, Value};
|
||||
use hashbrown::HashMap;
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::collections::BTreeSet as Set;
|
||||
|
||||
#[test]
|
||||
fn test_entry_basic() {
|
||||
|
|
|
@ -15,6 +15,21 @@
|
|||
//! with the operation, and a clear path to know how to transform events between
|
||||
//! various types.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
#[cfg(test)]
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::{
|
||||
AuthCredential, AuthMech, AuthRequest, AuthStep, CreateRequest, DeleteRequest,
|
||||
Entry as ProtoEntry, ModifyList as ProtoModifyList, ModifyRequest, OperationError,
|
||||
SearchRequest, SearchResponse, WhoamiResponse,
|
||||
};
|
||||
use ldap3_proto::simple::LdapFilter;
|
||||
use uuid::Uuid;
|
||||
#[cfg(test)]
|
||||
use webauthn_rs::prelude::PublicKeyCredential;
|
||||
|
||||
use crate::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced};
|
||||
use crate::filter::{Filter, FilterInvalid, FilterValid};
|
||||
use crate::identity::Limits;
|
||||
|
@ -23,24 +38,6 @@ use crate::modify::{ModifyInvalid, ModifyList, ModifyValid};
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::value::PartialValue;
|
||||
use kanidm_proto::v1::Entry as ProtoEntry;
|
||||
use kanidm_proto::v1::ModifyList as ProtoModifyList;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{
|
||||
AuthCredential, AuthMech, AuthRequest, AuthStep, CreateRequest, DeleteRequest, ModifyRequest,
|
||||
SearchRequest, SearchResponse, WhoamiResponse,
|
||||
};
|
||||
|
||||
use ldap3_proto::simple::LdapFilter;
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(test)]
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
use webauthn_rs::prelude::PublicKeyCredential;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SearchResult {
|
||||
|
|
|
@ -8,28 +8,28 @@
|
|||
//! [`Filter`]: struct.Filter.html
|
||||
//! [`Entry`]: ../entry/struct.Entry.html
|
||||
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::Hash;
|
||||
use std::iter;
|
||||
use std::num::NonZeroU8;
|
||||
|
||||
use concread::arcache::ARCacheReadTxn;
|
||||
use hashbrown::HashMap;
|
||||
#[cfg(test)]
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{Filter as ProtoFilter, OperationError, SchemaError};
|
||||
use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter};
|
||||
// use smartstring::alias::String as AttrString;
|
||||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::be::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxMeta, IdxSlope};
|
||||
use crate::identity::IdentityId;
|
||||
use crate::ldap::ldap_attr_filter_map;
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::value::{IndexType, PartialValue};
|
||||
use concread::arcache::ARCacheReadTxn;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use kanidm_proto::v1::{OperationError, SchemaError};
|
||||
use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter};
|
||||
// use smartstring::alias::String as AttrString;
|
||||
use serde::Deserialize;
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::Hash;
|
||||
use std::iter;
|
||||
use std::num::NonZeroU8;
|
||||
use uuid::Uuid;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
#[cfg(test)]
|
||||
use hashbrown::HashSet;
|
||||
|
||||
const FILTER_DEPTH_MAX: usize = 16;
|
||||
|
||||
|
@ -491,51 +491,48 @@ impl Filter<FilterInvalid> {
|
|||
// This has to have two versions to account for ro/rw traits, because RS can't
|
||||
// monomorphise on the trait to call clone_value. An option is to make a fn that
|
||||
// takes "clone_value(t, a, v) instead, but that may have a similar issue.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn from_ro(
|
||||
ev: &Identity,
|
||||
f: &ProtoFilter,
|
||||
qs: &QueryServerReadTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("filer::from_ro", {
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn from_rw(
|
||||
ev: &Identity,
|
||||
f: &ProtoFilter,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("filter::from_rw", {
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn from_ldap_ro(
|
||||
ev: &Identity,
|
||||
f: &LdapFilter,
|
||||
qs: &QueryServerReadTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("filter::from_ldap_ro", {
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
let depth = FILTER_DEPTH_MAX;
|
||||
let mut elems = ev.limits.filter_max_elements;
|
||||
Ok(Filter {
|
||||
state: FilterInvalid {
|
||||
inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -948,7 +945,6 @@ impl PartialOrd for FilterResolved {
|
|||
|
||||
impl Ord for FilterResolved {
|
||||
/// Ordering of filters for optimisation and subsequent dead term elimination.
|
||||
///
|
||||
fn cmp(&self, rhs: &FilterResolved) -> Ordering {
|
||||
let left_slopey = self.get_slopeyness_factor();
|
||||
let right_slopey = rhs.get_slopeyness_factor();
|
||||
|
@ -1330,10 +1326,6 @@ impl FilterResolved {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::event::CreateEvent;
|
||||
use crate::event::DeleteEvent;
|
||||
use crate::filter::{Filter, FilterInvalid, FILTER_DEPTH_MAX};
|
||||
use crate::prelude::*;
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Duration;
|
||||
|
@ -1341,6 +1333,10 @@ mod tests {
|
|||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use ldap3_proto::simple::LdapFilter;
|
||||
|
||||
use crate::event::{CreateEvent, DeleteEvent};
|
||||
use crate::filter::{Filter, FilterInvalid, FILTER_DEPTH_MAX};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_filter_simple() {
|
||||
// Test construction.
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
//! and this provides the set of `Limits` to confine how many resources that the
|
||||
//! identity may consume during operations to prevent denial-of-service.
|
||||
|
||||
use crate::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Limits on the resources a single event can consume. These are defined per-event
|
||||
/// as they are derived from the userAuthToken based on that individual session
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed};
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::UiHint;
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
use kanidm_proto::v1::{BackupCodesView, CredentialStatus};
|
||||
|
||||
use webauthn_rs::prelude::CredentialID;
|
||||
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
use kanidm_proto::v1::{
|
||||
AuthType, BackupCodesView, CredentialStatus, OperationError, UiHint, UserAuthToken,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::{
|
||||
AuthenticationResult, CredentialID, DeviceKey as DeviceKeyV4, Passkey as PasskeyV4,
|
||||
};
|
||||
|
||||
use crate::constants::UUID_ANONYMOUS;
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::{softlock::CredSoftLockPolicy, Credential};
|
||||
use crate::credential::softlock::CredSoftLockPolicy;
|
||||
use crate::credential::Credential;
|
||||
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed};
|
||||
use crate::idm::group::Group;
|
||||
use crate::idm::server::IdmServerProxyWriteTransaction;
|
||||
use crate::modify::{ModifyInvalid, ModifyList};
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::value::{IntentTokenState, PartialValue, Value};
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::AuthenticationResult;
|
||||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");
|
||||
static ref PVCLASS_POSIXACCOUNT: PartialValue = PartialValue::new_class("posixaccount");
|
||||
|
@ -153,34 +150,31 @@ pub(crate) struct Account {
|
|||
}
|
||||
|
||||
impl Account {
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub(crate) fn try_from_entry_ro(
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
qs: &mut QueryServerReadTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("idm::account::try_from_entry_ro", {
|
||||
let groups = Group::try_from_account_entry_ro(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
})
|
||||
let groups = Group::try_from_account_entry_ro(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub(crate) fn try_from_entry_rw(
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
qs: &mut QueryServerWriteTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("idm::account::try_from_entry_rw", {
|
||||
let groups = Group::try_from_account_entry_rw(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
})
|
||||
let groups = Group::try_from_account_entry_rw(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub(crate) fn try_from_entry_reduced(
|
||||
value: &Entry<EntryReduced, EntryCommitted>,
|
||||
qs: &mut QueryServerReadTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("idm::account::try_from_entry_reduced", {
|
||||
let groups = Group::try_from_account_entry_red_ro(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
})
|
||||
let groups = Group::try_from_account_entry_red_ro(value, qs)?;
|
||||
try_from_entry!(value, groups)
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_entry_no_groups(
|
||||
|
|
|
@ -2,34 +2,32 @@
|
|||
//! Generally this has to process an authentication attempt, and validate each
|
||||
//! factor to assert that the user is legitimate. This also contains some
|
||||
//! support code for asynchronous task execution.
|
||||
use crate::credential::BackupCodes;
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::delayed::BackupCodeRemoval;
|
||||
use crate::idm::AuthState;
|
||||
use crate::prelude::*;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech, AuthType};
|
||||
|
||||
use crate::credential::{totp::Totp, Credential, CredentialType, Password};
|
||||
|
||||
use crate::idm::delayed::{DelayedAction, PasswordUpgrade, WebauthnCounterIncrement};
|
||||
// use crossbeam::channel::Sender;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
// use webauthn_rs::proto::Credential as WebauthnCredential;
|
||||
use compact_jwt::{Jws, JwsSigner};
|
||||
use std::collections::BTreeMap;
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
|
||||
// use webauthn_rs::proto::Credential as WebauthnCredential;
|
||||
use compact_jwt::{Jws, JwsSigner};
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech, AuthType, OperationError};
|
||||
// use crossbeam::channel::Sender;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
use uuid::Uuid;
|
||||
// use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
use webauthn_rs::prelude::{
|
||||
PasskeyAuthentication, RequestChallengeResponse, SecurityKeyAuthentication, Webauthn,
|
||||
};
|
||||
// use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
|
||||
use crate::credential::totp::Totp;
|
||||
use crate::credential::{BackupCodes, Credential, CredentialType, Password};
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::delayed::{
|
||||
BackupCodeRemoval, DelayedAction, PasswordUpgrade, WebauthnCounterIncrement,
|
||||
};
|
||||
use crate::idm::AuthState;
|
||||
use crate::prelude::*;
|
||||
|
||||
// Each CredHandler takes one or more credentials and determines if the
|
||||
// handlers requirements can be 100% fufilled. This is where MFA or other
|
||||
|
@ -405,7 +403,9 @@ impl CredHandler {
|
|||
CredState::Denied(BAD_AUTH_TYPE_MSG)
|
||||
}
|
||||
}
|
||||
} // end CredHandler::PasswordMfa
|
||||
}
|
||||
|
||||
// end CredHandler::PasswordMfa
|
||||
|
||||
/// Validate a webauthn authentication attempt
|
||||
pub fn validate_webauthn(
|
||||
|
@ -832,6 +832,16 @@ impl AuthSession {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
use std::time::Duration;
|
||||
|
||||
use compact_jwt::JwsSigner;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
|
||||
use tokio::sync::mpsc::unbounded_channel as unbounded;
|
||||
use webauthn_authenticator_rs::softpasskey::SoftPasskey;
|
||||
use webauthn_authenticator_rs::WebauthnAuthenticator;
|
||||
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::{BackupCodes, Credential};
|
||||
|
@ -842,17 +852,7 @@ mod tests {
|
|||
use crate::idm::delayed::DelayedAction;
|
||||
use crate::idm::AuthState;
|
||||
use crate::prelude::*;
|
||||
use hashbrown::HashSet;
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
|
||||
use crate::utils::{duration_from_epoch_now, readable_password_from_random};
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::sync::mpsc::unbounded_channel as unbounded;
|
||||
use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator};
|
||||
|
||||
use compact_jwt::JwsSigner;
|
||||
|
||||
fn create_pw_badlist_cache() -> HashSet<String> {
|
||||
let mut s = HashSet::new();
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
use crate::access::AccessControlsTransaction;
|
||||
use crate::credential::{BackupCodes, Credential};
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::server::IdmServerCredUpdateTransaction;
|
||||
use crate::idm::server::IdmServerProxyWriteTransaction;
|
||||
use crate::prelude::*;
|
||||
use crate::value::IntentTokenState;
|
||||
use hashbrown::HashSet;
|
||||
use core::ops::Deref;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{
|
||||
CURegState, CUStatus, CredentialDetail, PasskeyDetail, PasswordFeedback, TotpSecret,
|
||||
};
|
||||
|
||||
use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration};
|
||||
|
||||
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
|
||||
use webauthn_rs::prelude::Passkey as PasskeyV4;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use webauthn_rs::prelude::{
|
||||
CreationChallengeResponse, PasskeyRegistration, RegisterPublicKeyCredential,
|
||||
CreationChallengeResponse, DeviceKey as DeviceKeyV4, Passkey as PasskeyV4, PasskeyRegistration,
|
||||
RegisterPublicKeyCredential,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use core::ops::Deref;
|
||||
use crate::access::AccessControlsTransaction;
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::{BackupCodes, Credential};
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::server::{IdmServerCredUpdateTransaction, IdmServerProxyWriteTransaction};
|
||||
use crate::prelude::*;
|
||||
use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration};
|
||||
use crate::value::IntentTokenState;
|
||||
|
||||
const MAXIMUM_CRED_UPDATE_TTL: Duration = Duration::from_secs(900);
|
||||
const MAXIMUM_INTENT_TTL: Duration = Duration::from_secs(86400);
|
||||
|
@ -155,7 +147,6 @@ pub struct CredentialUpdateSessionStatus {
|
|||
// The target user's display name
|
||||
displayname: String,
|
||||
// ttl: Duration,
|
||||
//
|
||||
can_commit: bool,
|
||||
primary: Option<CredentialDetail>,
|
||||
passkeys: Vec<PasskeyDetail>,
|
||||
|
@ -384,85 +375,85 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
Ok((CredentialUpdateSessionToken { token_enc }, status))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn init_credential_update_intent(
|
||||
&mut self,
|
||||
event: &InitCredentialUpdateIntentEvent,
|
||||
ct: Duration,
|
||||
) -> Result<CredentialUpdateIntentToken, OperationError> {
|
||||
spanned!("idm::server::credupdatesession<Init>", {
|
||||
let account = self.validate_init_credential_update(event.target, &event.ident)?;
|
||||
let account = self.validate_init_credential_update(event.target, &event.ident)?;
|
||||
|
||||
// ==== AUTHORISATION CHECKED ===
|
||||
// ==== AUTHORISATION CHECKED ===
|
||||
|
||||
// Build the intent token.
|
||||
let mttl = event.max_ttl.unwrap_or_else(|| Duration::new(0, 0));
|
||||
let max_ttl = ct + mttl.clamp(MINIMUM_INTENT_TTL, MAXIMUM_INTENT_TTL);
|
||||
// let sessionid = uuid_from_duration(max_ttl, self.sid);
|
||||
let intent_id = readable_password_from_random();
|
||||
// Build the intent token.
|
||||
let mttl = event.max_ttl.unwrap_or_else(|| Duration::new(0, 0));
|
||||
let max_ttl = ct + mttl.clamp(MINIMUM_INTENT_TTL, MAXIMUM_INTENT_TTL);
|
||||
// let sessionid = uuid_from_duration(max_ttl, self.sid);
|
||||
let intent_id = readable_password_from_random();
|
||||
|
||||
/*
|
||||
let token = CredentialUpdateIntentTokenInner {
|
||||
sessionid,
|
||||
target,
|
||||
intent_id,
|
||||
max_ttl,
|
||||
};
|
||||
/*
|
||||
let token = CredentialUpdateIntentTokenInner {
|
||||
sessionid,
|
||||
target,
|
||||
intent_id,
|
||||
max_ttl,
|
||||
};
|
||||
|
||||
let token_data = serde_json::to_vec(&token).map_err(|e| {
|
||||
admin_error!(err = ?e, "Unable to encode token data");
|
||||
OperationError::SerdeJsonError
|
||||
let token_data = serde_json::to_vec(&token).map_err(|e| {
|
||||
admin_error!(err = ?e, "Unable to encode token data");
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
||||
let token_enc = self
|
||||
.token_enc_key
|
||||
.encrypt_at_time(&token_data, ct.as_secs());
|
||||
*/
|
||||
|
||||
// Mark that we have created an intent token on the user.
|
||||
// ⚠️ -- remember, there is a risk, very low, but still a risk of collision of the intent_id.
|
||||
// instead of enforcing unique, which would divulge that the collision occured, we
|
||||
// write anyway, and instead on the intent access path we invalidate IF the collision
|
||||
// occurs.
|
||||
let mut modlist = ModifyList::new_append(
|
||||
"credential_update_intent_token",
|
||||
Value::IntentToken(intent_id.clone(), IntentTokenState::Valid { max_ttl }),
|
||||
);
|
||||
|
||||
// Remove any old credential update intents
|
||||
account
|
||||
.credential_update_intent_tokens
|
||||
.iter()
|
||||
.for_each(|(existing_intent_id, state)| {
|
||||
let max_ttl = match state {
|
||||
IntentTokenState::Valid { max_ttl }
|
||||
| IntentTokenState::InProgress {
|
||||
max_ttl,
|
||||
session_id: _,
|
||||
session_ttl: _,
|
||||
}
|
||||
| IntentTokenState::Consumed { max_ttl } => *max_ttl,
|
||||
};
|
||||
|
||||
if ct >= max_ttl {
|
||||
modlist.push_mod(Modify::Removed(
|
||||
AttrString::from("credential_update_intent_token"),
|
||||
PartialValue::IntentToken(existing_intent_id.clone()),
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
request_error!(error = ?e);
|
||||
e
|
||||
})?;
|
||||
|
||||
let token_enc = self
|
||||
.token_enc_key
|
||||
.encrypt_at_time(&token_data, ct.as_secs());
|
||||
*/
|
||||
|
||||
// Mark that we have created an intent token on the user.
|
||||
// ⚠️ -- remember, there is a risk, very low, but still a risk of collision of the intent_id.
|
||||
// instead of enforcing unique, which would divulge that the collision occured, we
|
||||
// write anyway, and instead on the intent access path we invalidate IF the collision
|
||||
// occurs.
|
||||
let mut modlist = ModifyList::new_append(
|
||||
"credential_update_intent_token",
|
||||
Value::IntentToken(intent_id.clone(), IntentTokenState::Valid { max_ttl }),
|
||||
);
|
||||
|
||||
// Remove any old credential update intents
|
||||
account.credential_update_intent_tokens.iter().for_each(
|
||||
|(existing_intent_id, state)| {
|
||||
let max_ttl = match state {
|
||||
IntentTokenState::Valid { max_ttl }
|
||||
| IntentTokenState::InProgress {
|
||||
max_ttl,
|
||||
session_id: _,
|
||||
session_ttl: _,
|
||||
}
|
||||
| IntentTokenState::Consumed { max_ttl } => *max_ttl,
|
||||
};
|
||||
|
||||
if ct >= max_ttl {
|
||||
modlist.push_mod(Modify::Removed(
|
||||
AttrString::from("credential_update_intent_token"),
|
||||
PartialValue::IntentToken(existing_intent_id.clone()),
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
request_error!(error = ?e);
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok(CredentialUpdateIntentToken { intent_id })
|
||||
})
|
||||
Ok(CredentialUpdateIntentToken { intent_id })
|
||||
}
|
||||
|
||||
pub fn exchange_intent_credential_update(
|
||||
|
@ -642,21 +633,20 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.create_credupdate_session(session_id, Some(intent_id), account, current_time)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn init_credential_update(
|
||||
&mut self,
|
||||
event: &InitCredentialUpdateEvent,
|
||||
ct: Duration,
|
||||
) -> Result<(CredentialUpdateSessionToken, CredentialUpdateSessionStatus), OperationError> {
|
||||
spanned!("idm::server::credupdatesession<Init>", {
|
||||
let account = self.validate_init_credential_update(event.target, &event.ident)?;
|
||||
// ==== AUTHORISATION CHECKED ===
|
||||
// This is the expiry time, so that our cleanup task can "purge up to now" rather
|
||||
// than needing to do calculations.
|
||||
let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
|
||||
let account = self.validate_init_credential_update(event.target, &event.ident)?;
|
||||
// ==== AUTHORISATION CHECKED ===
|
||||
// This is the expiry time, so that our cleanup task can "purge up to now" rather
|
||||
// than needing to do calculations.
|
||||
let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
|
||||
|
||||
// Build the cred update session.
|
||||
self.create_credupdate_session(sessionid, None, account, ct)
|
||||
})
|
||||
// Build the cred update session.
|
||||
self.create_credupdate_session(sessionid, None, account, ct)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
|
@ -1472,6 +1462,14 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use async_std::task;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech, CredentialDetailType};
|
||||
use uuid::uuid;
|
||||
use webauthn_authenticator_rs::softpasskey::SoftPasskey;
|
||||
use webauthn_authenticator_rs::WebauthnAuthenticator;
|
||||
|
||||
use super::{
|
||||
CredentialUpdateSessionStatus, CredentialUpdateSessionToken, InitCredentialUpdateEvent,
|
||||
InitCredentialUpdateIntentEvent, MfaRegStateStatus, MAXIMUM_CRED_UPDATE_TTL,
|
||||
|
@ -1481,16 +1479,8 @@ mod tests {
|
|||
use crate::event::{AuthEvent, AuthResult, CreateEvent};
|
||||
use crate::idm::delayed::DelayedAction;
|
||||
use crate::idm::server::IdmServer;
|
||||
use crate::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator};
|
||||
|
||||
use crate::idm::AuthState;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech, CredentialDetailType};
|
||||
use uuid::uuid;
|
||||
|
||||
use async_std::task;
|
||||
use crate::prelude::*;
|
||||
|
||||
const TEST_CURRENT_TIME: u64 = 6000;
|
||||
const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) struct PasswordChangeEvent {
|
||||
pub ident: Identity,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use kanidm_proto::v1::{Group as ProtoGroup, OperationError};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed};
|
||||
use crate::prelude::*;
|
||||
use crate::value::PartialValue;
|
||||
use kanidm_proto::v1::Group as ProtoGroup;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_GROUP: PartialValue = PartialValue::new_class("group");
|
||||
|
|
|
@ -15,10 +15,10 @@ pub mod server;
|
|||
pub(crate) mod serviceaccount;
|
||||
pub(crate) mod unix;
|
||||
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech};
|
||||
|
||||
pub enum AuthState {
|
||||
Choose(Vec<AuthMech>),
|
||||
Continue(Vec<AuthAllowed>),
|
||||
|
|
|
@ -3,30 +3,19 @@
|
|||
//! This contains the in memory and loaded set of active oauth2 resource server
|
||||
//! integrations, which are then able to be used an accessed from the IDM layer
|
||||
//! for operations involving oauth2 authentication processing.
|
||||
//!
|
||||
|
||||
use crate::identity::IdentityId;
|
||||
use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant};
|
||||
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction};
|
||||
use crate::prelude::*;
|
||||
use crate::value::OAUTHSCOPE_RE;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use base64urlsafedata::Base64UrlSafeData;
|
||||
pub use compact_jwt::{JwkKeySet, OidcToken};
|
||||
use compact_jwt::{JwsSigner, OidcClaims, OidcSubject};
|
||||
use concread::cowcell::*;
|
||||
use fernet::Fernet;
|
||||
use hashbrown::HashMap;
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
use openssl::sha;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
use tracing::trace;
|
||||
use url::{Origin, Url};
|
||||
|
||||
pub use kanidm_proto::oauth2::{
|
||||
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
||||
AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod, ErrorResponse,
|
||||
|
@ -36,9 +25,19 @@ use kanidm_proto::oauth2::{
|
|||
ClaimType, DisplayValue, GrantType, IdTokenSignAlg, ResponseMode, ResponseType, SubjectType,
|
||||
TokenEndpointAuthMethod,
|
||||
};
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
use openssl::sha;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
use tracing::trace;
|
||||
use url::{Origin, Url};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
use crate::identity::IdentityId;
|
||||
use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant};
|
||||
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction};
|
||||
use crate::prelude::*;
|
||||
use crate::value::OAUTHSCOPE_RE;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLASS_OAUTH2: PartialValue = PartialValue::new_class("oauth2_resource_server");
|
||||
|
@ -1351,26 +1350,22 @@ fn parse_basic_authz(client_authz: &str) -> Result<(String, String), Oauth2Error
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::event::CreateEvent;
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use base64urlsafedata::Base64UrlSafeData;
|
||||
use compact_jwt::{JwaAlg, Jwk, JwkUse, JwsValidator, OidcSubject, OidcUnverified};
|
||||
use kanidm_proto::oauth2::*;
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
use openssl::sha;
|
||||
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
use crate::idm::delayed::DelayedAction;
|
||||
use crate::idm::oauth2::{AuthoriseResponse, Oauth2Error};
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::event::{DeleteEvent, ModifyEvent};
|
||||
|
||||
use base64urlsafedata::Base64UrlSafeData;
|
||||
use kanidm_proto::oauth2::*;
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
|
||||
use compact_jwt::{JwaAlg, Jwk, JwkUse, JwsValidator, OidcSubject, OidcUnverified};
|
||||
|
||||
use openssl::sha;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
const TEST_CURRENT_TIME: u64 = 6000;
|
||||
const UAT_EXPIRE: u64 = 5;
|
||||
const TOKEN_EXPIRE: u64 = 900;
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use crate::idm::group::Group;
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::{OperationError, RadiusAuthToken};
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::entry::{Entry, EntryCommitted, EntryReduced};
|
||||
use crate::idm::group::Group;
|
||||
use crate::prelude::*;
|
||||
use crate::value::PartialValue;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::RadiusAuthToken;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");
|
||||
|
|
|
@ -1,3 +1,35 @@
|
|||
use core::task::{Context, Poll};
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_std::task;
|
||||
use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator};
|
||||
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
|
||||
use concread::cowcell::{CowCellReadTxn, CowCellWriteTxn};
|
||||
use concread::hashmap::HashMap;
|
||||
use concread::CowCell;
|
||||
use fernet::Fernet;
|
||||
// #[cfg(any(test,bench))]
|
||||
use futures::task as futures_task;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{
|
||||
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, UnixGroupToken,
|
||||
UnixUserToken, UserAuthToken,
|
||||
};
|
||||
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 super::delayed::BackupCodeRemoval;
|
||||
use super::event::ReadBackupCodeEvent;
|
||||
use crate::actors::v1_write::QueryServerWriteV1;
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::softlock::CredSoftLock;
|
||||
use crate::event::{AuthEvent, AuthEventStep, AuthResult};
|
||||
|
@ -5,6 +37,10 @@ use crate::identity::{IdentType, IdentUser, Limits};
|
|||
use crate::idm::account::Account;
|
||||
use crate::idm::authsession::AuthSession;
|
||||
use crate::idm::credupdatesession::CredentialUpdateSessionMutex;
|
||||
use crate::idm::delayed::{
|
||||
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
|
||||
WebauthnCounterIncrement,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use crate::idm::event::PasswordChangeEvent;
|
||||
use crate::idm::event::{
|
||||
|
@ -26,54 +62,6 @@ use crate::ldap::{LdapBoundToken, LdapSession};
|
|||
use crate::prelude::*;
|
||||
use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid};
|
||||
|
||||
use crate::actors::v1_write::QueryServerWriteV1;
|
||||
use crate::idm::delayed::{
|
||||
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
|
||||
WebauthnCounterIncrement,
|
||||
};
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{
|
||||
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, UnixGroupToken,
|
||||
UnixUserToken, UserAuthToken,
|
||||
};
|
||||
|
||||
use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator};
|
||||
use fernet::Fernet;
|
||||
|
||||
use tokio::sync::mpsc::{
|
||||
unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use async_std::task;
|
||||
|
||||
// #[cfg(any(test,bench))]
|
||||
use core::task::{Context, Poll};
|
||||
// #[cfg(any(test,bench))]
|
||||
use futures::task as futures_task;
|
||||
|
||||
use concread::{
|
||||
bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn},
|
||||
cowcell::{CowCellReadTxn, CowCellWriteTxn},
|
||||
hashmap::HashMap,
|
||||
CowCell,
|
||||
};
|
||||
|
||||
use rand::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tokio::sync::Mutex;
|
||||
use url::Url;
|
||||
|
||||
use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
|
||||
|
||||
use super::delayed::BackupCodeRemoval;
|
||||
use super::event::ReadBackupCodeEvent;
|
||||
|
||||
use tracing::trace;
|
||||
|
||||
type AuthSessionMutex = Arc<Mutex<AuthSession>>;
|
||||
type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
|
||||
|
||||
|
@ -298,6 +286,7 @@ impl IdmServer {
|
|||
}
|
||||
|
||||
/// Read from the database, in a transaction.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn proxy_read_async(&self) -> IdmServerProxyReadTransaction<'_> {
|
||||
IdmServerProxyReadTransaction {
|
||||
qs_read: self.qs.read_async().await,
|
||||
|
@ -312,6 +301,7 @@ impl IdmServer {
|
|||
task::block_on(self.proxy_write_async(ts))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn proxy_write_async(&self, ts: Duration) -> IdmServerProxyWriteTransaction<'_> {
|
||||
let mut sid = [0; 4];
|
||||
let mut rng = StdRng::from_entropy();
|
||||
|
@ -421,6 +411,7 @@ pub(crate) trait IdmServerTransaction<'a> {
|
|||
/// The primary method of verification selection is the use of the KID parameter
|
||||
/// that we internally sign with. We can use this to select the appropriate token type
|
||||
/// and validation method.
|
||||
#[instrument(level = "info", skip_all)]
|
||||
fn validate_and_parse_token_to_ident(
|
||||
&self,
|
||||
token: Option<&str>,
|
||||
|
@ -532,6 +523,7 @@ pub(crate) trait IdmServerTransaction<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn validate_and_parse_uat(
|
||||
&self,
|
||||
token: Option<&str>,
|
||||
|
@ -598,6 +590,7 @@ pub(crate) trait IdmServerTransaction<'a> {
|
|||
/// something we can pin access controls and other limits and references to.
|
||||
/// This is why it is the location where validity windows are checked and other
|
||||
/// relevant session information is injected.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn process_uat_to_identity(
|
||||
&self,
|
||||
uat: &UserAuthToken,
|
||||
|
@ -663,6 +656,7 @@ pub(crate) trait IdmServerTransaction<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn process_apit_to_identity(
|
||||
&self,
|
||||
apit: &ApiToken,
|
||||
|
@ -683,6 +677,7 @@ pub(crate) trait IdmServerTransaction<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn validate_ldap_session(
|
||||
&self,
|
||||
session: &LdapSession,
|
||||
|
@ -883,16 +878,14 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
match auth_session {
|
||||
Some(auth_session) => {
|
||||
let mut session_write = self.sessions.write();
|
||||
spanned!("idm::server::auth<Init> -> sessions", {
|
||||
if session_write.contains_key(&sessionid) {
|
||||
Err(OperationError::InvalidSessionState)
|
||||
} else {
|
||||
session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
|
||||
// Debugging: ensure we really inserted ...
|
||||
debug_assert!(session_write.get(&sessionid).is_some());
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
if session_write.contains_key(&sessionid) {
|
||||
Err(OperationError::InvalidSessionState)
|
||||
} else {
|
||||
session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
|
||||
// Debugging: ensure we really inserted ...
|
||||
debug_assert!(session_write.get(&sessionid).is_some());
|
||||
Ok(())
|
||||
}?;
|
||||
session_write.commit();
|
||||
}
|
||||
None => {
|
||||
|
@ -2024,65 +2017,64 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn commit(mut self) -> Result<(), OperationError> {
|
||||
spanned!("idm::server::IdmServerProxyWriteTransaction::commit", {
|
||||
if self
|
||||
.qs_write
|
||||
.get_changed_uuids()
|
||||
.contains(&UUID_SYSTEM_CONFIG)
|
||||
{
|
||||
self.reload_password_badlist()?;
|
||||
};
|
||||
if self.qs_write.get_changed_ouath2() {
|
||||
self.qs_write
|
||||
.get_oauth2rs_set()
|
||||
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?;
|
||||
}
|
||||
if self.qs_write.get_changed_domain() {
|
||||
// reload token_key?
|
||||
self.qs_write
|
||||
.get_domain_fernet_private_key()
|
||||
.and_then(|token_key| {
|
||||
Fernet::new(&token_key).ok_or_else(|| {
|
||||
admin_error!("Failed to generate token_enc_key");
|
||||
if self
|
||||
.qs_write
|
||||
.get_changed_uuids()
|
||||
.contains(&UUID_SYSTEM_CONFIG)
|
||||
{
|
||||
self.reload_password_badlist()?;
|
||||
};
|
||||
if self.qs_write.get_changed_ouath2() {
|
||||
self.qs_write
|
||||
.get_oauth2rs_set()
|
||||
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?;
|
||||
}
|
||||
if self.qs_write.get_changed_domain() {
|
||||
// reload token_key?
|
||||
self.qs_write
|
||||
.get_domain_fernet_private_key()
|
||||
.and_then(|token_key| {
|
||||
Fernet::new(&token_key).ok_or_else(|| {
|
||||
admin_error!("Failed to generate token_enc_key");
|
||||
OperationError::InvalidState
|
||||
})
|
||||
})
|
||||
.map(|new_handle| {
|
||||
*self.token_enc_key = new_handle;
|
||||
})?;
|
||||
self.qs_write
|
||||
.get_domain_es256_private_key()
|
||||
.and_then(|key_der| {
|
||||
JwsSigner::from_es256_der(&key_der).map_err(|e| {
|
||||
admin_error!("Failed to generate uat_jwt_signer - {:?}", e);
|
||||
OperationError::InvalidState
|
||||
})
|
||||
})
|
||||
.and_then(|signer| {
|
||||
signer
|
||||
.get_validator()
|
||||
.map_err(|e| {
|
||||
admin_error!("Failed to generate uat_jwt_validator - {:?}", e);
|
||||
OperationError::InvalidState
|
||||
})
|
||||
})
|
||||
.map(|new_handle| {
|
||||
*self.token_enc_key = new_handle;
|
||||
})?;
|
||||
self.qs_write
|
||||
.get_domain_es256_private_key()
|
||||
.and_then(|key_der| {
|
||||
JwsSigner::from_es256_der(&key_der).map_err(|e| {
|
||||
admin_error!("Failed to generate uat_jwt_signer - {:?}", e);
|
||||
OperationError::InvalidState
|
||||
})
|
||||
})
|
||||
.and_then(|signer| {
|
||||
signer
|
||||
.get_validator()
|
||||
.map_err(|e| {
|
||||
admin_error!("Failed to generate uat_jwt_validator - {:?}", e);
|
||||
OperationError::InvalidState
|
||||
})
|
||||
.map(|validator| (signer, validator))
|
||||
})
|
||||
.map(|(new_signer, new_validator)| {
|
||||
*self.uat_jwt_signer = new_signer;
|
||||
*self.uat_jwt_validator = new_validator;
|
||||
})?;
|
||||
}
|
||||
// Commit everything.
|
||||
self.oauth2rs.commit();
|
||||
self.uat_jwt_signer.commit();
|
||||
self.uat_jwt_validator.commit();
|
||||
self.token_enc_key.commit();
|
||||
self.pw_badlist_cache.commit();
|
||||
self.cred_update_sessions.commit();
|
||||
trace!("cred_update_session.commit");
|
||||
self.qs_write.commit()
|
||||
})
|
||||
.map(|validator| (signer, validator))
|
||||
})
|
||||
.map(|(new_signer, new_validator)| {
|
||||
*self.uat_jwt_signer = new_signer;
|
||||
*self.uat_jwt_validator = new_validator;
|
||||
})?;
|
||||
}
|
||||
// Commit everything.
|
||||
self.oauth2rs.commit();
|
||||
self.uat_jwt_signer.commit();
|
||||
self.uat_jwt_validator.commit();
|
||||
self.token_enc_key.commit();
|
||||
self.pw_badlist_cache.commit();
|
||||
self.cred_update_sessions.commit();
|
||||
trace!("cred_update_session.commit");
|
||||
self.qs_write.commit()
|
||||
}
|
||||
|
||||
fn reload_password_badlist(&mut self) -> Result<(), OperationError> {
|
||||
|
@ -2100,6 +2092,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_std::task;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType, OperationError};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::{Credential, Password};
|
||||
use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent};
|
||||
|
@ -2107,20 +2107,12 @@ mod tests {
|
|||
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
||||
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
|
||||
};
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
use crate::idm::AuthState;
|
||||
use crate::modify::{Modify, ModifyList};
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType};
|
||||
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
// , IdmServerDelayed;
|
||||
use crate::utils::duration_from_epoch_now;
|
||||
use async_std::task;
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";
|
||||
const TEST_PASSWORD_INC: &'static str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use compact_jwt::{Jws, JwsSigner};
|
||||
use kanidm_proto::v1::ApiToken;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::event::SearchEvent;
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerProxyWriteTransaction};
|
||||
use crate::prelude::*;
|
||||
use crate::value::Session;
|
||||
|
||||
use compact_jwt::{Jws, JwsSigner};
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use kanidm_proto::v1::ApiToken;
|
||||
|
||||
// Need to add KID to es256 der for lookups ✅
|
||||
|
||||
// Need to generate the es256 on the account on modifies ✅
|
||||
|
@ -87,14 +87,13 @@ pub struct ServiceAccount {
|
|||
}
|
||||
|
||||
impl ServiceAccount {
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub(crate) fn try_from_entry_rw(
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
// qs: &mut QueryServerWriteTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
spanned!("idm::serviceaccount::try_from_entry_rw", {
|
||||
// let groups = Group::try_from_account_entry_rw(value, qs)?;
|
||||
try_from_entry!(value)
|
||||
})
|
||||
// let groups = Group::try_from_account_entry_rw(value, qs)?;
|
||||
try_from_entry!(value)
|
||||
}
|
||||
|
||||
pub(crate) fn check_api_token_valid(
|
||||
|
@ -354,16 +353,17 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{DestroyApiTokenEvent, GenerateApiTokenEvent, GRACE_WINDOW};
|
||||
use crate::idm::server::IdmServerTransaction;
|
||||
// use crate::prelude::*;
|
||||
|
||||
use crate::event::CreateEvent;
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use kanidm_proto::v1::ApiToken;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use kanidm_proto::v1::ApiToken;
|
||||
|
||||
use super::{DestroyApiTokenEvent, GenerateApiTokenEvent, GRACE_WINDOW};
|
||||
// use crate::prelude::*;
|
||||
use crate::event::CreateEvent;
|
||||
use crate::idm::server::IdmServerTransaction;
|
||||
|
||||
const TEST_CURRENT_TIME: u64 = 6000;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
use std::iter;
|
||||
// use crossbeam::channel::Sender;
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::{softlock::CredSoftLockPolicy, Credential};
|
||||
use crate::credential::softlock::CredSoftLockPolicy;
|
||||
use crate::credential::Credential;
|
||||
use crate::idm::delayed::{DelayedAction, UnixPasswordUpgrade};
|
||||
use crate::modify::{ModifyInvalid, ModifyList};
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
||||
|
||||
use crate::idm::delayed::{DelayedAction, UnixPasswordUpgrade};
|
||||
|
||||
// use crossbeam::channel::Sender;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::mpsc::UnboundedSender as Sender;
|
||||
|
||||
use std::iter;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct UnixUserAccount {
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
//! This contains scheduled tasks/interval tasks that are run inside of the server on a schedule
|
||||
//! as background operations.
|
||||
|
||||
use crate::actors::v1_read::QueryServerReadV1;
|
||||
use crate::actors::v1_write::QueryServerWriteV1;
|
||||
|
||||
use crate::config::OnlineBackup;
|
||||
use crate::constants::PURGE_FREQUENCY;
|
||||
use crate::event::{OnlineBackupEvent, PurgeRecycledEvent, PurgeTombstoneEvent};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use chrono::Utc;
|
||||
use saffron::parse::{CronExpr, English};
|
||||
use saffron::Cron;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tokio::time::{interval, sleep, Duration};
|
||||
|
||||
use crate::actors::v1_read::QueryServerReadV1;
|
||||
use crate::actors::v1_write::QueryServerWriteV1;
|
||||
use crate::config::OnlineBackup;
|
||||
use crate::constants::PURGE_FREQUENCY;
|
||||
use crate::event::{OnlineBackupEvent, PurgeRecycledEvent, PurgeTombstoneEvent};
|
||||
|
||||
pub struct IntervalActor;
|
||||
|
||||
impl IntervalActor {
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
//! LDAP specific operations handling components. This is where LDAP operations
|
||||
//! are sent to for processing.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter;
|
||||
|
||||
use async_std::task;
|
||||
use kanidm_proto::v1::{ApiToken, OperationError, UserAuthToken};
|
||||
use ldap3_proto::simple::*;
|
||||
use regex::Regex;
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::event::SearchEvent;
|
||||
use crate::idm::event::{LdapAuthEvent, LdapTokenAuthEvent};
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
use crate::prelude::*;
|
||||
use async_std::task;
|
||||
use kanidm_proto::v1::{ApiToken, OperationError, UserAuthToken};
|
||||
use ldap3_proto::simple::*;
|
||||
use regex::Regex;
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter;
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
|
||||
// Clippy doesn't like Bind here. But proto needs unboxed ldapmsg,
|
||||
// and ldapboundtoken is moved. Really, it's not too bad, every message here is pretty sucky.
|
||||
|
@ -121,6 +123,7 @@ impl LdapServer {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn do_search(
|
||||
&self,
|
||||
idms: &IdmServer,
|
||||
|
@ -252,93 +255,82 @@ impl LdapServer {
|
|||
|
||||
let ct = duration_from_epoch_now();
|
||||
let idm_read = idms.proxy_read_async().await;
|
||||
spanned!("ldap::do_search<core>", {
|
||||
// Now start the txn - we need it for resolving filter components.
|
||||
// Now start the txn - we need it for resolving filter components.
|
||||
|
||||
// join the filter, with ext_filter
|
||||
let lfilter = match ext_filter {
|
||||
Some(ext) => LdapFilter::And(vec![
|
||||
sr.filter.clone(),
|
||||
ext,
|
||||
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
|
||||
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
|
||||
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
|
||||
LdapFilter::Equality(
|
||||
"class".to_string(),
|
||||
"access_control_profile".to_string(),
|
||||
),
|
||||
]))),
|
||||
]),
|
||||
None => LdapFilter::And(vec![
|
||||
sr.filter.clone(),
|
||||
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
|
||||
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
|
||||
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
|
||||
LdapFilter::Equality(
|
||||
"class".to_string(),
|
||||
"access_control_profile".to_string(),
|
||||
),
|
||||
]))),
|
||||
]),
|
||||
};
|
||||
// join the filter, with ext_filter
|
||||
let lfilter = match ext_filter {
|
||||
Some(ext) => LdapFilter::And(vec![
|
||||
sr.filter.clone(),
|
||||
ext,
|
||||
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
|
||||
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
|
||||
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
|
||||
LdapFilter::Equality(
|
||||
"class".to_string(),
|
||||
"access_control_profile".to_string(),
|
||||
),
|
||||
]))),
|
||||
]),
|
||||
None => LdapFilter::And(vec![
|
||||
sr.filter.clone(),
|
||||
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
|
||||
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
|
||||
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
|
||||
LdapFilter::Equality(
|
||||
"class".to_string(),
|
||||
"access_control_profile".to_string(),
|
||||
),
|
||||
]))),
|
||||
]),
|
||||
};
|
||||
|
||||
admin_info!(filter = ?lfilter, "LDAP Search Filter");
|
||||
admin_info!(filter = ?lfilter, "LDAP Search Filter");
|
||||
|
||||
// Build the event, with the permissions from effective_session
|
||||
//
|
||||
// ! Remember, searchEvent wraps to ignore hidden for us.
|
||||
let se = spanned!("ldap::do_search<core><prepare_se>", {
|
||||
let ident = idm_read
|
||||
.validate_ldap_session(&uat.effective_session, ct)
|
||||
.map_err(|e| {
|
||||
admin_error!("Invalid identity: {:?}", e);
|
||||
e
|
||||
})?;
|
||||
SearchEvent::new_ext_impersonate_uuid(
|
||||
&idm_read.qs_read,
|
||||
ident,
|
||||
&lfilter,
|
||||
k_attrs,
|
||||
)
|
||||
})
|
||||
// Build the event, with the permissions from effective_session
|
||||
//
|
||||
// ! Remember, searchEvent wraps to ignore hidden for us.
|
||||
let ident = idm_read
|
||||
.validate_ldap_session(&uat.effective_session, ct)
|
||||
.map_err(|e| {
|
||||
admin_error!("failed to create search event -> {:?}", e);
|
||||
admin_error!("Invalid identity: {:?}", e);
|
||||
e
|
||||
})?;
|
||||
let se =
|
||||
SearchEvent::new_ext_impersonate_uuid(&idm_read.qs_read, ident, &lfilter, k_attrs)
|
||||
.map_err(|e| {
|
||||
admin_error!("failed to create search event -> {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
|
||||
admin_error!("search failure {:?}", e);
|
||||
e
|
||||
})?;
|
||||
let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
|
||||
admin_error!("search failure {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
// These have already been fully reduced (access controls applied),
|
||||
// so we can just transform the values and open palm slam them into
|
||||
// the result structure.
|
||||
let lres = spanned!("ldap::do_search<core><prepare results>", {
|
||||
let lres: Result<Vec<_>, _> = res
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs)
|
||||
// if okay, wrap in a ldap msg.
|
||||
.map(|r| sr.gen_result_entry(r))
|
||||
})
|
||||
.chain(iter::once(Ok(sr.gen_success())))
|
||||
.collect();
|
||||
lres
|
||||
});
|
||||
// These have already been fully reduced (access controls applied),
|
||||
// so we can just transform the values and open palm slam them into
|
||||
// the result structure.
|
||||
let lres: Result<Vec<_>, _> = res
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs)
|
||||
// if okay, wrap in a ldap msg.
|
||||
.map(|r| sr.gen_result_entry(r))
|
||||
})
|
||||
.chain(iter::once(Ok(sr.gen_success())))
|
||||
.collect();
|
||||
|
||||
let lres = lres.map_err(|e| {
|
||||
admin_error!("entry resolve failure {:?}", e);
|
||||
e
|
||||
})?;
|
||||
let lres = lres.map_err(|e| {
|
||||
admin_error!("entry resolve failure {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
admin_info!(
|
||||
nentries = %lres.len(),
|
||||
"LDAP Search Success -> number of entries"
|
||||
);
|
||||
admin_info!(
|
||||
nentries = %lres.len(),
|
||||
"LDAP Search Success -> number of entries"
|
||||
);
|
||||
|
||||
Ok(lres)
|
||||
})
|
||||
Ok(lres)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,18 +542,19 @@ pub(crate) fn ldap_attr_filter_map(input: &str) -> AttrString {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
// use crate::prelude::*;
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::idm::event::UnixPasswordChangeEvent;
|
||||
use crate::idm::serviceaccount::GenerateApiTokenEvent;
|
||||
use crate::ldap::{LdapServer, LdapSession};
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_std::task;
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::ApiToken;
|
||||
use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope};
|
||||
use ldap3_proto::simple::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::idm::event::UnixPasswordChangeEvent;
|
||||
use crate::idm::serviceaccount::GenerateApiTokenEvent;
|
||||
use crate::ldap::{LdapServer, LdapSession};
|
||||
|
||||
const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";
|
||||
|
||||
|
|
|
@ -63,39 +63,36 @@ pub mod config;
|
|||
/// A prelude of imports that should be imported by all other Kanidm modules to
|
||||
/// help make imports cleaner.
|
||||
pub mod prelude {
|
||||
pub use crate::utils::duration_from_epoch_now;
|
||||
pub use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
pub use sketching::{
|
||||
admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace,
|
||||
filter_warn, perf_trace, request_error, request_info, request_trace, request_warn,
|
||||
security_access, security_critical, security_error, security_info, tagged_event, EventTag,
|
||||
};
|
||||
pub use smartstring::alias::String as AttrString;
|
||||
pub use url::Url;
|
||||
pub use uuid::Uuid;
|
||||
|
||||
pub use crate::constants::*;
|
||||
pub use crate::filter::{
|
||||
f_and, f_andnot, f_eq, f_id, f_inc, f_lt, f_or, f_pres, f_self, f_spn_name, f_sub,
|
||||
};
|
||||
pub use crate::filter::{Filter, FilterInvalid, FC};
|
||||
pub use crate::modify::{m_pres, m_purge, m_remove};
|
||||
pub use crate::modify::{Modify, ModifyInvalid, ModifyList};
|
||||
|
||||
pub use crate::entry::{
|
||||
Entry, EntryCommitted, EntryInit, EntryInvalid, EntryInvalidCommitted, EntryNew,
|
||||
EntryReduced, EntrySealed, EntrySealedCommitted, EntryTuple, EntryValid,
|
||||
};
|
||||
pub use crate::filter::{
|
||||
f_and, f_andnot, f_eq, f_id, f_inc, f_lt, f_or, f_pres, f_self, f_spn_name, f_sub, Filter,
|
||||
FilterInvalid, FC,
|
||||
};
|
||||
pub use crate::identity::Identity;
|
||||
pub use crate::modify::{m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList};
|
||||
pub use crate::server::{
|
||||
QueryServer, QueryServerReadTransaction, QueryServerTransaction,
|
||||
QueryServerWriteTransaction,
|
||||
};
|
||||
pub use crate::utils::duration_from_epoch_now;
|
||||
pub use crate::value::{IndexType, PartialValue, SyntaxType, Value};
|
||||
pub use crate::valueset::{
|
||||
ValueSet, ValueSetBool, ValueSetCid, ValueSetIndex, ValueSetIutf8, ValueSetRefer,
|
||||
ValueSetSecret, ValueSetSpn, ValueSetSyntax, ValueSetT, ValueSetUint32, ValueSetUtf8,
|
||||
ValueSetUuid,
|
||||
};
|
||||
pub use sketching::{
|
||||
admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace,
|
||||
filter_warn, perf_trace, request_error, request_info, request_trace, request_warn,
|
||||
security_access, security_critical, security_error, security_info, spanned, tagged_event,
|
||||
EventTag,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ macro_rules! setup_test {
|
|||
(
|
||||
$preload_entries:expr
|
||||
) => {{
|
||||
use crate::utils::duration_from_epoch_now;
|
||||
use async_std::task;
|
||||
|
||||
use crate::utils::duration_from_epoch_now;
|
||||
|
||||
let _ = sketching::test_init();
|
||||
|
||||
// Create an in memory BE
|
||||
|
@ -105,10 +106,11 @@ macro_rules! run_test {
|
|||
#[cfg(test)]
|
||||
macro_rules! entry_str_to_account {
|
||||
($entry_str:expr) => {{
|
||||
use std::iter::once;
|
||||
|
||||
use crate::entry::{Entry, EntryInvalid, EntryNew};
|
||||
use crate::idm::account::Account;
|
||||
use crate::value::Value;
|
||||
use std::iter::once;
|
||||
|
||||
let mut e: Entry<EntryInvalid, EntryNew> =
|
||||
unsafe { Entry::unsafe_from_entry_str($entry_str).into_invalid_new() };
|
||||
|
@ -195,37 +197,35 @@ macro_rules! run_create_test {
|
|||
use crate::schema::Schema;
|
||||
use crate::utils::duration_from_epoch_now;
|
||||
|
||||
spanned!("plugins::macros::run_create_test", {
|
||||
let qs = setup_test!($preload_entries);
|
||||
let qs = setup_test!($preload_entries);
|
||||
|
||||
let ce = match $internal {
|
||||
None => CreateEvent::new_internal($create_entries.clone()),
|
||||
Some(e_str) => unsafe {
|
||||
CreateEvent::new_impersonate_entry_ser(e_str, $create_entries.clone())
|
||||
},
|
||||
};
|
||||
let ce = match $internal {
|
||||
None => CreateEvent::new_internal($create_entries.clone()),
|
||||
Some(e_str) => unsafe {
|
||||
CreateEvent::new_impersonate_entry_ser(e_str, $create_entries.clone())
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = qs_write.create(&ce);
|
||||
trace!("test result: {:?}", r);
|
||||
assert!(r == $expect);
|
||||
$check(&qs_write);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = qs_write.create(&ce);
|
||||
trace!("test result: {:?}", r);
|
||||
assert!(r == $expect);
|
||||
$check(&qs_write);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
});
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -246,49 +246,41 @@ macro_rules! run_modify_test {
|
|||
use crate::prelude::*;
|
||||
use crate::schema::Schema;
|
||||
|
||||
spanned!("plugins::macros::run_modify_test", {
|
||||
let qs = setup_test!($preload_entries);
|
||||
let qs = setup_test!($preload_entries);
|
||||
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
spanned!("plugins::macros::run_modify_test -> pre_test hook", {
|
||||
$pre_hook(&qs_write)
|
||||
});
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
$pre_hook(&qs_write);
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
|
||||
let me = match $internal {
|
||||
None => unsafe { ModifyEvent::new_internal_invalid($modify_filter, $modify_list) },
|
||||
Some(e_str) => unsafe {
|
||||
ModifyEvent::new_impersonate_entry_ser(e_str, $modify_filter, $modify_list)
|
||||
},
|
||||
};
|
||||
let me = match $internal {
|
||||
None => unsafe { ModifyEvent::new_internal_invalid($modify_filter, $modify_list) },
|
||||
Some(e_str) => unsafe {
|
||||
ModifyEvent::new_impersonate_entry_ser(e_str, $modify_filter, $modify_list)
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = spanned!("plugins::macros::run_modify_test -> main_test", {
|
||||
qs_write.modify(&me)
|
||||
});
|
||||
spanned!("plugins::macros::run_modify_test -> post_test check", {
|
||||
$check(&qs_write)
|
||||
});
|
||||
trace!("test result: {:?}", r);
|
||||
assert!(r == $expect);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = qs_write.modify(&me);
|
||||
$check(&qs_write);
|
||||
trace!("test result: {:?}", r);
|
||||
assert!(r == $expect);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
});
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -308,37 +300,35 @@ macro_rules! run_delete_test {
|
|||
use crate::schema::Schema;
|
||||
use crate::utils::duration_from_epoch_now;
|
||||
|
||||
spanned!("plugins::macros::run_delete_test", {
|
||||
let qs = setup_test!($preload_entries);
|
||||
let qs = setup_test!($preload_entries);
|
||||
|
||||
let de = match $internal {
|
||||
Some(e_str) => unsafe {
|
||||
DeleteEvent::new_impersonate_entry_ser(e_str, $delete_filter.clone())
|
||||
},
|
||||
None => unsafe { DeleteEvent::new_internal_invalid($delete_filter.clone()) },
|
||||
};
|
||||
let de = match $internal {
|
||||
Some(e_str) => unsafe {
|
||||
DeleteEvent::new_impersonate_entry_ser(e_str, $delete_filter.clone())
|
||||
},
|
||||
None => unsafe { DeleteEvent::new_internal_invalid($delete_filter.clone()) },
|
||||
};
|
||||
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = qs_write.delete(&de);
|
||||
trace!("test result: {:?}", r);
|
||||
$check(&qs_write);
|
||||
assert!(r == $expect);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
let r = qs_write.delete(&de);
|
||||
trace!("test result: {:?}", r);
|
||||
$check(&qs_write);
|
||||
assert!(r == $expect);
|
||||
match r {
|
||||
Ok(_) => {
|
||||
qs_write.commit().expect("commit failure!");
|
||||
}
|
||||
Err(e) => {
|
||||
admin_error!("Rolling back => {:?}", e);
|
||||
}
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
});
|
||||
}
|
||||
// Make sure there are no errors.
|
||||
trace!("starting verification");
|
||||
let ver = qs.verify();
|
||||
trace!("verification -> {:?}", ver);
|
||||
assert!(ver.len() == 0);
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
//! express the series of Modifications that should be applied. These are expressed
|
||||
//! as "states" on what attribute-values should appear as within the `Entry`
|
||||
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::Entry as ProtoEntry;
|
||||
use kanidm_proto::v1::Modify as ProtoModify;
|
||||
use kanidm_proto::v1::ModifyList as ProtoModifyList;
|
||||
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{OperationError, SchemaError};
|
||||
use std::slice;
|
||||
|
||||
use kanidm_proto::v1::{
|
||||
Entry as ProtoEntry, Modify as ProtoModify, ModifyList as ProtoModifyList, OperationError,
|
||||
SchemaError,
|
||||
};
|
||||
// Should this be std?
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::slice;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::value::{PartialValue, Value};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ModifyValid;
|
||||
|
@ -69,8 +69,8 @@ pub struct ModifyList<VALID> {
|
|||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
|
||||
type Item = &'a Modify;
|
||||
type IntoIter = slice::Iter<'a, Modify>;
|
||||
type Item = &'a Modify;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.mods.iter()
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
// both change approaches.
|
||||
//
|
||||
//
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
use tracing::trace;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct AttrUnique;
|
||||
|
||||
|
@ -192,9 +193,10 @@ impl Plugin for AttrUnique {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::PluginError;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
// Test entry in db, and same name, reject.
|
||||
#[test]
|
||||
fn test_pre_create_name_unique() {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::plugins::Plugin;
|
||||
use hashbrown::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter::once;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::modify::Modify;
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
|
||||
lazy_static! {
|
||||
static ref CLASS_OBJECT: Value = Value::new_class("object");
|
||||
|
@ -43,15 +44,9 @@ impl Plugin for Base {
|
|||
// debug!("Entering base pre_create_transform");
|
||||
// For each candidate
|
||||
for entry in cand.iter_mut() {
|
||||
trace!("Base check on entry: {:?}", entry);
|
||||
|
||||
// First, ensure we have the 'object', class in the class set.
|
||||
entry.add_ava("class", CLASS_OBJECT.clone());
|
||||
|
||||
trace!("Object should now be in entry: {:?}", entry);
|
||||
|
||||
// If they have a name, but no principal name, derive it.
|
||||
|
||||
// if they don't have uuid, create it.
|
||||
match entry.get_ava_set("uuid").map(|s| s.len()) {
|
||||
None => {
|
||||
|
@ -85,7 +80,6 @@ impl Plugin for Base {
|
|||
let uuid_ref: Uuid = entry
|
||||
.get_ava_single_uuid("uuid")
|
||||
.ok_or_else(|| OperationError::InvalidAttribute("uuid".to_string()))?;
|
||||
trace!("Entry valid UUID: {:?}", entry);
|
||||
if !cand_uuid.insert(uuid_ref) {
|
||||
trace!("uuid duplicate found in create set! {:?}", uuid_ref);
|
||||
return Err(OperationError::Plugin(PluginError::Base(
|
||||
|
@ -224,9 +218,10 @@ impl Plugin for Base {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::PluginError;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
const JSON_ADMIN_ALLOW_ALL: &'static str = r#"{
|
||||
"attrs": {
|
||||
"class": [
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
// The primary point of this is to generate a unique domain UUID on startup
|
||||
// which is importart for management of the replication topo and trust
|
||||
// relationships.
|
||||
use crate::plugins::Plugin;
|
||||
use std::iter::once;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::prelude::*;
|
||||
use compact_jwt::JwsSigner;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use std::iter::once;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_DOMAIN_INFO: PartialValue = PartialValue::new_class("domain_info");
|
||||
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::filter::FilterInvalid;
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLASS_DYNGROUP: PartialValue = PartialValue::new_class("dyngroup");
|
||||
|
@ -361,9 +363,10 @@ impl DynGroup {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
const UUID_TEST_GROUP: Uuid = uuid::uuid!("7bfd9931-06c2-4608-8a46-78719bb746fe");
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// A plugin that generates gid numbers on types that require them for posix
|
||||
// support.
|
||||
|
||||
use std::iter::once;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::uuid_to_gid_u32;
|
||||
use std::iter::once;
|
||||
|
||||
/// Systemd dynamic units allocate between 61184–65519, most distros allocate
|
||||
/// system uids from 0 - 1000, and many others give user ids between 1000 to
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use compact_jwt::JwsSigner;
|
||||
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::password_from_random;
|
||||
use compact_jwt::JwsSigner;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLASS_OAUTH2_BASIC: PartialValue =
|
||||
|
|
|
@ -10,16 +10,17 @@
|
|||
// As a result, we first need to run refint to clean up all dangling references, then memberof
|
||||
// fixes the graph of memberships
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
use crate::entry::{Entry, EntryCommitted, EntrySealed, EntryTuple};
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group");
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
//! helps to ensure that data is always in specific known states within the
|
||||
//! `QueryServer`
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use std::sync::Arc;
|
||||
use tracing::trace_span;
|
||||
|
||||
mod attrunique;
|
||||
mod base;
|
||||
|
@ -118,108 +119,99 @@ macro_rules! run_verify_plugin {
|
|||
}
|
||||
|
||||
impl Plugins {
|
||||
#[instrument(level = "debug", name = "plugins::run_pre_create_transform", skip_all)]
|
||||
pub fn run_pre_create_transform(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
|
||||
ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_pre_create_transform", {
|
||||
base::Base::pre_create_transform(qs, cand, ce)
|
||||
.and_then(|_| password_import::PasswordImport::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| jwskeygen::JwsKeygen::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| gidnumber::GidNumber::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| domain::Domain::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| spn::Spn::pre_create_transform(qs, cand, ce))
|
||||
// Should always be last
|
||||
.and_then(|_| attrunique::AttrUnique::pre_create_transform(qs, cand, ce))
|
||||
})
|
||||
base::Base::pre_create_transform(qs, cand, ce)
|
||||
.and_then(|_| password_import::PasswordImport::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| jwskeygen::JwsKeygen::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| gidnumber::GidNumber::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| domain::Domain::pre_create_transform(qs, cand, ce))
|
||||
.and_then(|_| spn::Spn::pre_create_transform(qs, cand, ce))
|
||||
// Should always be last
|
||||
.and_then(|_| attrunique::AttrUnique::pre_create_transform(qs, cand, ce))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_pre_create", skip_all)]
|
||||
pub fn run_pre_create(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &[Entry<EntrySealed, EntryNew>],
|
||||
ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_pre_create", {
|
||||
protected::Protected::pre_create(qs, cand, ce)
|
||||
})
|
||||
protected::Protected::pre_create(qs, cand, ce)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_post_create", skip_all)]
|
||||
pub fn run_post_create(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &[Entry<EntrySealed, EntryCommitted>],
|
||||
ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_post_create", {
|
||||
refint::ReferentialIntegrity::post_create(qs, cand, ce)
|
||||
.and_then(|_| memberof::MemberOf::post_create(qs, cand, ce))
|
||||
})
|
||||
refint::ReferentialIntegrity::post_create(qs, cand, ce)
|
||||
.and_then(|_| memberof::MemberOf::post_create(qs, cand, ce))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_pre_modify", skip_all)]
|
||||
pub fn run_pre_modify(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
||||
me: &ModifyEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_pre_modify", {
|
||||
protected::Protected::pre_modify(qs, cand, me)
|
||||
.and_then(|_| base::Base::pre_modify(qs, cand, me))
|
||||
.and_then(|_| password_import::PasswordImport::pre_modify(qs, cand, me))
|
||||
.and_then(|_| jwskeygen::JwsKeygen::pre_modify(qs, cand, me))
|
||||
.and_then(|_| gidnumber::GidNumber::pre_modify(qs, cand, me))
|
||||
.and_then(|_| domain::Domain::pre_modify(qs, cand, me))
|
||||
.and_then(|_| spn::Spn::pre_modify(qs, cand, me))
|
||||
// attr unique should always be last
|
||||
.and_then(|_| attrunique::AttrUnique::pre_modify(qs, cand, me))
|
||||
})
|
||||
protected::Protected::pre_modify(qs, cand, me)
|
||||
.and_then(|_| base::Base::pre_modify(qs, cand, me))
|
||||
.and_then(|_| password_import::PasswordImport::pre_modify(qs, cand, me))
|
||||
.and_then(|_| jwskeygen::JwsKeygen::pre_modify(qs, cand, me))
|
||||
.and_then(|_| gidnumber::GidNumber::pre_modify(qs, cand, me))
|
||||
.and_then(|_| domain::Domain::pre_modify(qs, cand, me))
|
||||
.and_then(|_| spn::Spn::pre_modify(qs, cand, me))
|
||||
// attr unique should always be last
|
||||
.and_then(|_| attrunique::AttrUnique::pre_modify(qs, cand, me))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_post_modify", skip_all)]
|
||||
pub fn run_post_modify(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
|
||||
cand: &[Entry<EntrySealed, EntryCommitted>],
|
||||
me: &ModifyEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_post_modify", {
|
||||
refint::ReferentialIntegrity::post_modify(qs, pre_cand, cand, me)
|
||||
.and_then(|_| spn::Spn::post_modify(qs, pre_cand, cand, me))
|
||||
.and_then(|_| memberof::MemberOf::post_modify(qs, pre_cand, cand, me))
|
||||
})
|
||||
refint::ReferentialIntegrity::post_modify(qs, pre_cand, cand, me)
|
||||
.and_then(|_| spn::Spn::post_modify(qs, pre_cand, cand, me))
|
||||
.and_then(|_| memberof::MemberOf::post_modify(qs, pre_cand, cand, me))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_pre_delete", skip_all)]
|
||||
pub fn run_pre_delete(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
||||
de: &DeleteEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_pre_delete", {
|
||||
protected::Protected::pre_delete(qs, cand, de)
|
||||
})
|
||||
protected::Protected::pre_delete(qs, cand, de)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_post_delete", skip_all)]
|
||||
pub fn run_post_delete(
|
||||
qs: &QueryServerWriteTransaction,
|
||||
cand: &[Entry<EntrySealed, EntryCommitted>],
|
||||
de: &DeleteEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("plugins::run_post_delete", {
|
||||
refint::ReferentialIntegrity::post_delete(qs, cand, de)
|
||||
.and_then(|_| memberof::MemberOf::post_delete(qs, cand, de))
|
||||
})
|
||||
refint::ReferentialIntegrity::post_delete(qs, cand, de)
|
||||
.and_then(|_| memberof::MemberOf::post_delete(qs, cand, de))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "plugins::run_verify", skip_all)]
|
||||
pub fn run_verify(
|
||||
qs: &QueryServerReadTransaction,
|
||||
results: &mut Vec<Result<(), ConsistencyError>>,
|
||||
) {
|
||||
let _entered = trace_span!("plugins::run_verify").entered();
|
||||
spanned!("plugins::run_verify", {
|
||||
run_verify_plugin!(qs, results, base::Base);
|
||||
run_verify_plugin!(qs, results, attrunique::AttrUnique);
|
||||
run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
|
||||
run_verify_plugin!(qs, results, dyngroup::DynGroup);
|
||||
run_verify_plugin!(qs, results, memberof::MemberOf);
|
||||
run_verify_plugin!(qs, results, spn::Spn);
|
||||
})
|
||||
run_verify_plugin!(qs, results, base::Base);
|
||||
run_verify_plugin!(qs, results, attrunique::AttrUnique);
|
||||
run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
|
||||
run_verify_plugin!(qs, results, dyngroup::DynGroup);
|
||||
run_verify_plugin!(qs, results, memberof::MemberOf);
|
||||
run_verify_plugin!(qs, results, spn::Spn);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Transform password import requests into proper kanidm credentials.
|
||||
use std::convert::TryFrom;
|
||||
use std::iter::once;
|
||||
|
||||
use kanidm_proto::v1::PluginError;
|
||||
|
||||
use crate::credential::{Credential, Password};
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::PluginError;
|
||||
use std::convert::TryFrom;
|
||||
use std::iter::once;
|
||||
|
||||
pub struct PasswordImport {}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// System protected objects. Items matching specific requirements
|
||||
// may only have certain modifications performed.
|
||||
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
use crate::modify::Modify;
|
||||
use hashbrown::HashSet;
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct Protected {}
|
||||
|
||||
|
|
|
@ -9,19 +9,19 @@
|
|||
// when that is written, as they *both* manipulate and alter entry reference
|
||||
// data, so we should be careful not to step on each other.
|
||||
|
||||
use hashbrown::HashSet as Set;
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use hashbrown::HashSet as Set;
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
use crate::filter::f_eq;
|
||||
use crate::modify::Modify;
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use kanidm_proto::v1::{ConsistencyError, PluginError};
|
||||
use std::sync::Arc;
|
||||
use tracing::trace;
|
||||
|
||||
// NOTE: This *must* be after base.rs!!!
|
||||
|
||||
|
@ -265,9 +265,10 @@ impl Plugin for ReferentialIntegrity {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::PluginError;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
// The create references a uuid that doesn't exist - reject
|
||||
#[test]
|
||||
fn test_create_uuid_reference_not_exist() {
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
// Generate and manage spn's for all entries in the domain. Also deals with
|
||||
// the infrequent - but possible - case where a domain is renamed.
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use std::iter::once;
|
||||
use std::sync::Arc;
|
||||
|
||||
// use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
use crate::constants::UUID_DOMAIN_INFO;
|
||||
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::value::PartialValue;
|
||||
// use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use std::iter::once;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Spn {}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use kanidm_proto::v1::OperationError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -73,11 +74,13 @@ impl Cid {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::repl::cid::Cid;
|
||||
use std::cmp::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::repl::cid::Cid;
|
||||
|
||||
#[test]
|
||||
fn test_cid_ordering() {
|
||||
// Check diff ts
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
use super::cid::Cid;
|
||||
use crate::prelude::*;
|
||||
use crate::valueset;
|
||||
use kanidm_proto::v1::ConsistencyError;
|
||||
|
||||
use crate::entry::{compare_attrs, Eattrs};
|
||||
use crate::schema::SchemaTransaction;
|
||||
|
||||
use std::collections::btree_map::Keys;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Bound;
|
||||
use std::ops::Bound::*;
|
||||
|
||||
use kanidm_proto::v1::ConsistencyError;
|
||||
|
||||
use super::cid::Cid;
|
||||
use crate::entry::{compare_attrs, Eattrs};
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::valueset;
|
||||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_TOMBSTONE: PartialValue = PartialValue::new_class("tombstone");
|
||||
static ref PVCLASS_RECYCLED: PartialValue = PartialValue::new_class("recycled");
|
||||
|
@ -518,12 +517,13 @@ impl EntryChangelog {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::entry::Eattrs;
|
||||
// use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use crate::repl::entry::{Change, EntryChangelog, State, Transition};
|
||||
use crate::schema::{Schema, SchemaTransaction};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_entrychangelog_basic() {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
|
||||
use idlset::v2::IDLBitRange;
|
||||
use kanidm_proto::v1::ConsistencyError;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Bound::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
|
||||
use idlset::v2::IDLBitRange;
|
||||
use kanidm_proto::v1::ConsistencyError;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
|
||||
pub struct ReplicationUpdateVector {
|
||||
// This sorts by time. Should we look up by IDL or by UUID?
|
||||
// I think IDL, because when we need to actually do the look ups we'll need
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue