Rework deps (#1079)

This commit is contained in:
Firstyear 2022-10-01 16:08:51 +10:00 committed by GitHub
parent 8e0238ab97
commit 821b2c05c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
181 changed files with 6355 additions and 7591 deletions

11
.rustfmt.toml Normal file
View 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
View file

@ -82,6 +82,21 @@ dependencies = [
"memchr", "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]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -186,6 +201,7 @@ version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
dependencies = [ dependencies = [
"brotli",
"flate2", "flate2",
"futures-core", "futures-core",
"futures-io", "futures-io",
@ -578,6 +594,27 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" 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]] [[package]]
name = "bstr" name = "bstr"
version = "0.2.17" version = "0.2.17"
@ -602,6 +639,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -753,6 +796,12 @@ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "compact_jwt" name = "compact_jwt"
version = "0.2.8" version = "0.2.8"
@ -850,9 +899,9 @@ dependencies = [
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.16.0" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
dependencies = [ dependencies = [
"percent-encoding", "percent-encoding",
"time 0.3.14", "time 0.3.14",
@ -865,7 +914,7 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd" checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd"
dependencies = [ dependencies = [
"cookie 0.16.0", "cookie 0.16.1",
"idna 0.2.3", "idna 0.2.3",
"log", "log",
"publicsuffix", "publicsuffix",
@ -1991,6 +2040,19 @@ dependencies = [
"want", "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]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.5.0" version = "0.5.0"
@ -2061,6 +2123,20 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" 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]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.1" version = "1.9.1"
@ -2174,7 +2250,7 @@ dependencies = [
"rusqlite", "rusqlite",
"saffron", "saffron",
"serde", "serde",
"serde_cbor", "serde_cbor_2",
"serde_json", "serde_json",
"sketching", "sketching",
"smartstring", "smartstring",
@ -2664,6 +2740,28 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.15"
@ -2724,6 +2822,7 @@ dependencies = [
"getrandom 0.2.7", "getrandom 0.2.7",
"http", "http",
"rand 0.8.5", "rand 0.8.5",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"serde_path_to_error", "serde_path_to_error",
@ -2773,9 +2872,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.41" version = "0.10.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if 1.0.0", "cfg-if 1.0.0",
@ -2805,9 +2904,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.75" version = "0.9.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@ -2919,6 +3018,24 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 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]] [[package]]
name = "phonenumber" name = "phonenumber"
version = "0.3.1+8.12.9" version = "0.3.1+8.12.9"
@ -3085,9 +3202,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.43" version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -3124,6 +3241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f" checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f"
dependencies = [ dependencies = [
"checked_int_cast", "checked_int_cast",
"image",
] ]
[[package]] [[package]]
@ -3341,7 +3459,7 @@ checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
dependencies = [ dependencies = [
"base64 0.13.0", "base64 0.13.0",
"bytes", "bytes",
"cookie 0.16.0", "cookie 0.16.1",
"cookie_store", "cookie_store",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -3350,6 +3468,7 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"hyper", "hyper",
"hyper-rustls",
"hyper-tls", "hyper-tls",
"ipnet", "ipnet",
"js-sys", "js-sys",
@ -3360,19 +3479,38 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite 0.2.9", "pin-project-lite 0.2.9",
"proc-macro-hack", "proc-macro-hack",
"rustls",
"rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots",
"winreg", "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]] [[package]]
name = "route-recognizer" name = "route-recognizer"
version = "0.2.0" version = "0.2.0"
@ -3443,6 +3581,27 @@ dependencies = [
"nom 7.1.1", "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]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.11" version = "1.0.11"
@ -3511,7 +3670,6 @@ version = "1.1.0-alpha.9"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
"base64 0.13.0",
"compact_jwt", "compact_jwt",
"futures", "futures",
"futures-util", "futures-util",
@ -3541,6 +3699,16 @@ dependencies = [
"webauthn-authenticator-rs", "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]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.7.0" version = "2.7.0"
@ -3800,9 +3968,15 @@ dependencies = [
"event-listener", "event-listener",
] ]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "sketching" name = "sketching"
version = "0.1.0" version = "1.1.0-alpha.9"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"num_enum", "num_enum",
@ -3861,6 +4035,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "sptr" name = "sptr"
version = "0.3.2" version = "0.3.2"
@ -3965,9 +4145,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.100" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4027,18 +4207,18 @@ checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.35" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.35" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4086,6 +4266,7 @@ dependencies = [
"async-compression", "async-compression",
"futures-lite", "futures-lite",
"http-types", "http-types",
"phf",
"regex", "regex",
"tide", "tide",
] ]
@ -4273,6 +4454,17 @@ dependencies = [
"tokio", "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]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.4" version = "0.7.4"
@ -4441,6 +4633,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "url" name = "url"
version = "2.3.1" version = "2.3.1"
@ -4774,6 +4972,25 @@ dependencies = [
"web-sys", "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]] [[package]]
name = "wepoll-ffi" name = "wepoll-ffi"
version = "0.1.2" version = "0.1.2"

View file

@ -17,20 +17,131 @@ members = [
"kanidmd/score", "kanidmd/score",
"orca", "orca",
"profiles", "profiles",
"sketching", "sketching"
] ]
exclude = [ exclude = [
"kanidm_unix_int/pam_tester" "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] [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" } # compact_jwt = { path = "../compact_jwt" }
concread = "^0.4.0"
# concread = { path = "../concread" } # 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" } # 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-authenticator-rs = "0.4.7"
webauthn-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 = { path = "../webauthn-rs/webauthn-rs" }
# webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" } # webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" }
# webauthn-rs-proto = { path = "../webauthn-rs/webauthn-rs-proto" } # 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 # enshrinken the WASMs
[profile.release.package.kanidmd_web_ui] [profile.release.package.kanidmd_web_ui]

View file

@ -1,25 +1,26 @@
[package] [package]
name = "kanidm_client" 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" description = "Kanidm Client Library"
documentation = "https://docs.rs/kanidm_client/latest/kanidm_client/" 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] [dependencies]
tracing = "^0.1.35" tracing.workspace = true
reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] } reqwest = { workspace = true, features=["cookies", "json", "native-tls"] }
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" } kanidm_proto.workspace = true
serde = { version = "^1.0.142", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = "^1.0.83" serde_json.workspace = true
time = { version = "=0.2.27", features = ["serde", "std"] } time = { workspace = true, features = ["serde", "std"] }
tokio = { version = "^1.21.1", features = ["rt", "net", "time", "macros", "sync", "signal"] } tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] }
toml = "^0.5.9" toml.workspace = true
uuid = { version = "^1.1.2", features = ["serde", "v4"] } uuid = { workspace = true, features = ["serde", "v4"] }
url = { version = "^2.3.1", features = ["serde"] } url = { workspace = true, features = ["serde"] }
webauthn-rs-proto = { workspace = true, features = ["wasm"] } webauthn-rs-proto = { workspace = true, features = ["wasm"] }

View file

@ -13,32 +13,26 @@
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
use reqwest::header::CONTENT_TYPE; use std::collections::{BTreeMap, BTreeSet as Set};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::Serialize;
use serde_json::error::Error as SerdeJsonError;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::File; use std::fs::File;
#[cfg(target_family = "unix")] // not needed for windows builds #[cfg(target_family = "unix")] // not needed for windows builds
use std::fs::{metadata, Metadata}; use std::fs::{metadata, Metadata};
use std::io::ErrorKind; use std::io::{ErrorKind, Read};
use std::io::Read;
#[cfg(target_family = "unix")] // not needed for windows builds #[cfg(target_family = "unix")] // not needed for windows builds
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::collections::BTreeMap;
use std::collections::BTreeSet as Set;
use std::path::Path; use std::path::Path;
use std::time::Duration; 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 tokio::sync::RwLock;
use url::Url; use url::Url;
use uuid::Uuid; use uuid::Uuid;
pub use reqwest::StatusCode;
use kanidm_proto::v1::*;
use webauthn_rs_proto::{ use webauthn_rs_proto::{
PublicKeyCredential, RegisterPublicKeyCredential, RequestChallengeResponse, PublicKeyCredential, RegisterPublicKeyCredential, RequestChallengeResponse,
}; };
@ -1767,6 +1761,7 @@ impl KanidmClient {
self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs) self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs)
.await .await
} }
pub async fn idm_oauth2_rs_prefer_spn_username(&self, id: &str) -> Result<(), ClientError> { pub async fn idm_oauth2_rs_prefer_spn_username(&self, id: &str) -> Result<(), ClientError> {
let mut update_oauth2_rs = Entry { let mut update_oauth2_rs = Entry {
attrs: BTreeMap::new(), attrs: BTreeMap::new(),

View file

@ -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 std::collections::BTreeMap;
use kanidm_proto::v1::{AccountUnixExtend, CredentialStatus, Entry, SingleStringRequest};
use crate::{ClientError, KanidmClient};
impl KanidmClient { impl KanidmClient {
pub async fn idm_person_account_list(&self) -> Result<Vec<Entry>, ClientError> { pub async fn idm_person_account_list(&self) -> Result<Vec<Entry>, ClientError> {
self.perform_get_request("/v1/person").await self.perform_get_request("/v1/person").await

View file

@ -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 std::collections::BTreeMap;
use kanidm_proto::v1::{AccountUnixExtend, ApiToken, ApiTokenGenerate, CredentialStatus, Entry};
use time::OffsetDateTime; use time::OffsetDateTime;
use uuid::Uuid; use uuid::Uuid;
use crate::{ClientError, KanidmClient};
impl KanidmClient { impl KanidmClient {
pub async fn idm_service_account_list(&self) -> Result<Vec<Entry>, ClientError> { pub async fn idm_service_account_list(&self) -> Result<Vec<Entry>, ClientError> {
self.perform_get_request("/v1/service_account").await self.perform_get_request("/v1/service_account").await

View file

@ -1,30 +1,30 @@
[package] [package]
name = "kanidm_proto" 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" description = "Kanidm Protocol Bindings for serde"
documentation = "https://docs.rs/kanidm_proto/latest/kanidm_proto/" 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] [features]
wasm = ["webauthn-rs-proto/wasm"] wasm = ["webauthn-rs-proto/wasm"]
[dependencies] [dependencies]
base32 = "^0.4.0" base32.workspace = true
base64urlsafedata = "0.1.0" base64urlsafedata.workspace = true
serde = { version = "^1.0.142", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = "^1.0.83" serde_json.workspace = true
# Can not upgrade due to breaking timezone apis. time = { workspace = true, features = ["serde", "std"] }
time = { version = "=0.2.27", features = ["serde", "std"] } url = { workspace = true, features = ["serde"] }
url = { version = "^2.3.1", features = ["serde"] } urlencoding.workspace = true
urlencoding = "2.1.2" uuid = { workspace = true, features = ["serde"] }
uuid = { version = "^1.1.2", features = ["serde"] }
webauthn-rs-proto.workspace = true webauthn-rs-proto.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
last-git-commit = "0.2.0" last-git-commit.workspace = true

View file

@ -1,9 +1,10 @@
// User-facing output things // User-facing output things
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// This is used in user-facing CLIs to set the formatting for output, /// This is used in user-facing CLIs to set the formatting for output,
/// and defaults to text. /// and defaults to text.
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
@ -21,6 +22,7 @@ impl Default for ConsoleOutputMode {
impl FromStr for ConsoleOutputMode { impl FromStr for ConsoleOutputMode {
type Err = &'static str; type Err = &'static str;
/// This can be safely unwrap'd because it'll always return a default of text /// This can be safely unwrap'd because it'll always return a default of text
/// ``` /// ```
/// use kanidm_proto::messages::ConsoleOutputMode; /// use kanidm_proto::messages::ConsoleOutputMode;
@ -141,7 +143,6 @@ impl Default for AccountChangeMessage {
/// msg.output_mode = ConsoleOutputMode::JSON; /// msg.output_mode = ConsoleOutputMode::JSON;
/// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\",\"src_user\":\"Kani\",\"dest_user\":\"Krabby\"}"; /// let expected_result = "{\"action\":\"cake_eating\",\"result\":\"It was amazing\",\"status\":\"success\",\"src_user\":\"Kani\",\"dest_user\":\"Krabby\"}";
/// assert_eq!(format!("{}", msg), expected_result); /// assert_eq!(format!("{}", msg), expected_result);
///
/// ``` /// ```
impl fmt::Display for AccountChangeMessage { impl fmt::Display for AccountChangeMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -149,7 +150,7 @@ impl fmt::Display for AccountChangeMessage {
ConsoleOutputMode::JSON => write!( ConsoleOutputMode::JSON => write!(
f, 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!( ConsoleOutputMode::Text => write!(
f, f,
@ -182,20 +183,20 @@ impl Default for BasicMessage {
/// This outputs in either JSON or Text depending on the output_mode setting /// This outputs in either JSON or Text depending on the output_mode setting
/// ``` /// ```
/// use std::fmt::format;
/// use kanidm_proto::messages::*; /// use kanidm_proto::messages::*;
/// use std::fmt::format;
/// let mut msg = BasicMessage::default(); /// let mut msg = BasicMessage::default();
/// msg.action=String::from("cake_eating"); /// msg.action = String::from("cake_eating");
/// msg.result=String::from("It was amazing"); /// msg.result = String::from("It was amazing");
/// assert_eq!(msg.status, MessageStatus::Success); /// assert_eq!(msg.status, MessageStatus::Success);
/// ///
/// let expected_result = "success - cake_eating: It was amazing"; /// let expected_result = "success - cake_eating: It was amazing";
/// assert_eq!(format!("{}", msg), expected_result); /// assert_eq!(format!("{}", msg), expected_result);
/// ///
/// msg.output_mode = ConsoleOutputMode::JSON; /// 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); /// assert_eq!(format!("{}", msg), expected_result);
///
/// ``` /// ```
impl fmt::Display for BasicMessage { impl fmt::Display for BasicMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -203,7 +204,7 @@ impl fmt::Display for BasicMessage {
ConsoleOutputMode::JSON => write!( ConsoleOutputMode::JSON => write!(
f, 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 => { ConsoleOutputMode::Text => {
write!(f, "{} - {}: {}", self.status, self.action, self.result,) write!(f, "{} - {}: {}", self.status, self.action, self.result,)

View file

@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use base64urlsafedata::Base64UrlSafeData; use base64urlsafedata::Base64UrlSafeData;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use url::Url; use url::Url;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]

View file

@ -1,8 +1,8 @@
use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use std::fmt; use std::fmt;
use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use webauthn_rs_proto::{ use webauthn_rs_proto::{
CreationChallengeResponse, PublicKeyCredential, RegisterPublicKeyCredential, CreationChallengeResponse, PublicKeyCredential, RegisterPublicKeyCredential,
@ -1068,8 +1068,7 @@ impl SingleStringRequest {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::v1::Filter as ProtoFilter; use crate::v1::{Filter as ProtoFilter, TotpAlgo, TotpSecret};
use crate::v1::{TotpAlgo, TotpSecret};
#[test] #[test]
fn test_protofilter_simple() { fn test_protofilter_simple() {

View file

@ -1,15 +1,16 @@
[package] [package]
name = "kanidm_tools" 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" default-run = "kanidm"
license = "MPL-2.0"
description = "Kanidm Client Tools" description = "Kanidm Client Tools"
documentation = "https://docs.rs/kanidm_tools/latest/kanidm_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] [lib]
name = "kanidm_cli" name = "kanidm_cli"
@ -29,28 +30,28 @@ name = "kanidm_badlist_preprocess"
path = "src/badlist_preprocess.rs" path = "src/badlist_preprocess.rs"
[dependencies] [dependencies]
clap = { version = "^3.2", features = ["derive", "env"] } clap = { workspace = true, features = ["derive", "env"] }
compact_jwt = "^0.2.3" compact_jwt.workspace = true
dialoguer = "^0.10.1" dialoguer.workspace = true
libc = "^0.2.127" libc.workspace = true
kanidm_client = { path = "../kanidm_client", version = "1.1.0-alpha.8" } kanidm_client.workspace = true
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" } kanidm_proto.workspace = true
qrcode = { version = "^0.12.0", default-features = false } qrcode = { workspace = true, default-features = false }
rayon = "^1.5.3" rayon.workspace = true
rpassword = "^7.0.0" rpassword.workspace = true
serde = { version = "^1.0.142", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = "^1.0.83" serde_json.workspace = true
shellexpand = "^2.1.2" shellexpand.workspace = true
time = { version = "=0.2.27", features = ["serde", "std"] } time = { workspace = true, features = ["serde", "std"] }
tracing = "^0.1.35" tracing.workspace = true
tracing-subscriber = { version = "^0.3.14", features = ["env-filter", "fmt"] } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
tokio = { version = "^1.21.1", features = ["rt", "macros"] } tokio = { workspace = true, features = ["rt", "macros"] }
url = { version = "^2.3.1", features = ["serde"] } url = { workspace = true, features = ["serde"] }
uuid = "^1.1.2" uuid.workspace = true
webauthn-authenticator-rs = { workspace = true, features = ["u2fhid"] } webauthn-authenticator-rs = { workspace = true, features = ["u2fhid"] }
zxcvbn = "^2.2.1" zxcvbn.workspace = true
[build-dependencies] [build-dependencies]
clap = { version = "^3.2", features = ["derive"] } clap = { workspace = true, features = ["derive"] }
clap_complete = { version = "^3.2.5"} clap_complete.workspace = true
uuid = "^1.1.2" uuid.workspace = true

View file

@ -2,10 +2,10 @@
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use uuid::Uuid;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use clap_complete::{generate_to, Shell}; use clap_complete::{generate_to, Shell};
use uuid::Uuid;
include!("src/opt/ssh_authorizedkeys.rs"); include!("src/opt/ssh_authorizedkeys.rs");
include!("src/opt/badlist_preprocess.rs"); include!("src/opt/badlist_preprocess.rs");

View file

@ -14,9 +14,8 @@ use std::io::BufWriter;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use kanidm_proto::v1::Modify;
use clap::Parser; use clap::Parser;
use kanidm_proto::v1::Modify;
use rayon::prelude::*; use rayon::prelude::*;
use tracing::{debug, error, info}; use tracing::{debug, error, info};

View file

@ -1,11 +1,14 @@
use crate::session::read_tokens; use std::str::FromStr;
use crate::CommonOpt;
use compact_jwt::{Jws, JwsUnverified}; use compact_jwt::{Jws, JwsUnverified};
use dialoguer::{theme::ColorfulTheme, Select}; use dialoguer::theme::ColorfulTheme;
use dialoguer::Select;
use kanidm_client::{KanidmClient, KanidmClientBuilder}; use kanidm_client::{KanidmClient, KanidmClientBuilder};
use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME}; use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME};
use kanidm_proto::v1::UserAuthToken; use kanidm_proto::v1::UserAuthToken;
use std::str::FromStr;
use crate::session::read_tokens;
use crate::CommonOpt;
impl CommonOpt { impl CommonOpt {
pub fn to_unauth_client(&self) -> KanidmClient { pub fn to_unauth_client(&self) -> KanidmClient {

View file

@ -15,6 +15,7 @@
extern crate tracing; extern crate tracing;
use std::path::PathBuf; use std::path::PathBuf;
use uuid::Uuid; use uuid::Uuid;
include!("../opt/kanidm.rs"); include!("../opt/kanidm.rs");

View file

@ -13,7 +13,8 @@
use clap::Parser; use clap::Parser;
use kanidm_cli::KanidmClientParser; use kanidm_cli::KanidmClientParser;
use tracing_subscriber::{fmt, prelude::*, EnvFilter}; use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() { async fn main() {

View file

@ -1,22 +1,25 @@
use crate::password_prompt; use std::fmt::{self, Debug};
use crate::{ use std::str::FromStr;
AccountCredential, AccountRadius, AccountSsh, AccountValidity, PersonOpt, PersonPosix,
}; use dialoguer::theme::ColorfulTheme;
use dialoguer::{theme::ColorfulTheme, Select}; use dialoguer::{Confirm, Input, Password, Select};
use dialoguer::{Confirm, Input, Password};
use kanidm_client::ClientError::Http as ClientErrorHttp; use kanidm_client::ClientError::Http as ClientErrorHttp;
use kanidm_client::KanidmClient; use kanidm_client::KanidmClient;
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus}; use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
use kanidm_proto::v1::OperationError::PasswordQuality; use kanidm_proto::v1::OperationError::PasswordQuality;
use kanidm_proto::v1::{CUIntentToken, CURegState, CUSessionToken, CUStatus, TotpSecret}; use kanidm_proto::v1::{CUIntentToken, CURegState, CUSessionToken, CUStatus, TotpSecret};
use qrcode::{render::unicode, QrCode}; use qrcode::render::unicode;
use std::fmt::{self, Debug}; use qrcode::QrCode;
use std::str::FromStr;
use time::OffsetDateTime; use time::OffsetDateTime;
use url::Url; use url::Url;
use uuid::Uuid; 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 { impl PersonOpt {
pub fn debug(&self) -> bool { pub fn debug(&self) -> bool {

View file

@ -1,14 +1,14 @@
use crate::RawOpt;
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use std::path::Path; use std::path::Path;
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use crate::RawOpt;
fn read_file<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T, Box<dyn Error>> { fn read_file<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T, Box<dyn Error>> {
let f = File::open(path)?; let f = File::open(path)?;
let r = BufReader::new(f); let r = BufReader::new(f);

View file

@ -1,9 +1,10 @@
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
use time::OffsetDateTime;
use crate::{ use crate::{
AccountSsh, AccountValidity, ServiceAccountApiToken, ServiceAccountCredential, AccountSsh, AccountValidity, ServiceAccountApiToken, ServiceAccountCredential,
ServiceAccountOpt, ServiceAccountPosix, ServiceAccountOpt, ServiceAccountPosix,
}; };
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
use time::OffsetDateTime;
impl ServiceAccountOpt { impl ServiceAccountOpt {
pub fn debug(&self) -> bool { pub fn debug(&self) -> bool {

View file

@ -1,23 +1,22 @@
use crate::common::prompt_for_username_get_username; use std::collections::BTreeMap;
use crate::{LoginOpt, LogoutOpt, SessionOpt}; 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_client::{ClientError, KanidmClient};
use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState, UserAuthToken}; use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState, UserAuthToken};
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
use libc::umask; use libc::umask;
use std::collections::BTreeMap; use webauthn_authenticator_rs::prelude::RequestChallengeResponse;
use std::fs::{create_dir, File}; use webauthn_authenticator_rs::u2fhid::U2FHid;
use std::io::ErrorKind; use webauthn_authenticator_rs::WebauthnAuthenticator;
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 dialoguer::{theme::ColorfulTheme, Select}; use crate::common::prompt_for_username_get_username;
use crate::{LoginOpt, LogoutOpt, SessionOpt};
use compact_jwt::JwsUnverified;
static TOKEN_DIR: &str = "~/.cache"; static TOKEN_DIR: &str = "~/.cache";
static TOKEN_PATH: &str = "~/.cache/kanidm_tokens"; static TOKEN_PATH: &str = "~/.cache/kanidm_tokens";

View file

@ -12,7 +12,6 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use kanidm_client::{ClientError, KanidmClientBuilder}; use kanidm_client::{ClientError, KanidmClientBuilder};
use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME}; use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME};
use tracing::{debug, error}; use tracing::{debug, error};

View file

@ -1,14 +1,15 @@
[package] [package]
name = "kanidm_unix_int" 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" description = "Kanidm Unix Integration Clients"
documentation = "https://docs.rs/kanidm/latest/kanidm/" 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] [lib]
name = "kanidm_unix_common" name = "kanidm_unix_common"
@ -43,46 +44,42 @@ name = "kanidm_test_auth"
path = "src/test_auth.rs" path = "src/test_auth.rs"
[dependencies] [dependencies]
kanidm_client = { path = "../kanidm_client" } bytes.workspace = true
kanidm_proto = { path = "../kanidm_proto" } clap = { workspace = true, features = ["derive", "env"] }
kanidm = { path = "../kanidmd/idm" } 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" r2d2.workspace = true
sketching = { path = "../sketching" } 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" toml.workspace = true
rpassword = "^7.0.0" tokio = { workspace = true, features = ["rt", "macros", "sync", "time", "net", "io-util"] }
tokio = { version = "^1.21.1", features = ["rt", "macros", "sync", "time", "net", "io-util"] } tokio-util = { workspace = true, features = ["codec"] }
tokio-util = { version = "^0.7.4", features = ["codec"] } tracing.workspace = true
reqwest.workspace = true
futures = "^0.3.21" users.workspace = true
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"
[features] [features]
# default = [ "libsqlite3-sys/bundled" ] # default = [ "libsqlite3-sys/bundled" ]
[dev-dependencies] [dev-dependencies]
# kanidm = { path = "../kanidmd/idm" } # kanidm = { path = "../kanidmd/idm" }
score = { path = "../kanidmd/score" } score.workspace = true
[build-dependencies] [build-dependencies]
clap = { version = "^3.2", features = ["derive"] } clap = { workspace = true, features = ["derive"] }
clap_complete = "^3.2.5" clap_complete.workspace = true
profiles = { path = "../profiles" } profiles.workspace = true

View file

@ -1,11 +1,10 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::env; use std::env;
use std::path::PathBuf;
use clap::{IntoApp, Parser}; use clap::{IntoApp, Parser};
use clap_complete::{generate_to, Shell}; use clap_complete::{generate_to, Shell};
use std::path::PathBuf;
include!("src/opt/ssh_authorizedkeys.rs"); include!("src/opt/ssh_authorizedkeys.rs");
include!("src/opt/cache_invalidate.rs"); include!("src/opt/cache_invalidate.rs");
include!("src/opt/cache_clear.rs"); include!("src/opt/cache_clear.rs");

View file

@ -1,9 +1,13 @@
[package] [package]
name = "nss_kanidm" name = "nss_kanidm"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"] version.workspace = true
rust-version = "1.59" authors.workspace = true
edition = "2021" rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[lib] [lib]
name = "nss_kanidm" name = "nss_kanidm"
@ -11,9 +15,9 @@ crate-type = [ "cdylib" ]
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
kanidm_unix_int = { path = "../" } kanidm_unix_int.workspace = true
libnss = "^0.4.0" libnss.workspace = true
libc = "^0.2.127" libc.workspace = true
paste = "^1.0.9" paste.workspace = true
lazy_static = "^1.4.0" lazy_static.workspace = true

View file

@ -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::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, NssGroup, NssUser}; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, NssGroup, NssUser};
use libnss::group::{Group, GroupHooks}; use libnss::group::{Group, GroupHooks};
use libnss::interop::Response; use libnss::interop::Response;
use libnss::passwd::{Passwd, PasswdHooks}; use libnss::passwd::{Passwd, PasswdHooks};

View file

@ -1,19 +1,23 @@
[package] [package]
name = "pam_kanidm" name = "pam_kanidm"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.59"
edition = "2021"
links = "pam" 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] [lib]
name = "pam_kanidm" name = "pam_kanidm"
crate-type = [ "cdylib" ] crate-type = [ "cdylib" ]
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
kanidm_unix_int = { path = "../" } kanidm_unix_int.workspace = true
libc = "^0.2.127" libc.workspace = true
[build-dependencies] [build-dependencies]
pkg-config = "^0.3.25" pkg-config.workspace = true

View file

@ -14,19 +14,20 @@
// extern crate libc; // extern crate libc;
mod pam; mod pam;
use crate::pam::constants::*;
use crate::pam::conv::PamConv;
use crate::pam::module::{PamHandle, PamHooks};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ffi::CStr; use std::ffi::CStr;
// use std::os::raw::c_char; // use std::os::raw::c_char;
use kanidm_unix_common::client_sync::call_daemon_blocking; use kanidm_unix_common::client_sync::call_daemon_blocking;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; 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)] #[derive(Debug)]
struct Options { struct Options {
debug: bool, debug: bool,

View file

@ -1,9 +1,9 @@
use libc::{c_char, c_int};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::ptr; use std::ptr;
use crate::pam::constants::PamResultCode; use libc::{c_char, c_int};
use crate::pam::constants::*;
use crate::pam::constants::{PamResultCode, *};
use crate::pam::module::{PamItem, PamResult}; use crate::pam::module::{PamItem, PamResult};
#[allow(missing_copy_implementations)] #[allow(missing_copy_implementations)]

View file

@ -7,10 +7,11 @@
/// Here is full example of a PAM module that would authenticate and authorize everybody: /// 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::{PamFlag, PamResultCode};
/// use pam::constants::{PamResultCode, PamFlag}; /// use pam::module::{PamHandle, PamHooks};
/// use std::ffi::CStr; /// use std::ffi::CStr;
/// ///
/// # fn main() {} /// # fn main() {}
@ -18,15 +19,15 @@
/// pam_hooks!(MyPamModule); /// pam_hooks!(MyPamModule);
/// ///
/// impl PamHooks for MyPamModule { /// impl PamHooks for MyPamModule {
/// fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { /// fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
/// println!("Everybody is authenticated!"); /// println!("Everybody is authenticated!");
/// PamResultCode::PAM_SUCCESS /// PamResultCode::PAM_SUCCESS
/// } /// }
/// ///
/// fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { /// fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
/// println!("Everybody is authorized!"); /// println!("Everybody is authorized!");
/// PamResultCode::PAM_SUCCESS /// PamResultCode::PAM_SUCCESS
/// } /// }
/// } /// }
/// ``` /// ```
#[macro_export] #[macro_export]
@ -36,6 +37,7 @@ macro_rules! pam_hooks {
mod pam_hooks_scope { mod pam_hooks_scope {
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
use $crate::pam::constants::{PamFlag, PamResultCode}; use $crate::pam::constants::{PamFlag, PamResultCode};
use $crate::pam::module::{PamHandle, PamHooks}; use $crate::pam::module::{PamHandle, PamHooks};

View file

@ -1,9 +1,10 @@
//! Functions for use in pam modules. //! Functions for use in pam modules.
use libc::c_char;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::{mem, ptr}; use std::{mem, ptr};
use libc::c_char;
use crate::pam::constants::{PamFlag, PamItemType, PamResultCode, PAM_AUTHTOK}; use crate::pam::constants::{PamFlag, PamItemType, PamResultCode, PAM_AUTHTOK};
/// Opaque type, used as a pointer when making pam API calls. /// Opaque type, used as a pointer when making pam API calls.

View file

@ -1,11 +1,14 @@
[package] [package]
name = "pam_tester" 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] [dependencies]
pam = "^0.7.0" pam = "^0.7.0"

View file

@ -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::collections::BTreeSet;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
use std::path::Path; use std::path::Path;
use std::string::ToString; use std::string::ToString;
use std::time::{Duration, SystemTime}; 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 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; const NXCACHE_SIZE: usize = 2048;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -14,9 +14,7 @@
extern crate tracing; extern crate tracing;
use clap::Parser; use clap::Parser;
use futures::executor::block_on; use futures::executor::block_on;
use kanidm_unix_common::client::call_daemon; use kanidm_unix_common::client::call_daemon;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;

View file

@ -14,9 +14,7 @@
extern crate tracing; extern crate tracing;
use clap::Parser; use clap::Parser;
use futures::executor::block_on; use futures::executor::block_on;
use kanidm_unix_common::client::call_daemon; use kanidm_unix_common::client::call_daemon;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;

View file

@ -1,9 +1,8 @@
use bytes::{BufMut, BytesMut};
use futures::SinkExt;
use futures::StreamExt;
use std::error::Error; use std::error::Error;
use std::io::Error as IoError; use std::io::{Error as IoError, ErrorKind};
use std::io::ErrorKind;
use bytes::{BufMut, BytesMut};
use futures::{SinkExt, StreamExt};
use tokio::net::UnixStream; use tokio::net::UnixStream;
// use tokio::runtime::Builder; // use tokio::runtime::Builder;
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
@ -14,8 +13,8 @@ use crate::unix_proto::{ClientRequest, ClientResponse};
struct ClientCodec; struct ClientCodec;
impl Decoder for ClientCodec { impl Decoder for ClientCodec {
type Item = ClientResponse;
type Error = IoError; type Error = IoError;
type Item = ClientResponse;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match serde_json::from_slice::<ClientResponse>(&src) { match serde_json::from_slice::<ClientResponse>(&src) {

View file

@ -1,9 +1,6 @@
use std::error::Error; use std::error::Error;
use std::io::Error as IoError; use std::io::{Error as IoError, ErrorKind, Read, Write};
use std::io::ErrorKind;
use std::io::{Read, Write};
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use crate::unix_proto::{ClientRequest, ClientResponse}; use crate::unix_proto::{ClientRequest, ClientResponse};

View file

@ -10,10 +10,18 @@
#![deny(clippy::needless_pass_by_value)] #![deny(clippy::needless_pass_by_value)]
#![deny(clippy::trivially_copy_pass_by_ref)] #![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 bytes::{BufMut, BytesMut};
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use futures::SinkExt; use futures::{SinkExt, StreamExt};
use futures::StreamExt;
use kanidm::utils::file_permissions_readonly; use kanidm::utils::file_permissions_readonly;
use kanidm_client::KanidmClientBuilder; use kanidm_client::KanidmClientBuilder;
use kanidm_proto::constants::DEFAULT_CLIENT_CONFIG_PATH; 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_config::KanidmUnixdConfig;
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, TaskRequest, TaskResponse}; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, TaskRequest, TaskResponse};
use libc::umask; use libc::umask;
use sketching::tracing_forest::{self, traits::*, util::*}; use sketching::tracing_forest::traits::*;
use std::error::Error; use sketching::tracing_forest::util::*;
use std::fs::metadata; use sketching::tracing_forest::{self};
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 tokio::net::{UnixListener, UnixStream}; use tokio::net::{UnixListener, UnixStream};
use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use tokio::time; use tokio::time;
use tokio_util::codec::Framed; use tokio_util::codec::{Decoder, Encoder, Framed};
use tokio_util::codec::{Decoder, Encoder};
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid}; use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
//=== the codec //=== the codec
@ -47,8 +47,8 @@ type AsyncTaskRequest = (TaskRequest, oneshot::Sender<()>);
struct ClientCodec; struct ClientCodec;
impl Decoder for ClientCodec { impl Decoder for ClientCodec {
type Item = ClientRequest;
type Error = io::Error; type Error = io::Error;
type Item = ClientRequest;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match serde_json::from_slice::<ClientRequest>(&src) { match serde_json::from_slice::<ClientRequest>(&src) {
@ -85,8 +85,8 @@ impl ClientCodec {
struct TaskCodec; struct TaskCodec;
impl Decoder for TaskCodec { impl Decoder for TaskCodec {
type Item = TaskResponse;
type Error = io::Error; type Error = io::Error;
type Item = TaskResponse;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match serde_json::from_slice::<TaskResponse>(&src) { match serde_json::from_slice::<TaskResponse>(&src) {

View file

@ -13,12 +13,10 @@
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
use clap::Parser;
use std::path::PathBuf; use std::path::PathBuf;
use clap::Parser;
// use futures::executor::block_on; // use futures::executor::block_on;
use kanidm_unix_common::client_sync::call_daemon_blocking; use kanidm_unix_common::client_sync::call_daemon_blocking;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;

View file

@ -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::convert::TryFrom;
use std::fmt; use std::fmt;
use std::time::Duration; use std::time::Duration;
use crate::cache::Id;
use tokio::sync::{Mutex, MutexGuard};
use kanidm::be::dbvalue::DbPasswordV1; use kanidm::be::dbvalue::DbPasswordV1;
use kanidm::credential::policy::CryptoPolicy; use kanidm::credential::policy::CryptoPolicy;
use kanidm::credential::Password; 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 { pub struct Db {
pool: Pool<SqliteConnectionManager>, pool: Pool<SqliteConnectionManager>,
@ -732,9 +732,10 @@ impl<'a> Drop for DbTxn<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
use super::Db; use super::Db;
use crate::cache::Id; use crate::cache::Id;
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test"; const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test"; const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test";

View file

@ -13,11 +13,10 @@
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
use clap::Parser;
use std::path::PathBuf; use std::path::PathBuf;
use clap::Parser;
use futures::executor::block_on; use futures::executor::block_on;
use kanidm_unix_common::client::call_daemon; use kanidm_unix_common::client::call_daemon;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;

View file

@ -10,35 +10,31 @@
#![deny(clippy::needless_pass_by_value)] #![deny(clippy::needless_pass_by_value)]
#![deny(clippy::trivially_copy_pass_by_ref)] #![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 std::ffi::CString;
use std::os::unix::fs::symlink;
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::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
use tokio::net::UnixStream; use std::{fs, io};
use tokio::time;
use tokio_util::codec::Framed;
use tokio_util::codec::{Decoder, Encoder};
use bytes::{BufMut, BytesMut};
use futures::{SinkExt, StreamExt};
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;
use kanidm_unix_common::unix_proto::{HomeDirectoryInfo, TaskRequest, TaskResponse}; 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; struct TaskCodec;
impl Decoder for TaskCodec { impl Decoder for TaskCodec {
type Item = TaskRequest;
type Error = io::Error; type Error = io::Error;
type Item = TaskRequest;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match serde_json::from_slice::<TaskRequest>(&src) { match serde_json::from_slice::<TaskRequest>(&src) {

View file

@ -3,9 +3,7 @@
extern crate tracing; extern crate tracing;
use clap::Parser; use clap::Parser;
use futures::executor::block_on; use futures::executor::block_on;
use kanidm_unix_common::client::call_daemon; use kanidm_unix_common::client::call_daemon;
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_config::KanidmUnixdConfig;

View file

@ -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::env;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::File; use std::fs::File;
use std::io::{ErrorKind, Read}; use std::io::{ErrorKind, Read};
use std::path::Path; 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)] #[derive(Debug, Deserialize)]
struct ConfigInt { struct ConfigInt {
db_path: Option<String>, db_path: Option<String>,

View file

@ -1,23 +1,19 @@
#![deny(warnings)] #![deny(warnings)]
use std::future::Future;
use std::net::TcpStream; use std::net::TcpStream;
use std::pin::Pin;
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::atomic::{AtomicU16, Ordering};
use std::time::Duration; use std::time::Duration;
use kanidm::audit::LogLevel; use kanidm::audit::LogLevel;
use kanidm::config::{Configuration, IntegrationTestConfig, ServerRole}; 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::cache::{CacheLayer, Id};
use kanidm_unix_common::constants::{ use kanidm_unix_common::constants::{
DEFAULT_GID_ATTR_MAP, DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_GID_ATTR_MAP, DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX,
DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP, DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP,
}; };
use score::create_server_core;
use kanidm_client::{KanidmClient, KanidmClientBuilder};
use std::future::Future;
use std::pin::Pin;
use tokio::task; use tokio::task;
static PORT_ALLOC: AtomicU16 = AtomicU16::new(28080); static PORT_ALLOC: AtomicU16 = AtomicU16::new(28080);

View file

@ -1,14 +1,15 @@
[package] [package]
name = "daemon" 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" description = "Kanidm Server Daemon"
documentation = "https://docs.rs/kanidm/latest/kanidm/" 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 # 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" path = "src/main.rs"
[dependencies] [dependencies]
kanidm = { path = "../idm" } kanidm.workspace = true
kanidm_proto = { path = "../../kanidm_proto" } kanidm_proto.workspace = true
score = { path = "../score" } score.workspace = true
sketching = { path = "../../sketching" } sketching.workspace = true
clap = { version = "^3.2", features = ["derive", "env"] } clap = { workspace = true, features = ["env"] }
serde = { version = "^1.0.142", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
tokio = { version = "^1.21.1", features = ["rt-multi-thread", "macros", "signal"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
toml = "0.5.9" toml.workspace = true
[target.'cfg(target_family = "windows")'.dependencies] [target.'cfg(target_family = "windows")'.dependencies]
whoami = "^1.2.3" whoami.workspace = true
[target.'cfg(not(target_family = "windows"))'.dependencies] [target.'cfg(not(target_family = "windows"))'.dependencies]
users = "^0.11.0" users.workspace = true
tikv-jemallocator = "0.5" tikv-jemallocator.workspace = true
[build-dependencies] [build-dependencies]
serde = { version = "1", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
clap = { version = "^3.2", features = ["derive"] } clap = { workspace = true, features = ["derive"] }
clap_complete = "^3.2.5" clap_complete.workspace = true
profiles = { path = "../../profiles" } profiles.workspace = true

View file

@ -5,7 +5,6 @@ use std::path::PathBuf;
use clap::{Args, IntoApp, Parser, Subcommand}; use clap::{Args, IntoApp, Parser, Subcommand};
use clap_complete::{generate_to, Shell}; use clap_complete::{generate_to, Shell};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
include!("../idm/src/audit_loglevel.rs"); include!("../idm/src/audit_loglevel.rs");

View file

@ -14,25 +14,15 @@
#[global_allocator] #[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; 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::fs::{metadata, File, Metadata};
use std::io::Read;
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use std::str::FromStr; use std::str::FromStr;
use sketching::tracing_forest::{self, traits::*, util::*}; use clap::{Args, Parser, Subcommand};
use kanidm::audit::LogLevel; use kanidm::audit::LogLevel;
use kanidm::config::{Configuration, OnlineBackup, ServerRole}; use kanidm::config::{Configuration, OnlineBackup, ServerRole};
#[cfg(not(target_family = "windows"))] #[cfg(not(target_family = "windows"))]
@ -43,8 +33,14 @@ use score::{
domain_rename_core, recover_account_core, reindex_server_core, restore_server_core, domain_rename_core, recover_account_core, reindex_server_core, restore_server_core,
vacuum_server_core, verify_server_core, vacuum_server_core, verify_server_core,
}; };
use serde::Deserialize;
use clap::{Args, Parser, Subcommand}; 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"); include!("./opt.rs");

View file

@ -1,98 +1,91 @@
[package] [package]
name = "kanidm" 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" description = "Kanidm Server Library and Binary"
documentation = "https://docs.rs/kanidm/latest/kanidm/" 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] [lib]
name = "kanidm" name = "kanidm"
path = "src/lib.rs" path = "src/lib.rs"
[[bench]]
name = "scaling_10k"
harness = false
[dependencies] [dependencies]
async-std = { version = "^1.12.0", features = ["tokio1"] } async-std.workspace = true
async-trait = "^0.1.57" async-trait.workspace = true
base64 = "^0.13.0" base64.workspace = true
base64urlsafedata = "0.1.0" base64urlsafedata.workspace = true
chrono = "^0.4.20" chrono.workspace = true
compact_jwt = "^0.2.3" compact_jwt.workspace = true
concread = "^0.4.0" concread.workspace = true
dyn-clone = "^1.0.9" dyn-clone.workspace = true
fernet = { version = "^0.2.0", features = ["fernet_danger_timestamps"] } fernet = { workspace = true, features = ["fernet_danger_timestamps"] }
filetime = "^0.2.17" filetime.workspace = true
futures = "^0.3.21" futures.workspace = true
futures-util = "^0.3.21" futures-util.workspace = true
hashbrown = { version = "0.12.3", features = ["serde", "inline-more", "ahash"] } hashbrown.workspace = true
idlset = { version = "^0.2.4" } idlset.workspace = true
kanidm_proto = { path = "../../kanidm_proto" } kanidm_proto.workspace = true
lazy_static = "^1.4.0" lazy_static.workspace = true
ldap3_proto = "^0.2.3" ldap3_proto.workspace = true
libc = "^0.2.127" libc.workspace = true
libsqlite3-sys = "^0.25.0" libsqlite3-sys.workspace = true
num_enum = "^0.5.7" num_enum.workspace = true
openssl = "^0.10.41" openssl.workspace = true
r2d2 = "^0.8.9" r2d2.workspace = true
r2d2_sqlite = "^0.21.0" r2d2_sqlite.workspace = true
rand = "^0.8.5" rand.workspace = true
regex = "^1.5.6" regex.workspace = true
saffron = "^0.1.0" saffron.workspace = true
serde = { version = "^1.0.142", features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_cbor = "^0.11.2" serde_cbor.workspace = true
serde_json = "^1.0.83" serde_json.workspace = true
smartstring = { version = "^1.0.1", features = ["serde"] } sketching.workspace = true
smolset = "^1.3.1" smartstring = { workspace = true, features = ["serde"] }
sshkeys = "^0.3.1" smolset.workspace = true
tide = "^0.16.0" sshkeys.workspace = true
time = { version = "=0.2.27", features = ["serde", "std"] } tide.workspace = true
tokio = { version = "^1.21.1", features = ["net", "sync", "time"] } time = { workspace = true, features = ["serde", "std"] }
tokio-util = { version = "^0.7.4", features = ["codec"] } tokio = { workspace = true, features = ["net", "sync", "time"] }
toml = "^0.5.9" tokio-util = { workspace = true, features = ["codec"] }
touch = "^0.0.1" toml.workspace = true
touch.workspace = true
sketching = { path = "../../sketching" } tracing = { workspace = true, features = ["attributes"] }
tracing = { version = "^0.1.35", features = ["attributes"] }
url = { version = "^2.3.1", features = ["serde"] } url = { workspace = true, features = ["serde"] }
urlencoding = "2.1.2" urlencoding.workspace = true
uuid = { version = "^1.1.2", features = ["serde", "v4" ] } uuid = { workspace = true, features = ["serde", "v4" ] }
validator = { version = "^0.16.0", features = ["phone"] } validator = { workspace = true, features = ["phone"] }
webauthn-rs = { workspace = true, features = ["resident-key-support", "preview-features", "danger-credential-internals"] } webauthn-rs = { workspace = true, features = ["resident-key-support", "preview-features", "danger-credential-internals"] }
webauthn-rs-core.workspace = true webauthn-rs-core.workspace = true
zxcvbn = "^2.2.1" zxcvbn.workspace = true
# because windows really can't build without the bundled one # 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] [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] [target.'cfg(not(target_family = "windows"))'.dependencies]
users = "^0.11.0" rusqlite.workspace = true
users.workspace = true
[features] [features]
# default = [ "libsqlite3-sys/bundled", "openssl/vendored" ] # default = [ "libsqlite3-sys/bundled", "openssl/vendored" ]
[dev-dependencies] [dev-dependencies]
criterion = { version = "^0.4.0", features = ["html_reports"] } criterion = { workspace = true, features = ["html_reports"] }
# For testing webauthn
webauthn-authenticator-rs.workspace = true webauthn-authenticator-rs.workspace = true
[build-dependencies] [build-dependencies]
profiles = { path = "../../profiles" } profiles.workspace = true
[[bench]]
name = "scaling_10k"
harness = false

View file

@ -1,7 +1,9 @@
use std::time::{Duration, Instant};
use async_std::task;
use criterion::{ use criterion::{
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput, criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
}; };
use kanidm; use kanidm;
use kanidm::entry::{Entry, EntryInit, EntryNew}; use kanidm::entry::{Entry, EntryInit, EntryNew};
use kanidm::entry_init; use kanidm::entry_init;
@ -11,9 +13,6 @@ use kanidm::server::QueryServer;
use kanidm::utils::duration_from_epoch_now; use kanidm::utils::duration_from_epoch_now;
use kanidm::value::Value; use kanidm::value::Value;
use async_std::task;
use std::time::{Duration, Instant};
pub fn scaling_user_create_single(c: &mut Criterion) { pub fn scaling_user_create_single(c: &mut Criterion) {
let mut group = c.benchmark_group("user_create_single"); let mut group = c.benchmark_group("user_create_single");
group.sample_size(10); 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

View file

@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use serde::{Deserialize, Serialize};
include!("./audit_loglevel.rs"); include!("./audit_loglevel.rs");
pub const AUDIT_LINE_SIZE: usize = 512; pub const AUDIT_LINE_SIZE: usize = 512;

View file

@ -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::collections::BTreeMap;
use std::time::Duration; use std::time::Duration;
use serde::{Deserialize, Serialize};
use smartstring::alias::String as AttrString;
use uuid::Uuid; use uuid::Uuid;
use crate::be::dbvalue::{DbValueEmailAddressV1, DbValuePhoneNumberV1, DbValueSetV2, DbValueV1};
use crate::prelude::OperationError;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct DbEntryV1 { pub struct DbEntryV1 {
pub attrs: BTreeMap<AttrString, Vec<DbValueV1>>, pub attrs: BTreeMap<AttrString, Vec<DbValueV1>>,

View file

@ -1,15 +1,15 @@
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::time::Duration; use std::time::Duration;
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
use uuid::Uuid; 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_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)] #[derive(Serialize, Deserialize, Debug)]
pub struct DbCidV1 { pub struct DbCidV1 {
#[serde(rename = "d")] #[serde(rename = "d")]
@ -556,11 +556,11 @@ impl DbValueSetV2 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::DbCred;
use super::{DbBackupCodeV1, DbPasswordV1, DbTotpV1, DbWebauthnV1};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use super::{DbBackupCodeV1, DbCred, DbPasswordV1, DbTotpV1, DbWebauthnV1};
fn dbcred_type_default_pw() -> DbCredTypeV1 { fn dbcred_type_default_pw() -> DbCredTypeV1 {
DbCredTypeV1::Pw DbCredTypeV1::Pw
} }

View file

@ -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::{ use crate::be::idl_sqlite::{
IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction, IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction,
}; };
@ -6,24 +21,8 @@ use crate::be::idxkey::{
}; };
use crate::be::{BackendConfig, IdList, IdRawEntry}; use crate::be::{BackendConfig, IdList, IdRawEntry};
use crate::entry::{Entry, EntryCommitted, EntrySealed}; 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 crate::prelude::*;
use tracing::trace; use crate::value::{IndexType, Value};
// use std::borrow::Borrow; // use std::borrow::Borrow;
@ -82,58 +81,56 @@ macro_rules! get_identry {
$idl:expr, $idl:expr,
$is_read_op:expr $is_read_op:expr
) => {{ ) => {{
spanned!("be::idl_arc_sqlite::get_identry", { let mut result: Vec<Arc<EntrySealedCommitted>> = Vec::new();
let mut result: Vec<Arc<EntrySealedCommitted>> = Vec::new(); match $idl {
match $idl { IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => {
IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => { let mut nidl = IDLBitRange::new();
let mut nidl = IDLBitRange::new();
idli.into_iter().for_each(|i| { idli.into_iter().for_each(|i| {
// For all the id's in idl. // For all the id's in idl.
// is it in the cache? // is it in the cache?
match $self.entry_cache.get(&i) { match $self.entry_cache.get(&i) {
Some(eref) => result.push(eref.clone()), Some(eref) => result.push(eref.clone()),
None => unsafe { nidl.push_id(i) }, 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() { if !nidl.is_empty() {
// Now, get anything from nidl that is needed. // Now, get anything from nidl that is needed.
let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?; let mut db_result = $self.db.get_identry(&IdList::Partial(nidl))?;
// Clone everything from db_result into the cache. // Merge the two vecs
if $is_read_op { result.append(&mut db_result);
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. // Return
let idli = (*$self.allids).clone(); Ok(result)
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)
})
}}; }};
} }
@ -165,7 +162,6 @@ macro_rules! get_idl {
$itype:expr, $itype:expr,
$idx_key:expr $idx_key:expr
) => {{ ) => {{
spanned!("be::idl_arc_sqlite::get_idl", {
// SEE ALSO #259: Find a way to implement borrow for this properly. // 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 // 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 // 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 hit, continue.
if let Some(ref data) = cache_r { if let Some(ref data) = cache_r {
trace!( trace!(
%data, cached_index = ?$itype,
"Got cached idl for index {:?} {:?}", attr = ?$attr,
$itype, idl = %data,
$attr,
); );
return Ok(Some(data.as_ref().clone())); 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())) $self.idl_cache.insert(ncache_key, Box::new(idl.clone()))
} }
Ok(db_r) Ok(db_r)
})
}}; }};
} }
@ -215,24 +209,22 @@ macro_rules! name2uuid {
$self:expr, $self:expr,
$name:expr $name:expr
) => {{ ) => {{
spanned!("be::idl_arc_sqlite::name2uuid", { let cache_key = NameCacheKey::Name2Uuid($name.to_string());
let cache_key = NameCacheKey::Name2Uuid($name.to_string()); let cache_r = $self.name_cache.get(&cache_key);
let cache_r = $self.name_cache.get(&cache_key); if let Some(NameCacheValue::U(uuid)) = cache_r {
if let Some(NameCacheValue::U(uuid)) = cache_r { trace!(?uuid, "Got cached name2uuid");
trace!(?uuid, "Got cached name2uuid"); return Ok(Some(uuid.clone()));
return Ok(Some(uuid.clone())); } else {
} else { trace!("Cache miss uuid for name2uuid");
trace!("Cache miss uuid for name2uuid"); }
}
let db_r = $self.db.name2uuid($name)?; let db_r = $self.db.name2uuid($name)?;
if let Some(uuid) = db_r { if let Some(uuid) = db_r {
$self $self
.name_cache .name_cache
.insert(cache_key, NameCacheValue::U(uuid.clone())) .insert(cache_key, NameCacheValue::U(uuid.clone()))
} }
Ok(db_r) Ok(db_r)
})
}}; }};
} }
@ -241,24 +233,22 @@ macro_rules! uuid2spn {
$self:expr, $self:expr,
$uuid:expr $uuid:expr
) => {{ ) => {{
spanned!("be::idl_arc_sqlite::uuid2spn", { let cache_key = NameCacheKey::Uuid2Spn($uuid);
let cache_key = NameCacheKey::Uuid2Spn($uuid); let cache_r = $self.name_cache.get(&cache_key);
let cache_r = $self.name_cache.get(&cache_key); if let Some(NameCacheValue::S(ref spn)) = cache_r {
if let Some(NameCacheValue::S(ref spn)) = cache_r { trace!(?spn, "Got cached uuid2spn");
trace!(?spn, "Got cached uuid2spn"); return Ok(Some(spn.as_ref().clone()));
return Ok(Some(spn.as_ref().clone())); } else {
} else { trace!("Cache miss spn for uuid2spn");
trace!("Cache miss spn for uuid2spn"); }
}
let db_r = $self.db.uuid2spn($uuid)?; let db_r = $self.db.uuid2spn($uuid)?;
if let Some(ref data) = db_r { if let Some(ref data) = db_r {
$self $self
.name_cache .name_cache
.insert(cache_key, NameCacheValue::S(Box::new(data.clone()))) .insert(cache_key, NameCacheValue::S(Box::new(data.clone())))
} }
Ok(db_r) Ok(db_r)
})
}}; }};
} }
@ -267,23 +257,21 @@ macro_rules! uuid2rdn {
$self:expr, $self:expr,
$uuid:expr $uuid:expr
) => {{ ) => {{
spanned!("be::idl_arc_sqlite::uuid2rdn", { let cache_key = NameCacheKey::Uuid2Rdn($uuid);
let cache_key = NameCacheKey::Uuid2Rdn($uuid); let cache_r = $self.name_cache.get(&cache_key);
let cache_r = $self.name_cache.get(&cache_key); if let Some(NameCacheValue::R(ref rdn)) = cache_r {
if let Some(NameCacheValue::R(ref rdn)) = cache_r { return Ok(Some(rdn.clone()));
return Ok(Some(rdn.clone())); } else {
} else { trace!("Cache miss rdn for uuid2rdn");
trace!("Cache miss rdn for uuid2rdn"); }
}
let db_r = $self.db.uuid2rdn($uuid)?; let db_r = $self.db.uuid2rdn($uuid)?;
if let Some(ref data) = db_r { if let Some(ref data) = db_r {
$self $self
.name_cache .name_cache
.insert(cache_key, NameCacheValue::R(data.clone())) .insert(cache_key, NameCacheValue::R(data.clone()))
} }
Ok(db_r) Ok(db_r)
})
}}; }};
} }
@ -528,88 +516,83 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteWriteTransaction<'a> {
} }
impl<'a> IdlArcSqliteWriteTransaction<'a> { impl<'a> IdlArcSqliteWriteTransaction<'a> {
#[instrument(level = "debug", name = "idl_arc_sqlite::commit", skip_all)]
pub fn commit(self) -> Result<(), OperationError> { pub fn commit(self) -> Result<(), OperationError> {
spanned!("be::idl_arc_sqlite::commit", { let IdlArcSqliteWriteTransaction {
let IdlArcSqliteWriteTransaction { db,
db, mut entry_cache,
mut entry_cache, mut idl_cache,
mut idl_cache, mut name_cache,
mut name_cache, op_ts_max,
op_ts_max, allids,
allids, maxid,
maxid, } = self;
} = self;
// Write any dirty items to the disk. // Write any dirty items to the disk.
spanned!("be::idl_arc_sqlite::commit<entry>", { entry_cache
entry_cache .iter_mut_mark_clean()
.iter_mut_mark_clean() .try_for_each(|(k, v)| match v {
.try_for_each(|(k, v)| match v { Some(e) => db.write_identry(e),
Some(e) => db.write_identry(e), None => db.delete_identry(*k),
None => db.delete_identry(*k),
})
}) })
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "Failed to sync entry cache to sqlite"); admin_error!(?e, "Failed to sync entry cache to sqlite");
e e
})?; })?;
spanned!("be::idl_arc_sqlite::commit<idl>", { idl_cache
idl_cache.iter_mut_mark_clean().try_for_each(|(k, v)| { .iter_mut_mark_clean()
match v { .try_for_each(|(k, v)| {
Some(idl) => db.write_idl(k.a.as_str(), k.i, k.k.as_str(), idl), match v {
#[allow(clippy::unreachable)] Some(idl) => db.write_idl(k.a.as_str(), k.i, k.k.as_str(), idl),
None => { #[allow(clippy::unreachable)]
// Due to how we remove items, we always write an empty idl None => {
// to the cache, so this should never be 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. // If it is none, this means we have memory corruption so we MUST
// Why is `v` the `Option` type then? // panic.
unreachable!(); // Why is `v` the `Option` type then?
} unreachable!();
} }
}) }
}) })
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "Failed to sync idl cache to sqlite"); admin_error!(?e, "Failed to sync idl cache to sqlite");
e e
})?; })?;
spanned!("be::idl_arc_sqlite::commit<names>", { name_cache
name_cache .iter_mut_mark_clean()
.iter_mut_mark_clean() .try_for_each(|(k, v)| match (k, v) {
.try_for_each(|(k, v)| match (k, v) { (NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => {
(NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => { db.write_name2uuid_add(k, *v)
db.write_name2uuid_add(k, *v) }
} (NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k),
(NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k), (NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => {
(NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => { db.write_uuid2spn(*uuid, Some(v))
db.write_uuid2spn(*uuid, Some(v)) }
} (NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None),
(NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None), (NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => {
(NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => { db.write_uuid2rdn(*uuid, Some(v))
db.write_uuid2rdn(*uuid, Some(v)) }
} (NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
(NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
_ => Err(OperationError::InvalidCacheState), _ => Err(OperationError::InvalidCacheState),
})
}) })
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "Failed to sync name cache to sqlite"); admin_error!(?e, "Failed to sync name cache to sqlite");
e e
})?; })?;
// Undo the caches in the reverse order. // Undo the caches in the reverse order.
db.commit().map(|()| { db.commit().map(|()| {
op_ts_max.commit(); op_ts_max.commit();
name_cache.commit(); name_cache.commit();
idl_cache.commit(); idl_cache.commit();
entry_cache.commit(); entry_cache.commit();
allids.commit(); allids.commit();
maxid.commit(); maxid.commit();
})
}) })
} }
@ -626,18 +609,16 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
where where
I: Iterator<Item = &'b Entry<EntrySealed, EntryCommitted>>, I: Iterator<Item = &'b Entry<EntrySealed, EntryCommitted>>,
{ {
spanned!("be::idl_arc_sqlite::write_identries", { entries.try_for_each(|e| {
entries.try_for_each(|e| { trace!("Inserting {:?} to cache", e.get_id());
trace!("Inserting {:?} to cache", e.get_id()); if e.get_id() == 0 {
if e.get_id() == 0 { Err(OperationError::InvalidEntryId)
Err(OperationError::InvalidEntryId) } else {
} else { (*self.allids).insert_id(e.get_id());
(*self.allids).insert_id(e.get_id()); self.entry_cache
self.entry_cache .insert_dirty(e.get_id(), Arc::new(e.clone()));
.insert_dirty(e.get_id(), Arc::new(e.clone())); Ok(())
Ok(()) }
}
})
}) })
} }
@ -661,17 +642,15 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
where where
I: Iterator<Item = u64>, I: Iterator<Item = u64>,
{ {
spanned!("be::idl_arc_sqlite::delete_identry", { idl.try_for_each(|i| {
idl.try_for_each(|i| { trace!("Removing {:?} from cache", i);
trace!("Removing {:?} from cache", i); if i == 0 {
if i == 0 { Err(OperationError::InvalidEntryId)
Err(OperationError::InvalidEntryId) } else {
} else { (*self.allids).remove_id(i);
(*self.allids).remove_id(i); self.entry_cache.remove_dirty(i);
self.entry_cache.remove_dirty(i); Ok(())
Ok(()) }
}
})
}) })
} }
@ -682,32 +661,30 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
idx_key: &str, idx_key: &str,
idl: &IDLBitRange, idl: &IDLBitRange,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("be::idl_arc_sqlite::write_idl", { let cache_key = IdlCacheKey {
let cache_key = IdlCacheKey { a: attr.into(),
a: attr.into(), i: itype,
i: itype, k: idx_key.into(),
k: idx_key.into(), };
}; // On idl == 0 the db will remove this, and synthesise an empty IdList on a miss
// 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
// but we can cache this as a new empty IdList instead, so that we can avoid the // db lookup on this idl.
// db lookup on this idl. if idl.is_empty() {
if idl.is_empty() { self.idl_cache
self.idl_cache .insert_dirty(cache_key, Box::new(IDLBitRange::new()));
.insert_dirty(cache_key, Box::new(IDLBitRange::new())); } else {
} else { self.idl_cache
self.idl_cache .insert_dirty(cache_key, Box::new(idl.clone()));
.insert_dirty(cache_key, Box::new(idl.clone())); }
} // self.db.write_idl(audit, attr, itype, idx_key, idl)
// self.db.write_idl(audit, attr, itype, idx_key, idl) Ok(())
Ok(())
})
} }
pub fn optimise_dirty_idls(&mut self) { pub fn optimise_dirty_idls(&mut self) {
self.idl_cache.iter_mut_dirty().for_each(|(k, maybe_idl)| { self.idl_cache.iter_mut_dirty().for_each(|(k, maybe_idl)| {
if let Some(idl) = maybe_idl { if let Some(idl) = maybe_idl {
if idl.maybe_compress() { if idl.maybe_compress() {
filter_trace!(?k, "Compressed idl"); trace!(?k, "Compressed idl");
} }
} }
}) })
@ -971,27 +948,21 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
uuid: Uuid, uuid: Uuid,
add: BTreeSet<String>, add: BTreeSet<String>,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("be::idl_arc_sqlite::write_name2uuid_add", { add.into_iter().for_each(|k| {
add.into_iter().for_each(|k| { let cache_key = NameCacheKey::Name2Uuid(k);
let cache_key = NameCacheKey::Name2Uuid(k); let cache_value = NameCacheValue::U(uuid);
let cache_value = NameCacheValue::U(uuid); self.name_cache.insert_dirty(cache_key, cache_value)
self.name_cache.insert_dirty(cache_key, cache_value) });
}); Ok(())
Ok(())
})
} }
pub fn write_name2uuid_rem(&mut self, rem: BTreeSet<String>) -> Result<(), OperationError> { pub fn write_name2uuid_rem(&mut self, rem: BTreeSet<String>) -> Result<(), OperationError> {
spanned!("be::idl_arc_sqlite::write_name2uuid_rem", { rem.into_iter().for_each(|k| {
// self.db.write_name2uuid_rem(audit, &rem).and_then(|_| { // why not just a for loop here...
rem.into_iter().for_each(|k| { let cache_key = NameCacheKey::Name2Uuid(k);
// why not just a for loop here... self.name_cache.remove_dirty(cache_key)
let cache_key = NameCacheKey::Name2Uuid(k); });
self.name_cache.remove_dirty(cache_key) Ok(())
});
Ok(())
// })
})
} }
pub fn create_uuid2spn(&self) -> Result<(), OperationError> { 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> { 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);
let cache_key = NameCacheKey::Uuid2Spn(uuid); match k {
match k { Some(v) => self
Some(v) => self .name_cache
.name_cache .insert_dirty(cache_key, NameCacheValue::S(Box::new(v))),
.insert_dirty(cache_key, NameCacheValue::S(Box::new(v))), None => self.name_cache.remove_dirty(cache_key),
None => self.name_cache.remove_dirty(cache_key), }
} Ok(())
Ok(())
})
} }
pub fn create_uuid2rdn(&self) -> Result<(), OperationError> { 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> { 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);
let cache_key = NameCacheKey::Uuid2Rdn(uuid); match k {
match k { Some(s) => self
Some(s) => self .name_cache
.name_cache .insert_dirty(cache_key, NameCacheValue::R(s)),
.insert_dirty(cache_key, NameCacheValue::R(s)), None => self.name_cache.remove_dirty(cache_key),
None => self.name_cache.remove_dirty(cache_key), }
} Ok(())
Ok(())
})
} }
pub fn create_idx(&self, attr: &str, itype: IndexType) -> Result<(), OperationError> { pub fn create_idx(&self, attr: &str, itype: IndexType) -> Result<(), OperationError> {

View file

@ -1,24 +1,22 @@
use crate::be::dbentry::DbEntry; use std::convert::{TryFrom, TryInto};
use crate::be::dbentry::DbIdentSpn; use std::sync::Arc;
use crate::be::{BackendConfig, IdList, IdRawEntry, IdxKey, IdxSlope}; use std::time::Duration;
use crate::entry::{Entry, EntryCommitted, EntrySealed};
use crate::prelude::*;
use crate::value::{IndexType, Value};
// use crate::valueset; // use crate::valueset;
use hashbrown::HashMap; use hashbrown::HashMap;
use idlset::v2::IDLBitRange; use idlset::v2::IDLBitRange;
use kanidm_proto::v1::{ConsistencyError, OperationError}; use kanidm_proto::v1::{ConsistencyError, OperationError};
use r2d2::Pool; use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::Connection; use rusqlite::{Connection, OpenFlags, OptionalExtension};
use rusqlite::OpenFlags;
use rusqlite::OptionalExtension;
use std::convert::{TryFrom, TryInto};
use std::sync::Arc;
use std::time::Duration;
use tracing::trace;
use uuid::Uuid; 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; // use uuid::Uuid;
const DBV_ID2ENTRY: &str = "id2entry"; const DBV_ID2ENTRY: &str = "id2entry";
@ -117,12 +115,10 @@ pub trait IdlSqliteTransaction {
fn get_conn(&self) -> &r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>; fn get_conn(&self) -> &r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
fn get_identry(&self, idl: &IdList) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> { fn get_identry(&self, idl: &IdList) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
spanned!("be::idl_sqlite::get_identry", { self.get_identry_raw(idl)?
self.get_identry_raw(idl)? .into_iter()
.into_iter() .map(|ide| ide.into_entry().map(Arc::new))
.map(|ide| ide.into_entry().map(Arc::new)) .collect()
.collect()
})
} }
fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError> { fn get_identry_raw(&self, idl: &IdList) -> Result<Vec<IdRawEntry>, OperationError> {
@ -220,112 +216,104 @@ pub trait IdlSqliteTransaction {
itype: IndexType, itype: IndexType,
idx_key: &str, idx_key: &str,
) -> Result<Option<IDLBitRange>, OperationError> { ) -> Result<Option<IDLBitRange>, OperationError> {
spanned!("be::idl_sqlite::get_idl", { if !(self.exists_idx(attr, itype)?) {
if !(self.exists_idx(attr, itype)?) { filter_error!(
filter_error!( "IdlSqliteTransaction: Index {:?} {:?} not found",
"IdlSqliteTransaction: Index {:?} {:?} not found", itype,
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(),
attr attr
); );
let mut stmt = self return Ok(None);
.get_conn() }
.prepare(query.as_str()) // The table exists - lets now get the actual index itself.
.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)?;
let idl = match idl_raw { let query = format!(
Some(d) => serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?, "SELECT idl FROM idx_{}_{} WHERE key = :idx_key",
// We don't have this value, it must be empty (or we itype.as_idx_str(),
// have a corrupted index ..... attr
None => IDLBitRange::new(), );
}; let mut stmt = self
trace!(%idl, "Got idl for index {:?} {:?}", itype, attr); .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> { 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.
// The table exists - lets now get the actual index itself. let mut stmt = self
let mut stmt = self .get_conn()
.get_conn() .prepare("SELECT uuid FROM idx_name2uuid WHERE name = :name")
.prepare("SELECT uuid FROM idx_name2uuid WHERE name = :name") .map_err(sqlite_error)?;
.map_err(sqlite_error)?; let uuid_raw: Option<String> = stmt
let uuid_raw: Option<String> = stmt .query_row(&[(":name", &name)], |row| row.get(0))
.query_row(&[(":name", &name)], |row| row.get(0)) // We don't mind if it doesn't exist
// We don't mind if it doesn't exist .optional()
.optional() .map_err(sqlite_error)?;
.map_err(sqlite_error)?;
let uuid = uuid_raw.as_ref().and_then(|u| Uuid::parse_str(u).ok()); let uuid = uuid_raw.as_ref().and_then(|u| Uuid::parse_str(u).ok());
trace!(%name, ?uuid, "Got uuid for index");
Ok(uuid) Ok(uuid)
})
} }
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> { fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
spanned!("be::idl_sqlite::uuid2spn", { let uuids = uuid.as_hyphenated().to_string();
let uuids = uuid.as_hyphenated().to_string(); // The table exists - lets now get the actual index itself.
// The table exists - lets now get the actual index itself. let mut stmt = self
let mut stmt = self .get_conn()
.get_conn() .prepare("SELECT spn FROM idx_uuid2spn WHERE uuid = :uuid")
.prepare("SELECT spn FROM idx_uuid2spn WHERE uuid = :uuid") .map_err(sqlite_error)?;
.map_err(sqlite_error)?; let spn_raw: Option<Vec<u8>> = stmt
let spn_raw: Option<Vec<u8>> = stmt .query_row(&[(":uuid", &uuids)], |row| row.get(0))
.query_row(&[(":uuid", &uuids)], |row| row.get(0)) // We don't mind if it doesn't exist
// We don't mind if it doesn't exist .optional()
.optional() .map_err(sqlite_error)?;
.map_err(sqlite_error)?;
let spn: Option<Value> = match spn_raw { let spn: Option<Value> = match spn_raw {
Some(d) => { Some(d) => {
let dbv: DbIdentSpn = let dbv: DbIdentSpn =
serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?; serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?;
Some(Value::from(dbv)) Some(Value::from(dbv))
} }
None => None, None => None,
}; };
trace!(?uuid, ?spn, "Got spn for uuid"); Ok(spn)
Ok(spn)
})
} }
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> { fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
spanned!("be::idl_sqlite::uuid2rdn", { let uuids = uuid.as_hyphenated().to_string();
let uuids = uuid.as_hyphenated().to_string(); // The table exists - lets now get the actual index itself.
// The table exists - lets now get the actual index itself. let mut stmt = self
let mut stmt = self .get_conn()
.get_conn() .prepare("SELECT rdn FROM idx_uuid2rdn WHERE uuid = :uuid")
.prepare("SELECT rdn FROM idx_uuid2rdn WHERE uuid = :uuid") .map_err(sqlite_error)?;
.map_err(sqlite_error)?; let rdn: Option<String> = stmt
let rdn: Option<String> = stmt .query_row(&[(":uuid", &uuids)], |row| row.get(0))
.query_row(&[(":uuid", &uuids)], |row| row.get(0)) // We don't mind if it doesn't exist
// We don't mind if it doesn't exist .optional()
.optional() .map_err(sqlite_error)?;
.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> { 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> { fn get_allids(&self) -> Result<IDLBitRange, OperationError> {
trace!("Building allids...");
let mut stmt = self let mut stmt = self
.get_conn() .get_conn()
.prepare("SELECT id FROM id2entry") .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> { pub fn commit(mut self) -> Result<(), OperationError> {
spanned!("be::idl_sqlite::commit", { assert!(!self.committed);
trace!("Commiting BE WR txn"); self.committed = true;
assert!(!self.committed);
self.committed = true;
self.conn self.conn
.execute("COMMIT TRANSACTION", []) .execute("COMMIT TRANSACTION", [])
.map(|_| ()) .map(|_| ())
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "CRITICAL: failed to commit sqlite txn"); admin_error!(?e, "CRITICAL: failed to commit sqlite txn");
OperationError::BackendEngine OperationError::BackendEngine
}) })
})
} }
pub fn get_id2entry_max_id(&self) -> Result<u64, OperationError> { pub fn get_id2entry_max_id(&self) -> Result<u64, OperationError> {
@ -770,46 +756,42 @@ impl IdlSqliteWriteTransaction {
idx_key: &str, idx_key: &str,
idl: &IDLBitRange, idl: &IDLBitRange,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("be::idl_sqlite::write_idl", { if idl.is_empty() {
if idl.is_empty() { // delete it
trace!(?idl, "purging idl"); // Delete this idx_key from the table.
// delete it let query = format!(
// Delete this idx_key from the table. "DELETE FROM idx_{}_{} WHERE key = :key",
let query = format!( itype.as_idx_str(),
"DELETE FROM idx_{}_{} WHERE key = :key", attr
itype.as_idx_str(), );
attr
);
self.conn self.conn
.prepare(query.as_str()) .prepare(query.as_str())
.and_then(|mut stmt| stmt.execute(&[(":key", &idx_key)])) .and_then(|mut stmt| stmt.execute(&[(":key", &idx_key)]))
.map_err(sqlite_error) .map_err(sqlite_error)
} else { } else {
trace!(?idl, "writing idl"); // Serialise the IdList to Vec<u8>
// Serialise the IdList to Vec<u8> let idl_raw = serde_json::to_vec(idl).map_err(serde_json_error)?;
let idl_raw = serde_json::to_vec(idl).map_err(serde_json_error)?;
// update or create it. // update or create it.
let query = format!( let query = format!(
"INSERT OR REPLACE INTO idx_{}_{} (key, idl) VALUES(:key, :idl)", "INSERT OR REPLACE INTO idx_{}_{} (key, idl) VALUES(:key, :idl)",
itype.as_idx_str(), itype.as_idx_str(),
attr attr
); );
self.conn self.conn
.prepare(query.as_str()) .prepare(query.as_str())
.and_then(|mut stmt| { .and_then(|mut stmt| {
stmt.execute(named_params! { stmt.execute(named_params! {
":key": &idx_key, ":key": &idx_key,
":idl": &idl_raw ":idl": &idl_raw
})
}) })
.map_err(sqlite_error) })
} .map_err(sqlite_error)
// Get rid of the sqlite rows usize }
.map(|_| ()) // Get rid of the sqlite rows usize
}) .map(|_| ())
} }
pub fn create_name2uuid(&self) -> Result<(), OperationError> { pub fn create_name2uuid(&self) -> Result<(), OperationError> {
@ -944,7 +926,7 @@ impl IdlSqliteWriteTransaction {
itype.as_idx_str(), itype.as_idx_str(),
attr attr
); );
trace!(idx = %idx_stmt, "Creating index"); trace!(idx = %idx_stmt, "creating index");
self.conn self.conn
.execute(idx_stmt.as_str(), []) .execute(idx_stmt.as_str(), [])
@ -1034,7 +1016,6 @@ impl IdlSqliteWriteTransaction {
} }
pub unsafe fn purge_id2entry(&self) -> Result<(), OperationError> { pub unsafe fn purge_id2entry(&self) -> Result<(), OperationError> {
trace!("purge id2entry ...");
self.conn self.conn
.execute("DELETE FROM id2entry", []) .execute("DELETE FROM id2entry", [])
.map(|_| ()) .map(|_| ())
@ -1175,7 +1156,6 @@ impl IdlSqliteWriteTransaction {
// If the table is empty, populate the versions as 0. // If the table is empty, populate the versions as 0.
let mut dbv_id2entry = self.get_db_version_key(DBV_ID2ENTRY); let mut dbv_id2entry = self.get_db_version_key(DBV_ID2ENTRY);
trace!(initial = %dbv_id2entry, "dbv_id2entry");
// Check db_version here. // Check db_version here.
// * if 0 -> create v1. // * if 0 -> create v1.
@ -1374,13 +1354,12 @@ impl IdlSqlite {
} }
pub(crate) fn get_allids_count(&self) -> Result<u64, OperationError> { pub(crate) fn get_allids_count(&self) -> Result<u64, OperationError> {
trace!("Counting allids...");
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
self.pool self.pool
.try_get() .try_get()
.expect("Unable to get connection from pool!!!") .expect("Unable to get connection from pool!!!")
.query_row("select count(id) from id2entry", [], |row| row.get(0)) .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 { pub fn read(&self) -> IdlSqliteReadTransaction {

View file

@ -1,9 +1,11 @@
use crate::value::IndexType;
use smartstring::alias::String as AttrString;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use smartstring::alias::String as AttrString;
use crate::value::IndexType;
pub type IdxSlope = u8; pub type IdxSlope = u8;
// Huge props to https://github.com/sunshowers/borrow-complex-key-example/blob/master/src/lib.rs // Huge props to https://github.com/sunshowers/borrow-complex-key-example/blob/master/src/lib.rs

View file

@ -4,35 +4,32 @@
//! is to persist content safely to disk, load that content, and execute queries //! is to persist content safely to disk, load that content, and execute queries
//! utilising indexes in the most effective way possible. //! 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::cell::UnsafeCell;
use std::fs;
use std::ops::DerefMut;
use std::sync::Arc; 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 tracing::{trace, trace_span};
use uuid::Uuid;
use crate::be::dbentry::{DbBackup, DbEntry}; use crate::be::dbentry::{DbBackup, DbEntry};
use crate::entry::{Entry, EntryCommitted, EntryNew, EntrySealed}; use crate::entry::{Entry, EntryCommitted, EntryNew, EntrySealed};
use crate::filter::{Filter, FilterPlan, FilterResolved, FilterValidResolved}; use crate::filter::{Filter, FilterPlan, FilterResolved, FilterValidResolved};
use crate::identity::Limits; use crate::identity::Limits;
use crate::value::Value; use crate::prelude::*;
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::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::repl::ruv::{ use crate::repl::ruv::{
ReplicationUpdateVector, ReplicationUpdateVectorReadTransaction, ReplicationUpdateVector, ReplicationUpdateVectorReadTransaction,
ReplicationUpdateVectorTransaction, ReplicationUpdateVectorWriteTransaction, ReplicationUpdateVectorTransaction, ReplicationUpdateVectorWriteTransaction,
}; };
use crate::value::{IndexType, Value};
pub mod dbentry; pub mod dbentry;
pub mod dbvalue; pub mod dbvalue;
@ -41,12 +38,10 @@ mod idl_sqlite;
pub(crate) mod idxkey; pub(crate) mod idxkey;
pub(crate) use self::idxkey::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxSlope}; pub(crate) use self::idxkey::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxSlope};
use crate::be::idl_arc_sqlite::{ use crate::be::idl_arc_sqlite::{
IdlArcSqlite, IdlArcSqliteReadTransaction, IdlArcSqliteTransaction, IdlArcSqlite, IdlArcSqliteReadTransaction, IdlArcSqliteTransaction,
IdlArcSqliteWriteTransaction, IdlArcSqliteWriteTransaction,
}; };
// Re-export this // Re-export this
pub use crate::be::idl_sqlite::FsType; 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 /// 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. /// execution log, so that it can be examined how an operation proceeded.
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
#[instrument(level = "debug", name = "be::filter2idl", skip_all)]
fn filter2idl( fn filter2idl(
&self, &self,
filt: &FilterResolved, filt: &FilterResolved,
@ -534,6 +530,7 @@ pub trait BackendTransaction {
}) })
} }
#[instrument(level = "debug", name = "be::search", skip_all)]
fn search( fn search(
&self, &self,
erl: &Limits, erl: &Limits,
@ -543,165 +540,150 @@ pub trait BackendTransaction {
// Unlike DS, even if we don't get the index back, we can just pass // Unlike DS, even if we don't get the index back, we can just pass
// to the in-memory filter test and be done. // to the in-memory filter test and be done.
spanned!("be::search", { debug!(filter_optimised = ?filt);
filter_trace!(?filt, "filter optimized");
let (idl, fplan) = trace_span!("be::search -> filter2idl").in_scope(|| { let (idl, fplan) = trace_span!("be::search -> filter2idl")
spanned!("be::search -> filter2idl", { .in_scope(|| self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD))?;
self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD)
})
})?;
filter_trace!(?fplan, "filter executed plan"); debug!(filter_executed_plan = ?fplan);
match &idl { match &idl {
IdList::AllIds => { IdList::AllIds => {
if !erl.unindexed_allow { if !erl.unindexed_allow {
admin_error!( admin_error!(
"filter (search) is fully unindexed, and not allowed by resource limits" "filter (search) is fully unindexed, and not allowed by resource limits"
); );
return Err(OperationError::ResourceLimit); 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. /// Given a filter, assert some condition exists.
/// Basically, this is a specialised case of search, where we don't need to /// 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 /// load any candidates if they match. This is heavily used in uuid
/// refint and attr uniqueness. /// refint and attr uniqueness.
#[instrument(level = "debug", name = "be::exists", skip_all)]
fn exists( fn exists(
&self, &self,
erl: &Limits, erl: &Limits,
filt: &Filter<FilterValidResolved>, filt: &Filter<FilterValidResolved>,
) -> Result<bool, OperationError> { ) -> Result<bool, OperationError> {
let _entered = trace_span!("be::exists").entered(); debug!(filter_optimised = ?filt);
spanned!("be::exists", {
filter_trace!(?filt, "filter optimised");
// Using the indexes, resolve the IdList here, or AllIds. // Using the indexes, resolve the IdList here, or AllIds.
// Also get if the filter was 100% resolved or not. // Also get if the filter was 100% resolved or not.
let (idl, fplan) = spanned!("be::exists -> filter2idl", { let (idl, fplan) = self.filter2idl(filt.to_inner(), FILTER_EXISTS_TEST_THRESHOLD)?;
spanned!("be::exists -> filter2idl", {
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. // Apply limits to the IdList.
match &idl { match &idl {
IdList::AllIds => { IdList::AllIds => {
if !erl.unindexed_allow { if !erl.unindexed_allow {
admin_error!("filter (exists) is fully unindexed, and not allowed by resource limits"); admin_error!(
return Err(OperationError::ResourceLimit); "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(_) => {}
} }
IdList::Partial(idl_br) => {
// Now, check the idl -- if it's fully resolved, we can skip this because the query if !idl_br.below_threshold(erl.search_max_filter_test) {
// was fully indexed. admin_error!("filter (exists) is partial indexed and greater than search_max_filter_test allowed by resource limits");
match &idl { return Err(OperationError::ResourceLimit);
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())
} }
} // 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>> { fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
@ -878,6 +860,7 @@ pub trait BackendTransaction {
impl<'a> BackendTransaction for BackendReadTransaction<'a> { impl<'a> BackendTransaction for BackendReadTransaction<'a> {
type IdlLayerType = IdlArcSqliteReadTransaction<'a>; type IdlLayerType = IdlArcSqliteReadTransaction<'a>;
type RuvType = ReplicationUpdateVectorReadTransaction<'a>;
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
fn get_idlayer(&self) -> &mut IdlArcSqliteReadTransaction<'a> { fn get_idlayer(&self) -> &mut IdlArcSqliteReadTransaction<'a> {
@ -895,8 +878,6 @@ impl<'a> BackendTransaction for BackendReadTransaction<'a> {
unsafe { &mut (*self.idlayer.get()) } unsafe { &mut (*self.idlayer.get()) }
} }
type RuvType = ReplicationUpdateVectorReadTransaction<'a>;
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
fn get_ruv(&self) -> &mut ReplicationUpdateVectorReadTransaction<'a> { fn get_ruv(&self) -> &mut ReplicationUpdateVectorReadTransaction<'a> {
unsafe { &mut (*self.ruv.get()) } unsafe { &mut (*self.ruv.get()) }
@ -930,14 +911,13 @@ impl<'a> BackendReadTransaction<'a> {
impl<'a> BackendTransaction for BackendWriteTransaction<'a> { impl<'a> BackendTransaction for BackendWriteTransaction<'a> {
type IdlLayerType = IdlArcSqliteWriteTransaction<'a>; type IdlLayerType = IdlArcSqliteWriteTransaction<'a>;
type RuvType = ReplicationUpdateVectorWriteTransaction<'a>;
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
fn get_idlayer(&self) -> &mut IdlArcSqliteWriteTransaction<'a> { fn get_idlayer(&self) -> &mut IdlArcSqliteWriteTransaction<'a> {
unsafe { &mut (*self.idlayer.get()) } unsafe { &mut (*self.idlayer.get()) }
} }
type RuvType = ReplicationUpdateVectorWriteTransaction<'a>;
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
fn get_ruv(&self) -> &mut ReplicationUpdateVectorWriteTransaction<'a> { fn get_ruv(&self) -> &mut ReplicationUpdateVectorWriteTransaction<'a> {
unsafe { &mut (*self.ruv.get()) } unsafe { &mut (*self.ruv.get()) }
@ -949,181 +929,179 @@ impl<'a> BackendTransaction for BackendWriteTransaction<'a> {
} }
impl<'a> BackendWriteTransaction<'a> { impl<'a> BackendWriteTransaction<'a> {
#[instrument(level = "debug", name = "be::create", skip_all)]
pub fn create( pub fn create(
&self, &self,
cid: &Cid, cid: &Cid,
entries: Vec<Entry<EntrySealed, EntryNew>>, entries: Vec<Entry<EntrySealed, EntryNew>>,
) -> Result<Vec<Entry<EntrySealed, EntryCommitted>>, OperationError> { ) -> Result<Vec<Entry<EntrySealed, EntryCommitted>>, OperationError> {
spanned!("be::create", { if entries.is_empty() {
if entries.is_empty() { admin_error!("No entries provided to BE to create, invalid server call!");
admin_error!("No entries provided to BE to create, invalid server call!"); return Err(OperationError::EmptyRequest);
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 let idlayer = self.get_idlayer();
// that matches the cid? // Now, assign id's to all the new entries.
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(); let mut id_max = idlayer.get_id2entry_max_id()?;
// Now, assign id's to all the new entries. 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()?; // All good, lets update the RUV.
let c_entries: Vec<_> = entries // This auto compresses.
.into_iter() let ruv_idl = IDLBitRange::from_iter(c_entries.iter().map(|e| e.get_id()));
.map(|e| {
id_max += 1;
e.into_sealed_committed_id(id_max)
})
.collect();
// All good, lets update the RUV. self.get_ruv().insert_change(cid, ruv_idl)?;
// 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)?; 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. Ok(c_entries)
for e in c_entries.iter() {
self.entry_index(None, Some(e))?
}
Ok(c_entries)
})
} }
#[instrument(level = "debug", name = "be::modify", skip_all)]
pub fn modify( pub fn modify(
&self, &self,
cid: &Cid, cid: &Cid,
pre_entries: &[Arc<EntrySealedCommitted>], pre_entries: &[Arc<EntrySealedCommitted>],
post_entries: &[EntrySealedCommitted], post_entries: &[EntrySealedCommitted],
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("be::modify", { if post_entries.is_empty() || pre_entries.is_empty() {
if post_entries.is_empty() || pre_entries.is_empty() { admin_error!("No entries provided to BE to modify, invalid server call!");
admin_error!("No entries provided to BE to modify, invalid server call!"); return Err(OperationError::EmptyRequest);
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| { // Now, given the list of id's, update them
if e.get_changelog().contains_tail_cid(cid) { self.get_idlayer().write_identries(post_entries.iter())?;
Ok(())
} else {
admin_error!(
"Entry changelog does not contain a change related to this transaction"
);
Err(OperationError::ReplEntryNotChanged)
}
})?;
// All good, lets update the RUV. // Finally, we now reindex all the changed entries. We do this by iterating and zipping
// This auto compresses. // over the set, because we know the list is in the same order.
let ruv_idl = IDLBitRange::from_iter(post_entries.iter().map(|e| e.get_id())); pre_entries
self.get_ruv().insert_change(cid, ruv_idl)?; .iter()
.zip(post_entries.iter())
// Now, given the list of id's, update them .try_for_each(|(pre, post)| self.entry_index(Some(pre.as_ref()), Some(post)))
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)))
})
} }
#[instrument(level = "debug", name = "be::reap_tombstones", skip_all)]
pub fn reap_tombstones(&self, cid: &Cid) -> Result<usize, OperationError> { 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
// 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.
// of all the entries we need to examine. let idl = self.get_ruv().trim_up_to(cid).map_err(|e| {
let idl = self.get_ruv().trim_up_to(cid).map_err(|e| { admin_error!(?e, "failed to trim RUV to {:?}", cid);
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 e
})?; })?;
let entries = self if entries.is_empty() {
.get_idlayer() admin_info!("No entries affected - reap_tombstones operation success");
.get_identry(&IdList::Indexed(idl)) return Ok(0);
.map_err(|e| { }
admin_error!(?e, "get_identry failed");
e
})?;
if entries.is_empty() { // Now that we have a list of entries we need to partition them into
admin_info!("No entries affected - reap_tombstones operation success"); // two sets. The entries that are tombstoned and ready to reap_tombstones, and
return Ok(0); // the entries that need to have their change logs trimmed.
}
// Now that we have a list of entries we need to partition them into // First we trim changelogs. Go through each entry, and trim the CL, and write it back.
// two sets. The entries that are tombstoned and ready to reap_tombstones, and let mut entries: Vec<_> = entries.iter().map(|er| er.as_ref().clone()).collect();
// 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. entries
let mut entries: Vec<_> = entries.iter().map(|er| er.as_ref().clone()).collect(); .iter_mut()
.try_for_each(|e| e.get_changelog_mut().trim_up_to(cid))?;
entries // Write down the cl trims
.iter_mut() self.get_idlayer().write_identries(entries.iter())?;
.try_for_each(|e| e.get_changelog_mut().trim_up_to(cid))?;
// Write down the cl trims let (tombstones, leftover): (Vec<_>, Vec<_>) = entries
self.get_idlayer().write_identries(entries.iter())?; .into_iter()
.partition(|e| e.get_changelog().can_delete());
let (tombstones, leftover): (Vec<_>, Vec<_>) = entries // Assert that anything leftover still either is *alive* OR is a tombstone
.into_iter() // and has entries in the RUV!
.partition(|e| e.get_changelog().can_delete()); let ruv_idls = self.get_ruv().ruv_idls();
// Assert that anything leftover still either is *alive* OR is a tombstone if !leftover
// and has entries in the RUV! .iter()
let ruv_idls = self.get_ruv().ruv_idls(); .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 // Now setup to reap_tombstones the tombstones. Remember, in the post cleanup, it's could
.iter() // now have been trimmed to a point we can purge them!
.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 // Assert the id's exist on the entry.
// now have been trimmed to a point we can purge them! let id_list: IDLBitRange = tombstones.iter().map(|e| e.get_id()).collect();
// Assert the id's exist on the entry. // Ensure nothing here exists in the RUV index, else it means
let id_list: IDLBitRange = tombstones.iter().map(|e| e.get_id()).collect(); // 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 // Now, given the list of id's, reap_tombstones them.
// we didn't trim properly, or some other state violation has occured. let sz = id_list.len();
if !((&ruv_idls & &id_list).is_empty()) { self.get_idlayer().delete_identry(id_list.into_iter())?;
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. // Finally, purge the indexes from the entries we removed.
let sz = id_list.len(); tombstones
self.get_idlayer().delete_identry(id_list.into_iter())?; .iter()
.try_for_each(|e| self.entry_index(Some(e), None))?;
// Finally, purge the indexes from the entries we removed. Ok(sz)
tombstones
.iter()
.try_for_each(|e| self.entry_index(Some(e), None))?;
Ok(sz)
})
} }
#[instrument(level = "debug", name = "be::update_idxmeta", skip_all)]
pub fn update_idxmeta(&mut self, idxkeys: Vec<IdxKey>) -> Result<(), OperationError> { pub fn update_idxmeta(&mut self, idxkeys: Vec<IdxKey>) -> Result<(), OperationError> {
if self.is_idx_slopeyness_generated()? { if self.is_idx_slopeyness_generated()? {
trace!("Indexing slopes available"); 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> { pub fn ruv_rebuild(&mut self) -> Result<(), OperationError> {
// Rebuild the ruv! // Rebuild the ruv!
spanned!("server::ruv_rebuild", { // For now this has to read from all the entries in the DB, but in the future
// 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'll actually store this properly (?). If it turns out this is really fast // we may just rebuild this always on startup.
// 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 idl = IdList::AllIds;
let entries = self.get_idlayer().get_identry(&idl).map_err(|e| { let entries = self.get_idlayer().get_identry(&idl).map_err(|e| {
admin_error!(?e, "get_identry failed"); admin_error!(?e, "get_identry failed");
e e
})?; })?;
self.get_ruv().rebuild(&entries)?; self.get_ruv().rebuild(&entries)?;
Ok(()) Ok(())
})
} }
pub fn commit(self) -> Result<(), OperationError> { 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. // In the future this will do the routing between the chosen backends etc.
impl Backend { impl Backend {
#[instrument(level = "debug", name = "be::new", skip_all)]
pub fn new( pub fn new(
mut cfg: BackendConfig, mut cfg: BackendConfig,
// path: &str, // path: &str,
@ -1675,40 +1653,38 @@ impl Backend {
let ruv = Arc::new(ReplicationUpdateVector::default()); let ruv = Arc::new(ReplicationUpdateVector::default());
// this has a ::memory() type, but will path == "" work? // this has a ::memory() type, but will path == "" work?
spanned!("be::new", { let idlayer = Arc::new(IdlArcSqlite::new(&cfg, vacuum)?);
let idlayer = Arc::new(IdlArcSqlite::new(&cfg, vacuum)?); let be = Backend {
let be = Backend { cfg,
cfg, idlayer,
idlayer, ruv,
ruv, idxmeta: Arc::new(CowCell::new(IdxMeta::new(idxkeys))),
idxmeta: Arc::new(CowCell::new(IdxMeta::new(idxkeys))), };
};
// Now complete our setup with a txn // Now complete our setup with a txn
// In this case we can use an empty idx meta because we don't // In this case we can use an empty idx meta because we don't
// access any parts of // access any parts of
// the indexing subsystem here. // the indexing subsystem here.
let mut idl_write = be.idlayer.write(); let mut idl_write = be.idlayer.write();
idl_write idl_write
.setup() .setup()
.and_then(|_| idl_write.commit()) .and_then(|_| idl_write.commit())
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "Failed to setup idlayer"); admin_error!(?e, "Failed to setup idlayer");
e e
})?; })?;
// Now rebuild the ruv. // Now rebuild the ruv.
let mut be_write = be.write(); let mut be_write = be.write();
be_write be_write
.ruv_rebuild() .ruv_rebuild()
.and_then(|_| be_write.commit()) .and_then(|_| be_write.commit())
.map_err(|e| { .map_err(|e| {
admin_error!(?e, "Failed to reload ruv"); admin_error!(?e, "Failed to reload ruv");
e e
})?; })?;
Ok(be) Ok(be)
})
} }
pub fn get_pool_size(&self) -> u32 { pub fn get_pool_size(&self) -> u32 {
@ -1762,22 +1738,23 @@ impl Backend {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use idlset::v2::IDLBitRange;
use std::fs; use std::fs;
use std::iter::FromIterator; use std::iter::FromIterator;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use idlset::v2::IDLBitRange;
use uuid::Uuid; use uuid::Uuid;
use super::super::entry::{Entry, EntryInit, EntryNew}; use super::super::entry::{Entry, EntryInit, EntryNew};
use super::{ 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::identity::Limits;
use crate::prelude::*; use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::value::{IndexType, PartialValue, Value}; use crate::value::{IndexType, PartialValue, Value};
use std::time::Duration;
lazy_static! { lazy_static! {
static ref CID_ZERO: Cid = unsafe { Cid::new_zero() }; static ref CID_ZERO: Cid = unsafe { Cid::new_zero() };

View file

@ -4,11 +4,12 @@
//! These components should be "per server". Any "per domain" config should be in the system //! These components should be "per server". Any "per domain" config should be in the system
//! or domain entries that are able to be replicated. //! or domain entries that are able to be replicated.
use std::fmt;
use std::str::FromStr;
use kanidm_proto::messages::ConsoleOutputMode; use kanidm_proto::messages::ConsoleOutputMode;
use rand::prelude::*; use rand::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct IntegrationTestConfig { pub struct IntegrationTestConfig {

View file

@ -1,20 +1,17 @@
use crate::be::dbvalue::DbBackupCodeV1; use std::convert::TryFrom;
use crate::be::dbvalue::{DbCred, DbPasswordV1}; use std::time::{Duration, Instant};
use hashbrown::HashMap as Map;
use hashbrown::HashSet; use hashbrown::{HashMap as Map, HashSet};
use kanidm_proto::v1::{BackupCodesView, CredentialDetail, CredentialDetailType, OperationError}; use kanidm_proto::v1::{BackupCodesView, CredentialDetail, CredentialDetailType, OperationError};
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
use openssl::pkcs5::pbkdf2_hmac; use openssl::pkcs5::pbkdf2_hmac;
use openssl::sha::Sha512; use openssl::sha::Sha512;
use rand::prelude::*; use rand::prelude::*;
use std::convert::TryFrom;
use std::time::{Duration, Instant};
use uuid::Uuid; 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::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 policy;
pub mod softlock; pub mod softlock;
@ -234,12 +231,15 @@ impl BackupCodes {
pub fn new(code_set: HashSet<String>) -> Self { pub fn new(code_set: HashSet<String>) -> Self {
BackupCodes { code_set } BackupCodes { code_set }
} }
pub fn verify(&self, code_chal: &str) -> bool { pub fn verify(&self, code_chal: &str) -> bool {
self.code_set.contains(code_chal) self.code_set.contains(code_chal)
} }
pub fn remove(&mut self, code_chal: &str) -> bool { pub fn remove(&mut self, code_chal: &str) -> bool {
self.code_set.remove(code_chal) self.code_set.remove(code_chal)
} }
pub fn to_dbbackupcodev1(&self) -> DbBackupCodeV1 { pub fn to_dbbackupcodev1(&self) -> DbBackupCodeV1 {
DbBackupCodeV1 { DbBackupCodeV1 {
code_set: self.code_set.clone(), code_set: self.code_set.clone(),
@ -892,9 +892,10 @@ impl CredentialType {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::convert::TryFrom;
use crate::credential::policy::CryptoPolicy; use crate::credential::policy::CryptoPolicy;
use crate::credential::*; use crate::credential::*;
use std::convert::TryFrom;
#[test] #[test]
fn test_credential_simple() { fn test_credential_simple() {

View file

@ -1,6 +1,7 @@
use super::Password;
use std::time::Duration; use std::time::Duration;
use super::Password;
const PBKDF2_MIN_NIST_COST: u64 = 10000; const PBKDF2_MIN_NIST_COST: u64 = 10000;
#[derive(Debug)] #[derive(Debug)]

View file

@ -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::hash::MessageDigest;
use openssl::pkey::PKey; use openssl::pkey::PKey;
use openssl::sign::Signer; use openssl::sign::Signer;
use rand::prelude::*; use rand::prelude::*;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::time::{Duration, SystemTime};
use kanidm_proto::v1::TotpAlgo as ProtoTotpAlgo; use crate::be::dbvalue::{DbTotpAlgoV1, DbTotpV1};
use kanidm_proto::v1::TotpSecret as ProtoTotp;
// This is 64 bits of entropy, as the examples in https://tools.ietf.org/html/rfc6238 show. // This is 64 bits of entropy, as the examples in https://tools.ietf.org/html/rfc6238 show.
const SECRET_SIZE_BYTES: usize = 8; const SECRET_SIZE_BYTES: usize = 8;
@ -192,9 +191,10 @@ impl Totp {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::credential::totp::{Totp, TotpAlgo, TotpError, TOTP_DEFAULT_STEP};
use std::time::Duration; use std::time::Duration;
use crate::credential::totp::{Totp, TotpAlgo, TotpError, TOTP_DEFAULT_STEP};
#[test] #[test]
fn hotp_basic() { fn hotp_basic() {
let otp_sha1 = Totp::new(vec![0], 30, TotpAlgo::Sha1); let otp_sha1 = Totp::new(vec![0], 30, TotpAlgo::Sha1);

View file

@ -1,10 +1,11 @@
//! This module contains cryptographic setup code, a long with what policy //! This module contains cryptographic setup code, a long with what policy
//! and ciphers we accept. //! and ciphers we accept.
use crate::config::Configuration;
use openssl::error::ErrorStack; use openssl::error::ErrorStack;
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
use crate::config::Configuration;
/// From the server configuration, generate an OpenSSL acceptor that we can use /// From the server configuration, generate an OpenSSL acceptor that we can use
/// to build our sockets for https/ldaps. /// to build our sockets for https/ldaps.
pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> { pub fn setup_tls(config: &Configuration) -> Result<Option<SslAcceptorBuilder>, ErrorStack> {

View file

@ -24,6 +24,26 @@
//! [`filter`]: ../filter/index.html //! [`filter`]: ../filter/index.html
//! [`schema`]: ../schema/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::credential::Credential;
use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved}; use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
use crate::ldap::ldap_vattr_map; use crate::ldap::ldap_vattr_map;
@ -32,33 +52,8 @@ use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::repl::entry::EntryChangelog; use crate::repl::entry::EntryChangelog;
use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction}; use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
use crate::value::{IndexType, SyntaxType}; use crate::value::{IndexType, IntentTokenState, PartialValue, Session, SyntaxType, Value};
use crate::value::{IntentTokenState, PartialValue, Session, Value};
use crate::valueset::{self, ValueSet}; 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::convert::TryFrom;
// use std::str::FromStr; // use std::str::FromStr;
@ -222,7 +217,6 @@ pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
/// [`schema`]: ../schema/index.html /// [`schema`]: ../schema/index.html
/// [`access`]: ../access/index.html /// [`access`]: ../access/index.html
/// [`event`]: ../event/index.html /// [`event`]: ../event/index.html
///
pub struct Entry<VALID, STATE> { pub struct Entry<VALID, STATE> {
valid: VALID, valid: VALID,
state: STATE, state: STATE,
@ -323,29 +317,28 @@ impl Entry<EntryInit, EntryNew> {
/// Given a proto entry in JSON formed as a serialised string, processed that string /// Given a proto entry in JSON formed as a serialised string, processed that string
/// into an Entry. /// into an Entry.
#[instrument(level = "debug", skip_all)]
pub fn from_proto_entry_str( pub fn from_proto_entry_str(
es: &str, es: &str,
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("from_proto_entry_str", { if cfg!(test) {
if cfg!(test) { if es.len() > 256 {
if es.len() > 256 { let (dsp_es, _) = es.split_at(255);
let (dsp_es, _) = es.split_at(255); trace!("Parsing -> {}...", dsp_es);
trace!("Parsing -> {}...", dsp_es); } else {
} else { trace!("Parsing -> {}", es);
trace!("Parsing -> {}", es);
}
} }
// str -> Proto entry }
let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| { // str -> Proto entry
// We probably shouldn't print ES here because that would allow users let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
// to inject content into our logs :) // We probably shouldn't print ES here because that would allow users
admin_error!(?e, "SerdeJson Failure"); // to inject content into our logs :)
OperationError::SerdeJsonError admin_error!(?e, "SerdeJson Failure");
})?; OperationError::SerdeJsonError
// now call from_proto_entry })?;
Self::from_proto_entry(&pe, qs) // now call from_proto_entry
}) Self::from_proto_entry(&pe, qs)
} }
#[cfg(test)] #[cfg(test)]
@ -2471,13 +2464,15 @@ impl From<&SchemaClass> for Entry<EntryInit, EntryNew> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeSet as Set;
use hashbrown::HashMap;
use smartstring::alias::String as AttrString;
use crate::be::{IdxKey, IdxSlope}; use crate::be::{IdxKey, IdxSlope};
use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew}; use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
use crate::modify::{Modify, ModifyList}; use crate::modify::{Modify, ModifyList};
use crate::value::{IndexType, PartialValue, Value}; use crate::value::{IndexType, PartialValue, Value};
use hashbrown::HashMap;
use smartstring::alias::String as AttrString;
use std::collections::BTreeSet as Set;
#[test] #[test]
fn test_entry_basic() { fn test_entry_basic() {

View file

@ -15,6 +15,21 @@
//! with the operation, and a clear path to know how to transform events between //! with the operation, and a clear path to know how to transform events between
//! various types. //! 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::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced};
use crate::filter::{Filter, FilterInvalid, FilterValid}; use crate::filter::{Filter, FilterInvalid, FilterValid};
use crate::identity::Limits; use crate::identity::Limits;
@ -23,24 +38,6 @@ use crate::modify::{ModifyInvalid, ModifyList, ModifyValid};
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaTransaction; use crate::schema::SchemaTransaction;
use crate::value::PartialValue; 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)] #[derive(Debug)]
pub struct SearchResult { pub struct SearchResult {

View file

@ -8,28 +8,28 @@
//! [`Filter`]: struct.Filter.html //! [`Filter`]: struct.Filter.html
//! [`Entry`]: ../entry/struct.Entry.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::be::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxMeta, IdxSlope};
use crate::identity::IdentityId; use crate::identity::IdentityId;
use crate::ldap::ldap_attr_filter_map; use crate::ldap::ldap_attr_filter_map;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaTransaction; use crate::schema::SchemaTransaction;
use crate::value::{IndexType, PartialValue}; 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; 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 // 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 // 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. // takes "clone_value(t, a, v) instead, but that may have a similar issue.
#[instrument(level = "debug", skip_all)]
pub fn from_ro( pub fn from_ro(
ev: &Identity, ev: &Identity,
f: &ProtoFilter, f: &ProtoFilter,
qs: &QueryServerReadTransaction, qs: &QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("filer::from_ro", { let depth = FILTER_DEPTH_MAX;
let depth = FILTER_DEPTH_MAX; let mut elems = ev.limits.filter_max_elements;
let mut elems = ev.limits.filter_max_elements; Ok(Filter {
Ok(Filter { state: FilterInvalid {
state: FilterInvalid { inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
inner: FilterComp::from_ro(f, qs, depth, &mut elems)?, },
},
})
}) })
} }
#[instrument(level = "debug", skip_all)]
pub fn from_rw( pub fn from_rw(
ev: &Identity, ev: &Identity,
f: &ProtoFilter, f: &ProtoFilter,
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("filter::from_rw", { let depth = FILTER_DEPTH_MAX;
let depth = FILTER_DEPTH_MAX; let mut elems = ev.limits.filter_max_elements;
let mut elems = ev.limits.filter_max_elements; Ok(Filter {
Ok(Filter { state: FilterInvalid {
state: FilterInvalid { inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
inner: FilterComp::from_rw(f, qs, depth, &mut elems)?, },
},
})
}) })
} }
#[instrument(level = "debug", skip_all)]
pub fn from_ldap_ro( pub fn from_ldap_ro(
ev: &Identity, ev: &Identity,
f: &LdapFilter, f: &LdapFilter,
qs: &QueryServerReadTransaction, qs: &QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("filter::from_ldap_ro", { let depth = FILTER_DEPTH_MAX;
let depth = FILTER_DEPTH_MAX; let mut elems = ev.limits.filter_max_elements;
let mut elems = ev.limits.filter_max_elements; Ok(Filter {
Ok(Filter { state: FilterInvalid {
state: FilterInvalid { inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?, },
},
})
}) })
} }
} }
@ -948,7 +945,6 @@ impl PartialOrd for FilterResolved {
impl Ord for FilterResolved { impl Ord for FilterResolved {
/// Ordering of filters for optimisation and subsequent dead term elimination. /// Ordering of filters for optimisation and subsequent dead term elimination.
///
fn cmp(&self, rhs: &FilterResolved) -> Ordering { fn cmp(&self, rhs: &FilterResolved) -> Ordering {
let left_slopey = self.get_slopeyness_factor(); let left_slopey = self.get_slopeyness_factor();
let right_slopey = rhs.get_slopeyness_factor(); let right_slopey = rhs.get_slopeyness_factor();
@ -1330,10 +1326,6 @@ impl FilterResolved {
#[cfg(test)] #[cfg(test)]
mod tests { 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::cmp::{Ordering, PartialOrd};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::time::Duration; use std::time::Duration;
@ -1341,6 +1333,10 @@ mod tests {
use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::Filter as ProtoFilter;
use ldap3_proto::simple::LdapFilter; use ldap3_proto::simple::LdapFilter;
use crate::event::{CreateEvent, DeleteEvent};
use crate::filter::{Filter, FilterInvalid, FILTER_DEPTH_MAX};
use crate::prelude::*;
#[test] #[test]
fn test_filter_simple() { fn test_filter_simple() {
// Test construction. // Test construction.

View file

@ -3,12 +3,14 @@
//! and this provides the set of `Limits` to confine how many resources that the //! and this provides the set of `Limits` to confine how many resources that the
//! identity may consume during operations to prevent denial-of-service. //! identity may consume during operations to prevent denial-of-service.
use crate::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::hash::Hash; use std::hash::Hash;
use std::sync::Arc; use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::prelude::*;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Limits on the resources a single event can consume. These are defined per-event /// 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 /// as they are derived from the userAuthToken based on that individual session

View file

@ -1,30 +1,27 @@
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed}; use std::collections::{BTreeMap, BTreeSet};
use crate::prelude::*; use std::time::Duration;
use crate::schema::SchemaTransaction;
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::{
use kanidm_proto::v1::UiHint; AuthType, BackupCodesView, CredentialStatus, OperationError, UiHint, UserAuthToken,
use kanidm_proto::v1::{AuthType, UserAuthToken}; };
use kanidm_proto::v1::{BackupCodesView, CredentialStatus}; use time::OffsetDateTime;
use uuid::Uuid;
use webauthn_rs::prelude::CredentialID; use webauthn_rs::prelude::{
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4; AuthenticationResult, CredentialID, DeviceKey as DeviceKeyV4, Passkey as PasskeyV4,
use webauthn_rs::prelude::Passkey as PasskeyV4; };
use crate::constants::UUID_ANONYMOUS; use crate::constants::UUID_ANONYMOUS;
use crate::credential::policy::CryptoPolicy; 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::group::Group;
use crate::idm::server::IdmServerProxyWriteTransaction; use crate::idm::server::IdmServerProxyWriteTransaction;
use crate::modify::{ModifyInvalid, ModifyList}; use crate::modify::{ModifyInvalid, ModifyList};
use crate::prelude::*;
use crate::schema::SchemaTransaction;
use crate::value::{IntentTokenState, PartialValue, Value}; 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! { lazy_static! {
static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account"); static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");
static ref PVCLASS_POSIXACCOUNT: PartialValue = PartialValue::new_class("posixaccount"); static ref PVCLASS_POSIXACCOUNT: PartialValue = PartialValue::new_class("posixaccount");
@ -153,34 +150,31 @@ pub(crate) struct Account {
} }
impl Account { impl Account {
#[instrument(level = "trace", skip_all)]
pub(crate) fn try_from_entry_ro( pub(crate) fn try_from_entry_ro(
value: &Entry<EntrySealed, EntryCommitted>, value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerReadTransaction, qs: &mut QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("idm::account::try_from_entry_ro", { let groups = Group::try_from_account_entry_ro(value, qs)?;
let groups = Group::try_from_account_entry_ro(value, qs)?; try_from_entry!(value, groups)
try_from_entry!(value, groups)
})
} }
#[instrument(level = "trace", skip_all)]
pub(crate) fn try_from_entry_rw( pub(crate) fn try_from_entry_rw(
value: &Entry<EntrySealed, EntryCommitted>, value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerWriteTransaction, qs: &mut QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("idm::account::try_from_entry_rw", { let groups = Group::try_from_account_entry_rw(value, qs)?;
let groups = Group::try_from_account_entry_rw(value, qs)?; try_from_entry!(value, groups)
try_from_entry!(value, groups)
})
} }
#[instrument(level = "trace", skip_all)]
pub(crate) fn try_from_entry_reduced( pub(crate) fn try_from_entry_reduced(
value: &Entry<EntryReduced, EntryCommitted>, value: &Entry<EntryReduced, EntryCommitted>,
qs: &mut QueryServerReadTransaction, qs: &mut QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("idm::account::try_from_entry_reduced", { let groups = Group::try_from_account_entry_red_ro(value, qs)?;
let groups = Group::try_from_account_entry_red_ro(value, qs)?; try_from_entry!(value, groups)
try_from_entry!(value, groups)
})
} }
pub(crate) fn try_from_entry_no_groups( pub(crate) fn try_from_entry_no_groups(

View file

@ -2,34 +2,32 @@
//! Generally this has to process an authentication attempt, and validate each //! Generally this has to process an authentication attempt, and validate each
//! factor to assert that the user is legitimate. This also contains some //! factor to assert that the user is legitimate. This also contains some
//! support code for asynchronous task execution. //! 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; use std::collections::BTreeMap;
pub use std::collections::BTreeSet as Set; pub use std::collections::BTreeSet as Set;
use std::convert::TryFrom; 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::{ use webauthn_rs::prelude::{
PasskeyAuthentication, RequestChallengeResponse, SecurityKeyAuthentication, Webauthn, 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 // Each CredHandler takes one or more credentials and determines if the
// handlers requirements can be 100% fufilled. This is where MFA or other // handlers requirements can be 100% fufilled. This is where MFA or other
@ -405,7 +403,9 @@ impl CredHandler {
CredState::Denied(BAD_AUTH_TYPE_MSG) CredState::Denied(BAD_AUTH_TYPE_MSG)
} }
} }
} // end CredHandler::PasswordMfa }
// end CredHandler::PasswordMfa
/// Validate a webauthn authentication attempt /// Validate a webauthn authentication attempt
pub fn validate_webauthn( pub fn validate_webauthn(
@ -832,6 +832,16 @@ impl AuthSession {
#[cfg(test)] #[cfg(test)]
mod tests { 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::policy::CryptoPolicy;
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP}; use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
use crate::credential::{BackupCodes, Credential}; use crate::credential::{BackupCodes, Credential};
@ -842,17 +852,7 @@ mod tests {
use crate::idm::delayed::DelayedAction; use crate::idm::delayed::DelayedAction;
use crate::idm::AuthState; use crate::idm::AuthState;
use crate::prelude::*; 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 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> { fn create_pw_badlist_cache() -> HashSet<String> {
let mut s = HashSet::new(); let mut s = HashSet::new();

View file

@ -1,36 +1,28 @@
use crate::access::AccessControlsTransaction; use core::ops::Deref;
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 std::collections::BTreeMap; 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::{ use kanidm_proto::v1::{
CURegState, CUStatus, CredentialDetail, PasskeyDetail, PasswordFeedback, TotpSecret, CURegState, CUStatus, CredentialDetail, PasskeyDetail, PasswordFeedback, TotpSecret,
}; };
use serde::{Deserialize, Serialize};
use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration}; use time::OffsetDateTime;
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4;
use webauthn_rs::prelude::{ use webauthn_rs::prelude::{
CreationChallengeResponse, PasskeyRegistration, RegisterPublicKeyCredential, CreationChallengeResponse, DeviceKey as DeviceKeyV4, Passkey as PasskeyV4, PasskeyRegistration,
RegisterPublicKeyCredential,
}; };
use serde::{Deserialize, Serialize}; use crate::access::AccessControlsTransaction;
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
use std::fmt; use crate::credential::{BackupCodes, Credential};
use std::sync::Arc; use crate::idm::account::Account;
use std::sync::Mutex; use crate::idm::server::{IdmServerCredUpdateTransaction, IdmServerProxyWriteTransaction};
use std::time::Duration; use crate::prelude::*;
use time::OffsetDateTime; use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration};
use crate::value::IntentTokenState;
use core::ops::Deref;
const MAXIMUM_CRED_UPDATE_TTL: Duration = Duration::from_secs(900); const MAXIMUM_CRED_UPDATE_TTL: Duration = Duration::from_secs(900);
const MAXIMUM_INTENT_TTL: Duration = Duration::from_secs(86400); const MAXIMUM_INTENT_TTL: Duration = Duration::from_secs(86400);
@ -155,7 +147,6 @@ pub struct CredentialUpdateSessionStatus {
// The target user's display name // The target user's display name
displayname: String, displayname: String,
// ttl: Duration, // ttl: Duration,
//
can_commit: bool, can_commit: bool,
primary: Option<CredentialDetail>, primary: Option<CredentialDetail>,
passkeys: Vec<PasskeyDetail>, passkeys: Vec<PasskeyDetail>,
@ -384,85 +375,85 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
Ok((CredentialUpdateSessionToken { token_enc }, status)) Ok((CredentialUpdateSessionToken { token_enc }, status))
} }
#[instrument(level = "debug", skip_all)]
pub fn init_credential_update_intent( pub fn init_credential_update_intent(
&mut self, &mut self,
event: &InitCredentialUpdateIntentEvent, event: &InitCredentialUpdateIntentEvent,
ct: Duration, ct: Duration,
) -> Result<CredentialUpdateIntentToken, OperationError> { ) -> 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. // Build the intent token.
let mttl = event.max_ttl.unwrap_or_else(|| Duration::new(0, 0)); 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 max_ttl = ct + mttl.clamp(MINIMUM_INTENT_TTL, MAXIMUM_INTENT_TTL);
// let sessionid = uuid_from_duration(max_ttl, self.sid); // let sessionid = uuid_from_duration(max_ttl, self.sid);
let intent_id = readable_password_from_random(); let intent_id = readable_password_from_random();
/* /*
let token = CredentialUpdateIntentTokenInner { let token = CredentialUpdateIntentTokenInner {
sessionid, sessionid,
target, target,
intent_id, intent_id,
max_ttl, max_ttl,
}; };
let token_data = serde_json::to_vec(&token).map_err(|e| { let token_data = serde_json::to_vec(&token).map_err(|e| {
admin_error!(err = ?e, "Unable to encode token data"); admin_error!(err = ?e, "Unable to encode token data");
OperationError::SerdeJsonError 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 Ok(CredentialUpdateIntentToken { intent_id })
.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 })
})
} }
pub fn exchange_intent_credential_update( 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) self.create_credupdate_session(session_id, Some(intent_id), account, current_time)
} }
#[instrument(level = "debug", skip_all)]
pub fn init_credential_update( pub fn init_credential_update(
&mut self, &mut self,
event: &InitCredentialUpdateEvent, event: &InitCredentialUpdateEvent,
ct: Duration, ct: Duration,
) -> Result<(CredentialUpdateSessionToken, CredentialUpdateSessionStatus), OperationError> { ) -> Result<(CredentialUpdateSessionToken, CredentialUpdateSessionStatus), 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 === // This is the expiry time, so that our cleanup task can "purge up to now" rather
// This is the expiry time, so that our cleanup task can "purge up to now" rather // than needing to do calculations.
// than needing to do calculations. let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
// Build the cred update session. // Build the cred update session.
self.create_credupdate_session(sessionid, None, account, ct) self.create_credupdate_session(sessionid, None, account, ct)
})
} }
#[instrument(level = "trace", skip(self))] #[instrument(level = "trace", skip(self))]
@ -1472,6 +1462,14 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { 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::{ use super::{
CredentialUpdateSessionStatus, CredentialUpdateSessionToken, InitCredentialUpdateEvent, CredentialUpdateSessionStatus, CredentialUpdateSessionToken, InitCredentialUpdateEvent,
InitCredentialUpdateIntentEvent, MfaRegStateStatus, MAXIMUM_CRED_UPDATE_TTL, InitCredentialUpdateIntentEvent, MfaRegStateStatus, MAXIMUM_CRED_UPDATE_TTL,
@ -1481,16 +1479,8 @@ mod tests {
use crate::event::{AuthEvent, AuthResult, CreateEvent}; use crate::event::{AuthEvent, AuthResult, CreateEvent};
use crate::idm::delayed::DelayedAction; use crate::idm::delayed::DelayedAction;
use crate::idm::server::IdmServer; use crate::idm::server::IdmServer;
use crate::prelude::*;
use std::time::Duration;
use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator};
use crate::idm::AuthState; use crate::idm::AuthState;
use kanidm_proto::v1::{AuthAllowed, AuthMech, CredentialDetailType}; use crate::prelude::*;
use uuid::uuid;
use async_std::task;
const TEST_CURRENT_TIME: u64 = 6000; const TEST_CURRENT_TIME: u64 = 6000;
const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86"); const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86");

View file

@ -1,6 +1,7 @@
use crate::prelude::*;
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use crate::prelude::*;
#[cfg(test)] #[cfg(test)]
pub(crate) struct PasswordChangeEvent { pub(crate) struct PasswordChangeEvent {
pub ident: Identity, pub ident: Identity,

View file

@ -1,10 +1,9 @@
use kanidm_proto::v1::{Group as ProtoGroup, OperationError};
use uuid::Uuid;
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed}; use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed};
use crate::prelude::*; use crate::prelude::*;
use crate::value::PartialValue; use crate::value::PartialValue;
use kanidm_proto::v1::Group as ProtoGroup;
use kanidm_proto::v1::OperationError;
use uuid::Uuid;
lazy_static! { lazy_static! {
static ref PVCLASS_GROUP: PartialValue = PartialValue::new_class("group"); static ref PVCLASS_GROUP: PartialValue = PartialValue::new_class("group");

View file

@ -15,10 +15,10 @@ pub mod server;
pub(crate) mod serviceaccount; pub(crate) mod serviceaccount;
pub(crate) mod unix; pub(crate) mod unix;
use kanidm_proto::v1::{AuthAllowed, AuthMech};
use std::fmt; use std::fmt;
use kanidm_proto::v1::{AuthAllowed, AuthMech};
pub enum AuthState { pub enum AuthState {
Choose(Vec<AuthMech>), Choose(Vec<AuthMech>),
Continue(Vec<AuthAllowed>), Continue(Vec<AuthAllowed>),

View file

@ -3,30 +3,19 @@
//! This contains the in memory and loaded set of active oauth2 resource server //! 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 //! integrations, which are then able to be used an accessed from the IDM layer
//! for operations involving oauth2 authentication processing. //! for operations involving oauth2 authentication processing.
//!
use crate::identity::IdentityId; use std::collections::{BTreeMap, BTreeSet};
use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant}; use std::convert::TryFrom;
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction}; use std::fmt;
use crate::prelude::*; use std::sync::Arc;
use crate::value::OAUTHSCOPE_RE; use std::time::Duration;
use base64urlsafedata::Base64UrlSafeData; use base64urlsafedata::Base64UrlSafeData;
pub use compact_jwt::{JwkKeySet, OidcToken}; pub use compact_jwt::{JwkKeySet, OidcToken};
use compact_jwt::{JwsSigner, OidcClaims, OidcSubject}; use compact_jwt::{JwsSigner, OidcClaims, OidcSubject};
use concread::cowcell::*; use concread::cowcell::*;
use fernet::Fernet; use fernet::Fernet;
use hashbrown::HashMap; 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::{ pub use kanidm_proto::oauth2::{
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest, AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod, ErrorResponse, AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod, ErrorResponse,
@ -36,9 +25,19 @@ use kanidm_proto::oauth2::{
ClaimType, DisplayValue, GrantType, IdTokenSignAlg, ResponseMode, ResponseType, SubjectType, ClaimType, DisplayValue, GrantType, IdTokenSignAlg, ResponseMode, ResponseType, SubjectType,
TokenEndpointAuthMethod, 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 crate::identity::IdentityId;
use std::time::Duration; use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant};
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction};
use crate::prelude::*;
use crate::value::OAUTHSCOPE_RE;
lazy_static! { lazy_static! {
static ref CLASS_OAUTH2: PartialValue = PartialValue::new_class("oauth2_resource_server"); 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)] #[cfg(test)]
mod tests { 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::delayed::DelayedAction;
use crate::idm::oauth2::{AuthoriseResponse, Oauth2Error}; use crate::idm::oauth2::{AuthoriseResponse, Oauth2Error};
use crate::idm::server::{IdmServer, IdmServerTransaction}; use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::prelude::*; 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 TEST_CURRENT_TIME: u64 = 6000;
const UAT_EXPIRE: u64 = 5; const UAT_EXPIRE: u64 = 5;
const TOKEN_EXPIRE: u64 = 900; const TOKEN_EXPIRE: u64 = 900;

View file

@ -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 uuid::Uuid;
use crate::prelude::*;
use crate::entry::{Entry, EntryCommitted, EntryReduced}; use crate::entry::{Entry, EntryCommitted, EntryReduced};
use crate::idm::group::Group;
use crate::prelude::*;
use crate::value::PartialValue; use crate::value::PartialValue;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::RadiusAuthToken;
use std::time::Duration;
use time::OffsetDateTime;
lazy_static! { lazy_static! {
static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account"); static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");

View file

@ -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::policy::CryptoPolicy;
use crate::credential::softlock::CredSoftLock; use crate::credential::softlock::CredSoftLock;
use crate::event::{AuthEvent, AuthEventStep, AuthResult}; use crate::event::{AuthEvent, AuthEventStep, AuthResult};
@ -5,6 +37,10 @@ use crate::identity::{IdentType, IdentUser, Limits};
use crate::idm::account::Account; use crate::idm::account::Account;
use crate::idm::authsession::AuthSession; use crate::idm::authsession::AuthSession;
use crate::idm::credupdatesession::CredentialUpdateSessionMutex; use crate::idm::credupdatesession::CredentialUpdateSessionMutex;
use crate::idm::delayed::{
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
WebauthnCounterIncrement,
};
#[cfg(test)] #[cfg(test)]
use crate::idm::event::PasswordChangeEvent; use crate::idm::event::PasswordChangeEvent;
use crate::idm::event::{ use crate::idm::event::{
@ -26,54 +62,6 @@ use crate::ldap::{LdapBoundToken, LdapSession};
use crate::prelude::*; use crate::prelude::*;
use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid}; 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 AuthSessionMutex = Arc<Mutex<AuthSession>>;
type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>; type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
@ -298,6 +286,7 @@ impl IdmServer {
} }
/// Read from the database, in a transaction. /// Read from the database, in a transaction.
#[instrument(level = "debug", skip_all)]
pub async fn proxy_read_async(&self) -> IdmServerProxyReadTransaction<'_> { pub async fn proxy_read_async(&self) -> IdmServerProxyReadTransaction<'_> {
IdmServerProxyReadTransaction { IdmServerProxyReadTransaction {
qs_read: self.qs.read_async().await, qs_read: self.qs.read_async().await,
@ -312,6 +301,7 @@ impl IdmServer {
task::block_on(self.proxy_write_async(ts)) task::block_on(self.proxy_write_async(ts))
} }
#[instrument(level = "debug", skip_all)]
pub async fn proxy_write_async(&self, ts: Duration) -> IdmServerProxyWriteTransaction<'_> { pub async fn proxy_write_async(&self, ts: Duration) -> IdmServerProxyWriteTransaction<'_> {
let mut sid = [0; 4]; let mut sid = [0; 4];
let mut rng = StdRng::from_entropy(); 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 /// 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 /// that we internally sign with. We can use this to select the appropriate token type
/// and validation method. /// and validation method.
#[instrument(level = "info", skip_all)]
fn validate_and_parse_token_to_ident( fn validate_and_parse_token_to_ident(
&self, &self,
token: Option<&str>, token: Option<&str>,
@ -532,6 +523,7 @@ pub(crate) trait IdmServerTransaction<'a> {
} }
} }
#[instrument(level = "debug", skip_all)]
fn validate_and_parse_uat( fn validate_and_parse_uat(
&self, &self,
token: Option<&str>, token: Option<&str>,
@ -598,6 +590,7 @@ pub(crate) trait IdmServerTransaction<'a> {
/// something we can pin access controls and other limits and references to. /// 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 /// This is why it is the location where validity windows are checked and other
/// relevant session information is injected. /// relevant session information is injected.
#[instrument(level = "debug", skip_all)]
fn process_uat_to_identity( fn process_uat_to_identity(
&self, &self,
uat: &UserAuthToken, uat: &UserAuthToken,
@ -663,6 +656,7 @@ pub(crate) trait IdmServerTransaction<'a> {
}) })
} }
#[instrument(level = "debug", skip_all)]
fn process_apit_to_identity( fn process_apit_to_identity(
&self, &self,
apit: &ApiToken, apit: &ApiToken,
@ -683,6 +677,7 @@ pub(crate) trait IdmServerTransaction<'a> {
}) })
} }
#[instrument(level = "debug", skip_all)]
fn validate_ldap_session( fn validate_ldap_session(
&self, &self,
session: &LdapSession, session: &LdapSession,
@ -883,16 +878,14 @@ impl<'a> IdmServerAuthTransaction<'a> {
match auth_session { match auth_session {
Some(auth_session) => { Some(auth_session) => {
let mut session_write = self.sessions.write(); let mut session_write = self.sessions.write();
spanned!("idm::server::auth<Init> -> sessions", { if session_write.contains_key(&sessionid) {
if session_write.contains_key(&sessionid) { Err(OperationError::InvalidSessionState)
Err(OperationError::InvalidSessionState) } else {
} else { session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
session_write.insert(sessionid, Arc::new(Mutex::new(auth_session))); // Debugging: ensure we really inserted ...
// Debugging: ensure we really inserted ... debug_assert!(session_write.get(&sessionid).is_some());
debug_assert!(session_write.get(&sessionid).is_some()); Ok(())
Ok(()) }?;
}
})?;
session_write.commit(); session_write.commit();
} }
None => { None => {
@ -2024,65 +2017,64 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
} }
} }
#[instrument(level = "debug", skip_all)]
pub fn commit(mut self) -> Result<(), OperationError> { pub fn commit(mut self) -> Result<(), OperationError> {
spanned!("idm::server::IdmServerProxyWriteTransaction::commit", { if self
if self .qs_write
.qs_write .get_changed_uuids()
.get_changed_uuids() .contains(&UUID_SYSTEM_CONFIG)
.contains(&UUID_SYSTEM_CONFIG) {
{ self.reload_password_badlist()?;
self.reload_password_badlist()?; };
}; if self.qs_write.get_changed_ouath2() {
if self.qs_write.get_changed_ouath2() { self.qs_write
self.qs_write .get_oauth2rs_set()
.get_oauth2rs_set() .and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?;
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?; }
} if self.qs_write.get_changed_domain() {
if self.qs_write.get_changed_domain() { // reload token_key?
// reload token_key? self.qs_write
self.qs_write .get_domain_fernet_private_key()
.get_domain_fernet_private_key() .and_then(|token_key| {
.and_then(|token_key| { Fernet::new(&token_key).ok_or_else(|| {
Fernet::new(&token_key).ok_or_else(|| { admin_error!("Failed to generate token_enc_key");
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 OperationError::InvalidState
}) })
}) .map(|validator| (signer, validator))
.map(|new_handle| { })
*self.token_enc_key = new_handle; .map(|(new_signer, new_validator)| {
})?; *self.uat_jwt_signer = new_signer;
self.qs_write *self.uat_jwt_validator = new_validator;
.get_domain_es256_private_key() })?;
.and_then(|key_der| { }
JwsSigner::from_es256_der(&key_der).map_err(|e| { // Commit everything.
admin_error!("Failed to generate uat_jwt_signer - {:?}", e); self.oauth2rs.commit();
OperationError::InvalidState self.uat_jwt_signer.commit();
}) self.uat_jwt_validator.commit();
}) self.token_enc_key.commit();
.and_then(|signer| { self.pw_badlist_cache.commit();
signer self.cred_update_sessions.commit();
.get_validator() trace!("cred_update_session.commit");
.map_err(|e| { self.qs_write.commit()
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()
})
} }
fn reload_password_badlist(&mut self) -> Result<(), OperationError> { fn reload_password_badlist(&mut self) -> Result<(), OperationError> {
@ -2100,6 +2092,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { 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::policy::CryptoPolicy;
use crate::credential::{Credential, Password}; use crate::credential::{Credential, Password};
use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent}; use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent};
@ -2107,20 +2107,12 @@ mod tests {
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
}; };
use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::idm::AuthState; use crate::idm::AuthState;
use crate::modify::{Modify, ModifyList}; use crate::modify::{Modify, ModifyList};
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType};
use crate::idm::server::{IdmServer, IdmServerTransaction};
// , IdmServerDelayed; // , IdmServerDelayed;
use crate::utils::duration_from_epoch_now; 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: &'static str = "ntaoeuntnaoeuhraohuercahu😍";
const TEST_PASSWORD_INC: &'static str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx "; const TEST_PASSWORD_INC: &'static str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";

View file

@ -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::event::SearchEvent;
use crate::idm::account::Account; use crate::idm::account::Account;
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerProxyWriteTransaction}; use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerProxyWriteTransaction};
use crate::prelude::*; use crate::prelude::*;
use crate::value::Session; 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 add KID to es256 der for lookups ✅
// Need to generate the es256 on the account on modifies ✅ // Need to generate the es256 on the account on modifies ✅
@ -87,14 +87,13 @@ pub struct ServiceAccount {
} }
impl ServiceAccount { impl ServiceAccount {
#[instrument(level = "debug", skip_all)]
pub(crate) fn try_from_entry_rw( pub(crate) fn try_from_entry_rw(
value: &Entry<EntrySealed, EntryCommitted>, value: &Entry<EntrySealed, EntryCommitted>,
// qs: &mut QueryServerWriteTransaction, // qs: &mut QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
spanned!("idm::serviceaccount::try_from_entry_rw", { // let groups = Group::try_from_account_entry_rw(value, qs)?;
// let groups = Group::try_from_account_entry_rw(value, qs)?; try_from_entry!(value)
try_from_entry!(value)
})
} }
pub(crate) fn check_api_token_valid( pub(crate) fn check_api_token_valid(
@ -354,16 +353,17 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { 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::str::FromStr;
use std::time::Duration; 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; const TEST_CURRENT_TIME: u64 = 6000;
#[test] #[test]

View file

@ -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 uuid::Uuid;
use crate::credential::policy::CryptoPolicy; 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::modify::{ModifyInvalid, ModifyList};
use crate::prelude::*; 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)] #[derive(Debug, Clone)]
pub(crate) struct UnixUserAccount { pub(crate) struct UnixUserAccount {

View file

@ -1,20 +1,20 @@
//! This contains scheduled tasks/interval tasks that are run inside of the server on a schedule //! This contains scheduled tasks/interval tasks that are run inside of the server on a schedule
//! as background operations. //! as background operations.
use crate::actors::v1_read::QueryServerReadV1; use std::fs;
use crate::actors::v1_write::QueryServerWriteV1; use std::path::Path;
use crate::config::OnlineBackup;
use crate::constants::PURGE_FREQUENCY;
use crate::event::{OnlineBackupEvent, PurgeRecycledEvent, PurgeTombstoneEvent};
use chrono::Utc; use chrono::Utc;
use saffron::parse::{CronExpr, English}; use saffron::parse::{CronExpr, English};
use saffron::Cron; use saffron::Cron;
use std::fs;
use std::path::Path;
use tokio::time::{interval, sleep, Duration}; 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; pub struct IntervalActor;
impl IntervalActor { impl IntervalActor {

View file

@ -1,18 +1,20 @@
//! LDAP specific operations handling components. This is where LDAP operations //! LDAP specific operations handling components. This is where LDAP operations
//! are sent to for processing. //! 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::event::SearchEvent;
use crate::idm::event::{LdapAuthEvent, LdapTokenAuthEvent}; use crate::idm::event::{LdapAuthEvent, LdapTokenAuthEvent};
use crate::idm::server::{IdmServer, IdmServerTransaction}; use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::prelude::*; 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, // 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. // 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( async fn do_search(
&self, &self,
idms: &IdmServer, idms: &IdmServer,
@ -252,93 +255,82 @@ impl LdapServer {
let ct = duration_from_epoch_now(); let ct = duration_from_epoch_now();
let idm_read = idms.proxy_read_async().await; 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 // join the filter, with ext_filter
let lfilter = match ext_filter { let lfilter = match ext_filter {
Some(ext) => LdapFilter::And(vec![ Some(ext) => LdapFilter::And(vec![
sr.filter.clone(), sr.filter.clone(),
ext, ext,
LdapFilter::Not(Box::new(LdapFilter::Or(vec![ LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()), LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()), LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality( LdapFilter::Equality(
"class".to_string(), "class".to_string(),
"access_control_profile".to_string(), "access_control_profile".to_string(),
), ),
]))), ]))),
]), ]),
None => LdapFilter::And(vec![ None => LdapFilter::And(vec![
sr.filter.clone(), sr.filter.clone(),
LdapFilter::Not(Box::new(LdapFilter::Or(vec![ LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()), LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()), LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality( LdapFilter::Equality(
"class".to_string(), "class".to_string(),
"access_control_profile".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 // Build the event, with the permissions from effective_session
// //
// ! Remember, searchEvent wraps to ignore hidden for us. // ! Remember, searchEvent wraps to ignore hidden for us.
let se = spanned!("ldap::do_search<core><prepare_se>", { let ident = idm_read
let ident = idm_read .validate_ldap_session(&uat.effective_session, ct)
.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,
)
})
.map_err(|e| { .map_err(|e| {
admin_error!("failed to create search event -> {:?}", e); admin_error!("Invalid identity: {:?}", e);
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| { let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
admin_error!("search failure {:?}", e); admin_error!("search failure {:?}", e);
e e
})?; })?;
// These have already been fully reduced (access controls applied), // These have already been fully reduced (access controls applied),
// so we can just transform the values and open palm slam them into // so we can just transform the values and open palm slam them into
// the result structure. // the result structure.
let lres = spanned!("ldap::do_search<core><prepare results>", { let lres: Result<Vec<_>, _> = res
let lres: Result<Vec<_>, _> = res .into_iter()
.into_iter() .map(|e| {
.map(|e| { e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs)
e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs) // if okay, wrap in a ldap msg.
// if okay, wrap in a ldap msg. .map(|r| sr.gen_result_entry(r))
.map(|r| sr.gen_result_entry(r)) })
}) .chain(iter::once(Ok(sr.gen_success())))
.chain(iter::once(Ok(sr.gen_success()))) .collect();
.collect();
lres
});
let lres = lres.map_err(|e| { let lres = lres.map_err(|e| {
admin_error!("entry resolve failure {:?}", e); admin_error!("entry resolve failure {:?}", e);
e e
})?; })?;
admin_info!( admin_info!(
nentries = %lres.len(), nentries = %lres.len(),
"LDAP Search Success -> number of entries" "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)] #[cfg(test)]
mod tests { mod tests {
// use crate::prelude::*; // use crate::prelude::*;
use crate::event::{CreateEvent, ModifyEvent}; use std::str::FromStr;
use crate::idm::event::UnixPasswordChangeEvent;
use crate::idm::serviceaccount::GenerateApiTokenEvent;
use crate::ldap::{LdapServer, LdapSession};
use async_std::task; use async_std::task;
use compact_jwt::{Jws, JwsUnverified};
use hashbrown::HashSet; use hashbrown::HashSet;
use kanidm_proto::v1::ApiToken; use kanidm_proto::v1::ApiToken;
use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope}; use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope};
use ldap3_proto::simple::*; 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😍"; const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";

View file

@ -63,39 +63,36 @@ pub mod config;
/// A prelude of imports that should be imported by all other Kanidm modules to /// A prelude of imports that should be imported by all other Kanidm modules to
/// help make imports cleaner. /// help make imports cleaner.
pub mod prelude { pub mod prelude {
pub use crate::utils::duration_from_epoch_now;
pub use kanidm_proto::v1::{ConsistencyError, OperationError}; 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 smartstring::alias::String as AttrString;
pub use url::Url; pub use url::Url;
pub use uuid::Uuid; pub use uuid::Uuid;
pub use crate::constants::*; 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::{ pub use crate::entry::{
Entry, EntryCommitted, EntryInit, EntryInvalid, EntryInvalidCommitted, EntryNew, Entry, EntryCommitted, EntryInit, EntryInvalid, EntryInvalidCommitted, EntryNew,
EntryReduced, EntrySealed, EntrySealedCommitted, EntryTuple, EntryValid, 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::identity::Identity;
pub use crate::modify::{m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList};
pub use crate::server::{ pub use crate::server::{
QueryServer, QueryServerReadTransaction, QueryServerTransaction, QueryServer, QueryServerReadTransaction, QueryServerTransaction,
QueryServerWriteTransaction, QueryServerWriteTransaction,
}; };
pub use crate::utils::duration_from_epoch_now;
pub use crate::value::{IndexType, PartialValue, SyntaxType, Value}; pub use crate::value::{IndexType, PartialValue, SyntaxType, Value};
pub use crate::valueset::{ pub use crate::valueset::{
ValueSet, ValueSetBool, ValueSetCid, ValueSetIndex, ValueSetIutf8, ValueSetRefer, ValueSet, ValueSetBool, ValueSetCid, ValueSetIndex, ValueSetIutf8, ValueSetRefer,
ValueSetSecret, ValueSetSpn, ValueSetSyntax, ValueSetT, ValueSetUint32, ValueSetUtf8, ValueSetSecret, ValueSetSpn, ValueSetSyntax, ValueSetT, ValueSetUint32, ValueSetUtf8,
ValueSetUuid, 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,
};
} }

View file

@ -19,9 +19,10 @@ macro_rules! setup_test {
( (
$preload_entries:expr $preload_entries:expr
) => {{ ) => {{
use crate::utils::duration_from_epoch_now;
use async_std::task; use async_std::task;
use crate::utils::duration_from_epoch_now;
let _ = sketching::test_init(); let _ = sketching::test_init();
// Create an in memory BE // Create an in memory BE
@ -105,10 +106,11 @@ macro_rules! run_test {
#[cfg(test)] #[cfg(test)]
macro_rules! entry_str_to_account { macro_rules! entry_str_to_account {
($entry_str:expr) => {{ ($entry_str:expr) => {{
use std::iter::once;
use crate::entry::{Entry, EntryInvalid, EntryNew}; use crate::entry::{Entry, EntryInvalid, EntryNew};
use crate::idm::account::Account; use crate::idm::account::Account;
use crate::value::Value; use crate::value::Value;
use std::iter::once;
let mut e: Entry<EntryInvalid, EntryNew> = let mut e: Entry<EntryInvalid, EntryNew> =
unsafe { Entry::unsafe_from_entry_str($entry_str).into_invalid_new() }; 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::schema::Schema;
use crate::utils::duration_from_epoch_now; 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 { let ce = match $internal {
None => CreateEvent::new_internal($create_entries.clone()), None => CreateEvent::new_internal($create_entries.clone()),
Some(e_str) => unsafe { Some(e_str) => unsafe {
CreateEvent::new_impersonate_entry_ser(e_str, $create_entries.clone()) CreateEvent::new_impersonate_entry_ser(e_str, $create_entries.clone())
}, },
}; };
{ {
let qs_write = qs.write(duration_from_epoch_now()); let qs_write = qs.write(duration_from_epoch_now());
let r = qs_write.create(&ce); let r = qs_write.create(&ce);
trace!("test result: {:?}", r); trace!("test result: {:?}", r);
assert!(r == $expect); assert!(r == $expect);
$check(&qs_write); $check(&qs_write);
match r { match r {
Ok(_) => { Ok(_) => {
qs_write.commit().expect("commit failure!"); qs_write.commit().expect("commit failure!");
} }
Err(e) => { Err(e) => {
admin_error!("Rolling back => {:?}", e); admin_error!("Rolling back => {:?}", e);
}
} }
} }
// Make sure there are no errors. }
trace!("starting verification"); // Make sure there are no errors.
let ver = qs.verify(); trace!("starting verification");
trace!("verification -> {:?}", ver); let ver = qs.verify();
assert!(ver.len() == 0); trace!("verification -> {:?}", ver);
}); assert!(ver.len() == 0);
}}; }};
} }
@ -246,49 +246,41 @@ macro_rules! run_modify_test {
use crate::prelude::*; use crate::prelude::*;
use crate::schema::Schema; 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()); let qs_write = qs.write(duration_from_epoch_now());
spanned!("plugins::macros::run_modify_test -> pre_test hook", { $pre_hook(&qs_write);
$pre_hook(&qs_write) qs_write.commit().expect("commit failure!");
}); }
qs_write.commit().expect("commit failure!");
}
let me = match $internal { let me = match $internal {
None => unsafe { ModifyEvent::new_internal_invalid($modify_filter, $modify_list) }, None => unsafe { ModifyEvent::new_internal_invalid($modify_filter, $modify_list) },
Some(e_str) => unsafe { Some(e_str) => unsafe {
ModifyEvent::new_impersonate_entry_ser(e_str, $modify_filter, $modify_list) ModifyEvent::new_impersonate_entry_ser(e_str, $modify_filter, $modify_list)
}, },
}; };
{ {
let qs_write = qs.write(duration_from_epoch_now()); let qs_write = qs.write(duration_from_epoch_now());
let r = spanned!("plugins::macros::run_modify_test -> main_test", { let r = qs_write.modify(&me);
qs_write.modify(&me) $check(&qs_write);
}); trace!("test result: {:?}", r);
spanned!("plugins::macros::run_modify_test -> post_test check", { assert!(r == $expect);
$check(&qs_write) match r {
}); Ok(_) => {
trace!("test result: {:?}", r); qs_write.commit().expect("commit failure!");
assert!(r == $expect); }
match r { Err(e) => {
Ok(_) => { admin_error!("Rolling back => {:?}", e);
qs_write.commit().expect("commit failure!");
}
Err(e) => {
admin_error!("Rolling back => {:?}", e);
}
} }
} }
// Make sure there are no errors. }
trace!("starting verification"); // Make sure there are no errors.
let ver = qs.verify(); trace!("starting verification");
trace!("verification -> {:?}", ver); let ver = qs.verify();
assert!(ver.len() == 0); trace!("verification -> {:?}", ver);
}); assert!(ver.len() == 0);
}}; }};
} }
@ -308,37 +300,35 @@ macro_rules! run_delete_test {
use crate::schema::Schema; use crate::schema::Schema;
use crate::utils::duration_from_epoch_now; 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 { let de = match $internal {
Some(e_str) => unsafe { Some(e_str) => unsafe {
DeleteEvent::new_impersonate_entry_ser(e_str, $delete_filter.clone()) DeleteEvent::new_impersonate_entry_ser(e_str, $delete_filter.clone())
}, },
None => unsafe { DeleteEvent::new_internal_invalid($delete_filter.clone()) }, None => unsafe { DeleteEvent::new_internal_invalid($delete_filter.clone()) },
}; };
{ {
let qs_write = qs.write(duration_from_epoch_now()); let qs_write = qs.write(duration_from_epoch_now());
let r = qs_write.delete(&de); let r = qs_write.delete(&de);
trace!("test result: {:?}", r); trace!("test result: {:?}", r);
$check(&qs_write); $check(&qs_write);
assert!(r == $expect); assert!(r == $expect);
match r { match r {
Ok(_) => { Ok(_) => {
qs_write.commit().expect("commit failure!"); qs_write.commit().expect("commit failure!");
} }
Err(e) => { Err(e) => {
admin_error!("Rolling back => {:?}", e); admin_error!("Rolling back => {:?}", e);
}
} }
} }
// Make sure there are no errors. }
trace!("starting verification"); // Make sure there are no errors.
let ver = qs.verify(); trace!("starting verification");
trace!("verification -> {:?}", ver); let ver = qs.verify();
assert!(ver.len() == 0); trace!("verification -> {:?}", ver);
}); assert!(ver.len() == 0);
}}; }};
} }

View file

@ -2,19 +2,19 @@
//! express the series of Modifications that should be applied. These are expressed //! express the series of Modifications that should be applied. These are expressed
//! as "states" on what attribute-values should appear as within the `Entry` //! as "states" on what attribute-values should appear as within the `Entry`
use crate::prelude::*; use std::slice;
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 kanidm_proto::v1::{
Entry as ProtoEntry, Modify as ProtoModify, ModifyList as ProtoModifyList, OperationError,
SchemaError,
};
// Should this be std? // Should this be std?
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smartstring::alias::String as AttrString; 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)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ModifyValid; pub struct ModifyValid;
@ -69,8 +69,8 @@ pub struct ModifyList<VALID> {
} }
impl<'a> IntoIterator for &'a ModifyList<ModifyValid> { impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
type Item = &'a Modify;
type IntoIter = slice::Iter<'a, Modify>; type IntoIter = slice::Iter<'a, Modify>;
type Item = &'a Modify;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.mods.iter() self.mods.iter()

View file

@ -4,14 +4,15 @@
// both change approaches. // both change approaches.
// //
// //
use std::collections::BTreeMap;
use kanidm_proto::v1::{ConsistencyError, PluginError};
use tracing::trace;
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin; use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaTransaction; use crate::schema::SchemaTransaction;
use kanidm_proto::v1::{ConsistencyError, PluginError};
use tracing::trace;
use std::collections::BTreeMap;
pub struct AttrUnique; pub struct AttrUnique;
@ -192,9 +193,10 @@ impl Plugin for AttrUnique {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*;
use kanidm_proto::v1::PluginError; use kanidm_proto::v1::PluginError;
use crate::prelude::*;
// Test entry in db, and same name, reject. // Test entry in db, and same name, reject.
#[test] #[test]
fn test_pre_create_name_unique() { fn test_pre_create_name_unique() {

View file

@ -1,12 +1,13 @@
use crate::plugins::Plugin;
use hashbrown::HashSet;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::iter::once; use std::iter::once;
use hashbrown::HashSet;
use kanidm_proto::v1::{ConsistencyError, PluginError};
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::modify::Modify; use crate::modify::Modify;
use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::v1::{ConsistencyError, PluginError};
lazy_static! { lazy_static! {
static ref CLASS_OBJECT: Value = Value::new_class("object"); static ref CLASS_OBJECT: Value = Value::new_class("object");
@ -43,15 +44,9 @@ impl Plugin for Base {
// debug!("Entering base pre_create_transform"); // debug!("Entering base pre_create_transform");
// For each candidate // For each candidate
for entry in cand.iter_mut() { for entry in cand.iter_mut() {
trace!("Base check on entry: {:?}", entry);
// First, ensure we have the 'object', class in the class set. // First, ensure we have the 'object', class in the class set.
entry.add_ava("class", CLASS_OBJECT.clone()); 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. // if they don't have uuid, create it.
match entry.get_ava_set("uuid").map(|s| s.len()) { match entry.get_ava_set("uuid").map(|s| s.len()) {
None => { None => {
@ -85,7 +80,6 @@ impl Plugin for Base {
let uuid_ref: Uuid = entry let uuid_ref: Uuid = entry
.get_ava_single_uuid("uuid") .get_ava_single_uuid("uuid")
.ok_or_else(|| OperationError::InvalidAttribute("uuid".to_string()))?; .ok_or_else(|| OperationError::InvalidAttribute("uuid".to_string()))?;
trace!("Entry valid UUID: {:?}", entry);
if !cand_uuid.insert(uuid_ref) { if !cand_uuid.insert(uuid_ref) {
trace!("uuid duplicate found in create set! {:?}", uuid_ref); trace!("uuid duplicate found in create set! {:?}", uuid_ref);
return Err(OperationError::Plugin(PluginError::Base( return Err(OperationError::Plugin(PluginError::Base(
@ -224,9 +218,10 @@ impl Plugin for Base {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*;
use kanidm_proto::v1::PluginError; use kanidm_proto::v1::PluginError;
use crate::prelude::*;
const JSON_ADMIN_ALLOW_ALL: &'static str = r#"{ const JSON_ADMIN_ALLOW_ALL: &'static str = r#"{
"attrs": { "attrs": {
"class": [ "class": [

View file

@ -4,15 +4,16 @@
// The primary point of this is to generate a unique domain UUID on startup // 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 // which is importart for management of the replication topo and trust
// relationships. // relationships.
use crate::plugins::Plugin; use std::iter::once;
use crate::event::{CreateEvent, ModifyEvent};
use crate::prelude::*;
use compact_jwt::JwsSigner; use compact_jwt::JwsSigner;
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use std::iter::once;
use tracing::trace; use tracing::trace;
use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::prelude::*;
lazy_static! { lazy_static! {
static ref PVCLASS_DOMAIN_INFO: PartialValue = PartialValue::new_class("domain_info"); static ref PVCLASS_DOMAIN_INFO: PartialValue = PartialValue::new_class("domain_info");
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO); static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO);

View file

@ -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::event::{CreateEvent, ModifyEvent};
use crate::filter::FilterInvalid; use crate::filter::FilterInvalid;
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::v1::Filter as ProtoFilter;
use std::collections::BTreeMap;
use std::sync::Arc;
lazy_static! { lazy_static! {
static ref CLASS_DYNGROUP: PartialValue = PartialValue::new_class("dyngroup"); static ref CLASS_DYNGROUP: PartialValue = PartialValue::new_class("dyngroup");
@ -361,9 +363,10 @@ impl DynGroup {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*;
use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::Filter as ProtoFilter;
use crate::prelude::*;
const UUID_TEST_GROUP: Uuid = uuid::uuid!("7bfd9931-06c2-4608-8a46-78719bb746fe"); const UUID_TEST_GROUP: Uuid = uuid::uuid!("7bfd9931-06c2-4608-8a46-78719bb746fe");
#[test] #[test]

View file

@ -1,11 +1,12 @@
// A plugin that generates gid numbers on types that require them for posix // A plugin that generates gid numbers on types that require them for posix
// support. // support.
use std::iter::once;
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin; use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::uuid_to_gid_u32; use crate::utils::uuid_to_gid_u32;
use std::iter::once;
/// Systemd dynamic units allocate between 6118465519, most distros allocate /// Systemd dynamic units allocate between 6118465519, most distros allocate
/// system uids from 0 - 1000, and many others give user ids between 1000 to /// system uids from 0 - 1000, and many others give user ids between 1000 to

View file

@ -1,8 +1,9 @@
use compact_jwt::JwsSigner;
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin; use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::password_from_random; use crate::utils::password_from_random;
use compact_jwt::JwsSigner;
lazy_static! { lazy_static! {
static ref CLASS_OAUTH2_BASIC: PartialValue = static ref CLASS_OAUTH2_BASIC: PartialValue =

View file

@ -10,16 +10,17 @@
// As a result, we first need to run refint to clean up all dangling references, then memberof // As a result, we first need to run refint to clean up all dangling references, then memberof
// fixes the graph of memberships // 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::entry::{Entry, EntryCommitted, EntrySealed, EntryTuple};
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent}; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::plugins::Plugin; use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use crate::value::{PartialValue, Value}; use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError};
use std::collections::BTreeSet;
use hashbrown::HashMap;
use std::sync::Arc;
lazy_static! { lazy_static! {
static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group"); static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group");

View file

@ -3,12 +3,13 @@
//! helps to ensure that data is always in specific known states within the //! helps to ensure that data is always in specific known states within the
//! `QueryServer` //! `QueryServer`
use std::sync::Arc;
use kanidm_proto::v1::{ConsistencyError, OperationError};
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed}; use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent}; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::v1::{ConsistencyError, OperationError};
use std::sync::Arc;
use tracing::trace_span;
mod attrunique; mod attrunique;
mod base; mod base;
@ -118,108 +119,99 @@ macro_rules! run_verify_plugin {
} }
impl Plugins { impl Plugins {
#[instrument(level = "debug", name = "plugins::run_pre_create_transform", skip_all)]
pub fn run_pre_create_transform( pub fn run_pre_create_transform(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>, cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent, ce: &CreateEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("plugins::run_pre_create_transform", { base::Base::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(|_| password_import::PasswordImport::pre_create_transform(qs, cand, ce)) .and_then(|_| jwskeygen::JwsKeygen::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(|_| gidnumber::GidNumber::pre_create_transform(qs, cand, ce)) .and_then(|_| domain::Domain::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))
.and_then(|_| spn::Spn::pre_create_transform(qs, cand, ce)) // Should always be last
// Should always be last .and_then(|_| attrunique::AttrUnique::pre_create_transform(qs, cand, ce))
.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( pub fn run_pre_create(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryNew>], cand: &[Entry<EntrySealed, EntryNew>],
ce: &CreateEvent, ce: &CreateEvent,
) -> Result<(), OperationError> { ) -> 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( pub fn run_post_create(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>], cand: &[Entry<EntrySealed, EntryCommitted>],
ce: &CreateEvent, ce: &CreateEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("plugins::run_post_create", { refint::ReferentialIntegrity::post_create(qs, cand, ce)
refint::ReferentialIntegrity::post_create(qs, cand, ce) .and_then(|_| memberof::MemberOf::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( pub fn run_pre_modify(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>, cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
me: &ModifyEvent, me: &ModifyEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("plugins::run_pre_modify", { protected::Protected::pre_modify(qs, cand, me)
protected::Protected::pre_modify(qs, cand, me) .and_then(|_| base::Base::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(|_| password_import::PasswordImport::pre_modify(qs, cand, me)) .and_then(|_| jwskeygen::JwsKeygen::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(|_| gidnumber::GidNumber::pre_modify(qs, cand, me)) .and_then(|_| domain::Domain::pre_modify(qs, cand, me))
.and_then(|_| domain::Domain::pre_modify(qs, cand, me)) .and_then(|_| spn::Spn::pre_modify(qs, cand, me))
.and_then(|_| spn::Spn::pre_modify(qs, cand, me)) // attr unique should always be last
// attr unique should always be last .and_then(|_| attrunique::AttrUnique::pre_modify(qs, cand, me))
.and_then(|_| attrunique::AttrUnique::pre_modify(qs, cand, me))
})
} }
#[instrument(level = "debug", name = "plugins::run_post_modify", skip_all)]
pub fn run_post_modify( pub fn run_post_modify(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>], pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
cand: &[Entry<EntrySealed, EntryCommitted>], cand: &[Entry<EntrySealed, EntryCommitted>],
me: &ModifyEvent, me: &ModifyEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("plugins::run_post_modify", { refint::ReferentialIntegrity::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(|_| spn::Spn::post_modify(qs, pre_cand, cand, me)) .and_then(|_| memberof::MemberOf::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( pub fn run_pre_delete(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>, cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
de: &DeleteEvent, de: &DeleteEvent,
) -> Result<(), OperationError> { ) -> 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( pub fn run_post_delete(
qs: &QueryServerWriteTransaction, qs: &QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>], cand: &[Entry<EntrySealed, EntryCommitted>],
de: &DeleteEvent, de: &DeleteEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
spanned!("plugins::run_post_delete", { refint::ReferentialIntegrity::post_delete(qs, cand, de)
refint::ReferentialIntegrity::post_delete(qs, cand, de) .and_then(|_| memberof::MemberOf::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( pub fn run_verify(
qs: &QueryServerReadTransaction, qs: &QueryServerReadTransaction,
results: &mut Vec<Result<(), ConsistencyError>>, results: &mut Vec<Result<(), ConsistencyError>>,
) { ) {
let _entered = trace_span!("plugins::run_verify").entered(); run_verify_plugin!(qs, results, base::Base);
spanned!("plugins::run_verify", { run_verify_plugin!(qs, results, attrunique::AttrUnique);
run_verify_plugin!(qs, results, base::Base); run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
run_verify_plugin!(qs, results, attrunique::AttrUnique); run_verify_plugin!(qs, results, dyngroup::DynGroup);
run_verify_plugin!(qs, results, refint::ReferentialIntegrity); run_verify_plugin!(qs, results, memberof::MemberOf);
run_verify_plugin!(qs, results, dyngroup::DynGroup); run_verify_plugin!(qs, results, spn::Spn);
run_verify_plugin!(qs, results, memberof::MemberOf);
run_verify_plugin!(qs, results, spn::Spn);
})
} }
} }

View file

@ -1,11 +1,13 @@
// Transform password import requests into proper kanidm credentials. // 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::credential::{Credential, Password};
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin; use crate::plugins::Plugin;
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::v1::PluginError;
use std::convert::TryFrom;
use std::iter::once;
pub struct PasswordImport {} pub struct PasswordImport {}

View file

@ -1,12 +1,12 @@
// System protected objects. Items matching specific requirements // System protected objects. Items matching specific requirements
// may only have certain modifications performed. // may only have certain modifications performed.
use crate::plugins::Plugin; use hashbrown::HashSet;
use crate::prelude::*;
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent}; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::modify::Modify; use crate::modify::Modify;
use hashbrown::HashSet; use crate::plugins::Plugin;
use crate::prelude::*;
pub struct Protected {} pub struct Protected {}

View file

@ -9,19 +9,19 @@
// when that is written, as they *both* manipulate and alter entry reference // when that is written, as they *both* manipulate and alter entry reference
// data, so we should be careful not to step on each other. // data, so we should be careful not to step on each other.
use hashbrown::HashSet as Set;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::sync::Arc;
use crate::plugins::Plugin; use hashbrown::HashSet as Set;
use crate::prelude::*; use kanidm_proto::v1::{ConsistencyError, PluginError};
use tracing::trace;
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent}; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::filter::f_eq; use crate::filter::f_eq;
use crate::modify::Modify; use crate::modify::Modify;
use crate::plugins::Plugin;
use crate::prelude::*;
use crate::schema::SchemaTransaction; use crate::schema::SchemaTransaction;
use kanidm_proto::v1::{ConsistencyError, PluginError};
use std::sync::Arc;
use tracing::trace;
// NOTE: This *must* be after base.rs!!! // NOTE: This *must* be after base.rs!!!
@ -265,9 +265,10 @@ impl Plugin for ReferentialIntegrity {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*;
use kanidm_proto::v1::PluginError; use kanidm_proto::v1::PluginError;
use crate::prelude::*;
// The create references a uuid that doesn't exist - reject // The create references a uuid that doesn't exist - reject
#[test] #[test]
fn test_create_uuid_reference_not_exist() { fn test_create_uuid_reference_not_exist() {

View file

@ -1,16 +1,17 @@
// Generate and manage spn's for all entries in the domain. Also deals with // Generate and manage spn's for all entries in the domain. Also deals with
// the infrequent - but possible - case where a domain is renamed. // the infrequent - but possible - case where a domain is renamed.
use crate::plugins::Plugin; use std::iter::once;
use crate::prelude::*; use std::sync::Arc;
// use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError};
use crate::constants::UUID_DOMAIN_INFO; use crate::constants::UUID_DOMAIN_INFO;
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed}; use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
use crate::event::{CreateEvent, ModifyEvent}; use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::prelude::*;
use crate::value::PartialValue; 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 {} pub struct Spn {}

View file

@ -1,7 +1,8 @@
use kanidm_proto::v1::OperationError;
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::time::Duration; use std::time::Duration;
use kanidm_proto::v1::OperationError;
use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)]
@ -73,11 +74,13 @@ impl Cid {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::repl::cid::Cid;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::time::Duration; use std::time::Duration;
use uuid::Uuid; use uuid::Uuid;
use crate::repl::cid::Cid;
#[test] #[test]
fn test_cid_ordering() { fn test_cid_ordering() {
// Check diff ts // Check diff ts

View file

@ -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::btree_map::Keys;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::ops::Bound; use std::ops::Bound;
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! { lazy_static! {
static ref PVCLASS_TOMBSTONE: PartialValue = PartialValue::new_class("tombstone"); static ref PVCLASS_TOMBSTONE: PartialValue = PartialValue::new_class("tombstone");
static ref PVCLASS_RECYCLED: PartialValue = PartialValue::new_class("recycled"); static ref PVCLASS_RECYCLED: PartialValue = PartialValue::new_class("recycled");
@ -518,12 +517,13 @@ impl EntryChangelog {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::time::Duration;
use crate::entry::Eattrs; use crate::entry::Eattrs;
// use crate::prelude::*; // use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::repl::entry::{Change, EntryChangelog, State, Transition}; use crate::repl::entry::{Change, EntryChangelog, State, Transition};
use crate::schema::{Schema, SchemaTransaction}; use crate::schema::{Schema, SchemaTransaction};
use std::time::Duration;
#[test] #[test]
fn test_entrychangelog_basic() { fn test_entrychangelog_basic() {

View file

@ -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::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ops::Bound::*; use std::ops::Bound::*;
use std::sync::Arc; 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 { pub struct ReplicationUpdateVector {
// This sorts by time. Should we look up by IDL or by UUID? // 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 // 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