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

View file

@ -17,20 +17,131 @@ members = [
"kanidmd/score",
"orca",
"profiles",
"sketching",
"sketching"
]
exclude = [
"kanidm_unix_int/pam_tester"
]
[workspace.package]
version = "1.1.0-alpha.9"
authors = [
"William Brown <william@blackhats.net.au>",
"James Hodgkinson <james@terminaloutcomes.com>",
]
rust-version = "1.64"
edition = "2021"
license = "MPL-2.0"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
[workspace.dependencies]
async-std = { version = "^1.12.0", features = ["tokio1"] }
async-trait = "^0.1.57"
base32 = "^0.4.0"
base64 = "^0.13.0"
base64urlsafedata = "0.1.0"
bytes = "^1.1.0"
clap = { version = "^3.2", features = ["derive"] }
clap_complete = "^3.2.5"
# Forced by saffron
chrono = "^0.4.20"
compact_jwt = "^0.2.3"
# compact_jwt = { path = "../compact_jwt" }
concread = "^0.4.0"
# concread = { path = "../concread" }
crossbeam = "0.8.1"
criterion = "^0.4.0"
csv = "1.1.6"
dialoguer = "0.10.1"
dyn-clone = "^1.0.9"
fernet = "^0.2.0"
filetime = "^0.2.17"
futures = "^0.3.21"
futures-util = "^0.3.21"
gloo = "^0.8.0"
gloo-net = "0.2.4"
hashbrown = { version = "0.12.3", features = ["serde", "inline-more", "ahash"] }
http-types = "^2.12.0"
idlset = "^0.2.4"
# idlset = { path = "../idlset" }
# ldap3_server = { path = "../ldap3_server" }
js-sys = "^0.3.58"
# RENAME THIS
kanidm = { path = "./kanidmd/idm" }
kanidm_client = { path = "./kanidm_client" }
kanidm_proto = { path = "./kanidm_proto" }
kanidm_unix_int = { path = "./kanidm_unix_int" }
last-git-commit = "0.2.0"
# REMOVE this
lazy_static = "^1.4.0"
ldap3_proto = "^0.2.3"
libc = "^0.2.127"
libnss = "^0.4.0"
libsqlite3-sys = "^0.25.0"
lru = "^0.8.0"
mathru = "^0.13.0"
num_enum = "^0.5.7"
oauth2_ext = { version = "^4.1.0", package = "oauth2" }
openssl = "^0.10.41"
paste = "^1.0.9"
pkg-config = "^0.3.25"
profiles = { path = "./profiles" }
qrcode = "^0.12.0"
r2d2 = "^0.8.9"
r2d2_sqlite = "^0.21.0"
rand = "^0.8.5"
# try to remove this
rayon = "^1.5.3"
regex = "1.5.6"
reqwest = "0.11.11"
rpassword = "^7.0.0"
rusqlite = "^0.28.0"
saffron = "^0.1.0"
# Rename this!
score = { path = "./kanidmd/score" }
serde = "^1.0.142"
serde_cbor = { version = "0.12.0-dev", package = "serde_cbor_2" }
serde_json = "^1.0.83"
serde-wasm-bindgen = "0.4"
shellexpand = "^2.1.2"
sketching = { path = "./sketching" }
smartstring = "^1.0.1"
smolset = "^1.3.1"
sshkeys = "^0.3.1"
tide = "^0.16.0"
tide-compress = "0.10.6"
tide-openssl = "^0.1.1"
# Unable to increase version due to removing ability to detect
# local platform time.
time = "=0.2.27"
tikv-jemallocator = "0.5"
tokio = "^1.21.1"
tokio-openssl = "^0.6.3"
tokio-util = "^0.7.4"
toml = "^0.5.9"
touch = "^0.0.1"
tracing = { version = "^0.1.35", features = ["max_level_trace", "release_max_level_debug"] }
tracing-subscriber = { version = "^0.3.14", features = ["env-filter"] }
# tracing-forest = { path = "/Users/william/development/tracing-forest/tracing-forest" }
tracing-forest = { git = "https://github.com/QnnOkabayashi/tracing-forest.git", rev = "48d78f7294ceee47a22eee5c80964143c4fb3fe1" }
url = "^2.3.1"
urlencoding = "2.1.2"
users = "^0.11.0"
uuid = "^1.1.2"
validator = "^0.16.0"
wasm-bindgen = "^0.2.81"
wasm-bindgen-futures = "^0.4.30"
wasm-bindgen-test = "0.3.33"
webauthn-authenticator-rs = "0.4.7"
webauthn-rs = "0.4.7"
@ -40,6 +151,13 @@ webauthn-rs-proto = "0.4.7"
# webauthn-rs = { path = "../webauthn-rs/webauthn-rs" }
# webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" }
# webauthn-rs-proto = { path = "../webauthn-rs/webauthn-rs-proto" }
web-sys = "^0.3.60"
whoami = "^1.2.3"
yew = "^0.19.3"
yew-agent = "^0.1.0"
yew-router = "^0.16.0"
zxcvbn = "^2.2.1"
# enshrinken the WASMs
[profile.release.package.kanidmd_web_ui]

View file

@ -1,25 +1,26 @@
[package]
name = "kanidm_client"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.64"
edition = "2021"
license = "MPL-2.0"
description = "Kanidm Client Library"
documentation = "https://docs.rs/kanidm_client/latest/kanidm_client/"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
version.workspace = true
authors.workspace = true
rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
tracing = "^0.1.35"
reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] }
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" }
serde = { version = "^1.0.142", features = ["derive"] }
serde_json = "^1.0.83"
time = { version = "=0.2.27", features = ["serde", "std"] }
tokio = { version = "^1.21.1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
toml = "^0.5.9"
uuid = { version = "^1.1.2", features = ["serde", "v4"] }
url = { version = "^2.3.1", features = ["serde"] }
tracing.workspace = true
reqwest = { workspace = true, features=["cookies", "json", "native-tls"] }
kanidm_proto.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
time = { workspace = true, features = ["serde", "std"] }
tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] }
toml.workspace = true
uuid = { workspace = true, features = ["serde", "v4"] }
url = { workspace = true, features = ["serde"] }
webauthn-rs-proto = { workspace = true, features = ["wasm"] }

View file

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

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

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

View file

@ -1,30 +1,30 @@
[package]
name = "kanidm_proto"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.64"
edition = "2021"
license = "MPL-2.0"
description = "Kanidm Protocol Bindings for serde"
documentation = "https://docs.rs/kanidm_proto/latest/kanidm_proto/"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
version.workspace = true
authors.workspace = true
rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[features]
wasm = ["webauthn-rs-proto/wasm"]
[dependencies]
base32 = "^0.4.0"
base64urlsafedata = "0.1.0"
serde = { version = "^1.0.142", features = ["derive"] }
serde_json = "^1.0.83"
# Can not upgrade due to breaking timezone apis.
time = { version = "=0.2.27", features = ["serde", "std"] }
url = { version = "^2.3.1", features = ["serde"] }
urlencoding = "2.1.2"
uuid = { version = "^1.1.2", features = ["serde"] }
base32.workspace = true
base64urlsafedata.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
time = { workspace = true, features = ["serde", "std"] }
url = { workspace = true, features = ["serde"] }
urlencoding.workspace = true
uuid = { workspace = true, features = ["serde"] }
webauthn-rs-proto.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies]
last-git-commit = "0.2.0"
last-git-commit.workspace = true

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,14 +1,15 @@
[package]
name = "kanidm_unix_int"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.64"
edition = "2021"
license = "MPL-2.0"
description = "Kanidm Unix Integration Clients"
documentation = "https://docs.rs/kanidm/latest/kanidm/"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
version.workspace = true
authors.workspace = true
rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[lib]
name = "kanidm_unix_common"
@ -43,46 +44,42 @@ name = "kanidm_test_auth"
path = "src/test_auth.rs"
[dependencies]
kanidm_client = { path = "../kanidm_client" }
kanidm_proto = { path = "../kanidm_proto" }
kanidm = { path = "../kanidmd/idm" }
bytes.workspace = true
clap = { workspace = true, features = ["derive", "env"] }
futures.workspace = true
libc.workspace = true
libsqlite3-sys.workspace = true
lru.workspace = true
kanidm_client.workspace = true
kanidm_proto.workspace = true
# This is just used for password hashing and tests, so we could
# clean this up
kanidm.workspace = true
tracing = "^0.1.35"
sketching = { path = "../sketching" }
r2d2.workspace = true
r2d2_sqlite.workspace = true
rpassword.workspace = true
rusqlite.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
sketching.workspace = true
toml = "^0.5.9"
rpassword = "^7.0.0"
tokio = { version = "^1.21.1", features = ["rt", "macros", "sync", "time", "net", "io-util"] }
tokio-util = { version = "^0.7.4", features = ["codec"] }
futures = "^0.3.21"
bytes = "^1.1.0"
libc = "^0.2.127"
serde = { version = "^1.0.142", features = ["derive"] }
serde_json = "^1.0.83"
clap = { version = "^3.2", features = ["derive", "env"] }
libsqlite3-sys = "0.25.0"
rusqlite = "^0.28.0"
r2d2 = "^0.8.10"
r2d2_sqlite = "^0.21.0"
reqwest = "^0.11.11"
users = "^0.11.0"
lru = "^0.8.0"
toml.workspace = true
tokio = { workspace = true, features = ["rt", "macros", "sync", "time", "net", "io-util"] }
tokio-util = { workspace = true, features = ["codec"] }
tracing.workspace = true
reqwest.workspace = true
users.workspace = true
[features]
# default = [ "libsqlite3-sys/bundled" ]
[dev-dependencies]
# kanidm = { path = "../kanidmd/idm" }
score = { path = "../kanidmd/score" }
score.workspace = true
[build-dependencies]
clap = { version = "^3.2", features = ["derive"] }
clap_complete = "^3.2.5"
profiles = { path = "../profiles" }
clap = { workspace = true, features = ["derive"] }
clap_complete.workspace = true
profiles.workspace = true

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,14 @@
[package]
name = "pam_tester"
version = "0.1.2"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.59"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
version.workspace = true
authors.workspace = true
rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
pam = "^0.7.0"

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::num::NonZeroUsize;
use std::ops::{Add, Sub};
use std::path::Path;
use std::string::ToString;
use std::time::{Duration, SystemTime};
use kanidm_client::{ClientError, KanidmClient};
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
use lru::LruCache;
use reqwest::StatusCode;
use tokio::sync::{Mutex, RwLock};
use crate::db::Db;
use crate::unix_config::{HomeAttr, UidAttr};
use crate::unix_proto::{HomeDirectoryInfo, NssGroup, NssUser};
const NXCACHE_SIZE: usize = 2048;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

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::fmt::{Display, Formatter};
use std::fs::File;
use std::io::{ErrorKind, Read};
use std::path::Path;
use serde::Deserialize;
use crate::constants::{
DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_GID_ATTR_MAP,
DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_SHELL, DEFAULT_SOCK_PATH,
DEFAULT_TASK_SOCK_PATH, DEFAULT_UID_ATTR_MAP,
};
#[derive(Debug, Deserialize)]
struct ConfigInt {
db_path: Option<String>,

View file

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

View file

@ -1,14 +1,15 @@
[package]
name = "daemon"
version = "1.1.0-alpha.9"
authors = ["William Brown <william@blackhats.net.au>"]
rust-version = "1.59"
edition = "2021"
license = "MPL-2.0"
description = "Kanidm Server Daemon"
documentation = "https://docs.rs/kanidm/latest/kanidm/"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
version.workspace = true
authors.workspace = true
rust-version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -17,25 +18,25 @@ name = "kanidmd"
path = "src/main.rs"
[dependencies]
kanidm = { path = "../idm" }
kanidm_proto = { path = "../../kanidm_proto" }
score = { path = "../score" }
sketching = { path = "../../sketching" }
kanidm.workspace = true
kanidm_proto.workspace = true
score.workspace = true
sketching.workspace = true
clap = { version = "^3.2", features = ["derive", "env"] }
serde = { version = "^1.0.142", features = ["derive"] }
tokio = { version = "^1.21.1", features = ["rt-multi-thread", "macros", "signal"] }
toml = "0.5.9"
clap = { workspace = true, features = ["env"] }
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] }
toml.workspace = true
[target.'cfg(target_family = "windows")'.dependencies]
whoami = "^1.2.3"
whoami.workspace = true
[target.'cfg(not(target_family = "windows"))'.dependencies]
users = "^0.11.0"
tikv-jemallocator = "0.5"
users.workspace = true
tikv-jemallocator.workspace = true
[build-dependencies]
serde = { version = "1", features = ["derive"] }
clap = { version = "^3.2", features = ["derive"] }
clap_complete = "^3.2.5"
profiles = { path = "../../profiles" }
serde = { workspace = true, features = ["derive"] }
clap = { workspace = true, features = ["derive"] }
clap_complete.workspace = true
profiles.workspace = true

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

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

View file

@ -1,15 +1,15 @@
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::Duration;
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;
use webauthn_rs::prelude::{
DeviceKey as DeviceKeyV4, Passkey as PasskeyV4, SecurityKey as SecurityKeyV4,
};
use webauthn_rs_core::proto::{COSEKey, UserVerificationPolicy};
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4;
use webauthn_rs::prelude::SecurityKey as SecurityKeyV4;
#[derive(Serialize, Deserialize, Debug)]
pub struct DbCidV1 {
#[serde(rename = "d")]
@ -556,11 +556,11 @@ impl DbValueSetV2 {
#[cfg(test)]
mod tests {
use super::DbCred;
use super::{DbBackupCodeV1, DbPasswordV1, DbTotpV1, DbWebauthnV1};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::{DbBackupCodeV1, DbCred, DbPasswordV1, DbTotpV1, DbWebauthnV1};
fn dbcred_type_default_pw() -> DbCredTypeV1 {
DbCredTypeV1::Pw
}

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,6 +15,21 @@
//! with the operation, and a clear path to know how to transform events between
//! various types.
use std::collections::BTreeSet;
#[cfg(test)]
use std::sync::Arc;
use std::time::Duration;
use kanidm_proto::v1::{
AuthCredential, AuthMech, AuthRequest, AuthStep, CreateRequest, DeleteRequest,
Entry as ProtoEntry, ModifyList as ProtoModifyList, ModifyRequest, OperationError,
SearchRequest, SearchResponse, WhoamiResponse,
};
use ldap3_proto::simple::LdapFilter;
use uuid::Uuid;
#[cfg(test)]
use webauthn_rs::prelude::PublicKeyCredential;
use crate::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced};
use crate::filter::{Filter, FilterInvalid, FilterValid};
use crate::identity::Limits;
@ -23,24 +38,6 @@ use crate::modify::{ModifyInvalid, ModifyList, ModifyValid};
use crate::prelude::*;
use crate::schema::SchemaTransaction;
use crate::value::PartialValue;
use kanidm_proto::v1::Entry as ProtoEntry;
use kanidm_proto::v1::ModifyList as ProtoModifyList;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{
AuthCredential, AuthMech, AuthRequest, AuthStep, CreateRequest, DeleteRequest, ModifyRequest,
SearchRequest, SearchResponse, WhoamiResponse,
};
use ldap3_proto::simple::LdapFilter;
use std::collections::BTreeSet;
use std::time::Duration;
use uuid::Uuid;
#[cfg(test)]
use std::sync::Arc;
#[cfg(test)]
use webauthn_rs::prelude::PublicKeyCredential;
#[derive(Debug)]
pub struct SearchResult {

View file

@ -8,28 +8,28 @@
//! [`Filter`]: struct.Filter.html
//! [`Entry`]: ../entry/struct.Entry.html
use std::cmp::{Ordering, PartialOrd};
use std::collections::BTreeSet;
use std::hash::Hash;
use std::iter;
use std::num::NonZeroU8;
use concread::arcache::ARCacheReadTxn;
use hashbrown::HashMap;
#[cfg(test)]
use hashbrown::HashSet;
use kanidm_proto::v1::{Filter as ProtoFilter, OperationError, SchemaError};
use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter};
// use smartstring::alias::String as AttrString;
use serde::Deserialize;
use uuid::Uuid;
use crate::be::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxMeta, IdxSlope};
use crate::identity::IdentityId;
use crate::ldap::ldap_attr_filter_map;
use crate::prelude::*;
use crate::schema::SchemaTransaction;
use crate::value::{IndexType, PartialValue};
use concread::arcache::ARCacheReadTxn;
use kanidm_proto::v1::Filter as ProtoFilter;
use kanidm_proto::v1::{OperationError, SchemaError};
use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter};
// use smartstring::alias::String as AttrString;
use serde::Deserialize;
use std::cmp::{Ordering, PartialOrd};
use std::collections::BTreeSet;
use std::hash::Hash;
use std::iter;
use std::num::NonZeroU8;
use uuid::Uuid;
use hashbrown::HashMap;
#[cfg(test)]
use hashbrown::HashSet;
const FILTER_DEPTH_MAX: usize = 16;
@ -491,51 +491,48 @@ impl Filter<FilterInvalid> {
// This has to have two versions to account for ro/rw traits, because RS can't
// monomorphise on the trait to call clone_value. An option is to make a fn that
// takes "clone_value(t, a, v) instead, but that may have a similar issue.
#[instrument(level = "debug", skip_all)]
pub fn from_ro(
ev: &Identity,
f: &ProtoFilter,
qs: &QueryServerReadTransaction,
) -> Result<Self, OperationError> {
spanned!("filer::from_ro", {
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
},
})
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
},
})
}
#[instrument(level = "debug", skip_all)]
pub fn from_rw(
ev: &Identity,
f: &ProtoFilter,
qs: &QueryServerWriteTransaction,
) -> Result<Self, OperationError> {
spanned!("filter::from_rw", {
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
},
})
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
},
})
}
#[instrument(level = "debug", skip_all)]
pub fn from_ldap_ro(
ev: &Identity,
f: &LdapFilter,
qs: &QueryServerReadTransaction,
) -> Result<Self, OperationError> {
spanned!("filter::from_ldap_ro", {
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
},
})
let depth = FILTER_DEPTH_MAX;
let mut elems = ev.limits.filter_max_elements;
Ok(Filter {
state: FilterInvalid {
inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
},
})
}
}
@ -948,7 +945,6 @@ impl PartialOrd for FilterResolved {
impl Ord for FilterResolved {
/// Ordering of filters for optimisation and subsequent dead term elimination.
///
fn cmp(&self, rhs: &FilterResolved) -> Ordering {
let left_slopey = self.get_slopeyness_factor();
let right_slopey = rhs.get_slopeyness_factor();
@ -1330,10 +1326,6 @@ impl FilterResolved {
#[cfg(test)]
mod tests {
use crate::event::CreateEvent;
use crate::event::DeleteEvent;
use crate::filter::{Filter, FilterInvalid, FILTER_DEPTH_MAX};
use crate::prelude::*;
use std::cmp::{Ordering, PartialOrd};
use std::collections::BTreeSet;
use std::time::Duration;
@ -1341,6 +1333,10 @@ mod tests {
use kanidm_proto::v1::Filter as ProtoFilter;
use ldap3_proto::simple::LdapFilter;
use crate::event::{CreateEvent, DeleteEvent};
use crate::filter::{Filter, FilterInvalid, FILTER_DEPTH_MAX};
use crate::prelude::*;
#[test]
fn test_filter_simple() {
// Test construction.

View file

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

View file

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

View file

@ -2,34 +2,32 @@
//! Generally this has to process an authentication attempt, and validate each
//! factor to assert that the user is legitimate. This also contains some
//! support code for asynchronous task execution.
use crate::credential::BackupCodes;
use crate::idm::account::Account;
use crate::idm::delayed::BackupCodeRemoval;
use crate::idm::AuthState;
use crate::prelude::*;
use hashbrown::HashSet;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech, AuthType};
use crate::credential::{totp::Totp, Credential, CredentialType, Password};
use crate::idm::delayed::{DelayedAction, PasswordUpgrade, WebauthnCounterIncrement};
// use crossbeam::channel::Sender;
use tokio::sync::mpsc::UnboundedSender as Sender;
use std::time::Duration;
use uuid::Uuid;
// use webauthn_rs::proto::Credential as WebauthnCredential;
use compact_jwt::{Jws, JwsSigner};
use std::collections::BTreeMap;
pub use std::collections::BTreeSet as Set;
use std::convert::TryFrom;
use std::time::Duration;
// use webauthn_rs::proto::Credential as WebauthnCredential;
use compact_jwt::{Jws, JwsSigner};
use hashbrown::HashSet;
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech, AuthType, OperationError};
// use crossbeam::channel::Sender;
use tokio::sync::mpsc::UnboundedSender as Sender;
use uuid::Uuid;
// use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4;
use webauthn_rs::prelude::{
PasskeyAuthentication, RequestChallengeResponse, SecurityKeyAuthentication, Webauthn,
};
// use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4;
use crate::credential::totp::Totp;
use crate::credential::{BackupCodes, Credential, CredentialType, Password};
use crate::idm::account::Account;
use crate::idm::delayed::{
BackupCodeRemoval, DelayedAction, PasswordUpgrade, WebauthnCounterIncrement,
};
use crate::idm::AuthState;
use crate::prelude::*;
// Each CredHandler takes one or more credentials and determines if the
// handlers requirements can be 100% fufilled. This is where MFA or other
@ -405,7 +403,9 @@ impl CredHandler {
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
}
} // end CredHandler::PasswordMfa
}
// end CredHandler::PasswordMfa
/// Validate a webauthn authentication attempt
pub fn validate_webauthn(
@ -832,6 +832,16 @@ impl AuthSession {
#[cfg(test)]
mod tests {
pub use std::collections::BTreeSet as Set;
use std::time::Duration;
use compact_jwt::JwsSigner;
use hashbrown::HashSet;
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
use tokio::sync::mpsc::unbounded_channel as unbounded;
use webauthn_authenticator_rs::softpasskey::SoftPasskey;
use webauthn_authenticator_rs::WebauthnAuthenticator;
use crate::credential::policy::CryptoPolicy;
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
use crate::credential::{BackupCodes, Credential};
@ -842,17 +852,7 @@ mod tests {
use crate::idm::delayed::DelayedAction;
use crate::idm::AuthState;
use crate::prelude::*;
use hashbrown::HashSet;
pub use std::collections::BTreeSet as Set;
use crate::utils::{duration_from_epoch_now, readable_password_from_random};
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
use std::time::Duration;
use tokio::sync::mpsc::unbounded_channel as unbounded;
use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator};
use compact_jwt::JwsSigner;
fn create_pw_badlist_cache() -> HashSet<String> {
let mut s = HashSet::new();

View file

@ -1,36 +1,28 @@
use crate::access::AccessControlsTransaction;
use crate::credential::{BackupCodes, Credential};
use crate::idm::account::Account;
use crate::idm::server::IdmServerCredUpdateTransaction;
use crate::idm::server::IdmServerProxyWriteTransaction;
use crate::prelude::*;
use crate::value::IntentTokenState;
use hashbrown::HashSet;
use core::ops::Deref;
use std::collections::BTreeMap;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
use hashbrown::HashSet;
use kanidm_proto::v1::{
CURegState, CUStatus, CredentialDetail, PasskeyDetail, PasswordFeedback, TotpSecret,
};
use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration};
use webauthn_rs::prelude::DeviceKey as DeviceKeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use webauthn_rs::prelude::{
CreationChallengeResponse, PasskeyRegistration, RegisterPublicKeyCredential,
CreationChallengeResponse, DeviceKey as DeviceKeyV4, Passkey as PasskeyV4, PasskeyRegistration,
RegisterPublicKeyCredential,
};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use time::OffsetDateTime;
use core::ops::Deref;
use crate::access::AccessControlsTransaction;
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
use crate::credential::{BackupCodes, Credential};
use crate::idm::account::Account;
use crate::idm::server::{IdmServerCredUpdateTransaction, IdmServerProxyWriteTransaction};
use crate::prelude::*;
use crate::utils::{backup_code_from_random, readable_password_from_random, uuid_from_duration};
use crate::value::IntentTokenState;
const MAXIMUM_CRED_UPDATE_TTL: Duration = Duration::from_secs(900);
const MAXIMUM_INTENT_TTL: Duration = Duration::from_secs(86400);
@ -155,7 +147,6 @@ pub struct CredentialUpdateSessionStatus {
// The target user's display name
displayname: String,
// ttl: Duration,
//
can_commit: bool,
primary: Option<CredentialDetail>,
passkeys: Vec<PasskeyDetail>,
@ -384,85 +375,85 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
Ok((CredentialUpdateSessionToken { token_enc }, status))
}
#[instrument(level = "debug", skip_all)]
pub fn init_credential_update_intent(
&mut self,
event: &InitCredentialUpdateIntentEvent,
ct: Duration,
) -> Result<CredentialUpdateIntentToken, OperationError> {
spanned!("idm::server::credupdatesession<Init>", {
let account = self.validate_init_credential_update(event.target, &event.ident)?;
let account = self.validate_init_credential_update(event.target, &event.ident)?;
// ==== AUTHORISATION CHECKED ===
// ==== AUTHORISATION CHECKED ===
// Build the intent token.
let mttl = event.max_ttl.unwrap_or_else(|| Duration::new(0, 0));
let max_ttl = ct + mttl.clamp(MINIMUM_INTENT_TTL, MAXIMUM_INTENT_TTL);
// let sessionid = uuid_from_duration(max_ttl, self.sid);
let intent_id = readable_password_from_random();
// Build the intent token.
let mttl = event.max_ttl.unwrap_or_else(|| Duration::new(0, 0));
let max_ttl = ct + mttl.clamp(MINIMUM_INTENT_TTL, MAXIMUM_INTENT_TTL);
// let sessionid = uuid_from_duration(max_ttl, self.sid);
let intent_id = readable_password_from_random();
/*
let token = CredentialUpdateIntentTokenInner {
sessionid,
target,
intent_id,
max_ttl,
};
/*
let token = CredentialUpdateIntentTokenInner {
sessionid,
target,
intent_id,
max_ttl,
};
let token_data = serde_json::to_vec(&token).map_err(|e| {
admin_error!(err = ?e, "Unable to encode token data");
OperationError::SerdeJsonError
let token_data = serde_json::to_vec(&token).map_err(|e| {
admin_error!(err = ?e, "Unable to encode token data");
OperationError::SerdeJsonError
})?;
let token_enc = self
.token_enc_key
.encrypt_at_time(&token_data, ct.as_secs());
*/
// Mark that we have created an intent token on the user.
// ⚠️ -- remember, there is a risk, very low, but still a risk of collision of the intent_id.
// instead of enforcing unique, which would divulge that the collision occured, we
// write anyway, and instead on the intent access path we invalidate IF the collision
// occurs.
let mut modlist = ModifyList::new_append(
"credential_update_intent_token",
Value::IntentToken(intent_id.clone(), IntentTokenState::Valid { max_ttl }),
);
// Remove any old credential update intents
account
.credential_update_intent_tokens
.iter()
.for_each(|(existing_intent_id, state)| {
let max_ttl = match state {
IntentTokenState::Valid { max_ttl }
| IntentTokenState::InProgress {
max_ttl,
session_id: _,
session_ttl: _,
}
| IntentTokenState::Consumed { max_ttl } => *max_ttl,
};
if ct >= max_ttl {
modlist.push_mod(Modify::Removed(
AttrString::from("credential_update_intent_token"),
PartialValue::IntentToken(existing_intent_id.clone()),
));
}
});
self.qs_write
.internal_modify(
// Filter as executed
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
&modlist,
)
.map_err(|e| {
request_error!(error = ?e);
e
})?;
let token_enc = self
.token_enc_key
.encrypt_at_time(&token_data, ct.as_secs());
*/
// Mark that we have created an intent token on the user.
// ⚠️ -- remember, there is a risk, very low, but still a risk of collision of the intent_id.
// instead of enforcing unique, which would divulge that the collision occured, we
// write anyway, and instead on the intent access path we invalidate IF the collision
// occurs.
let mut modlist = ModifyList::new_append(
"credential_update_intent_token",
Value::IntentToken(intent_id.clone(), IntentTokenState::Valid { max_ttl }),
);
// Remove any old credential update intents
account.credential_update_intent_tokens.iter().for_each(
|(existing_intent_id, state)| {
let max_ttl = match state {
IntentTokenState::Valid { max_ttl }
| IntentTokenState::InProgress {
max_ttl,
session_id: _,
session_ttl: _,
}
| IntentTokenState::Consumed { max_ttl } => *max_ttl,
};
if ct >= max_ttl {
modlist.push_mod(Modify::Removed(
AttrString::from("credential_update_intent_token"),
PartialValue::IntentToken(existing_intent_id.clone()),
));
}
},
);
self.qs_write
.internal_modify(
// Filter as executed
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
&modlist,
)
.map_err(|e| {
request_error!(error = ?e);
e
})?;
Ok(CredentialUpdateIntentToken { intent_id })
})
Ok(CredentialUpdateIntentToken { intent_id })
}
pub fn exchange_intent_credential_update(
@ -642,21 +633,20 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
self.create_credupdate_session(session_id, Some(intent_id), account, current_time)
}
#[instrument(level = "debug", skip_all)]
pub fn init_credential_update(
&mut self,
event: &InitCredentialUpdateEvent,
ct: Duration,
) -> Result<(CredentialUpdateSessionToken, CredentialUpdateSessionStatus), OperationError> {
spanned!("idm::server::credupdatesession<Init>", {
let account = self.validate_init_credential_update(event.target, &event.ident)?;
// ==== AUTHORISATION CHECKED ===
// This is the expiry time, so that our cleanup task can "purge up to now" rather
// than needing to do calculations.
let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
let account = self.validate_init_credential_update(event.target, &event.ident)?;
// ==== AUTHORISATION CHECKED ===
// This is the expiry time, so that our cleanup task can "purge up to now" rather
// than needing to do calculations.
let sessionid = uuid_from_duration(ct + MAXIMUM_CRED_UPDATE_TTL, self.sid);
// Build the cred update session.
self.create_credupdate_session(sessionid, None, account, ct)
})
// Build the cred update session.
self.create_credupdate_session(sessionid, None, account, ct)
}
#[instrument(level = "trace", skip(self))]
@ -1472,6 +1462,14 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
#[cfg(test)]
mod tests {
use std::time::Duration;
use async_std::task;
use kanidm_proto::v1::{AuthAllowed, AuthMech, CredentialDetailType};
use uuid::uuid;
use webauthn_authenticator_rs::softpasskey::SoftPasskey;
use webauthn_authenticator_rs::WebauthnAuthenticator;
use super::{
CredentialUpdateSessionStatus, CredentialUpdateSessionToken, InitCredentialUpdateEvent,
InitCredentialUpdateIntentEvent, MfaRegStateStatus, MAXIMUM_CRED_UPDATE_TTL,
@ -1481,16 +1479,8 @@ mod tests {
use crate::event::{AuthEvent, AuthResult, CreateEvent};
use crate::idm::delayed::DelayedAction;
use crate::idm::server::IdmServer;
use crate::prelude::*;
use std::time::Duration;
use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator};
use crate::idm::AuthState;
use kanidm_proto::v1::{AuthAllowed, AuthMech, CredentialDetailType};
use uuid::uuid;
use async_std::task;
use crate::prelude::*;
const TEST_CURRENT_TIME: u64 = 6000;
const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86");

View file

@ -1,6 +1,7 @@
use crate::prelude::*;
use kanidm_proto::v1::OperationError;
use crate::prelude::*;
#[cfg(test)]
pub(crate) struct PasswordChangeEvent {
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::prelude::*;
use crate::value::PartialValue;
use kanidm_proto::v1::Group as ProtoGroup;
use kanidm_proto::v1::OperationError;
use uuid::Uuid;
lazy_static! {
static ref PVCLASS_GROUP: PartialValue = PartialValue::new_class("group");

View file

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

View file

@ -3,30 +3,19 @@
//! This contains the in memory and loaded set of active oauth2 resource server
//! integrations, which are then able to be used an accessed from the IDM layer
//! for operations involving oauth2 authentication processing.
//!
use crate::identity::IdentityId;
use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant};
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction};
use crate::prelude::*;
use crate::value::OAUTHSCOPE_RE;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryFrom;
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use base64urlsafedata::Base64UrlSafeData;
pub use compact_jwt::{JwkKeySet, OidcToken};
use compact_jwt::{JwsSigner, OidcClaims, OidcSubject};
use concread::cowcell::*;
use fernet::Fernet;
use hashbrown::HashMap;
use kanidm_proto::v1::{AuthType, UserAuthToken};
use openssl::sha;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use std::sync::Arc;
use time::OffsetDateTime;
use tokio::sync::mpsc::UnboundedSender as Sender;
use tracing::trace;
use url::{Origin, Url};
pub use kanidm_proto::oauth2::{
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod, ErrorResponse,
@ -36,9 +25,19 @@ use kanidm_proto::oauth2::{
ClaimType, DisplayValue, GrantType, IdTokenSignAlg, ResponseMode, ResponseType, SubjectType,
TokenEndpointAuthMethod,
};
use kanidm_proto::v1::{AuthType, UserAuthToken};
use openssl::sha;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tokio::sync::mpsc::UnboundedSender as Sender;
use tracing::trace;
use url::{Origin, Url};
use std::convert::TryFrom;
use std::time::Duration;
use crate::identity::IdentityId;
use crate::idm::delayed::{DelayedAction, Oauth2ConsentGrant};
use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerTransaction};
use crate::prelude::*;
use crate::value::OAUTHSCOPE_RE;
lazy_static! {
static ref CLASS_OAUTH2: PartialValue = PartialValue::new_class("oauth2_resource_server");
@ -1351,26 +1350,22 @@ fn parse_basic_authz(client_authz: &str) -> Result<(String, String), Oauth2Error
#[cfg(test)]
mod tests {
use crate::event::CreateEvent;
use std::convert::TryFrom;
use std::str::FromStr;
use std::time::Duration;
use base64urlsafedata::Base64UrlSafeData;
use compact_jwt::{JwaAlg, Jwk, JwkUse, JwsValidator, OidcSubject, OidcUnverified};
use kanidm_proto::oauth2::*;
use kanidm_proto::v1::{AuthType, UserAuthToken};
use openssl::sha;
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::idm::delayed::DelayedAction;
use crate::idm::oauth2::{AuthoriseResponse, Oauth2Error};
use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::prelude::*;
use crate::event::{DeleteEvent, ModifyEvent};
use base64urlsafedata::Base64UrlSafeData;
use kanidm_proto::oauth2::*;
use kanidm_proto::v1::{AuthType, UserAuthToken};
use compact_jwt::{JwaAlg, Jwk, JwkUse, JwsValidator, OidcSubject, OidcUnverified};
use openssl::sha;
use std::convert::TryFrom;
use std::str::FromStr;
use std::time::Duration;
const TEST_CURRENT_TIME: u64 = 6000;
const UAT_EXPIRE: u64 = 5;
const TOKEN_EXPIRE: u64 = 900;

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

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::softlock::CredSoftLock;
use crate::event::{AuthEvent, AuthEventStep, AuthResult};
@ -5,6 +37,10 @@ use crate::identity::{IdentType, IdentUser, Limits};
use crate::idm::account::Account;
use crate::idm::authsession::AuthSession;
use crate::idm::credupdatesession::CredentialUpdateSessionMutex;
use crate::idm::delayed::{
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
WebauthnCounterIncrement,
};
#[cfg(test)]
use crate::idm::event::PasswordChangeEvent;
use crate::idm::event::{
@ -26,54 +62,6 @@ use crate::ldap::{LdapBoundToken, LdapSession};
use crate::prelude::*;
use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, Sid};
use crate::actors::v1_write::QueryServerWriteV1;
use crate::idm::delayed::{
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
WebauthnCounterIncrement,
};
use hashbrown::HashSet;
use kanidm_proto::v1::{
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, UnixGroupToken,
UnixUserToken, UserAuthToken,
};
use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator};
use fernet::Fernet;
use tokio::sync::mpsc::{
unbounded_channel as unbounded, UnboundedReceiver as Receiver, UnboundedSender as Sender,
};
use tokio::sync::Semaphore;
use async_std::task;
// #[cfg(any(test,bench))]
use core::task::{Context, Poll};
// #[cfg(any(test,bench))]
use futures::task as futures_task;
use concread::{
bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn},
cowcell::{CowCellReadTxn, CowCellWriteTxn},
hashmap::HashMap,
CowCell,
};
use rand::prelude::*;
use std::convert::TryFrom;
use std::str::FromStr;
use std::{sync::Arc, time::Duration};
use tokio::sync::Mutex;
use url::Url;
use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
use super::delayed::BackupCodeRemoval;
use super::event::ReadBackupCodeEvent;
use tracing::trace;
type AuthSessionMutex = Arc<Mutex<AuthSession>>;
type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
@ -298,6 +286,7 @@ impl IdmServer {
}
/// Read from the database, in a transaction.
#[instrument(level = "debug", skip_all)]
pub async fn proxy_read_async(&self) -> IdmServerProxyReadTransaction<'_> {
IdmServerProxyReadTransaction {
qs_read: self.qs.read_async().await,
@ -312,6 +301,7 @@ impl IdmServer {
task::block_on(self.proxy_write_async(ts))
}
#[instrument(level = "debug", skip_all)]
pub async fn proxy_write_async(&self, ts: Duration) -> IdmServerProxyWriteTransaction<'_> {
let mut sid = [0; 4];
let mut rng = StdRng::from_entropy();
@ -421,6 +411,7 @@ pub(crate) trait IdmServerTransaction<'a> {
/// The primary method of verification selection is the use of the KID parameter
/// that we internally sign with. We can use this to select the appropriate token type
/// and validation method.
#[instrument(level = "info", skip_all)]
fn validate_and_parse_token_to_ident(
&self,
token: Option<&str>,
@ -532,6 +523,7 @@ pub(crate) trait IdmServerTransaction<'a> {
}
}
#[instrument(level = "debug", skip_all)]
fn validate_and_parse_uat(
&self,
token: Option<&str>,
@ -598,6 +590,7 @@ pub(crate) trait IdmServerTransaction<'a> {
/// something we can pin access controls and other limits and references to.
/// This is why it is the location where validity windows are checked and other
/// relevant session information is injected.
#[instrument(level = "debug", skip_all)]
fn process_uat_to_identity(
&self,
uat: &UserAuthToken,
@ -663,6 +656,7 @@ pub(crate) trait IdmServerTransaction<'a> {
})
}
#[instrument(level = "debug", skip_all)]
fn process_apit_to_identity(
&self,
apit: &ApiToken,
@ -683,6 +677,7 @@ pub(crate) trait IdmServerTransaction<'a> {
})
}
#[instrument(level = "debug", skip_all)]
fn validate_ldap_session(
&self,
session: &LdapSession,
@ -883,16 +878,14 @@ impl<'a> IdmServerAuthTransaction<'a> {
match auth_session {
Some(auth_session) => {
let mut session_write = self.sessions.write();
spanned!("idm::server::auth<Init> -> sessions", {
if session_write.contains_key(&sessionid) {
Err(OperationError::InvalidSessionState)
} else {
session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
// Debugging: ensure we really inserted ...
debug_assert!(session_write.get(&sessionid).is_some());
Ok(())
}
})?;
if session_write.contains_key(&sessionid) {
Err(OperationError::InvalidSessionState)
} else {
session_write.insert(sessionid, Arc::new(Mutex::new(auth_session)));
// Debugging: ensure we really inserted ...
debug_assert!(session_write.get(&sessionid).is_some());
Ok(())
}?;
session_write.commit();
}
None => {
@ -2024,65 +2017,64 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
}
}
#[instrument(level = "debug", skip_all)]
pub fn commit(mut self) -> Result<(), OperationError> {
spanned!("idm::server::IdmServerProxyWriteTransaction::commit", {
if self
.qs_write
.get_changed_uuids()
.contains(&UUID_SYSTEM_CONFIG)
{
self.reload_password_badlist()?;
};
if self.qs_write.get_changed_ouath2() {
self.qs_write
.get_oauth2rs_set()
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?;
}
if self.qs_write.get_changed_domain() {
// reload token_key?
self.qs_write
.get_domain_fernet_private_key()
.and_then(|token_key| {
Fernet::new(&token_key).ok_or_else(|| {
admin_error!("Failed to generate token_enc_key");
if self
.qs_write
.get_changed_uuids()
.contains(&UUID_SYSTEM_CONFIG)
{
self.reload_password_badlist()?;
};
if self.qs_write.get_changed_ouath2() {
self.qs_write
.get_oauth2rs_set()
.and_then(|oauth2rs_set| self.oauth2rs.reload(oauth2rs_set))?;
}
if self.qs_write.get_changed_domain() {
// reload token_key?
self.qs_write
.get_domain_fernet_private_key()
.and_then(|token_key| {
Fernet::new(&token_key).ok_or_else(|| {
admin_error!("Failed to generate token_enc_key");
OperationError::InvalidState
})
})
.map(|new_handle| {
*self.token_enc_key = new_handle;
})?;
self.qs_write
.get_domain_es256_private_key()
.and_then(|key_der| {
JwsSigner::from_es256_der(&key_der).map_err(|e| {
admin_error!("Failed to generate uat_jwt_signer - {:?}", e);
OperationError::InvalidState
})
})
.and_then(|signer| {
signer
.get_validator()
.map_err(|e| {
admin_error!("Failed to generate uat_jwt_validator - {:?}", e);
OperationError::InvalidState
})
})
.map(|new_handle| {
*self.token_enc_key = new_handle;
})?;
self.qs_write
.get_domain_es256_private_key()
.and_then(|key_der| {
JwsSigner::from_es256_der(&key_der).map_err(|e| {
admin_error!("Failed to generate uat_jwt_signer - {:?}", e);
OperationError::InvalidState
})
})
.and_then(|signer| {
signer
.get_validator()
.map_err(|e| {
admin_error!("Failed to generate uat_jwt_validator - {:?}", e);
OperationError::InvalidState
})
.map(|validator| (signer, validator))
})
.map(|(new_signer, new_validator)| {
*self.uat_jwt_signer = new_signer;
*self.uat_jwt_validator = new_validator;
})?;
}
// Commit everything.
self.oauth2rs.commit();
self.uat_jwt_signer.commit();
self.uat_jwt_validator.commit();
self.token_enc_key.commit();
self.pw_badlist_cache.commit();
self.cred_update_sessions.commit();
trace!("cred_update_session.commit");
self.qs_write.commit()
})
.map(|validator| (signer, validator))
})
.map(|(new_signer, new_validator)| {
*self.uat_jwt_signer = new_signer;
*self.uat_jwt_validator = new_validator;
})?;
}
// Commit everything.
self.oauth2rs.commit();
self.uat_jwt_signer.commit();
self.uat_jwt_validator.commit();
self.token_enc_key.commit();
self.pw_badlist_cache.commit();
self.cred_update_sessions.commit();
trace!("cred_update_session.commit");
self.qs_write.commit()
}
fn reload_password_badlist(&mut self) -> Result<(), OperationError> {
@ -2100,6 +2092,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use std::time::Duration;
use async_std::task;
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType, OperationError};
use smartstring::alias::String as AttrString;
use uuid::Uuid;
use crate::credential::policy::CryptoPolicy;
use crate::credential::{Credential, Password};
use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent};
@ -2107,20 +2107,12 @@ mod tests {
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
};
use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::idm::AuthState;
use crate::modify::{Modify, ModifyList};
use crate::prelude::*;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType};
use crate::idm::server::{IdmServer, IdmServerTransaction};
// , IdmServerDelayed;
use crate::utils::duration_from_epoch_now;
use async_std::task;
use smartstring::alias::String as AttrString;
use std::convert::TryFrom;
use std::time::Duration;
use uuid::Uuid;
const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";
const TEST_PASSWORD_INC: &'static str = "ntaoentu nkrcgaeunhibwmwmqj;k wqjbkx ";

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

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 crate::credential::policy::CryptoPolicy;
use crate::credential::{softlock::CredSoftLockPolicy, Credential};
use crate::credential::softlock::CredSoftLockPolicy;
use crate::credential::Credential;
use crate::idm::delayed::{DelayedAction, UnixPasswordUpgrade};
use crate::modify::{ModifyInvalid, ModifyList};
use crate::prelude::*;
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
use crate::idm::delayed::{DelayedAction, UnixPasswordUpgrade};
// use crossbeam::channel::Sender;
use std::time::Duration;
use time::OffsetDateTime;
use tokio::sync::mpsc::UnboundedSender as Sender;
use std::iter;
#[derive(Debug, Clone)]
pub(crate) struct UnixUserAccount {

View file

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

View file

@ -1,18 +1,20 @@
//! LDAP specific operations handling components. This is where LDAP operations
//! are sent to for processing.
use std::collections::BTreeSet;
use std::iter;
use async_std::task;
use kanidm_proto::v1::{ApiToken, OperationError, UserAuthToken};
use ldap3_proto::simple::*;
use regex::Regex;
use tracing::trace;
use uuid::Uuid;
use crate::event::SearchEvent;
use crate::idm::event::{LdapAuthEvent, LdapTokenAuthEvent};
use crate::idm::server::{IdmServer, IdmServerTransaction};
use crate::prelude::*;
use async_std::task;
use kanidm_proto::v1::{ApiToken, OperationError, UserAuthToken};
use ldap3_proto::simple::*;
use regex::Regex;
use std::collections::BTreeSet;
use std::iter;
use tracing::trace;
use uuid::Uuid;
// Clippy doesn't like Bind here. But proto needs unboxed ldapmsg,
// and ldapboundtoken is moved. Really, it's not too bad, every message here is pretty sucky.
@ -121,6 +123,7 @@ impl LdapServer {
})
}
#[instrument(level = "debug", skip_all)]
async fn do_search(
&self,
idms: &IdmServer,
@ -252,93 +255,82 @@ impl LdapServer {
let ct = duration_from_epoch_now();
let idm_read = idms.proxy_read_async().await;
spanned!("ldap::do_search<core>", {
// Now start the txn - we need it for resolving filter components.
// Now start the txn - we need it for resolving filter components.
// join the filter, with ext_filter
let lfilter = match ext_filter {
Some(ext) => LdapFilter::And(vec![
sr.filter.clone(),
ext,
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality(
"class".to_string(),
"access_control_profile".to_string(),
),
]))),
]),
None => LdapFilter::And(vec![
sr.filter.clone(),
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality(
"class".to_string(),
"access_control_profile".to_string(),
),
]))),
]),
};
// join the filter, with ext_filter
let lfilter = match ext_filter {
Some(ext) => LdapFilter::And(vec![
sr.filter.clone(),
ext,
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality(
"class".to_string(),
"access_control_profile".to_string(),
),
]))),
]),
None => LdapFilter::And(vec![
sr.filter.clone(),
LdapFilter::Not(Box::new(LdapFilter::Or(vec![
LdapFilter::Equality("class".to_string(), "classtype".to_string()),
LdapFilter::Equality("class".to_string(), "attributetype".to_string()),
LdapFilter::Equality(
"class".to_string(),
"access_control_profile".to_string(),
),
]))),
]),
};
admin_info!(filter = ?lfilter, "LDAP Search Filter");
admin_info!(filter = ?lfilter, "LDAP Search Filter");
// Build the event, with the permissions from effective_session
//
// ! Remember, searchEvent wraps to ignore hidden for us.
let se = spanned!("ldap::do_search<core><prepare_se>", {
let ident = idm_read
.validate_ldap_session(&uat.effective_session, ct)
.map_err(|e| {
admin_error!("Invalid identity: {:?}", e);
e
})?;
SearchEvent::new_ext_impersonate_uuid(
&idm_read.qs_read,
ident,
&lfilter,
k_attrs,
)
})
// Build the event, with the permissions from effective_session
//
// ! Remember, searchEvent wraps to ignore hidden for us.
let ident = idm_read
.validate_ldap_session(&uat.effective_session, ct)
.map_err(|e| {
admin_error!("failed to create search event -> {:?}", e);
admin_error!("Invalid identity: {:?}", e);
e
})?;
let se =
SearchEvent::new_ext_impersonate_uuid(&idm_read.qs_read, ident, &lfilter, k_attrs)
.map_err(|e| {
admin_error!("failed to create search event -> {:?}", e);
e
})?;
let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
admin_error!("search failure {:?}", e);
e
})?;
let res = idm_read.qs_read.search_ext(&se).map_err(|e| {
admin_error!("search failure {:?}", e);
e
})?;
// These have already been fully reduced (access controls applied),
// so we can just transform the values and open palm slam them into
// the result structure.
let lres = spanned!("ldap::do_search<core><prepare results>", {
let lres: Result<Vec<_>, _> = res
.into_iter()
.map(|e| {
e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs)
// if okay, wrap in a ldap msg.
.map(|r| sr.gen_result_entry(r))
})
.chain(iter::once(Ok(sr.gen_success())))
.collect();
lres
});
// These have already been fully reduced (access controls applied),
// so we can just transform the values and open palm slam them into
// the result structure.
let lres: Result<Vec<_>, _> = res
.into_iter()
.map(|e| {
e.to_ldap(&idm_read.qs_read, self.basedn.as_str(), all_attrs, &l_attrs)
// if okay, wrap in a ldap msg.
.map(|r| sr.gen_result_entry(r))
})
.chain(iter::once(Ok(sr.gen_success())))
.collect();
let lres = lres.map_err(|e| {
admin_error!("entry resolve failure {:?}", e);
e
})?;
let lres = lres.map_err(|e| {
admin_error!("entry resolve failure {:?}", e);
e
})?;
admin_info!(
nentries = %lres.len(),
"LDAP Search Success -> number of entries"
);
admin_info!(
nentries = %lres.len(),
"LDAP Search Success -> number of entries"
);
Ok(lres)
})
Ok(lres)
}
}
@ -550,18 +542,19 @@ pub(crate) fn ldap_attr_filter_map(input: &str) -> AttrString {
#[cfg(test)]
mod tests {
// use crate::prelude::*;
use crate::event::{CreateEvent, ModifyEvent};
use crate::idm::event::UnixPasswordChangeEvent;
use crate::idm::serviceaccount::GenerateApiTokenEvent;
use crate::ldap::{LdapServer, LdapSession};
use std::str::FromStr;
use async_std::task;
use compact_jwt::{Jws, JwsUnverified};
use hashbrown::HashSet;
use kanidm_proto::v1::ApiToken;
use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope};
use ldap3_proto::simple::*;
use std::str::FromStr;
use compact_jwt::{Jws, JwsUnverified};
use crate::event::{CreateEvent, ModifyEvent};
use crate::idm::event::UnixPasswordChangeEvent;
use crate::idm::serviceaccount::GenerateApiTokenEvent;
use crate::ldap::{LdapServer, LdapSession};
const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";

View file

@ -63,39 +63,36 @@ pub mod config;
/// A prelude of imports that should be imported by all other Kanidm modules to
/// help make imports cleaner.
pub mod prelude {
pub use crate::utils::duration_from_epoch_now;
pub use kanidm_proto::v1::{ConsistencyError, OperationError};
pub use sketching::{
admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace,
filter_warn, perf_trace, request_error, request_info, request_trace, request_warn,
security_access, security_critical, security_error, security_info, tagged_event, EventTag,
};
pub use smartstring::alias::String as AttrString;
pub use url::Url;
pub use uuid::Uuid;
pub use crate::constants::*;
pub use crate::filter::{
f_and, f_andnot, f_eq, f_id, f_inc, f_lt, f_or, f_pres, f_self, f_spn_name, f_sub,
};
pub use crate::filter::{Filter, FilterInvalid, FC};
pub use crate::modify::{m_pres, m_purge, m_remove};
pub use crate::modify::{Modify, ModifyInvalid, ModifyList};
pub use crate::entry::{
Entry, EntryCommitted, EntryInit, EntryInvalid, EntryInvalidCommitted, EntryNew,
EntryReduced, EntrySealed, EntrySealedCommitted, EntryTuple, EntryValid,
};
pub use crate::filter::{
f_and, f_andnot, f_eq, f_id, f_inc, f_lt, f_or, f_pres, f_self, f_spn_name, f_sub, Filter,
FilterInvalid, FC,
};
pub use crate::identity::Identity;
pub use crate::modify::{m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList};
pub use crate::server::{
QueryServer, QueryServerReadTransaction, QueryServerTransaction,
QueryServerWriteTransaction,
};
pub use crate::utils::duration_from_epoch_now;
pub use crate::value::{IndexType, PartialValue, SyntaxType, Value};
pub use crate::valueset::{
ValueSet, ValueSetBool, ValueSetCid, ValueSetIndex, ValueSetIutf8, ValueSetRefer,
ValueSetSecret, ValueSetSpn, ValueSetSyntax, ValueSetT, ValueSetUint32, ValueSetUtf8,
ValueSetUuid,
};
pub use sketching::{
admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace,
filter_warn, perf_trace, request_error, request_info, request_trace, request_warn,
security_access, security_critical, security_error, security_info, spanned, tagged_event,
EventTag,
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -1,11 +1,12 @@
// A plugin that generates gid numbers on types that require them for posix
// support.
use std::iter::once;
use crate::event::{CreateEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::prelude::*;
use crate::utils::uuid_to_gid_u32;
use std::iter::once;
/// Systemd dynamic units allocate between 6118465519, most distros allocate
/// 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::plugins::Plugin;
use crate::prelude::*;
use crate::utils::password_from_random;
use compact_jwt::JwsSigner;
lazy_static! {
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
// fixes the graph of memberships
use std::collections::BTreeSet;
use std::sync::Arc;
use hashbrown::HashMap;
use kanidm_proto::v1::{ConsistencyError, OperationError};
use crate::entry::{Entry, EntryCommitted, EntrySealed, EntryTuple};
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::prelude::*;
use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError};
use std::collections::BTreeSet;
use hashbrown::HashMap;
use std::sync::Arc;
lazy_static! {
static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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::collections::BTreeMap;
use std::ops::Bound::*;
use std::sync::Arc;
use concread::bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn};
use idlset::v2::IDLBitRange;
use kanidm_proto::v1::ConsistencyError;
use crate::prelude::*;
use crate::repl::cid::Cid;
pub struct ReplicationUpdateVector {
// This sorts by time. Should we look up by IDL or by UUID?
// I think IDL, because when we need to actually do the look ups we'll need

Some files were not shown because too many files have changed in this diff Show more