From 821b2c05c4e3ad34c3d03561fb00b2720f6fbcbe Mon Sep 17 00:00:00 2001 From: Firstyear Date: Sat, 1 Oct 2022 16:08:51 +1000 Subject: [PATCH] Rework deps (#1079) --- .rustfmt.toml | 11 + Cargo.lock | 255 +- Cargo.toml | 122 +- kanidm_client/Cargo.toml | 35 +- kanidm_client/src/lib.rs | 25 +- kanidm_client/src/person.rs | 10 +- kanidm_client/src/service_account.rs | 10 +- kanidm_proto/Cargo.toml | 34 +- kanidm_proto/src/messages.rs | 19 +- kanidm_proto/src/oauth2.rs | 3 +- kanidm_proto/src/v1.rs | 9 +- kanidm_tools/Cargo.toml | 59 +- kanidm_tools/build.rs | 2 +- kanidm_tools/src/badlist_preprocess.rs | 3 +- kanidm_tools/src/cli/common.rs | 11 +- kanidm_tools/src/cli/lib.rs | 1 + kanidm_tools/src/cli/main.rs | 3 +- kanidm_tools/src/cli/person.rs | 23 +- kanidm_tools/src/cli/raw.rs | 6 +- kanidm_tools/src/cli/serviceaccount.rs | 5 +- kanidm_tools/src/cli/session.rs | 27 +- kanidm_tools/src/ssh_authorizedkeys.rs | 1 - kanidm_unix_int/Cargo.toml | 75 +- kanidm_unix_int/build.rs | 3 +- kanidm_unix_int/nss_kanidm/Cargo.toml | 22 +- kanidm_unix_int/nss_kanidm/src/lib.rs | 1 - kanidm_unix_int/pam_kanidm/Cargo.toml | 18 +- kanidm_unix_int/pam_kanidm/src/lib.rs | 9 +- kanidm_unix_int/pam_kanidm/src/pam/conv.rs | 6 +- kanidm_unix_int/pam_kanidm/src/pam/macros.rs | 24 +- kanidm_unix_int/pam_kanidm/src/pam/module.rs | 3 +- kanidm_unix_int/pam_tester/Cargo.toml | 13 +- kanidm_unix_int/src/cache.rs | 17 +- kanidm_unix_int/src/cache_clear.rs | 2 - kanidm_unix_int/src/cache_invalidate.rs | 2 - kanidm_unix_int/src/client.rs | 11 +- kanidm_unix_int/src/client_sync.rs | 5 +- kanidm_unix_int/src/daemon.rs | 32 +- kanidm_unix_int/src/daemon_status.rs | 4 +- kanidm_unix_int/src/db.rs | 17 +- kanidm_unix_int/src/ssh_authorizedkeys.rs | 3 +- kanidm_unix_int/src/tasks_daemon.rs | 30 +- kanidm_unix_int/src/test_auth.rs | 2 - kanidm_unix_int/src/unix_config.rs | 14 +- kanidm_unix_int/tests/cache_layer_test.rs | 12 +- kanidmd/daemon/Cargo.toml | 45 +- kanidmd/daemon/build.rs | 1 - kanidmd/daemon/src/main.rs | 26 +- kanidmd/idm/Cargo.toml | 133 +- kanidmd/idm/benches/scaling_10k.rs | 7 +- kanidmd/idm/src/access.rs | 1100 ++++---- kanidmd/idm/src/actors/v1_read.rs | 1346 +++++----- kanidmd/idm/src/actors/v1_write.rs | 1280 +++++----- kanidmd/idm/src/audit.rs | 3 +- kanidmd/idm/src/be/dbentry.rs | 10 +- kanidmd/idm/src/be/dbvalue.rs | 16 +- kanidmd/idm/src/be/idl_arc_sqlite.rs | 491 ++-- kanidmd/idm/src/be/idl_sqlite.rs | 299 +-- kanidmd/idm/src/be/idxkey.rs | 6 +- kanidmd/idm/src/be/mod.rs | 645 +++-- kanidmd/idm/src/config.rs | 5 +- kanidmd/idm/src/credential/mod.rs | 23 +- kanidmd/idm/src/credential/policy.rs | 3 +- kanidmd/idm/src/credential/totp.rs | 14 +- kanidmd/idm/src/crypto.rs | 3 +- kanidmd/idm/src/entry.rs | 91 +- kanidmd/idm/src/event.rs | 33 +- kanidmd/idm/src/filter.rs | 86 +- kanidmd/idm/src/identity.rs | 6 +- kanidmd/idm/src/idm/account.rs | 54 +- kanidmd/idm/src/idm/authsession.rs | 64 +- kanidmd/idm/src/idm/credupdatesession.rs | 214 +- kanidmd/idm/src/idm/event.rs | 3 +- kanidmd/idm/src/idm/group.rs | 7 +- kanidmd/idm/src/idm/mod.rs | 4 +- kanidmd/idm/src/idm/oauth2.rs | 63 +- kanidmd/idm/src/idm/radius.rs | 13 +- kanidmd/idm/src/idm/server.rs | 238 +- kanidmd/idm/src/idm/serviceaccount.rs | 36 +- kanidmd/idm/src/idm/unix.rs | 22 +- kanidmd/idm/src/interval.rs | 16 +- kanidmd/idm/src/ldap.rs | 173 +- kanidmd/idm/src/lib.rs | 25 +- kanidmd/idm/src/macros.rs | 178 +- kanidmd/idm/src/modify.rs | 20 +- kanidmd/idm/src/plugins/attrunique.rs | 12 +- kanidmd/idm/src/plugins/base.rs | 17 +- kanidmd/idm/src/plugins/domain.rs | 9 +- kanidmd/idm/src/plugins/dyngroup.rs | 11 +- kanidmd/idm/src/plugins/gidnumber.rs | 3 +- kanidmd/idm/src/plugins/jwskeygen.rs | 3 +- kanidmd/idm/src/plugins/memberof.rs | 11 +- kanidmd/idm/src/plugins/mod.rs | 96 +- kanidmd/idm/src/plugins/password_import.rs | 8 +- kanidmd/idm/src/plugins/protected.rs | 6 +- kanidmd/idm/src/plugins/refint.rs | 15 +- kanidmd/idm/src/plugins/spn.rs | 13 +- kanidmd/idm/src/repl/cid.rs | 9 +- kanidmd/idm/src/repl/entry.rs | 20 +- kanidmd/idm/src/repl/ruv.rs | 12 +- kanidmd/idm/src/schema.rs | 1546 +++++------ kanidmd/idm/src/server.rs | 2263 ++++++++--------- kanidmd/idm/src/status.rs | 3 +- kanidmd/idm/src/utils.rs | 27 +- kanidmd/idm/src/value.rs | 25 +- kanidmd/idm/src/valueset/address.rs | 10 +- kanidmd/idm/src/valueset/binary.rs | 12 +- kanidmd/idm/src/valueset/bool.rs | 6 +- kanidmd/idm/src/valueset/cid.rs | 7 +- kanidmd/idm/src/valueset/cred.rs | 13 +- kanidmd/idm/src/valueset/datetime.rs | 8 +- kanidmd/idm/src/valueset/iname.rs | 7 +- kanidmd/idm/src/valueset/index.rs | 6 +- kanidmd/idm/src/valueset/iutf8.rs | 7 +- kanidmd/idm/src/valueset/json.rs | 8 +- kanidmd/idm/src/valueset/jws.rs | 9 +- kanidmd/idm/src/valueset/mod.rs | 37 +- kanidmd/idm/src/valueset/nsuniqueid.rs | 6 +- kanidmd/idm/src/valueset/oauth.rs | 14 +- kanidmd/idm/src/valueset/restricted.rs | 6 +- kanidmd/idm/src/valueset/secret.rs | 6 +- kanidmd/idm/src/valueset/session.rs | 13 +- kanidmd/idm/src/valueset/spn.rs | 7 +- kanidmd/idm/src/valueset/ssh.rs | 7 +- kanidmd/idm/src/valueset/syntax.rs | 6 +- kanidmd/idm/src/valueset/uint32.rs | 6 +- kanidmd/idm/src/valueset/url.rs | 6 +- kanidmd/idm/src/valueset/utf8.rs | 6 +- kanidmd/idm/src/valueset/uuid.rs | 9 +- kanidmd/score/Cargo.toml | 72 +- kanidmd/score/src/https/manifest.rs | 3 +- kanidmd/score/src/https/middleware.rs | 3 +- kanidmd/score/src/https/mod.rs | 17 +- kanidmd/score/src/https/oauth2.rs | 5 +- kanidmd/score/src/https/routemaps.rs | 11 +- kanidmd/score/src/https/v1.rs | 18 +- kanidmd/score/src/ldaps.rs | 19 +- kanidmd/score/src/lib.rs | 11 +- kanidmd/score/tests/https_middleware.rs | 4 +- kanidmd/score/tests/oauth2_test.rs | 9 +- kanidmd/score/tests/proto_v1_test.rs | 11 +- kanidmd_web_ui/Cargo.toml | 54 +- kanidmd_web_ui/pkg/kanidmd_web_ui.js | 1093 -------- kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm | Bin 1599068 -> 0 bytes .../src/components/admin_accounts.rs | 11 +- kanidmd_web_ui/src/components/admin_groups.rs | 14 +- kanidmd_web_ui/src/components/admin_oauth2.rs | 13 +- kanidmd_web_ui/src/components/adminmenu.rs | 9 +- .../src/components/change_unix_password.rs | 9 +- kanidmd_web_ui/src/credential/delete.rs | 17 +- kanidmd_web_ui/src/credential/eventbus.rs | 9 +- kanidmd_web_ui/src/credential/passkey.rs | 19 +- .../src/credential/passkeyremove.rs | 20 +- kanidmd_web_ui/src/credential/pwmodal.rs | 17 +- kanidmd_web_ui/src/credential/reset.rs | 17 +- kanidmd_web_ui/src/credential/totpmodal.rs | 23 +- kanidmd_web_ui/src/error.rs | 1 + kanidmd_web_ui/src/login.rs | 12 +- kanidmd_web_ui/src/manager.rs | 2 +- kanidmd_web_ui/src/models/mod.rs | 14 +- kanidmd_web_ui/src/oauth2.rs | 16 +- kanidmd_web_ui/src/views/apps.rs | 5 +- kanidmd_web_ui/src/views/mod.rs | 14 +- kanidmd_web_ui/src/views/profile.rs | 6 +- kanidmd_web_ui/src/views/security.rs | 23 +- orca/Cargo.toml | 60 +- orca/src/data.rs | 2 +- orca/src/ds.rs | 10 +- orca/src/kani.rs | 12 +- orca/src/ldap.rs | 7 +- orca/src/main.rs | 8 +- orca/src/preprocess.rs | 10 +- orca/src/runner/mod.rs | 8 +- orca/src/runner/search.rs | 18 +- orca/src/setup.rs | 14 +- profiles/Cargo.toml | 23 +- profiles/build.rs | 3 +- profiles/src/lib.rs | 3 +- sketching/Cargo.toml | 25 +- sketching/src/lib.rs | 7 +- sketching/src/middleware.rs | 3 +- 181 files changed, 6355 insertions(+), 7591 deletions(-) create mode 100644 .rustfmt.toml delete mode 100644 kanidmd_web_ui/pkg/kanidmd_web_ui.js delete mode 100644 kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 000000000..b9129e6f1 --- /dev/null +++ b/.rustfmt.toml @@ -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 + + diff --git a/Cargo.lock b/Cargo.lock index 1ae114a29..6b6ef4b39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index f92ce1de4..93c34448b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,20 +17,131 @@ members = [ "kanidmd/score", "orca", "profiles", - "sketching", + "sketching" ] exclude = [ "kanidm_unix_int/pam_tester" ] +[workspace.package] +version = "1.1.0-alpha.9" +authors = [ + "William Brown ", + "James Hodgkinson ", + ] +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] diff --git a/kanidm_client/Cargo.toml b/kanidm_client/Cargo.toml index be83b9fc4..cf8cdd1b0 100644 --- a/kanidm_client/Cargo.toml +++ b/kanidm_client/Cargo.toml @@ -1,25 +1,26 @@ [package] name = "kanidm_client" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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"] } diff --git a/kanidm_client/src/lib.rs b/kanidm_client/src/lib.rs index ce788e192..17f693dee 100644 --- a/kanidm_client/src/lib.rs +++ b/kanidm_client/src/lib.rs @@ -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(), diff --git a/kanidm_client/src/person.rs b/kanidm_client/src/person.rs index 7da638571..f01678279 100644 --- a/kanidm_client/src/person.rs +++ b/kanidm_client/src/person.rs @@ -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, ClientError> { self.perform_get_request("/v1/person").await diff --git a/kanidm_client/src/service_account.rs b/kanidm_client/src/service_account.rs index 8b9cfe327..0a6930113 100644 --- a/kanidm_client/src/service_account.rs +++ b/kanidm_client/src/service_account.rs @@ -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, ClientError> { self.perform_get_request("/v1/service_account").await diff --git a/kanidm_proto/Cargo.toml b/kanidm_proto/Cargo.toml index d4b5b837e..000b4fd06 100644 --- a/kanidm_proto/Cargo.toml +++ b/kanidm_proto/Cargo.toml @@ -1,30 +1,30 @@ [package] name = "kanidm_proto" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidm_proto/src/messages.rs b/kanidm_proto/src/messages.rs index 79e2f6a7d..1ce3e5640 100644 --- a/kanidm_proto/src/messages.rs +++ b/kanidm_proto/src/messages.rs @@ -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,) diff --git a/kanidm_proto/src/oauth2.rs b/kanidm_proto/src/oauth2.rs index 987d0a345..a2c134356 100644 --- a/kanidm_proto/src/oauth2.rs +++ b/kanidm_proto/src/oauth2.rs @@ -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)] diff --git a/kanidm_proto/src/v1.rs b/kanidm_proto/src/v1.rs index ef37de3e7..29ff9a2d4 100644 --- a/kanidm_proto/src/v1.rs +++ b/kanidm_proto/src/v1.rs @@ -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() { diff --git a/kanidm_tools/Cargo.toml b/kanidm_tools/Cargo.toml index 293add408..01950e153 100644 --- a/kanidm_tools/Cargo.toml +++ b/kanidm_tools/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "kanidm_tools" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidm_tools/build.rs b/kanidm_tools/build.rs index 9468b3b44..0cd62aa96 100644 --- a/kanidm_tools/build.rs +++ b/kanidm_tools/build.rs @@ -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"); diff --git a/kanidm_tools/src/badlist_preprocess.rs b/kanidm_tools/src/badlist_preprocess.rs index 68e238853..5e9dceaf5 100644 --- a/kanidm_tools/src/badlist_preprocess.rs +++ b/kanidm_tools/src/badlist_preprocess.rs @@ -14,9 +14,8 @@ use std::io::BufWriter; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; -use kanidm_proto::v1::Modify; - use clap::Parser; +use kanidm_proto::v1::Modify; use rayon::prelude::*; use tracing::{debug, error, info}; diff --git a/kanidm_tools/src/cli/common.rs b/kanidm_tools/src/cli/common.rs index 71e89cdb2..d2cd0069e 100644 --- a/kanidm_tools/src/cli/common.rs +++ b/kanidm_tools/src/cli/common.rs @@ -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 { diff --git a/kanidm_tools/src/cli/lib.rs b/kanidm_tools/src/cli/lib.rs index ecf64dfa8..73f48821c 100644 --- a/kanidm_tools/src/cli/lib.rs +++ b/kanidm_tools/src/cli/lib.rs @@ -15,6 +15,7 @@ extern crate tracing; use std::path::PathBuf; + use uuid::Uuid; include!("../opt/kanidm.rs"); diff --git a/kanidm_tools/src/cli/main.rs b/kanidm_tools/src/cli/main.rs index 5c2c8fbc2..b9321208d 100644 --- a/kanidm_tools/src/cli/main.rs +++ b/kanidm_tools/src/cli/main.rs @@ -13,7 +13,8 @@ use clap::Parser; use kanidm_cli::KanidmClientParser; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use tracing_subscriber::prelude::*; +use tracing_subscriber::{fmt, EnvFilter}; #[tokio::main(flavor = "current_thread")] async fn main() { diff --git a/kanidm_tools/src/cli/person.rs b/kanidm_tools/src/cli/person.rs index 28da66ce9..809364510 100644 --- a/kanidm_tools/src/cli/person.rs +++ b/kanidm_tools/src/cli/person.rs @@ -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 { diff --git a/kanidm_tools/src/cli/raw.rs b/kanidm_tools/src/cli/raw.rs index 99e33d186..fb7cb1907 100644 --- a/kanidm_tools/src/cli/raw.rs +++ b/kanidm_tools/src/cli/raw.rs @@ -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>(path: P) -> Result> { let f = File::open(path)?; let r = BufReader::new(f); diff --git a/kanidm_tools/src/cli/serviceaccount.rs b/kanidm_tools/src/cli/serviceaccount.rs index 190945d31..af6a9b898 100644 --- a/kanidm_tools/src/cli/serviceaccount.rs +++ b/kanidm_tools/src/cli/serviceaccount.rs @@ -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 { diff --git a/kanidm_tools/src/cli/session.rs b/kanidm_tools/src/cli/session.rs index ab7c9870a..90bad6665 100644 --- a/kanidm_tools/src/cli/session.rs +++ b/kanidm_tools/src/cli/session.rs @@ -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"; diff --git a/kanidm_tools/src/ssh_authorizedkeys.rs b/kanidm_tools/src/ssh_authorizedkeys.rs index 3de10f740..29b209d87 100644 --- a/kanidm_tools/src/ssh_authorizedkeys.rs +++ b/kanidm_tools/src/ssh_authorizedkeys.rs @@ -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}; diff --git a/kanidm_unix_int/Cargo.toml b/kanidm_unix_int/Cargo.toml index 80f0d93a7..248182e4e 100644 --- a/kanidm_unix_int/Cargo.toml +++ b/kanidm_unix_int/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "kanidm_unix_int" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidm_unix_int/build.rs b/kanidm_unix_int/build.rs index 83e5e6656..4e0898aba 100644 --- a/kanidm_unix_int/build.rs +++ b/kanidm_unix_int/build.rs @@ -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"); diff --git a/kanidm_unix_int/nss_kanidm/Cargo.toml b/kanidm_unix_int/nss_kanidm/Cargo.toml index 2759a262b..49d6292e0 100644 --- a/kanidm_unix_int/nss_kanidm/Cargo.toml +++ b/kanidm_unix_int/nss_kanidm/Cargo.toml @@ -1,9 +1,13 @@ [package] name = "nss_kanidm" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidm_unix_int/nss_kanidm/src/lib.rs b/kanidm_unix_int/nss_kanidm/src/lib.rs index e41b9d731..84cea4273 100644 --- a/kanidm_unix_int/nss_kanidm/src/lib.rs +++ b/kanidm_unix_int/nss_kanidm/src/lib.rs @@ -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}; diff --git a/kanidm_unix_int/pam_kanidm/Cargo.toml b/kanidm_unix_int/pam_kanidm/Cargo.toml index 9db9c9bf5..d7931c623 100644 --- a/kanidm_unix_int/pam_kanidm/Cargo.toml +++ b/kanidm_unix_int/pam_kanidm/Cargo.toml @@ -1,19 +1,23 @@ [package] name = "pam_kanidm" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidm_unix_int/pam_kanidm/src/lib.rs b/kanidm_unix_int/pam_kanidm/src/lib.rs index e0b5996e6..f473e51ca 100644 --- a/kanidm_unix_int/pam_kanidm/src/lib.rs +++ b/kanidm_unix_int/pam_kanidm/src/lib.rs @@ -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, diff --git a/kanidm_unix_int/pam_kanidm/src/pam/conv.rs b/kanidm_unix_int/pam_kanidm/src/pam/conv.rs index aecc8d955..166fdf266 100644 --- a/kanidm_unix_int/pam_kanidm/src/pam/conv.rs +++ b/kanidm_unix_int/pam_kanidm/src/pam/conv.rs @@ -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)] diff --git a/kanidm_unix_int/pam_kanidm/src/pam/macros.rs b/kanidm_unix_int/pam_kanidm/src/pam/macros.rs index 393beca9e..9986e28f1 100644 --- a/kanidm_unix_int/pam_kanidm/src/pam/macros.rs +++ b/kanidm_unix_int/pam_kanidm/src/pam/macros.rs @@ -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}; diff --git a/kanidm_unix_int/pam_kanidm/src/pam/module.rs b/kanidm_unix_int/pam_kanidm/src/pam/module.rs index 994768ff5..4760b75ea 100755 --- a/kanidm_unix_int/pam_kanidm/src/pam/module.rs +++ b/kanidm_unix_int/pam_kanidm/src/pam/module.rs @@ -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. diff --git a/kanidm_unix_int/pam_tester/Cargo.toml b/kanidm_unix_int/pam_tester/Cargo.toml index b8634f588..bf0a279d8 100644 --- a/kanidm_unix_int/pam_tester/Cargo.toml +++ b/kanidm_unix_int/pam_tester/Cargo.toml @@ -1,11 +1,14 @@ [package] name = "pam_tester" -version = "0.1.2" -authors = ["William Brown "] -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" + diff --git a/kanidm_unix_int/src/cache.rs b/kanidm_unix_int/src/cache.rs index 9b54b67b1..744568e5f 100644 --- a/kanidm_unix_int/src/cache.rs +++ b/kanidm_unix_int/src/cache.rs @@ -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)] diff --git a/kanidm_unix_int/src/cache_clear.rs b/kanidm_unix_int/src/cache_clear.rs index 3101a7ea0..7dd3a5eae 100644 --- a/kanidm_unix_int/src/cache_clear.rs +++ b/kanidm_unix_int/src/cache_clear.rs @@ -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; diff --git a/kanidm_unix_int/src/cache_invalidate.rs b/kanidm_unix_int/src/cache_invalidate.rs index c6edf0200..0a59795aa 100644 --- a/kanidm_unix_int/src/cache_invalidate.rs +++ b/kanidm_unix_int/src/cache_invalidate.rs @@ -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; diff --git a/kanidm_unix_int/src/client.rs b/kanidm_unix_int/src/client.rs index fd10a8439..84ceec92a 100644 --- a/kanidm_unix_int/src/client.rs +++ b/kanidm_unix_int/src/client.rs @@ -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, Self::Error> { match serde_json::from_slice::(&src) { diff --git a/kanidm_unix_int/src/client_sync.rs b/kanidm_unix_int/src/client_sync.rs index 16e19042f..42d2261e6 100644 --- a/kanidm_unix_int/src/client_sync.rs +++ b/kanidm_unix_int/src/client_sync.rs @@ -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}; diff --git a/kanidm_unix_int/src/daemon.rs b/kanidm_unix_int/src/daemon.rs index 7701b882e..d03a62928 100644 --- a/kanidm_unix_int/src/daemon.rs +++ b/kanidm_unix_int/src/daemon.rs @@ -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, Self::Error> { match serde_json::from_slice::(&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, Self::Error> { match serde_json::from_slice::(&src) { diff --git a/kanidm_unix_int/src/daemon_status.rs b/kanidm_unix_int/src/daemon_status.rs index afc9cf4bc..b8370b85e 100644 --- a/kanidm_unix_int/src/daemon_status.rs +++ b/kanidm_unix_int/src/daemon_status.rs @@ -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; diff --git a/kanidm_unix_int/src/db.rs b/kanidm_unix_int/src/db.rs index 99b763495..68473507a 100644 --- a/kanidm_unix_int/src/db.rs +++ b/kanidm_unix_int/src/db.rs @@ -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, @@ -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"; diff --git a/kanidm_unix_int/src/ssh_authorizedkeys.rs b/kanidm_unix_int/src/ssh_authorizedkeys.rs index 7aca8d673..4633b82f8 100644 --- a/kanidm_unix_int/src/ssh_authorizedkeys.rs +++ b/kanidm_unix_int/src/ssh_authorizedkeys.rs @@ -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; diff --git a/kanidm_unix_int/src/tasks_daemon.rs b/kanidm_unix_int/src/tasks_daemon.rs index c9adeb3a6..ab89644ca 100644 --- a/kanidm_unix_int/src/tasks_daemon.rs +++ b/kanidm_unix_int/src/tasks_daemon.rs @@ -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, Self::Error> { match serde_json::from_slice::(&src) { diff --git a/kanidm_unix_int/src/test_auth.rs b/kanidm_unix_int/src/test_auth.rs index 1cb3fa5d3..84dc55976 100644 --- a/kanidm_unix_int/src/test_auth.rs +++ b/kanidm_unix_int/src/test_auth.rs @@ -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; diff --git a/kanidm_unix_int/src/unix_config.rs b/kanidm_unix_int/src/unix_config.rs index d7c9d0df6..aca10cd3d 100644 --- a/kanidm_unix_int/src/unix_config.rs +++ b/kanidm_unix_int/src/unix_config.rs @@ -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, diff --git a/kanidm_unix_int/tests/cache_layer_test.rs b/kanidm_unix_int/tests/cache_layer_test.rs index e42c11140..2ea53d103 100644 --- a/kanidm_unix_int/tests/cache_layer_test.rs +++ b/kanidm_unix_int/tests/cache_layer_test.rs @@ -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); diff --git a/kanidmd/daemon/Cargo.toml b/kanidmd/daemon/Cargo.toml index 01bc04dbf..9ba1265a7 100644 --- a/kanidmd/daemon/Cargo.toml +++ b/kanidmd/daemon/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "daemon" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidmd/daemon/build.rs b/kanidmd/daemon/build.rs index e04885924..73ef51b0e 100644 --- a/kanidmd/daemon/build.rs +++ b/kanidmd/daemon/build.rs @@ -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"); diff --git a/kanidmd/daemon/src/main.rs b/kanidmd/daemon/src/main.rs index 4b08825d1..98cbd4878 100644 --- a/kanidmd/daemon/src/main.rs +++ b/kanidmd/daemon/src/main.rs @@ -14,25 +14,15 @@ #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; -#[cfg(not(target_family = "windows"))] // not needed for windows builds -use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid}; -#[cfg(target_family = "windows")] // for windows builds -use whoami; - -use serde::Deserialize; use std::fs::{metadata, File, Metadata}; - +use std::io::Read; #[cfg(target_family = "unix")] use std::os::unix::fs::MetadataExt; - -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::exit; use std::str::FromStr; -use sketching::tracing_forest::{self, traits::*, util::*}; - +use clap::{Args, Parser, Subcommand}; use kanidm::audit::LogLevel; use kanidm::config::{Configuration, OnlineBackup, ServerRole}; #[cfg(not(target_family = "windows"))] @@ -43,8 +33,14 @@ use score::{ domain_rename_core, recover_account_core, reindex_server_core, restore_server_core, vacuum_server_core, verify_server_core, }; - -use clap::{Args, Parser, Subcommand}; +use serde::Deserialize; +use sketching::tracing_forest::traits::*; +use sketching::tracing_forest::util::*; +use sketching::tracing_forest::{self}; +#[cfg(not(target_family = "windows"))] // not needed for windows builds +use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid}; +#[cfg(target_family = "windows")] // for windows builds +use whoami; include!("./opt.rs"); diff --git a/kanidmd/idm/Cargo.toml b/kanidmd/idm/Cargo.toml index 8b994cc9c..ce55858d1 100644 --- a/kanidmd/idm/Cargo.toml +++ b/kanidmd/idm/Cargo.toml @@ -1,98 +1,91 @@ [package] name = "kanidm" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 diff --git a/kanidmd/idm/benches/scaling_10k.rs b/kanidmd/idm/benches/scaling_10k.rs index f0aa25e3c..cc3a49ad6 100644 --- a/kanidmd/idm/benches/scaling_10k.rs +++ b/kanidmd/idm/benches/scaling_10k.rs @@ -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); diff --git a/kanidmd/idm/src/access.rs b/kanidmd/idm/src/access.rs index 485c77c43..52deafe49 100644 --- a/kanidmd/idm/src/access.rs +++ b/kanidmd/idm/src/access.rs @@ -12,31 +12,28 @@ //! search. //! - the ability to turn an entry into a partial-entry for results send //! requirements (also search). -//! // use concread::collections::bptree::*; -use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn}; -use concread::cowcell::*; -use kanidm_proto::v1::Filter as ProtoFilter; -use kanidm_proto::v1::OperationError; -use std::collections::BTreeSet; // use hashbrown::HashSet; use std::cell::Cell; +use std::collections::BTreeSet; use std::ops::DerefMut; use std::sync::Arc; + +use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn}; +use concread::cowcell::*; +use kanidm_proto::v1::{Filter as ProtoFilter, OperationError}; +use tracing::trace; use uuid::Uuid; use crate::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced, EntrySealed}; +use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; use crate::filter::{Filter, FilterValid, FilterValidResolved}; +use crate::identity::{IdentType, IdentityId}; use crate::modify::Modify; use crate::prelude::*; use crate::value::PartialValue; -use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; -use crate::identity::{IdentType, IdentityId}; - -use tracing::trace; - // const ACP_RELATED_SEARCH_CACHE_MAX: usize = 2048; // const ACP_RELATED_SEARCH_CACHE_LOCAL: usize = 16; @@ -408,6 +405,7 @@ pub trait AccessControlsTransaction<'a> { &self, ) -> &mut ARCacheReadTxn<'a, (IdentityId, Filter), Filter, ()>; + #[instrument(level = "debug", name = "access::search_related_acp", skip_all)] fn search_related_acp<'b>( &'b self, rec_entry: &Entry, @@ -443,61 +441,51 @@ pub trait AccessControlsTransaction<'a> { } else { */ // else, we calculate this, and then stash/cache the uuids. - let related_acp: Vec<(&AccessControlSearch, Filter)> = - spanned!("access::search_related_acp", { - search_state - .iter() - .filter_map(|acs| { - // Now resolve the receiver filter - // Okay, so in filter resolution, the primary error case - // is that we have a non-user in the event. We have already - // checked for this above BUT we should still check here - // properly just in case. - // - // In this case, we assume that if the event is internal - // that the receiver can NOT match because it has no selfuuid - // and can as a result, never return true. This leads to this - // acp not being considered in that case ... which should never - // happen because we already bypassed internal ops above! - // - // A possible solution is to change the filter resolve function - // such that it takes an entry, rather than an event, but that - // would create issues in search. - match (&acs.acp.receiver).resolve( - ident, - None, - Some(acp_resolve_filter_cache), - ) { - Ok(f_res) => { - if rec_entry.entry_match_no_index(&f_res) { - // Now, for each of the acp's that apply to our receiver, resolve their - // related target filters. - (&acs.acp.targetscope) - .resolve(ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - ?e, - "A internal filter/event was passed for resolution!?!?" - ); - e - }) - .ok() - .map(|f_res| (acs, f_res)) - } else { - None - } - } - Err(e) => { - admin_error!( - ?e, - "A internal filter/event was passed for resolution!?!?" - ); - None - } + let related_acp: Vec<(&AccessControlSearch, Filter)> = search_state + .iter() + .filter_map(|acs| { + // Now resolve the receiver filter + // Okay, so in filter resolution, the primary error case + // is that we have a non-user in the event. We have already + // checked for this above BUT we should still check here + // properly just in case. + // + // In this case, we assume that if the event is internal + // that the receiver can NOT match because it has no selfuuid + // and can as a result, never return true. This leads to this + // acp not being considered in that case ... which should never + // happen because we already bypassed internal ops above! + // + // A possible solution is to change the filter resolve function + // such that it takes an entry, rather than an event, but that + // would create issues in search. + match (&acs.acp.receiver).resolve(ident, None, Some(acp_resolve_filter_cache)) { + Ok(f_res) => { + if rec_entry.entry_match_no_index(&f_res) { + // Now, for each of the acp's that apply to our receiver, resolve their + // related target filters. + (&acs.acp.targetscope) + .resolve(ident, None, Some(acp_resolve_filter_cache)) + .map_err(|e| { + admin_error!( + ?e, + "A internal filter/event was passed for resolution!?!?" + ); + e + }) + .ok() + .map(|f_res| (acs, f_res)) + } else { + None } - }) - .collect() - }); + } + Err(e) => { + admin_error!(?e, "A internal filter/event was passed for resolution!?!?"); + None + } + } + }) + .collect(); /* // Stash the uuids into the cache. @@ -511,6 +499,7 @@ pub trait AccessControlsTransaction<'a> { } // Contains all the way to eval acps to entries + #[instrument(level = "debug", name = "access::search_filter_entries", skip_all)] fn search_filter_entries( &self, se: &SearchEvent, @@ -525,27 +514,24 @@ pub trait AccessControlsTransaction<'a> { } IdentType::User(u) => &u.entry, }; - spanned!("access::search_filter_entries", { - trace!(event = %se.ident, "Access check for search (filter) event"); + trace!(event = %se.ident, "Access check for search (filter) event"); - // First get the set of acps that apply to this receiver - let related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, &se.ident); + // First get the set of acps that apply to this receiver + let related_acp: Vec<(&AccessControlSearch, _)> = + self.search_related_acp(rec_entry, &se.ident); - /* - related_acp.iter().for_each(|racp| { - security_access!(acs = ?racp.acp.name, "Event Origin Related acs"); - }); - */ + /* + related_acp.iter().for_each(|racp| { + security_access!(acs = ?racp.acp.name, "Event Origin Related acs"); + }); + */ - // Get the set of attributes requested by this se filter. This is what we are - // going to access check. - let requested_attrs: BTreeSet<&str> = se.filter_orig.get_attr_set(); + // Get the set of attributes requested by this se filter. This is what we are + // going to access check. + let requested_attrs: BTreeSet<&str> = se.filter_orig.get_attr_set(); - // For each entry - let allowed_entries: Vec> = spanned!( - "access::search_filter_entries", - { + // For each entry + let allowed_entries: Vec> = entries .into_iter() .filter(|e| { @@ -580,20 +566,22 @@ pub trait AccessControlsTransaction<'a> { security_access!(?decision, "search attr decision"); decision }) - .collect() - } - ); + .collect(); - if allowed_entries.is_empty() { - security_access!("denied ❌"); - } else { - security_access!("allowed {} entries ✅", allowed_entries.len()); - } + if allowed_entries.is_empty() { + security_access!("denied ❌"); + } else { + security_access!("allowed {} entries ✅", allowed_entries.len()); + } - Ok(allowed_entries) - }) + Ok(allowed_entries) } + #[instrument( + level = "debug", + name = "access::search_filter_entry_attributes", + skip_all + )] fn search_filter_entry_attributes( &self, se: &SearchEvent, @@ -620,108 +608,100 @@ pub trait AccessControlsTransaction<'a> { IdentType::User(u) => &u.entry, }; - spanned!("access::search_filter_entry_attributes", { - /* - * Super similar to above (could even re-use some parts). Given a set of entries, - * reduce the attribute sets on them to "what is visible". This is ONLY called on - * the server edge, such that clients only see what they can, but internally, - * impersonate and such actually still get the whole entry back as not to break - * modify and co. - */ + /* + * Super similar to above (could even re-use some parts). Given a set of entries, + * reduce the attribute sets on them to "what is visible". This is ONLY called on + * the server edge, such that clients only see what they can, but internally, + * impersonate and such actually still get the whole entry back as not to break + * modify and co. + */ - trace!("Access check for search (reduce) event: {}", se.ident); + trace!("Access check for search (reduce) event: {}", se.ident); - // Get the relevant acps for this receiver. - let related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, &se.ident); - let related_acp: Vec<(&AccessControlSearch, _)> = - if let Some(r_attrs) = se.attrs.as_ref() { - related_acp - .into_iter() - .filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs)) - .collect() - } else { - related_acp - }; + // Get the relevant acps for this receiver. + let related_acp: Vec<(&AccessControlSearch, _)> = + self.search_related_acp(rec_entry, &se.ident); + let related_acp: Vec<(&AccessControlSearch, _)> = if let Some(r_attrs) = se.attrs.as_ref() { + related_acp + .into_iter() + .filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs)) + .collect() + } else { + related_acp + }; - /* - related_acp.iter().for_each(|racp| { - lsecurity_access!( "Related acs -> {:?}", racp.acp.name); - }); - */ + /* + related_acp.iter().for_each(|racp| { + lsecurity_access!( "Related acs -> {:?}", racp.acp.name); + }); + */ - // Build a reference set from the req_attrs. This is what we test against - // to see if the attribute is something we currently want. - let req_attrs: Option> = se - .attrs - .as_ref() - .map(|vs| vs.iter().map(|s| s.as_str()).collect()); + // Build a reference set from the req_attrs. This is what we test against + // to see if the attribute is something we currently want. + let req_attrs: Option> = se + .attrs + .as_ref() + .map(|vs| vs.iter().map(|s| s.as_str()).collect()); - // For each entry - let allowed_entries: Vec> = - spanned!("access::search_filter_entry_attributes", { - entries - .into_iter() - .map(|e| { - // Get the set of attributes you can see for this entry - // this is within your related acp scope. - let allowed_attrs: BTreeSet<&str> = related_acp - .iter() - .filter_map(|(acs, f_res)| { - if e.entry_match_no_index(f_res) { - security_access!( - target = ?e.get_uuid(), - acs = %acs.acp.name, - "target entry matches acs", - ); - // add search_attrs to allowed iterator - Some(acs.attrs.iter().map(|s| s.as_str()).filter(|s| { - req_attrs - .as_ref() - .map(|r_attrs| r_attrs.contains(s)) - .unwrap_or(true) - })) - } else { - trace!( - target = ?e.get_uuid(), - acs = %acs.acp.name, - "target entry DOES NOT match acs", - ); - None - } - }) - .flatten() - .collect(); - - // Remove all others that are present on the entry. + // For each entry + let allowed_entries: Vec> = entries + .into_iter() + .map(|e| { + // Get the set of attributes you can see for this entry + // this is within your related acp scope. + let allowed_attrs: BTreeSet<&str> = related_acp + .iter() + .filter_map(|(acs, f_res)| { + if e.entry_match_no_index(f_res) { security_access!( - requested = ?req_attrs, - allowed = ?allowed_attrs, - "attributes" + target = ?e.get_uuid(), + acs = %acs.acp.name, + "target entry matches acs", ); + // add search_attrs to allowed iterator + Some(acs.attrs.iter().map(|s| s.as_str()).filter(|s| { + req_attrs + .as_ref() + .map(|r_attrs| r_attrs.contains(s)) + .unwrap_or(true) + })) + } else { + trace!( + target = ?e.get_uuid(), + acs = %acs.acp.name, + "target entry DOES NOT match acs", + ); + None + } + }) + .flatten() + .collect(); - // Now purge the attrs that are NOT allowed. - spanned!( - "access::search_filter_entry_attributes", - { e.reduce_attributes(&allowed_attrs) } - ) - }) - .collect() - }); - - if allowed_entries.is_empty() { - security_access!("reduced to empty set on all entries ❌"); - } else { + // Remove all others that are present on the entry. security_access!( - "attribute set reduced on {} entries ✅", - allowed_entries.len() + requested = ?req_attrs, + allowed = ?allowed_attrs, + "attributes" ); - } - Ok(allowed_entries) - }) + // Now purge the attrs that are NOT allowed. + e.reduce_attributes(&allowed_attrs) + }) + .collect(); + + if allowed_entries.is_empty() { + security_access!("reduced to empty set on all entries ❌"); + } else { + security_access!( + "attribute set reduced on {} entries ✅", + allowed_entries.len() + ); + } + + Ok(allowed_entries) } + #[instrument(level = "debug", name = "access::modify_related_acp", skip_all)] fn modify_related_acp<'b>( &'b self, rec_entry: &Entry, @@ -733,9 +713,7 @@ pub trait AccessControlsTransaction<'a> { // Find the acps that relate to the caller, and compile their related // target filters. - let related_acp: Vec<(&AccessControlModify, _)> = spanned!( - "access::modify_related_acp", - { + let related_acp: Vec<(&AccessControlModify, _)> = modify_state .iter() .filter_map(|acs| { @@ -766,14 +744,13 @@ pub trait AccessControlsTransaction<'a> { } } }) - .collect() - } - ); + .collect(); related_acp } #[allow(clippy::cognitive_complexity)] + #[instrument(level = "debug", name = "access::modify_allow_operation", skip_all)] fn modify_allow_operation( &self, me: &ModifyEvent, @@ -787,155 +764,154 @@ pub trait AccessControlsTransaction<'a> { } IdentType::User(u) => &u.entry, }; - spanned!("access::modify_allow_operation", { - trace!("Access check for modify event: {}", me.ident); + trace!("Access check for modify event: {}", me.ident); - // Pre-check if the no-no purge class is present - let disallow = me - .modlist - .iter() - .any(|m| matches!(m, Modify::Purged(a) if a == "class")); + // Pre-check if the no-no purge class is present + let disallow = me + .modlist + .iter() + .any(|m| matches!(m, Modify::Purged(a) if a == "class")); - if disallow { - security_access!("Disallowing purge class in modification"); - return Ok(false); - } + if disallow { + security_access!("Disallowing purge class in modification"); + return Ok(false); + } - // Find the acps that relate to the caller, and compile their related - // target filters. - let related_acp: Vec<(&AccessControlModify, _)> = - self.modify_related_acp(rec_entry, &me.ident); + // Find the acps that relate to the caller, and compile their related + // target filters. + let related_acp: Vec<(&AccessControlModify, _)> = + self.modify_related_acp(rec_entry, &me.ident); - related_acp.iter().for_each(|racp| { - trace!("Related acs -> {:?}", racp.0.acp.name); - }); + related_acp.iter().for_each(|racp| { + trace!("Related acs -> {:?}", racp.0.acp.name); + }); - // build two sets of "requested pres" and "requested rem" - let requested_pres: BTreeSet<&str> = me - .modlist - .iter() - .filter_map(|m| match m { - Modify::Present(a, _) => Some(a.as_str()), - _ => None, - }) - .collect(); + // build two sets of "requested pres" and "requested rem" + let requested_pres: BTreeSet<&str> = me + .modlist + .iter() + .filter_map(|m| match m { + Modify::Present(a, _) => Some(a.as_str()), + _ => None, + }) + .collect(); - let requested_rem: BTreeSet<&str> = me - .modlist - .iter() - .filter_map(|m| match m { - Modify::Removed(a, _) => Some(a.as_str()), - Modify::Purged(a) => Some(a.as_str()), - _ => None, - }) - .collect(); + let requested_rem: BTreeSet<&str> = me + .modlist + .iter() + .filter_map(|m| match m { + Modify::Removed(a, _) => Some(a.as_str()), + Modify::Purged(a) => Some(a.as_str()), + _ => None, + }) + .collect(); - // Build the set of classes that we to work on, only in terms of "addition". To remove - // I think we have no limit, but ... william of the future may find a problem with this - // policy. - let requested_classes: BTreeSet<&str> = me - .modlist - .iter() - .filter_map(|m| match m { - Modify::Present(a, v) => { - if a.as_str() == "class" { - // Here we have an option<&str> which could mean there is a risk of - // a malicious entity attempting to trick us by masking class mods - // in non-iutf8 types. However, the server first won't respect their - // existance, and second, we would have failed the mod at schema checking - // earlier in the process as these were not correctly type. As a result - // we can trust these to be correct here and not to be "None". - v.to_str() - } else { - None - } + // Build the set of classes that we to work on, only in terms of "addition". To remove + // I think we have no limit, but ... william of the future may find a problem with this + // policy. + let requested_classes: BTreeSet<&str> = me + .modlist + .iter() + .filter_map(|m| match m { + Modify::Present(a, v) => { + if a.as_str() == "class" { + // Here we have an option<&str> which could mean there is a risk of + // a malicious entity attempting to trick us by masking class mods + // in non-iutf8 types. However, the server first won't respect their + // existance, and second, we would have failed the mod at schema checking + // earlier in the process as these were not correctly type. As a result + // we can trust these to be correct here and not to be "None". + v.to_str() + } else { + None } - Modify::Removed(a, v) => { - if a.as_str() == "class" { - v.to_str() - } else { - None - } + } + Modify::Removed(a, v) => { + if a.as_str() == "class" { + v.to_str() + } else { + None + } + } + _ => None, + }) + .collect(); + + security_access!(?requested_pres, "Requested present set"); + security_access!(?requested_rem, "Requested remove set"); + security_access!(?requested_classes, "Requested class set"); + + let r = entries.iter().all(|e| { + // For this entry, find the acp's that apply to it from the + // set that apply to the entry that is performing the operation + let scoped_acp: Vec<&AccessControlModify> = related_acp + .iter() + .filter_map(|(acm, f_res)| { + if e.entry_match_no_index(f_res) { + Some(*acm) + } else { + None } - _ => None, }) .collect(); + // Build the sets of classes, pres and rem we are allowed to modify, extend + // or use based on the set of matched acps. + let allowed_pres: BTreeSet<&str> = scoped_acp + .iter() + .flat_map(|acp| acp.presattrs.iter().map(|v| v.as_str())) + .collect(); - security_access!(?requested_pres, "Requested present set"); - security_access!(?requested_rem, "Requested remove set"); - security_access!(?requested_classes, "Requested class set"); + let allowed_rem: BTreeSet<&str> = scoped_acp + .iter() + .flat_map(|acp| acp.remattrs.iter().map(|v| v.as_str())) + .collect(); - let r = entries.iter().all(|e| { - // For this entry, find the acp's that apply to it from the - // set that apply to the entry that is performing the operation - let scoped_acp: Vec<&AccessControlModify> = related_acp - .iter() - .filter_map(|(acm, f_res)| { - if e.entry_match_no_index(f_res) { - Some(*acm) - } else { - None - } - }) - .collect(); - // Build the sets of classes, pres and rem we are allowed to modify, extend - // or use based on the set of matched acps. - let allowed_pres: BTreeSet<&str> = scoped_acp - .iter() - .flat_map(|acp| acp.presattrs.iter().map(|v| v.as_str())) - .collect(); + let allowed_classes: BTreeSet<&str> = scoped_acp + .iter() + .flat_map(|acp| acp.classes.iter().map(|v| v.as_str())) + .collect(); - let allowed_rem: BTreeSet<&str> = scoped_acp - .iter() - .flat_map(|acp| acp.remattrs.iter().map(|v| v.as_str())) - .collect(); - - let allowed_classes: BTreeSet<&str> = scoped_acp - .iter() - .flat_map(|acp| acp.classes.iter().map(|v| v.as_str())) - .collect(); - - // Now check all the subsets are true. Remember, purge class - // is already checked above. - if !requested_pres.is_subset(&allowed_pres) { - security_access!("requested_pres is not a subset of allowed"); - security_access!( - "requested_pres: {:?} !⊆ allowed: {:?}", - requested_pres, - allowed_pres - ); - false - } else if !requested_rem.is_subset(&allowed_rem) { - security_access!("requested_rem is not a subset of allowed"); - security_access!( - "requested_rem: {:?} !⊆ allowed: {:?}", - requested_rem, - allowed_rem - ); - false - } else if !requested_classes.is_subset(&allowed_classes) { - security_access!("requested_classes is not a subset of allowed"); - security_access!( - "requested_classes: {:?} !⊆ allowed: {:?}", - requested_classes, - allowed_classes - ); - false - } else { - security_access!("passed pres, rem, classes check."); - true - } // if acc == false - }); - if r { - security_access!("allowed ✅"); + // Now check all the subsets are true. Remember, purge class + // is already checked above. + if !requested_pres.is_subset(&allowed_pres) { + security_access!("requested_pres is not a subset of allowed"); + security_access!( + "requested_pres: {:?} !⊆ allowed: {:?}", + requested_pres, + allowed_pres + ); + false + } else if !requested_rem.is_subset(&allowed_rem) { + security_access!("requested_rem is not a subset of allowed"); + security_access!( + "requested_rem: {:?} !⊆ allowed: {:?}", + requested_rem, + allowed_rem + ); + false + } else if !requested_classes.is_subset(&allowed_classes) { + security_access!("requested_classes is not a subset of allowed"); + security_access!( + "requested_classes: {:?} !⊆ allowed: {:?}", + requested_classes, + allowed_classes + ); + false } else { - security_access!("denied ❌"); - } - Ok(r) - }) + security_access!("passed pres, rem, classes check."); + true + } // if acc == false + }); + if r { + security_access!("allowed ✅"); + } else { + security_access!("denied ❌"); + } + Ok(r) } #[allow(clippy::cognitive_complexity)] + #[instrument(level = "debug", name = "access::create_allow_operation", skip_all)] fn create_allow_operation( &self, ce: &CreateEvent, @@ -949,125 +925,124 @@ pub trait AccessControlsTransaction<'a> { } IdentType::User(u) => &u.entry, }; - spanned!("access::create_allow_operation", { - trace!("Access check for create event: {}", ce.ident); + trace!("Access check for create event: {}", ce.ident); - // Some useful references we'll use for the remainder of the operation - let create_state = self.get_create(); - let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); + // Some useful references we'll use for the remainder of the operation + let create_state = self.get_create(); + let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); - // Find the acps that relate to the caller. - let related_acp: Vec<(&AccessControlCreate, _)> = create_state - .iter() - .filter_map(|acs| { - match acs + // Find the acps that relate to the caller. + let related_acp: Vec<(&AccessControlCreate, _)> = create_state + .iter() + .filter_map(|acs| { + match acs + .acp + .receiver + .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) + { + Ok(f_res) if rec_entry.entry_match_no_index(&f_res) => acs .acp - .receiver + .targetscope .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) - { - Ok(f_res) if rec_entry.entry_match_no_index(&f_res) => acs - .acp - .targetscope - .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", - e - ); - e - }) - .ok() - .map(|f_res| (acs, f_res)), - Ok(_) => None, - Err(e) => { + .map_err(|e| { admin_error!( "A internal filter/event was passed for resolution!?!? {:?}", e ); - None - } + e + }) + .ok() + .map(|f_res| (acs, f_res)), + Ok(_) => None, + Err(e) => { + admin_error!( + "A internal filter/event was passed for resolution!?!? {:?}", + e + ); + None } - }) - .collect(); + } + }) + .collect(); - // lsecurity_access!( "Related acc -> {:?}", related_acp); + // lsecurity_access!( "Related acc -> {:?}", related_acp); - // For each entry - let r = entries.iter().all(|e| { - // Build the set of requested classes and attrs here. - let create_attrs: BTreeSet<&str> = e.get_ava_names().collect(); - // If this is empty, we make an empty set, which is fine because - // the empty class set despite matching is_subset, will have the - // following effect: - // * there is no class on entry, so schema will fail - // * plugin-base will add object to give a class, but excess - // attrs will cause fail (could this be a weakness?) - // * class is a "may", so this could be empty in the rules, so - // if the accr is empty this would not be a true subset, - // so this would "fail", but any content in the accr would - // have to be validated. - // - // I still think if this is None, we should just fail here ... - // because it shouldn't be possible to match. + // For each entry + let r = entries.iter().all(|e| { + // Build the set of requested classes and attrs here. + let create_attrs: BTreeSet<&str> = e.get_ava_names().collect(); + // If this is empty, we make an empty set, which is fine because + // the empty class set despite matching is_subset, will have the + // following effect: + // * there is no class on entry, so schema will fail + // * plugin-base will add object to give a class, but excess + // attrs will cause fail (could this be a weakness?) + // * class is a "may", so this could be empty in the rules, so + // if the accr is empty this would not be a true subset, + // so this would "fail", but any content in the accr would + // have to be validated. + // + // I still think if this is None, we should just fail here ... + // because it shouldn't be possible to match. - let create_classes: BTreeSet<&str> = match e.get_ava_iter_iutf8("class") { - Some(s) => s.collect(), - None => { - admin_error!("Class set failed to build - corrupted entry?"); + let create_classes: BTreeSet<&str> = match e.get_ava_iter_iutf8("class") { + Some(s) => s.collect(), + None => { + admin_error!("Class set failed to build - corrupted entry?"); + return false; + } + }; + + related_acp.iter().any(|(accr, f_res)| { + // Check to see if allowed. + if e.entry_match_no_index(f_res) { + security_access!(?e, acs = ?accr, "entry matches acs"); + // It matches, so now we have to check attrs and classes. + // Remember, we have to match ALL requested attrs + // and classes to pass! + let allowed_attrs: BTreeSet<&str> = + accr.attrs.iter().map(|s| s.as_str()).collect(); + let allowed_classes: BTreeSet<&str> = + accr.classes.iter().map(|s| s.as_str()).collect(); + + if !create_attrs.is_subset(&allowed_attrs) { + security_access!("create_attrs is not a subset of allowed"); + security_access!("{:?} !⊆ {:?}", create_attrs, allowed_attrs); return false; } - }; - - related_acp.iter().any(|(accr, f_res)| { - // Check to see if allowed. - if e.entry_match_no_index(f_res) { - security_access!(?e, acs = ?accr, "entry matches acs"); - // It matches, so now we have to check attrs and classes. - // Remember, we have to match ALL requested attrs - // and classes to pass! - let allowed_attrs: BTreeSet<&str> = - accr.attrs.iter().map(|s| s.as_str()).collect(); - let allowed_classes: BTreeSet<&str> = - accr.classes.iter().map(|s| s.as_str()).collect(); - - if !create_attrs.is_subset(&allowed_attrs) { - security_access!("create_attrs is not a subset of allowed"); - security_access!("{:?} !⊆ {:?}", create_attrs, allowed_attrs); - return false; - } - if !create_classes.is_subset(&allowed_classes) { - security_access!("create_classes is not a subset of allowed"); - security_access!("{:?} !⊆ {:?}", create_classes, allowed_classes); - return false; - } - security_access!("passed"); - - true - } else { - trace!(?e, acs = %accr.acp.name, "entry DOES NOT match acs"); - // Does not match, fail this rule. - false + if !create_classes.is_subset(&allowed_classes) { + security_access!("create_classes is not a subset of allowed"); + security_access!("{:?} !⊆ {:?}", create_classes, allowed_classes); + return false; } - }) - // Find the set of related acps for this entry. - // - // For each "created" entry. - // If the created entry is 100% allowed by this acp - // IE: all attrs to be created AND classes match classes - // allow - // if no acp allows, fail operation. - }); + security_access!("passed"); - if r { - security_access!("allowed ✅"); - } else { - security_access!("denied ❌"); - } + true + } else { + trace!(?e, acs = %accr.acp.name, "entry DOES NOT match acs"); + // Does not match, fail this rule. + false + } + }) + // Find the set of related acps for this entry. + // + // For each "created" entry. + // If the created entry is 100% allowed by this acp + // IE: all attrs to be created AND classes match classes + // allow + // if no acp allows, fail operation. + }); - Ok(r) - }) + if r { + security_access!("allowed ✅"); + } else { + security_access!("denied ❌"); + } + + Ok(r) } + #[instrument(level = "debug", name = "access::delete_allow_operation", skip_all)] fn delete_allow_operation( &self, de: &DeleteEvent, @@ -1081,15 +1056,14 @@ pub trait AccessControlsTransaction<'a> { } IdentType::User(u) => &u.entry, }; - spanned!("access::delete_allow_operation", { - trace!("Access check for delete event: {}", de.ident); + trace!("Access check for delete event: {}", de.ident); - // Some useful references we'll use for the remainder of the operation - let delete_state = self.get_delete(); - let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); + // Some useful references we'll use for the remainder of the operation + let delete_state = self.get_delete(); + let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); - // Find the acps that relate to the caller. - let related_acp: Vec<(&AccessControlDelete, _)> = delete_state + // Find the acps that relate to the caller. + let related_acp: Vec<(&AccessControlDelete, _)> = delete_state .iter() .filter_map(|acs| { match acs.acp.receiver.resolve(&de.ident, None, Some(acp_resolve_filter_cache)) { @@ -1123,44 +1097,44 @@ pub trait AccessControlsTransaction<'a> { }) .collect(); - /* - related_acp.iter().for_each(|racp| { - lsecurity_access!( "Related acs -> {:?}", racp.acp.name); - }); - */ + /* + related_acp.iter().for_each(|racp| { + lsecurity_access!( "Related acs -> {:?}", racp.acp.name); + }); + */ - // For each entry - let r = entries.iter().all(|e| { - related_acp.iter().any(|(acd, f_res)| { - if e.entry_match_no_index(f_res) { - security_access!( - entry_uuid = ?e.get_uuid(), - acs = %acd.acp.name, - "entry matches acs" - ); - // It matches, so we can delete this! - security_access!("passed"); - true - } else { - trace!( - "entry {:?} DOES NOT match acs {}", - e.get_uuid(), - acd.acp.name - ); - // Does not match, fail. - false - } // else - }) // any related_acp - }); - if r { - security_access!("allowed ✅"); - } else { - security_access!("denied ❌"); - } - Ok(r) - }) + // For each entry + let r = entries.iter().all(|e| { + related_acp.iter().any(|(acd, f_res)| { + if e.entry_match_no_index(f_res) { + security_access!( + entry_uuid = ?e.get_uuid(), + acs = %acd.acp.name, + "entry matches acs" + ); + // It matches, so we can delete this! + security_access!("passed"); + true + } else { + trace!( + "entry {:?} DOES NOT match acs {}", + e.get_uuid(), + acd.acp.name + ); + // Does not match, fail. + false + } // else + }) // any related_acp + }); + if r { + security_access!("allowed ✅"); + } else { + security_access!("denied ❌"); + } + Ok(r) } + #[instrument(level = "debug", name = "access::effective_permission_check", skip_all)] fn effective_permission_check( &self, ident: &Identity, @@ -1186,116 +1160,114 @@ pub trait AccessControlsTransaction<'a> { IdentType::User(u) => &u.entry, }; - spanned!("access::effective_permission_check", { - trace!(ident = %ident, "Effective permission check"); - // I think we seperate this to multiple checks ...? + trace!(ident = %ident, "Effective permission check"); + // I think we seperate this to multiple checks ...? - // == search == - // Get the relevant acps for this receiver. - let search_related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, ident); - let search_related_acp: Vec<(&AccessControlSearch, _)> = - if let Some(r_attrs) = attrs.as_ref() { - search_related_acp - .into_iter() - .filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs)) - .collect() + // == search == + // Get the relevant acps for this receiver. + let search_related_acp: Vec<(&AccessControlSearch, _)> = + self.search_related_acp(rec_entry, ident); + let search_related_acp: Vec<(&AccessControlSearch, _)> = + if let Some(r_attrs) = attrs.as_ref() { + search_related_acp + .into_iter() + .filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs)) + .collect() + } else { + search_related_acp + }; + + /* + search_related_acp.iter().for_each(|(racp, _)| { + trace!("Related acs -> {:?}", racp.acp.name); + }); + */ + + // == modify == + + let modify_related_acp: Vec<(&AccessControlModify, _)> = + self.modify_related_acp(rec_entry, ident); + + /* + modify_related_acp.iter().for_each(|(racp, _)| { + trace!("Related acm -> {:?}", racp.acp.name); + }); + */ + + let effective_permissions: Vec<_> = entries + .iter() + .map(|e| { + // == search == + let allowed_attrs: BTreeSet = search_related_acp + .iter() + .filter_map(|(acs, f_res)| { + // if it applies + if e.entry_match_no_index(f_res) { + // security_access!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry matches acs"); + Some(acs.attrs.iter().cloned()) + } else { + trace!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry DOES NOT match acs"); // should this be `security_access`? + None + } + }) + .flatten() + .collect(); + + security_access!( + requested = ?attrs, + allows = ?allowed_attrs, + "attributes", + ); + + // intersect? + let search_effective = if let Some(r_attrs) = attrs.as_ref() { + r_attrs & &allowed_attrs } else { - search_related_acp + allowed_attrs }; - /* - search_related_acp.iter().for_each(|(racp, _)| { - trace!("Related acs -> {:?}", racp.acp.name); - }); - */ + // == modify == + let modify_scoped_acp: Vec<&AccessControlModify> = modify_related_acp + .iter() + .filter_map(|(acm, f_res)| { + if e.entry_match_no_index(f_res) { + Some(*acm) + } else { + None + } + }) + .collect(); - // == modify == + let modify_pres: BTreeSet = modify_scoped_acp + .iter() + .flat_map(|acp| acp.presattrs.iter().cloned()) + .collect(); - let modify_related_acp: Vec<(&AccessControlModify, _)> = - self.modify_related_acp(rec_entry, ident); + let modify_rem: BTreeSet = modify_scoped_acp + .iter() + .flat_map(|acp| acp.remattrs.iter().cloned()) + .collect(); - /* - modify_related_acp.iter().for_each(|(racp, _)| { - trace!("Related acm -> {:?}", racp.acp.name); - }); - */ + let modify_class: BTreeSet = modify_scoped_acp + .iter() + .flat_map(|acp| acp.classes.iter().cloned()) + .collect(); - let effective_permissions: Vec<_> = entries - .iter() - .map(|e| { - // == search == - let allowed_attrs: BTreeSet = search_related_acp - .iter() - .filter_map(|(acs, f_res)| { - // if it applies - if e.entry_match_no_index(f_res) { - // security_access!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry matches acs"); - Some(acs.attrs.iter().cloned()) - } else { - trace!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry DOES NOT match acs"); // should this be `security_access`? - None - } - }) - .flatten() - .collect(); + AccessEffectivePermission { + target: e.get_uuid(), + search: search_effective, + modify_pres, + modify_rem, + modify_class, + } + }) + .collect(); - security_access!( - requested = ?attrs, - allows = ?allowed_attrs, - "attributes", - ); + effective_permissions.iter().for_each(|ep| { + trace!(?ep); + }); - // intersect? - let search_effective = if let Some(r_attrs) = attrs.as_ref() { - r_attrs & &allowed_attrs - } else { - allowed_attrs - }; - - // == modify == - let modify_scoped_acp: Vec<&AccessControlModify> = modify_related_acp - .iter() - .filter_map(|(acm, f_res)| { - if e.entry_match_no_index(f_res) { - Some(*acm) - } else { - None - } - }) - .collect(); - - let modify_pres: BTreeSet = modify_scoped_acp - .iter() - .flat_map(|acp| acp.presattrs.iter().cloned()) - .collect(); - - let modify_rem: BTreeSet = modify_scoped_acp - .iter() - .flat_map(|acp| acp.remattrs.iter().cloned()) - .collect(); - - let modify_class: BTreeSet = modify_scoped_acp - .iter() - .flat_map(|acp| acp.classes.iter().cloned()) - .collect(); - - AccessEffectivePermission { - target: e.get_uuid(), - search: search_effective, - modify_pres, - modify_rem, - modify_class, - } - }) - .collect(); - - effective_permissions.iter().for_each(|ep| { - trace!(?ep); - }); - - Ok(effective_permissions) - }) + Ok(effective_permissions) } } @@ -1522,15 +1494,17 @@ impl AccessControls { #[cfg(test)] mod tests { + use std::collections::BTreeSet; + use std::sync::Arc; + + use uuid::uuid; + use crate::access::{ AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlProfile, AccessControlSearch, AccessControls, AccessControlsTransaction, AccessEffectivePermission, }; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; use crate::prelude::*; - use std::collections::BTreeSet; - use std::sync::Arc; - use uuid::uuid; macro_rules! acp_from_entry_err { ( diff --git a/kanidmd/idm/src/actors/v1_read.rs b/kanidmd/idm/src/actors/v1_read.rs index 38d1cba8b..65db4166d 100644 --- a/kanidmd/idm/src/actors/v1_read.rs +++ b/kanidmd/idm/src/actors/v1_read.rs @@ -1,23 +1,28 @@ -use tracing::{error, info, instrument, trace}; - -use chrono::{DateTime, SecondsFormat, Utc}; +use std::convert::TryFrom; +use std::fs; +use std::path::{Path, PathBuf}; use std::sync::Arc; -use crate::prelude::*; +use kanidm_proto::v1::{ + ApiToken, AuthRequest, BackupCodesView, CURequest, CUSessionToken, CUStatus, CredentialStatus, + Entry as ProtoEntry, OperationError, RadiusAuthToken, SearchRequest, SearchResponse, + UnixGroupToken, UnixUserToken, WhoamiResponse, +}; +use ldap3_proto::simple::*; +use regex::Regex; +use tracing::{error, info, instrument, trace}; +use uuid::Uuid; use crate::be::BackendTransaction; - use crate::event::{ AuthEvent, AuthResult, OnlineBackupEvent, SearchEvent, SearchResult, WhoamiResult, }; +use crate::filter::{Filter, FilterInvalid}; use crate::idm::credupdatesession::CredentialUpdateSessionToken; use crate::idm::event::{ CredentialStatusEvent, RadiusAuthTokenEvent, ReadBackupCodeEvent, UnixGroupTokenEvent, UnixUserAuthEvent, UnixUserTokenEvent, }; -use kanidm_proto::v1::{BackupCodesView, OperationError, RadiusAuthToken}; - -use crate::filter::{Filter, FilterInvalid}; use crate::idm::oauth2::{ AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisePermitSuccess, AuthoriseResponse, @@ -26,20 +31,7 @@ use crate::idm::oauth2::{ use crate::idm::server::{IdmServer, IdmServerTransaction}; use crate::idm::serviceaccount::ListApiTokenEvent; use crate::ldap::{LdapBoundToken, LdapResponseState, LdapServer}; - -use kanidm_proto::v1::Entry as ProtoEntry; -use kanidm_proto::v1::{ - ApiToken, AuthRequest, CURequest, CUSessionToken, CUStatus, CredentialStatus, SearchRequest, - SearchResponse, UnixGroupToken, UnixUserToken, WhoamiResponse, -}; - -use regex::Regex; -use std::fs; -use std::path::{Path, PathBuf}; -use uuid::Uuid; - -use ldap3_proto::simple::*; -use std::convert::TryFrom; +use crate::prelude::*; // =========================================================== @@ -94,35 +86,25 @@ impl QueryServerReadV1 { // Begin a read let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - // ! NOTICE: The inner function contains a short-circuiting `return`, which is only exits the closure. - // ! If we removed the `lperf_op_segment` and kept the inside, this would short circuit before logging `audit`. - // ! However, since we immediately return `res` after logging `audit`, and we should be removing the lperf stuff - // ! and the logging of `audit` at the same time, it is ok if the inner code short circuits the whole function because - // ! there is no work to be done afterwards. - // ! However, if we want to do work after `res` is calculated, we need to pass `spanned` a closure instead of a block - // ! in order to not short-circuit the entire function. - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(?e, "Invalid identity"); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(?e, "Invalid identity"); + e + })?; - // Make an event from the request - let search = - SearchEvent::from_message(ident, &req, &idms_prox_read.qs_read).map_err(|e| { - admin_error!(?e, "Failed to begin search"); - e - })?; + // Make an event from the request + let search = + SearchEvent::from_message(ident, &req, &idms_prox_read.qs_read).map_err(|e| { + admin_error!(?e, "Failed to begin search"); + e + })?; - trace!(?search, "Begin event"); + trace!(?search, "Begin event"); - let entries = idms_prox_read.qs_read.search_ext(&search)?; + let entries = idms_prox_read.qs_read.search_ext(&search)?; - SearchResult::new(&idms_prox_read.qs_read, &entries).map(SearchResult::response) - }); - res + SearchResult::new(&idms_prox_read.qs_read, &entries).map(SearchResult::response) } #[instrument( @@ -184,34 +166,33 @@ impl QueryServerReadV1 { ) -> Result<(), OperationError> { trace!(eventid = ?msg.eventid, "Begin online backup event"); - let now: DateTime = Utc::now(); - let timestamp = now.to_rfc3339_opts(SecondsFormat::Secs, true); + #[allow(deprecated)] + let now = time::OffsetDateTime::now_local(); + let timestamp = now.format(time::Format::Rfc3339); let dest_file = format!("{}/backup-{}.json", outpath, timestamp); - match Path::new(&dest_file).exists() { - true => { - error!( - "Online backup file {} already exists, will not owerwrite it.", - dest_file - ); - return Err(OperationError::InvalidState); - } - false => { - let idms_prox_read = self.idms.proxy_read_async().await; - spanned!("actors::v1_read::handle", { - idms_prox_read - .qs_read - .get_be_txn() - .backup(&dest_file) - .map(|()| { - info!("Online backup created {} successfully", dest_file); - }) - .map_err(|e| { - error!("Online backup failed to create {}: {:?}", dest_file, e); - OperationError::InvalidState - })?; - }); - } + if Path::new(&dest_file).exists() { + error!( + "Online backup file {} already exists, will not overwrite it.", + dest_file + ); + return Err(OperationError::InvalidState); + } + + // Scope to limit the read txn. + { + let idms_prox_read = self.idms.proxy_read_async().await; + idms_prox_read + .qs_read + .get_be_txn() + .backup(&dest_file) + .map(|()| { + info!("Online backup created {} successfully", dest_file); + }) + .map_err(|e| { + error!("Online backup failed to create {}: {:?}", dest_file, e); + OperationError::InvalidState + })?; } // pattern to find automatically generated backup files @@ -314,53 +295,42 @@ impl QueryServerReadV1 { // Begin a read let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - // ! NOTICE: The inner function contains a short-circuiting `return`, which is only exits the closure. - // ! If we removed the `lperf_op_segment` and kept the inside, this would short circuit before logging `audit`. - // ! However, since we immediately return `res` after logging `audit`, and we should be removing the lperf stuff - // ! and the logging of `audit` at the same time, it is ok if the inner code short circuits the whole function because - // ! there is no work to be done afterwards. - // ! However, if we want to do work after `res` is calculated, we need to pass `spanned` a closure instead of a block - // ! in order to not short-circuit the entire function. - let res = spanned!("actors::v1_read::handle", { - // Make an event from the whoami request. This will process the event and - // generate a selfuuid search. - // - // This current handles the unauthenticated check, and will - // trigger the failure, but if we can manage to work out async - // then move this to core.rs, and don't allow Option to get - // this far. - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(?e, "Invalid identity"); - e - })?; + // Make an event from the whoami request. This will process the event and + // generate a selfuuid search. + // + // This current handles the unauthenticated check, and will + // trigger the failure, but if we can manage to work out async + // then move this to core.rs, and don't allow Option to get + // this far. + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(?e, "Invalid identity"); + e + })?; - let srch = - SearchEvent::from_whoami_request(ident, &idms_prox_read.qs_read).map_err(|e| { - admin_error!(?e, "Failed to begin whoami"); - e - })?; + let srch = + SearchEvent::from_whoami_request(ident, &idms_prox_read.qs_read).map_err(|e| { + admin_error!(?e, "Failed to begin whoami"); + e + })?; - trace!(search = ?srch, "Begin event"); + trace!(search = ?srch, "Begin event"); - let mut entries = idms_prox_read.qs_read.search_ext(&srch)?; + let mut entries = idms_prox_read.qs_read.search_ext(&srch)?; - match entries.pop() { - Some(e) if entries.is_empty() => { - WhoamiResult::new(&idms_prox_read.qs_read, &e).map(WhoamiResult::response) - } - Some(_) => Err(OperationError::InvalidState), // Somehow matched multiple entries... - _ => Err(OperationError::NoMatchingEntries), + match entries.pop() { + Some(e) if entries.is_empty() => { + WhoamiResult::new(&idms_prox_read.qs_read, &e).map(WhoamiResult::response) } - }); - res + Some(_) => Err(OperationError::InvalidState), /* Somehow matched multiple entries... */ + _ => Err(OperationError::NoMatchingEntries), + } } #[instrument( level = "info", - name = "search2", - skip(self, uat, filter, attrs, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalsearch( @@ -372,42 +342,38 @@ impl QueryServerReadV1 { ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; - // Make an event from the request - let srch = match SearchEvent::from_internal_message( - ident, - &filter, - attrs.as_deref(), - &idms_prox_read.qs_read, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin internal api search: {:?}", e); - return Err(e); - } - }; - - trace!(?srch, "Begin event"); - - match idms_prox_read.qs_read.search_ext(&srch) { - Ok(entries) => SearchResult::new(&idms_prox_read.qs_read, &entries) - .map(|ok_sr| ok_sr.into_proto_array()), - Err(e) => Err(e), + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; + // Make an event from the request + let srch = match SearchEvent::from_internal_message( + ident, + &filter, + attrs.as_deref(), + &idms_prox_read.qs_read, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin internal api search: {:?}", e); + return Err(e); } - }); - res + }; + + trace!(?srch, "Begin event"); + + match idms_prox_read.qs_read.search_ext(&srch) { + Ok(entries) => SearchResult::new(&idms_prox_read.qs_read, &entries) + .map(|ok_sr| ok_sr.into_proto_array()), + Err(e) => Err(e), + } } #[instrument( level = "info", - name = "search_recycled", - skip(self, uat, filter, attrs, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalsearchrecycled( @@ -420,42 +386,38 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; - // Make an event from the request - let srch = match SearchEvent::from_internal_recycle_message( - ident, - &filter, - attrs.as_deref(), - &idms_prox_read.qs_read, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin recycled search: {:?}", e); - return Err(e); - } - }; - - trace!(?srch, "Begin event"); - - match idms_prox_read.qs_read.search_ext(&srch) { - Ok(entries) => SearchResult::new(&idms_prox_read.qs_read, &entries) - .map(|ok_sr| ok_sr.into_proto_array()), - Err(e) => Err(e), + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; + // Make an event from the request + let srch = match SearchEvent::from_internal_recycle_message( + ident, + &filter, + attrs.as_deref(), + &idms_prox_read.qs_read, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin recycled search: {:?}", e); + return Err(e); } - }); - res + }; + + trace!(?srch, "Begin event"); + + match idms_prox_read.qs_read.search_ext(&srch) { + Ok(entries) => SearchResult::new(&idms_prox_read.qs_read, &entries) + .map(|ok_sr| ok_sr.into_proto_array()), + Err(e) => Err(e), + } } #[instrument( level = "info", - name = "radius_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalradiusread( @@ -466,60 +428,56 @@ impl QueryServerReadV1 { ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!("Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!("Error resolving id to target"); + e + })?; - // Make an event from the request - let srch = match SearchEvent::from_target_uuid_request( - ident, - target_uuid, - &idms_prox_read.qs_read, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin radius read: {:?}", e); - return Err(e); - } - }; - - trace!(?srch, "Begin event"); - - // We have to use search_ext to guarantee acs was applied. - match idms_prox_read.qs_read.search_ext(&srch) { - Ok(mut entries) => { - let r = entries - .pop() - // From the entry, turn it into the value - .and_then(|entry| { - entry - .get_ava_single("radius_secret") - .and_then(|v| v.get_secret_str().map(str::to_string)) - }); - Ok(r) - } - Err(e) => Err(e), + // Make an event from the request + let srch = match SearchEvent::from_target_uuid_request( + ident, + target_uuid, + &idms_prox_read.qs_read, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin radius read: {:?}", e); + return Err(e); } - }); - res + }; + + trace!(?srch, "Begin event"); + + // We have to use search_ext to guarantee acs was applied. + match idms_prox_read.qs_read.search_ext(&srch) { + Ok(mut entries) => { + let r = entries + .pop() + // From the entry, turn it into the value + .and_then(|entry| { + entry + .get_ava_single("radius_secret") + .and_then(|v| v.get_secret_str().map(str::to_string)) + }); + Ok(r) + } + Err(e) => Err(e), + } } #[instrument( level = "info", - name = "radius_token_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalradiustokenread( @@ -531,46 +489,42 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!("Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!("Error resolving id to target"); + e + })?; - // Make an event from the request - let rate = match RadiusAuthTokenEvent::from_parts( - // &idms_prox_read.qs_read, - ident, - target_uuid, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin radius token read: {:?}", e); - return Err(e); - } - }; + // Make an event from the request + let rate = match RadiusAuthTokenEvent::from_parts( + // &idms_prox_read.qs_read, + ident, + target_uuid, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin radius token read: {:?}", e); + return Err(e); + } + }; - trace!(?rate, "Begin event"); + trace!(?rate, "Begin event"); - idms_prox_read.get_radiusauthtoken(&rate, ct) - }); - res + idms_prox_read.get_radiusauthtoken(&rate, ct) } #[instrument( level = "info", - name = "unix_user_token_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalunixusertokenread( @@ -582,49 +536,42 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!( - "actors::v1_read::handle", - { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_info!( - err = ?e, - "Error resolving {} as gidnumber continuing ...", - uuid_or_name - ); - e - })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_info!( + err = ?e, + "Error resolving {} as gidnumber continuing ...", + uuid_or_name + ); + e + })?; - // Make an event from the request - let rate = match UnixUserTokenEvent::from_parts(ident, target_uuid) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin unix token read: {:?}", e); - return Err(e); - } - }; - - trace!(?rate, "Begin event"); - - idms_prox_read.get_unixusertoken(&rate, ct) + // Make an event from the request + let rate = match UnixUserTokenEvent::from_parts(ident, target_uuid) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin unix token read: {:?}", e); + return Err(e); } - ); - res + }; + + trace!(?rate, "Begin event"); + + idms_prox_read.get_unixusertoken(&rate, ct) } #[instrument( level = "info", - name = "unix_group_token_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalunixgrouptokenread( @@ -635,49 +582,42 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!( - "actors::v1_read::handle", - { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_info!(err = ?e, "Error resolving as gidnumber continuing"); - e - })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_info!(err = ?e, "Error resolving as gidnumber continuing"); + e + })?; - // Make an event from the request - let rate = match UnixGroupTokenEvent::from_parts( - // &idms_prox_read.qs_read, - ident, - target_uuid, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin unix group token read: {:?}", e); - return Err(e); - } - }; - - trace!(?rate, "Begin event"); - - idms_prox_read.get_unixgrouptoken(&rate) + // Make an event from the request + let rate = match UnixGroupTokenEvent::from_parts( + // &idms_prox_read.qs_read, + ident, + target_uuid, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin unix group token read: {:?}", e); + return Err(e); } - ); - res + }; + + trace!(?rate, "Begin event"); + + idms_prox_read.get_unixgrouptoken(&rate) } #[instrument( level = "info", - name = "ssh_key_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalsshkeyread( @@ -688,62 +628,58 @@ impl QueryServerReadV1 { ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!("Error resolving id to target"); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!("Error resolving id to target"); + e + })?; - // Make an event from the request - let srch = match SearchEvent::from_target_uuid_request( - ident, - target_uuid, - &idms_prox_read.qs_read, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin ssh key read: {:?}", e); - return Err(e); - } - }; - - trace!(?srch, "Begin event"); - - match idms_prox_read.qs_read.search_ext(&srch) { - Ok(mut entries) => { - let r = entries - .pop() - // get the first entry - .and_then(|e| { - // From the entry, turn it into the value - e.get_ava_iter_sshpubkeys("ssh_publickey") - .map(|i| i.map(|s| s.to_string()).collect()) - }) - .unwrap_or_else(|| { - // No matching entry? Return none. - Vec::new() - }); - Ok(r) - } - Err(e) => Err(e), + // Make an event from the request + let srch = match SearchEvent::from_target_uuid_request( + ident, + target_uuid, + &idms_prox_read.qs_read, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin ssh key read: {:?}", e); + return Err(e); } - }); - res + }; + + trace!(?srch, "Begin event"); + + match idms_prox_read.qs_read.search_ext(&srch) { + Ok(mut entries) => { + let r = entries + .pop() + // get the first entry + .and_then(|e| { + // From the entry, turn it into the value + e.get_ava_iter_sshpubkeys("ssh_publickey") + .map(|i| i.map(|s| s.to_string()).collect()) + }) + .unwrap_or_else(|| { + // No matching entry? Return none. + Vec::new() + }); + Ok(r) + } + Err(e) => Err(e), + } } #[instrument( level = "info", - name = "ssh_key_tag_read", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalsshkeytagread( @@ -755,64 +691,60 @@ impl QueryServerReadV1 { ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_info!("Error resolving id to target"); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_info!("Error resolving id to target"); + e + })?; - // Make an event from the request - let srch = match SearchEvent::from_target_uuid_request( - ident, - target_uuid, - &idms_prox_read.qs_read, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin sshkey tag read: {:?}", e); - return Err(e); - } - }; - - trace!(?srch, "Begin event"); - - match idms_prox_read.qs_read.search_ext(&srch) { - Ok(mut entries) => { - let r = entries - .pop() - // get the first entry - .map(|e| { - // From the entry, turn it into the value - e.get_ava_set("ssh_publickey").and_then(|vs| { - // Get the one tagged value - vs.get_ssh_tag(&tag).map(str::to_string) - }) - }) - .unwrap_or_else(|| { - // No matching entry? Return none. - None - }); - Ok(r) - } - Err(e) => Err(e), + // Make an event from the request + let srch = match SearchEvent::from_target_uuid_request( + ident, + target_uuid, + &idms_prox_read.qs_read, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin sshkey tag read: {:?}", e); + return Err(e); } - }); - res + }; + + trace!(?srch, "Begin event"); + + match idms_prox_read.qs_read.search_ext(&srch) { + Ok(mut entries) => { + let r = entries + .pop() + // get the first entry + .map(|e| { + // From the entry, turn it into the value + e.get_ava_set("ssh_publickey").and_then(|vs| { + // Get the one tagged value + vs.get_ssh_tag(&tag).map(str::to_string) + }) + }) + .unwrap_or_else(|| { + // No matching entry? Return none. + None + }); + Ok(r) + } + Err(e) => Err(e), + } } #[instrument( level = "info", - name = "service_account_api_token_get", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_service_account_api_token_get( @@ -844,8 +776,7 @@ impl QueryServerReadV1 { #[instrument( level = "info", - name = "idm_account_unix_auth", - skip(self, uat, uuid_or_name, cred, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmaccountunixauth( @@ -857,7 +788,6 @@ impl QueryServerReadV1 { ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idm_auth = self.idms.auth_async().await; - // let res = spanned!("actors::v1_read::handle", { // resolve the id let ident = idm_auth .validate_and_parse_token_to_ident(uat.as_deref(), ct) @@ -891,14 +821,12 @@ impl QueryServerReadV1 { security_info!(?res, "Sending result"); - // res }); res } #[instrument( level = "info", - name = "idm_credential_status", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialstatus( @@ -910,45 +838,41 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - // Make an event from the request - let cse = match CredentialStatusEvent::from_parts( - // &idms_prox_read.qs_read, - ident, - target_uuid, - ) { - Ok(s) => s, - Err(e) => { - admin_error!(err = ?e, "Failed to begin credential status read"); - return Err(e); - } - }; + // Make an event from the request + let cse = match CredentialStatusEvent::from_parts( + // &idms_prox_read.qs_read, + ident, + target_uuid, + ) { + Ok(s) => s, + Err(e) => { + admin_error!(err = ?e, "Failed to begin credential status read"); + return Err(e); + } + }; - trace!(?cse, "Begin event"); + trace!(?cse, "Begin event"); - idms_prox_read.get_credentialstatus(&cse) - }); - res + idms_prox_read.get_credentialstatus(&cse) } #[instrument( level = "info", - name = "idm_backup_code_view", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmbackupcodeview( @@ -960,45 +884,41 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; - let target_uuid = idms_prox_read - .qs_read - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!("Error resolving id to target"); - e - })?; + let ident = idms_prox_read + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; + let target_uuid = idms_prox_read + .qs_read + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!("Error resolving id to target"); + e + })?; - // Make an event from the request - let rbce = match ReadBackupCodeEvent::from_parts( - // &idms_prox_read.qs_read, - ident, - target_uuid, - ) { - Ok(s) => s, - Err(e) => { - admin_error!("Failed to begin backup code read: {:?}", e); - return Err(e); - } - }; + // Make an event from the request + let rbce = match ReadBackupCodeEvent::from_parts( + // &idms_prox_read.qs_read, + ident, + target_uuid, + ) { + Ok(s) => s, + Err(e) => { + admin_error!("Failed to begin backup code read: {:?}", e); + return Err(e); + } + }; - trace!(?rbce, "Begin event"); + trace!(?rbce, "Begin event"); - idms_prox_read.get_backup_codes(&rbce) - }); - res + idms_prox_read.get_backup_codes(&rbce) } #[instrument( level = "info", - name = "idm_credential_update_status", - skip(self, session_token, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdatestatus( @@ -1008,29 +928,25 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_cred_update = self.idms.cred_update_transaction_async().await; - let res = spanned!("actors::v1_read::handle", { - let session_token = CredentialUpdateSessionToken { - token_enc: session_token.token, - }; + let session_token = CredentialUpdateSessionToken { + token_enc: session_token.token, + }; - idms_cred_update - .credential_update_status(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_update_status", - ); - e - }) - .map(|sta| sta.into()) - }); - res + idms_cred_update + .credential_update_status(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_update_status", + ); + e + }) + .map(|sta| sta.into()) } #[instrument( level = "info", - name = "idm_credential_update", - skip(self, session_token, scr, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdate( @@ -1041,132 +957,128 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_cred_update = self.idms.cred_update_transaction_async().await; - let res = spanned!("actors::v1_read::handle", { - let session_token = CredentialUpdateSessionToken { - token_enc: session_token.token, - }; + let session_token = CredentialUpdateSessionToken { + token_enc: session_token.token, + }; - debug!(?scr); + debug!(?scr); - match scr { - CURequest::PrimaryRemove => idms_cred_update - .credential_primary_delete(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_delete", - ); - e - }), - CURequest::Password(pw) => idms_cred_update - .credential_primary_set_password(&session_token, ct, &pw) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_set_password", - ); - e - }), - CURequest::CancelMFAReg => idms_cred_update - .credential_update_cancel_mfareg(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_update_cancel_mfareg", - ); - e - }), - CURequest::TotpGenerate => idms_cred_update - .credential_primary_init_totp(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_init_totp", - ); - e - }), - CURequest::TotpVerify(totp_chal) => idms_cred_update - .credential_primary_check_totp(&session_token, ct, totp_chal) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_check_totp", - ); - e - }), - CURequest::TotpAcceptSha1 => idms_cred_update - .credential_primary_accept_sha1_totp(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_accept_sha1_totp", - ); - e - }), - CURequest::TotpRemove => idms_cred_update - .credential_primary_remove_totp(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_remove_totp", - ); - e - }), - CURequest::BackupCodeGenerate => idms_cred_update - .credential_primary_init_backup_codes(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_init_backup_codes", - ); - e - }), - CURequest::BackupCodeRemove => idms_cred_update - .credential_primary_remove_backup_codes(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_primary_remove_backup_codes", - ); - e - }), - CURequest::PasskeyInit => idms_cred_update - .credential_passkey_init(&session_token, ct) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_passkey_init", - ); - e - }), - CURequest::PasskeyFinish(label, rpkc) => idms_cred_update - .credential_passkey_finish(&session_token, ct, label, &rpkc) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_passkey_init", - ); - e - }), - CURequest::PasskeyRemove(uuid) => idms_cred_update - .credential_passkey_remove(&session_token, ct, uuid) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin credential_passkey_init", - ); - e - }), - } - .map(|sta| sta.into()) - }); - res + match scr { + CURequest::PrimaryRemove => idms_cred_update + .credential_primary_delete(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_delete", + ); + e + }), + CURequest::Password(pw) => idms_cred_update + .credential_primary_set_password(&session_token, ct, &pw) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_set_password", + ); + e + }), + CURequest::CancelMFAReg => idms_cred_update + .credential_update_cancel_mfareg(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_update_cancel_mfareg", + ); + e + }), + CURequest::TotpGenerate => idms_cred_update + .credential_primary_init_totp(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_init_totp", + ); + e + }), + CURequest::TotpVerify(totp_chal) => idms_cred_update + .credential_primary_check_totp(&session_token, ct, totp_chal) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_check_totp", + ); + e + }), + CURequest::TotpAcceptSha1 => idms_cred_update + .credential_primary_accept_sha1_totp(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_accept_sha1_totp", + ); + e + }), + CURequest::TotpRemove => idms_cred_update + .credential_primary_remove_totp(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_remove_totp", + ); + e + }), + CURequest::BackupCodeGenerate => idms_cred_update + .credential_primary_init_backup_codes(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_init_backup_codes", + ); + e + }), + CURequest::BackupCodeRemove => idms_cred_update + .credential_primary_remove_backup_codes(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_primary_remove_backup_codes", + ); + e + }), + CURequest::PasskeyInit => idms_cred_update + .credential_passkey_init(&session_token, ct) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_passkey_init", + ); + e + }), + CURequest::PasskeyFinish(label, rpkc) => idms_cred_update + .credential_passkey_finish(&session_token, ct, label, &rpkc) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_passkey_init", + ); + e + }), + CURequest::PasskeyRemove(uuid) => idms_cred_update + .credential_passkey_remove(&session_token, ct, uuid) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin credential_passkey_init", + ); + e + }), + } + .map(|sta| sta.into()) } #[instrument( level = "info", - name = "oauth2_authorise", - skip(self, uat, auth_req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_authorise( @@ -1177,29 +1089,25 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let (ident, uat) = idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_read - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - Oauth2Error::AuthenticationRequired - })?; + let (ident, uat) = idms_prox_read + .validate_and_parse_uat(uat.as_deref(), ct) + .and_then(|uat| { + idms_prox_read + .process_uat_to_identity(&uat, ct) + .map(|ident| (ident, uat)) + }) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + Oauth2Error::AuthenticationRequired + })?; - // Now we can send to the idm server for authorisation checking. - idms_prox_read.check_oauth2_authorisation(&ident, &uat, &auth_req, ct) - }); - res + // Now we can send to the idm server for authorisation checking. + idms_prox_read.check_oauth2_authorisation(&ident, &uat, &auth_req, ct) } #[instrument( level = "info", - name = "oauth2_authorise_permit", - skip(self, uat, consent_req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_authorise_permit( @@ -1210,28 +1118,24 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let (ident, uat) = idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_read - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let (ident, uat) = idms_prox_read + .validate_and_parse_uat(uat.as_deref(), ct) + .and_then(|uat| { + idms_prox_read + .process_uat_to_identity(&uat, ct) + .map(|ident| (ident, uat)) + }) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - idms_prox_read.check_oauth2_authorise_permit(&ident, &uat, &consent_req, ct) - }); - res + idms_prox_read.check_oauth2_authorise_permit(&ident, &uat, &consent_req, ct) } #[instrument( level = "info", - name = "oauth2_authorise_reject", - skip(self, uat, consent_req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_authorise_reject( @@ -1242,28 +1146,24 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - let (ident, uat) = idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_read - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) - .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); - e - })?; + let (ident, uat) = idms_prox_read + .validate_and_parse_uat(uat.as_deref(), ct) + .and_then(|uat| { + idms_prox_read + .process_uat_to_identity(&uat, ct) + .map(|ident| (ident, uat)) + }) + .map_err(|e| { + admin_error!("Invalid identity: {:?}", e); + e + })?; - idms_prox_read.check_oauth2_authorise_reject(&ident, &uat, &consent_req, ct) - }); - res + idms_prox_read.check_oauth2_authorise_reject(&ident, &uat, &consent_req, ct) } #[instrument( level = "info", - name = "oauth2_token_exchange", - skip(self, client_authz, token_req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_token_exchange( @@ -1274,17 +1174,13 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - // Now we can send to the idm server for authorisation checking. - idms_prox_read.check_oauth2_token_exchange(client_authz.as_deref(), &token_req, ct) - }); - res + // Now we can send to the idm server for authorisation checking. + idms_prox_read.check_oauth2_token_exchange(client_authz.as_deref(), &token_req, ct) } #[instrument( level = "info", - name = "oauth2_token_introspect", - skip(self, client_authz, intr_req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_token_introspect( @@ -1295,17 +1191,13 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - // Now we can send to the idm server for introspection checking. - idms_prox_read.check_oauth2_token_introspect(&client_authz, &intr_req, ct) - }); - res + // Now we can send to the idm server for introspection checking. + idms_prox_read.check_oauth2_token_introspect(&client_authz, &intr_req, ct) } #[instrument( level = "info", - name = "oauth2_openid_userinfo", - skip(self, client_id, client_authz, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_openid_userinfo( @@ -1316,16 +1208,12 @@ impl QueryServerReadV1 { ) -> Result { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - idms_prox_read.oauth2_openid_userinfo(&client_id, &client_authz, ct) - }); - res + idms_prox_read.oauth2_openid_userinfo(&client_id, &client_authz, ct) } #[instrument( level = "info", - name = "oauth2_openid_discovery", - skip(self, client_id, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_openid_discovery( @@ -1334,16 +1222,12 @@ impl QueryServerReadV1 { eventid: Uuid, ) -> Result { let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - idms_prox_read.oauth2_openid_discovery(&client_id) - }); - res + idms_prox_read.oauth2_openid_discovery(&client_id) } #[instrument( level = "info", - name = "oauth2_openid_publickey", - skip(self, client_id, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_openid_publickey( @@ -1352,30 +1236,22 @@ impl QueryServerReadV1 { eventid: Uuid, ) -> Result { let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - idms_prox_read.oauth2_openid_publickey(&client_id) - }); - res + idms_prox_read.oauth2_openid_publickey(&client_id) } #[instrument( level = "info", - name = "get_domain_display_name", - skip(self, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn get_domain_display_name(&self, eventid: Uuid) -> String { let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - idms_prox_read.qs_read.get_domain_display_name().to_string() - }); - res + idms_prox_read.qs_read.get_domain_display_name().to_string() } #[instrument( level = "info", - name = "auth_valid", - skip(self, uat, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_auth_valid( @@ -1386,22 +1262,18 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let idms_prox_read = self.idms.proxy_read_async().await; - let res = spanned!("actors::v1_read::handle", { - idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .map(|_| ()) - .map_err(|e| { - admin_error!("Invalid token: {:?}", e); - e - }) - }); - res + idms_prox_read + .validate_and_parse_uat(uat.as_deref(), ct) + .map(|_| ()) + .map_err(|e| { + admin_error!("Invalid token: {:?}", e); + e + }) } #[instrument( level = "info", - name = "ldap_request", - skip(self, eventid, protomsg, uat) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_ldaprequest( @@ -1410,7 +1282,6 @@ impl QueryServerReadV1 { protomsg: LdapMsg, uat: Option, ) -> Option { - // let res = spanned!( "actors::v1_read::handle", { let res = match ServerOps::try_from(protomsg) { Ok(server_op) => self .ldap @@ -1428,7 +1299,6 @@ impl QueryServerReadV1 { format!("Invalid Request {:?}", &eventid).as_str(), )), }; - // }); Some(res) } } diff --git a/kanidmd/idm/src/actors/v1_write.rs b/kanidmd/idm/src/actors/v1_write.rs index 7a17cc096..ce17fedef 100644 --- a/kanidmd/idm/src/actors/v1_write.rs +++ b/kanidmd/idm/src/actors/v1_write.rs @@ -1,43 +1,35 @@ use std::iter; use std::sync::Arc; use std::time::Duration; -use tracing::{info, instrument, span, trace, Level}; -use crate::prelude::*; - -use crate::idm::credupdatesession::{ - CredentialUpdateIntentToken, CredentialUpdateSessionToken, InitCredentialUpdateEvent, - InitCredentialUpdateIntentEvent, +use kanidm_proto::v1::{ + AccountUnixExtend, CUIntentToken, CUSessionToken, CUStatus, CreateRequest, DeleteRequest, + Entry as ProtoEntry, GroupUnixExtend, Modify as ProtoModify, ModifyList as ProtoModifyList, + ModifyRequest, OperationError, }; +use time::OffsetDateTime; +use tracing::{info, instrument, span, trace, Level}; +use uuid::Uuid; use crate::event::{ CreateEvent, DeleteEvent, ModifyEvent, PurgeRecycledEvent, PurgeTombstoneEvent, ReviveRecycledEvent, }; +use crate::filter::{Filter, FilterInvalid}; +use crate::idm::credupdatesession::{ + CredentialUpdateIntentToken, CredentialUpdateSessionToken, InitCredentialUpdateEvent, + InitCredentialUpdateIntentEvent, +}; +use crate::idm::delayed::DelayedAction; use crate::idm::event::{ GeneratePasswordEvent, RegenerateRadiusSecretEvent, UnixPasswordChangeEvent, }; -use crate::modify::{Modify, ModifyInvalid, ModifyList}; -use crate::value::{PartialValue, Value}; -use kanidm_proto::v1::OperationError; - -use crate::filter::{Filter, FilterInvalid}; -use crate::idm::delayed::DelayedAction; use crate::idm::server::{IdmServer, IdmServerTransaction}; use crate::idm::serviceaccount::{DestroyApiTokenEvent, GenerateApiTokenEvent}; +use crate::modify::{Modify, ModifyInvalid, ModifyList}; +use crate::prelude::*; use crate::utils::duration_from_epoch_now; - -use kanidm_proto::v1::Entry as ProtoEntry; -use kanidm_proto::v1::Modify as ProtoModify; -use kanidm_proto::v1::ModifyList as ProtoModifyList; -use kanidm_proto::v1::{ - AccountUnixExtend, CUIntentToken, CUSessionToken, CUStatus, CreateRequest, DeleteRequest, - GroupUnixExtend, ModifyRequest, -}; - -use time::OffsetDateTime; - -use uuid::Uuid; +use crate::value::{PartialValue, Value}; pub struct QueryServerWriteV1 { _log_level: Option, @@ -63,6 +55,7 @@ impl QueryServerWriteV1 { &(*x_ptr) } + #[instrument(level = "debug", skip_all)] async fn modify_from_parts( &self, uat: Option, @@ -71,47 +64,46 @@ impl QueryServerWriteV1 { filter: Filter, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("modify_from_parts", { - let ct = duration_from_epoch_now(); + let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - let mdf = match ModifyEvent::from_parts( - ident, - target_uuid, - proto_ml, - filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err=?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_parts( + ident, + target_uuid, + proto_ml, + filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err=?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } + #[instrument(level = "debug", skip_all)] async fn modify_from_internal_parts( &self, uat: Option, @@ -120,54 +112,51 @@ impl QueryServerWriteV1 { filter: Filter, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("modify_from_internal_parts", { - let ct = duration_from_epoch_now(); + let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name) - .map_err(|e| { - admin_error!("Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name) + .map_err(|e| { + admin_error!("Error resolving id to target"); + e + })?; - let f_uuid = filter_all!(f_eq("uuid", PartialValue::new_uuid(target_uuid))); - // Add any supplemental conditions we have. - let joined_filter = Filter::join_parts_and(f_uuid, filter); + let f_uuid = filter_all!(f_eq("uuid", PartialValue::new_uuid(target_uuid))); + // Add any supplemental conditions we have. + let joined_filter = Filter::join_parts_and(f_uuid, filter); - let mdf = match ModifyEvent::from_internal_parts( - ident, - ml, - &joined_filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_internal_parts( + ident, + ml, + &joined_filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( level = "info", - name = "create", - skip(self, uat, req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_create( @@ -177,39 +166,34 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); + let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let crt = match CreateEvent::from_message(ident, &req, &idms_prox_write.qs_write) { - Ok(c) => c, - Err(e) => { - admin_warn!(err = ?e, "Failed to begin create"); - return Err(e); - } - }; + let crt = match CreateEvent::from_message(ident, &req, &idms_prox_write.qs_write) { + Ok(c) => c, + Err(e) => { + admin_warn!(err = ?e, "Failed to begin create"); + return Err(e); + } + }; - trace!(?crt, "Begin create event"); + trace!(?crt, "Begin create event"); - idms_prox_write - .qs_write - .create(&crt) - .and_then(|_| idms_prox_write.commit()) - }); - // At the end of the event we send it for logging. - res + idms_prox_write + .qs_write + .create(&crt) + .and_then(|_| idms_prox_write.commit()) } #[instrument( level = "info", - name = "modify", - skip(self, uat, req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_modify( @@ -219,37 +203,33 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let mdf = match ModifyEvent::from_message(ident, &req, &idms_prox_write.qs_write) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_message(ident, &req, &idms_prox_write.qs_write) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit()) - }); - res + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit()) } #[instrument( level = "info", - name = "delete", - skip(self, uat, req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_delete( @@ -259,36 +239,32 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let del = match DeleteEvent::from_message(ident, &req, &idms_prox_write.qs_write) { - Ok(d) => d, - Err(e) => { - admin_error!(err = ?e, "Failed to begin delete"); - return Err(e); - } - }; + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let del = match DeleteEvent::from_message(ident, &req, &idms_prox_write.qs_write) { + Ok(d) => d, + Err(e) => { + admin_error!(err = ?e, "Failed to begin delete"); + return Err(e); + } + }; - trace!(?del, "Begin delete event"); + trace!(?del, "Begin delete event"); - idms_prox_write - .qs_write - .delete(&del) - .and_then(|_| idms_prox_write.commit()) - }); - res + idms_prox_write + .qs_write + .delete(&del) + .and_then(|_| idms_prox_write.commit()) } #[instrument( level = "info", - name = "patch", - skip(self, uat, filter, update, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internalpatch( @@ -300,47 +276,38 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { // Given a protoEntry, turn this into a modification set. let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - - // Transform the ProtoEntry to a Modlist - let modlist = - ModifyList::from_patch(&update, &idms_prox_write.qs_write).map_err(|e| { - admin_error!(err = ?e, "Invalid Patch Request"); - e - })?; - - let mdf = ModifyEvent::from_internal_parts( - ident, - &modlist, - &filter, - &idms_prox_write.qs_write, - ) + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + + // Transform the ProtoEntry to a Modlist + let modlist = ModifyList::from_patch(&update, &idms_prox_write.qs_write).map_err(|e| { + admin_error!(err = ?e, "Invalid Patch Request"); + e + })?; + + let mdf = + ModifyEvent::from_internal_parts(ident, &modlist, &filter, &idms_prox_write.qs_write) + .map_err(|e| { admin_error!(err = ?e, "Failed to begin modify"); e })?; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit()) - }); - res.map(|_| ()) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit()) } #[instrument( level = "info", - name = "delete2", - skip(self, uat, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_internaldelete( @@ -350,36 +317,32 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let del = match DeleteEvent::from_parts(ident, &filter, &idms_prox_write.qs_write) { - Ok(d) => d, - Err(e) => { - admin_error!(err = ?e, "Failed to begin delete"); - return Err(e); - } - }; + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let del = match DeleteEvent::from_parts(ident, &filter, &idms_prox_write.qs_write) { + Ok(d) => d, + Err(e) => { + admin_error!(err = ?e, "Failed to begin delete"); + return Err(e); + } + }; - trace!(?del, "Begin delete event"); + trace!(?del, "Begin delete event"); - idms_prox_write - .qs_write - .delete(&del) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }); - res + idms_prox_write + .qs_write + .delete(&del) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( level = "info", - name = "revive_recycled", - skip(self, uat, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_reviverecycled( @@ -389,37 +352,32 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - let res = spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let rev = - match ReviveRecycledEvent::from_parts(ident, &filter, &idms_prox_write.qs_write) { - Ok(r) => r, - Err(e) => { - admin_error!(err = ?e, "Failed to begin revive"); - return Err(e); - } - }; + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let rev = match ReviveRecycledEvent::from_parts(ident, &filter, &idms_prox_write.qs_write) { + Ok(r) => r, + Err(e) => { + admin_error!(err = ?e, "Failed to begin revive"); + return Err(e); + } + }; - trace!(?rev, "Begin revive event"); + trace!(?rev, "Begin revive event"); - idms_prox_write - .qs_write - .revive_recycled(&rev) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }); - res + idms_prox_write + .qs_write + .revive_recycled(&rev) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( level = "info", - name = "service_account_credential_generate", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_service_account_credential_generate( @@ -430,45 +388,41 @@ impl QueryServerWriteV1 { ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - - // given the uuid_or_name, determine the target uuid. - // We can either do this by trying to parse the name or by creating a filter - // to find the entry - there are risks to both TBH ... especially when the uuid - // is also an entries name, but that they aren't the same entry. - - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; - - let gpe = GeneratePasswordEvent::from_parts(ident, target_uuid).map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin handle_service_account_credential_generate", - ); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); e })?; - idms_prox_write - .generate_account_password(&gpe) - .and_then(|r| idms_prox_write.commit().map(|_| r)) - }); - res + + // given the uuid_or_name, determine the target uuid. + // We can either do this by trying to parse the name or by creating a filter + // to find the entry - there are risks to both TBH ... especially when the uuid + // is also an entries name, but that they aren't the same entry. + + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; + + let gpe = GeneratePasswordEvent::from_parts(ident, target_uuid).map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin handle_service_account_credential_generate", + ); + e + })?; + idms_prox_write + .generate_account_password(&gpe) + .and_then(|r| idms_prox_write.commit().map(|_| r)) } #[instrument( level = "info", - name = "service_account_credential_generate", - skip(self, uat, uuid_or_name, label, expiry, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_service_account_api_token_generate( @@ -510,7 +464,6 @@ impl QueryServerWriteV1 { #[instrument( level = "info", - name = "service_account_credential_generate", skip_all, fields(uuid = ?eventid) )] @@ -551,8 +504,7 @@ impl QueryServerWriteV1 { #[instrument( level = "info", - name = "idm_credential_update", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdate( @@ -563,48 +515,44 @@ impl QueryServerWriteV1 { ) -> Result<(CUSessionToken, CUStatus), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - idms_prox_write - .init_credential_update(&InitCredentialUpdateEvent::new(ident, target_uuid), ct) - .and_then(|tok| idms_prox_write.commit().map(|_| tok)) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin init_credential_update", - ); - e - }) - .map(|(tok, sta)| { - ( - CUSessionToken { - token: tok.token_enc, - }, - sta.into(), - ) - }) - }); - res + idms_prox_write + .init_credential_update(&InitCredentialUpdateEvent::new(ident, target_uuid), ct) + .and_then(|tok| idms_prox_write.commit().map(|_| tok)) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin init_credential_update", + ); + e + }) + .map(|(tok, sta)| { + ( + CUSessionToken { + token: tok.token_enc, + }, + sta.into(), + ) + }) } #[instrument( level = "info", - name = "idm_credential_update_intent", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdateintent( @@ -616,46 +564,42 @@ impl QueryServerWriteV1 { ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - idms_prox_write - .init_credential_update_intent( - &InitCredentialUpdateIntentEvent::new(ident, target_uuid, ttl), - ct, - ) - .and_then(|tok| idms_prox_write.commit().map(|_| tok)) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin init_credential_update_intent", - ); - e - }) - .map(|tok| CUIntentToken { - token: tok.intent_id, - }) - }); - res + idms_prox_write + .init_credential_update_intent( + &InitCredentialUpdateIntentEvent::new(ident, target_uuid, ttl), + ct, + ) + .and_then(|tok| idms_prox_write.commit().map(|_| tok)) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin init_credential_update_intent", + ); + e + }) + .map(|tok| CUIntentToken { + token: tok.intent_id, + }) } #[instrument( level = "info", - name = "idm_credential_exchange_intent", - skip(self, intent_token, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialexchangeintent( @@ -665,37 +609,33 @@ impl QueryServerWriteV1 { ) -> Result<(CUSessionToken, CUStatus), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let intent_token = CredentialUpdateIntentToken { - intent_id: intent_token.token, - }; - // TODO: this is throwing a 500 error when a session is already in use, that seems bad? - idms_prox_write - .exchange_intent_credential_update(intent_token, ct) - .and_then(|tok| idms_prox_write.commit().map(|_| tok)) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin exchange_intent_credential_update", - ); - e - }) - .map(|(tok, sta)| { - ( - CUSessionToken { - token: tok.token_enc, - }, - sta.into(), - ) - }) - }); - res + let intent_token = CredentialUpdateIntentToken { + intent_id: intent_token.token, + }; + // TODO: this is throwing a 500 error when a session is already in use, that seems bad? + idms_prox_write + .exchange_intent_credential_update(intent_token, ct) + .and_then(|tok| idms_prox_write.commit().map(|_| tok)) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin exchange_intent_credential_update", + ); + e + }) + .map(|(tok, sta)| { + ( + CUSessionToken { + token: tok.token_enc, + }, + sta.into(), + ) + }) } #[instrument( level = "info", - name = "idm_credential_update_commit", - skip(self, session_token, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdatecommit( @@ -705,29 +645,25 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let session_token = CredentialUpdateSessionToken { - token_enc: session_token.token, - }; + let session_token = CredentialUpdateSessionToken { + token_enc: session_token.token, + }; - idms_prox_write - .commit_credential_update(&session_token, ct) - .and_then(|tok| idms_prox_write.commit().map(|_| tok)) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin commit_credential_update", - ); - e - }) - }); - res + idms_prox_write + .commit_credential_update(&session_token, ct) + .and_then(|tok| idms_prox_write.commit().map(|_| tok)) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin commit_credential_update", + ); + e + }) } #[instrument( level = "info", - name = "idm_credential_update_cancel", - skip(self, session_token, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmcredentialupdatecancel( @@ -737,29 +673,25 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let session_token = CredentialUpdateSessionToken { - token_enc: session_token.token, - }; + let session_token = CredentialUpdateSessionToken { + token_enc: session_token.token, + }; - idms_prox_write - .cancel_credential_update(&session_token, ct) - .and_then(|tok| idms_prox_write.commit().map(|_| tok)) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin commit_credential_cancel", - ); - e - }) - }); - res + idms_prox_write + .cancel_credential_update(&session_token, ct) + .and_then(|tok| idms_prox_write.commit().map(|_| tok)) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin commit_credential_cancel", + ); + e + }) } #[instrument( level = "info", - name = "handle_service_account_into_person", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_service_account_into_person( @@ -770,32 +702,28 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - idms_prox_write - .service_account_into_person(&ident, target_uuid) - .and_then(|_| idms_prox_write.commit()) - }); - res + idms_prox_write + .service_account_into_person(&ident, target_uuid) + .and_then(|_| idms_prox_write.commit()) } #[instrument( level = "info", - name = "regenerate_radius_secret", - skip(self, uat, uuid_or_name, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_regenerateradius( @@ -806,49 +734,42 @@ impl QueryServerWriteV1 { ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!( - "actors::v1_write::handle", - { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - let rrse = RegenerateRadiusSecretEvent::from_parts( - // &idms_prox_write.qs_write, - ident, - target_uuid, - ) - .map_err(|e| { - admin_error!( - err = ?e, - "Failed to begin idm_account_regenerate_radius", - ); - e - })?; + let rrse = RegenerateRadiusSecretEvent::from_parts( + // &idms_prox_write.qs_write, + ident, + target_uuid, + ) + .map_err(|e| { + admin_error!( + err = ?e, + "Failed to begin idm_account_regenerate_radius", + ); + e + })?; - idms_prox_write - .regenerate_radius_secret(&rrse) - .and_then(|r| idms_prox_write.commit().map(|_| r)) - } - ); - res + idms_prox_write + .regenerate_radius_secret(&rrse) + .and_then(|r| idms_prox_write.commit().map(|_| r)) } #[instrument( level = "info", - name = "purge_attribute", - skip(self, uat, uuid_or_name, attr, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_purgeattribute( @@ -861,48 +782,45 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let idms_prox_write = self.idms.proxy_write_async(ct).await; - spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - let mdf = match ModifyEvent::from_target_uuid_attr_purge( - ident, - target_uuid, - &attr, - filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_target_uuid_attr_purge( + ident, + target_uuid, + &attr, + filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( level = "info", - name = "remove_attribute_values", - skip(self, uat, uuid_or_name, attr, values, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_removeattributevalues( @@ -915,50 +833,48 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("actors::v1_write::handle", { - let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - let target_uuid = idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving id to target"); - e - })?; + let ct = duration_from_epoch_now(); + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; + let target_uuid = idms_prox_write + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving id to target"); + e + })?; - let proto_ml = ProtoModifyList::new_list( - values - .into_iter() - .map(|v| ProtoModify::Removed(attr.clone(), v)) - .collect(), - ); + let proto_ml = ProtoModifyList::new_list( + values + .into_iter() + .map(|v| ProtoModify::Removed(attr.clone(), v)) + .collect(), + ); - let mdf = match ModifyEvent::from_parts( - ident, - target_uuid, - &proto_ml, - filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_parts( + ident, + target_uuid, + &proto_ml, + filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( @@ -1121,8 +1037,7 @@ impl QueryServerWriteV1 { #[instrument( level = "info", - name = "idm_account_unix_set_cred", - skip(self, uat, uuid_or_name, cred, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmaccountunixsetcred( @@ -1134,46 +1049,42 @@ impl QueryServerWriteV1 { ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - let res = spanned!("actors::v1_write::handle", { - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; - - let target_uuid = Uuid::parse_str(uuid_or_name.as_str()).or_else(|_| { - idms_prox_write - .qs_write - .name_to_uuid(uuid_or_name.as_str()) - .map_err(|e| { - admin_info!("Error resolving as gidnumber continuing ..."); - e - }) - })?; - - let upce = UnixPasswordChangeEvent::from_parts( - // &idms_prox_write.qs_write, - ident, - target_uuid, - cred, - ) + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) .map_err(|e| { - admin_error!(err = ?e, "Failed to begin UnixPasswordChangeEvent"); + admin_error!(err = ?e, "Invalid identity"); e })?; + + let target_uuid = Uuid::parse_str(uuid_or_name.as_str()).or_else(|_| { idms_prox_write - .set_unix_account_password(&upce) - .and_then(|_| idms_prox_write.commit()) - .map(|_| ()) - }); - res + .qs_write + .name_to_uuid(uuid_or_name.as_str()) + .map_err(|e| { + admin_info!("Error resolving as gidnumber continuing ..."); + e + }) + })?; + + let upce = UnixPasswordChangeEvent::from_parts( + // &idms_prox_write.qs_write, + ident, + target_uuid, + cred, + ) + .map_err(|e| { + admin_error!(err = ?e, "Failed to begin UnixPasswordChangeEvent"); + e + })?; + idms_prox_write + .set_unix_account_password(&upce) + .and_then(|_| idms_prox_write.commit()) + .map(|_| ()) } #[instrument( level = "info", - name = "oauth2_scopemap_create", - skip(self, uat, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_scopemap_create( @@ -1187,61 +1098,54 @@ impl QueryServerWriteV1 { // Because this is from internal, we can generate a real modlist, rather // than relying on the proto ones. let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("handle_oauth2_scopemap_create", { - let ct = duration_from_epoch_now(); + let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let group_uuid = idms_prox_write - .qs_write - .name_to_uuid(group.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving group name to target"); - e - })?; + let group_uuid = idms_prox_write + .qs_write + .name_to_uuid(group.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving group name to target"); + e + })?; - let ml = ModifyList::new_append( - "oauth2_rs_scope_map", - Value::new_oauthscopemap(group_uuid, scopes.into_iter().collect()).ok_or_else( - || { - OperationError::InvalidAttribute( - "Invalid Oauth Scope Map syntax".to_string(), - ) - }, - )?, - ); + let ml = ModifyList::new_append( + "oauth2_rs_scope_map", + Value::new_oauthscopemap(group_uuid, scopes.into_iter().collect()).ok_or_else( + || OperationError::InvalidAttribute("Invalid Oauth Scope Map syntax".to_string()), + )?, + ); - let mdf = match ModifyEvent::from_internal_parts( - ident, - &ml, - &filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_internal_parts( + ident, + &ml, + &filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } #[instrument( level = "info", - name = "oauth2_scopemap_delete", - skip(self, uat, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_oauth2_scopemap_delete( @@ -1252,88 +1156,80 @@ impl QueryServerWriteV1 { eventid: Uuid, ) -> Result<(), OperationError> { let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("handle_oauth2_scopemap_create", { - let ct = duration_from_epoch_now(); + let ct = duration_from_epoch_now(); - let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity"); - e - })?; + let ident = idms_prox_write + .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e + })?; - let group_uuid = idms_prox_write - .qs_write - .name_to_uuid(group.as_str()) - .map_err(|e| { - admin_error!(err = ?e, "Error resolving group name to target"); - e - })?; + let group_uuid = idms_prox_write + .qs_write + .name_to_uuid(group.as_str()) + .map_err(|e| { + admin_error!(err = ?e, "Error resolving group name to target"); + e + })?; - let ml = ModifyList::new_remove("oauth2_rs_scope_map", PartialValue::Refer(group_uuid)); + let ml = ModifyList::new_remove("oauth2_rs_scope_map", PartialValue::Refer(group_uuid)); - let mdf = match ModifyEvent::from_internal_parts( - ident, - &ml, - &filter, - &idms_prox_write.qs_write, - ) { - Ok(m) => m, - Err(e) => { - admin_error!(err = ?e, "Failed to begin modify"); - return Err(e); - } - }; + let mdf = match ModifyEvent::from_internal_parts( + ident, + &ml, + &filter, + &idms_prox_write.qs_write, + ) { + Ok(m) => m, + Err(e) => { + admin_error!(err = ?e, "Failed to begin modify"); + return Err(e); + } + }; - trace!(?mdf, "Begin modify event"); + trace!(?mdf, "Begin modify event"); - idms_prox_write - .qs_write - .modify(&mdf) - .and_then(|_| idms_prox_write.commit().map(|_| ())) - }) + idms_prox_write + .qs_write + .modify(&mdf) + .and_then(|_| idms_prox_write.commit().map(|_| ())) } // ===== These below are internal only event types. ===== #[instrument( level = "info", - name = "purge_tombstone_event", - skip(self, msg) + skip_all, fields(uuid = ?msg.eventid) )] pub(crate) async fn handle_purgetombstoneevent(&self, msg: PurgeTombstoneEvent) { trace!(?msg, "Begin purge tombstone event"); let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("actors::v1_write::handle", { - let res = idms_prox_write - .qs_write - .purge_tombstones() - .and_then(|_| idms_prox_write.commit()); - admin_info!(?res, "Purge tombstones result"); - #[allow(clippy::expect_used)] - res.expect("Invalid Server State"); - }); + let res = idms_prox_write + .qs_write + .purge_tombstones() + .and_then(|_| idms_prox_write.commit()); + admin_info!(?res, "Purge tombstones result"); + #[allow(clippy::expect_used)] + res.expect("Invalid Server State"); } #[instrument( level = "info", - name = "purge_recycled_event", - skip(self, msg) + skip_all, fields(uuid = ?msg.eventid) )] pub(crate) async fn handle_purgerecycledevent(&self, msg: PurgeRecycledEvent) { trace!(?msg, "Begin purge recycled event"); let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await; - spanned!("actors::v1_write::handle", { - let res = idms_prox_write - .qs_write - .purge_recycled() - .and_then(|_| idms_prox_write.commit()); - admin_info!(?res, "Purge recycled result"); - #[allow(clippy::expect_used)] - res.expect("Invalid Server State"); - }); + let res = idms_prox_write + .qs_write + .purge_recycled() + .and_then(|_| idms_prox_write.commit()); + admin_info!(?res, "Purge recycled result"); + #[allow(clippy::expect_used)] + res.expect("Invalid Server State"); } pub(crate) async fn handle_delayedaction(&self, da: DelayedAction) { @@ -1344,13 +1240,11 @@ impl QueryServerWriteV1 { trace!("Begin delayed action ..."); let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write_async(ct).await; - spanned!("actors::v1_write::handle", { - if let Err(res) = idms_prox_write - .process_delayedaction(da) - .and_then(|_| idms_prox_write.commit()) - { - admin_info!(?res, "delayed action error"); - } - }); + if let Err(res) = idms_prox_write + .process_delayedaction(da) + .and_then(|_| idms_prox_write.commit()) + { + admin_info!(?res, "delayed action error"); + } } } diff --git a/kanidmd/idm/src/audit.rs b/kanidmd/idm/src/audit.rs index 89435428e..ee7f1661b 100644 --- a/kanidmd/idm/src/audit.rs +++ b/kanidmd/idm/src/audit.rs @@ -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; diff --git a/kanidmd/idm/src/be/dbentry.rs b/kanidmd/idm/src/be/dbentry.rs index ec438bd50..f29c3440c 100644 --- a/kanidmd/idm/src/be/dbentry.rs +++ b/kanidmd/idm/src/be/dbentry.rs @@ -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>, diff --git a/kanidmd/idm/src/be/dbvalue.rs b/kanidmd/idm/src/be/dbvalue.rs index 76f8eba9d..c2c681750 100644 --- a/kanidmd/idm/src/be/dbvalue.rs +++ b/kanidmd/idm/src/be/dbvalue.rs @@ -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 } diff --git a/kanidmd/idm/src/be/idl_arc_sqlite.rs b/kanidmd/idm/src/be/idl_arc_sqlite.rs index 6d71049d1..1483c3683 100644 --- a/kanidmd/idm/src/be/idl_arc_sqlite.rs +++ b/kanidmd/idm/src/be/idl_arc_sqlite.rs @@ -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> = Vec::new(); - match $idl { - IdList::Partial(idli) | IdList::PartialThreshold(idli) | IdList::Indexed(idli) => { - let mut nidl = IDLBitRange::new(); + let mut result: Vec> = 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_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_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", { - 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>, { - 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, { - 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, ) -> 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) -> 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) -> 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) -> 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> { diff --git a/kanidmd/idm/src/be/idl_sqlite.rs b/kanidmd/idm/src/be/idl_sqlite.rs index bc264cb5a..ecceb9f17 100644 --- a/kanidmd/idm/src/be/idl_sqlite.rs +++ b/kanidmd/idm/src/be/idl_sqlite.rs @@ -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; fn get_identry(&self, idl: &IdList) -> Result>, 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, OperationError> { @@ -220,112 +216,104 @@ pub trait IdlSqliteTransaction { itype: IndexType, idx_key: &str, ) -> Result, 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> = 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> = 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, 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 = 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 = 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, 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> = 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> = 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 = match spn_raw { - Some(d) => { - let dbv: DbIdentSpn = - serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?; + let spn: Option = 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, 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 = 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 = 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, OperationError> { @@ -422,8 +410,8 @@ pub trait IdlSqliteTransaction { }) } + #[instrument(level = "debug", name = "idl_sqlite::get_allids", skip_all)] fn get_allids(&self) -> Result { - 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 { @@ -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 - 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 + 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 { - 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 { diff --git a/kanidmd/idm/src/be/idxkey.rs b/kanidmd/idm/src/be/idxkey.rs index b27603103..3cdb4a9f0 100644 --- a/kanidmd/idm/src/be/idxkey.rs +++ b/kanidmd/idm/src/be/idxkey.rs @@ -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 diff --git a/kanidmd/idm/src/be/mod.rs b/kanidmd/idm/src/be/mod.rs index 680e0d544..0059697b9 100644 --- a/kanidmd/idm/src/be/mod.rs +++ b/kanidmd/idm/src/be/mod.rs @@ -4,35 +4,32 @@ //! is to persist content safely to disk, load that content, and execute queries //! utilising indexes in the most effective way possible. -use std::fs; - -use crate::prelude::*; -use crate::value::IndexType; -use hashbrown::HashMap as Map; -use hashbrown::HashSet; use std::cell::UnsafeCell; +use std::fs; +use std::ops::DerefMut; use std::sync::Arc; +use std::time::Duration; + +use concread::cowcell::*; +use hashbrown::{HashMap as Map, HashSet}; +use idlset::v2::IDLBitRange; +use idlset::AndNot; +use kanidm_proto::v1::{ConsistencyError, OperationError}; +use smartstring::alias::String as AttrString; use tracing::{trace, trace_span}; +use uuid::Uuid; use crate::be::dbentry::{DbBackup, DbEntry}; use crate::entry::{Entry, EntryCommitted, EntryNew, EntrySealed}; use crate::filter::{Filter, FilterPlan, FilterResolved, FilterValidResolved}; use crate::identity::Limits; -use crate::value::Value; -use concread::cowcell::*; -use idlset::v2::IDLBitRange; -use idlset::AndNot; -use kanidm_proto::v1::{ConsistencyError, OperationError}; -use smartstring::alias::String as AttrString; -use std::ops::DerefMut; -use std::time::Duration; -use uuid::Uuid; - +use crate::prelude::*; use crate::repl::cid::Cid; use crate::repl::ruv::{ ReplicationUpdateVector, ReplicationUpdateVectorReadTransaction, ReplicationUpdateVectorTransaction, ReplicationUpdateVectorWriteTransaction, }; +use crate::value::{IndexType, Value}; pub mod dbentry; pub mod dbvalue; @@ -41,12 +38,10 @@ mod idl_sqlite; pub(crate) mod idxkey; pub(crate) use self::idxkey::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxSlope}; - use crate::be::idl_arc_sqlite::{ IdlArcSqlite, IdlArcSqliteReadTransaction, IdlArcSqliteTransaction, IdlArcSqliteWriteTransaction, }; - // Re-export this pub use crate::be::idl_sqlite::FsType; @@ -175,6 +170,7 @@ pub trait BackendTransaction { /// Recursively apply a filter, transforming into IdList's on the way. This builds a query /// execution log, so that it can be examined how an operation proceeded. #[allow(clippy::cognitive_complexity)] + #[instrument(level = "debug", name = "be::filter2idl", skip_all)] fn filter2idl( &self, filt: &FilterResolved, @@ -534,6 +530,7 @@ pub trait BackendTransaction { }) } + #[instrument(level = "debug", name = "be::search", skip_all)] fn search( &self, erl: &Limits, @@ -543,165 +540,150 @@ pub trait BackendTransaction { // Unlike DS, even if we don't get the index back, we can just pass // to the in-memory filter test and be done. - spanned!("be::search", { - filter_trace!(?filt, "filter optimized"); + debug!(filter_optimised = ?filt); - let (idl, fplan) = trace_span!("be::search -> filter2idl").in_scope(|| { - spanned!("be::search -> filter2idl", { - self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD) - }) - })?; + let (idl, fplan) = trace_span!("be::search -> filter2idl") + .in_scope(|| self.filter2idl(filt.to_inner(), FILTER_SEARCH_TEST_THRESHOLD))?; - filter_trace!(?fplan, "filter executed plan"); + debug!(filter_executed_plan = ?fplan); - match &idl { - IdList::AllIds => { - if !erl.unindexed_allow { - admin_error!( - "filter (search) is fully unindexed, and not allowed by resource limits" - ); - return Err(OperationError::ResourceLimit); - } + match &idl { + IdList::AllIds => { + if !erl.unindexed_allow { + admin_error!( + "filter (search) is fully unindexed, and not allowed by resource limits" + ); + return Err(OperationError::ResourceLimit); } - IdList::Partial(idl_br) => { - // if idl_br.len() > erl.search_max_filter_test { - if !idl_br.below_threshold(erl.search_max_filter_test) { - admin_error!("filter (search) is partial indexed and greater than search_max_filter_test allowed by resource limits"); - return Err(OperationError::ResourceLimit); - } - } - IdList::PartialThreshold(_) => { - // Since we opted for this, this is not the fault - // of the user and we should not penalise them by limiting on partial. - } - IdList::Indexed(idl_br) => { - // We know this is resolved here, so we can attempt the limit - // check. This has to fold the whole index, but you know, class=pres is - // indexed ... - // if idl_br.len() > erl.search_max_results { - if !idl_br.below_threshold(erl.search_max_results) { - admin_error!("filter (search) is indexed and greater than search_max_results allowed by resource limits"); - return Err(OperationError::ResourceLimit); - } - } - }; - - let entries = self.get_idlayer().get_identry(&idl).map_err(|e| { - admin_error!(?e, "get_identry failed"); - e - })?; - - let entries_filtered = match idl { - IdList::AllIds => trace_span!("be::search").in_scope(|| { - spanned!("be::search", { - entries - .into_iter() - .filter(|e| e.entry_match_no_index(filt)) - .collect() - }) - }), - IdList::Partial(_) => { - trace_span!("be::search").in_scope(|| { - entries - .into_iter() - .filter(|e| e.entry_match_no_index(filt)) - .collect() - }) - } - IdList::PartialThreshold(_) => trace_span!("be::search") - .in_scope(|| { - spanned!("be::search", { - 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").in_scope(|| { + entries + .into_iter() + .filter(|e| e.entry_match_no_index(filt)) + .collect() + }), + IdList::Partial(_) => trace_span!("be::search").in_scope(|| { + entries + .into_iter() + .filter(|e| e.entry_match_no_index(filt)) + .collect() + }), + IdList::PartialThreshold(_) => trace_span!("be::search") + .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, ) -> Result { - 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").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> { @@ -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>, ) -> Result>, 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], 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 { - 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) -> 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() }; diff --git a/kanidmd/idm/src/config.rs b/kanidmd/idm/src/config.rs index c52035727..99c02e1d6 100644 --- a/kanidmd/idm/src/config.rs +++ b/kanidmd/idm/src/config.rs @@ -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 { diff --git a/kanidmd/idm/src/credential/mod.rs b/kanidmd/idm/src/credential/mod.rs index 386feab81..b84d45425 100644 --- a/kanidmd/idm/src/credential/mod.rs +++ b/kanidmd/idm/src/credential/mod.rs @@ -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) -> 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() { diff --git a/kanidmd/idm/src/credential/policy.rs b/kanidmd/idm/src/credential/policy.rs index 51bfa58fc..c0d34f00e 100644 --- a/kanidmd/idm/src/credential/policy.rs +++ b/kanidmd/idm/src/credential/policy.rs @@ -1,6 +1,7 @@ -use super::Password; use std::time::Duration; +use super::Password; + const PBKDF2_MIN_NIST_COST: u64 = 10000; #[derive(Debug)] diff --git a/kanidmd/idm/src/credential/totp.rs b/kanidmd/idm/src/credential/totp.rs index f25f02997..e42d2b50a 100644 --- a/kanidmd/idm/src/credential/totp.rs +++ b/kanidmd/idm/src/credential/totp.rs @@ -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); diff --git a/kanidmd/idm/src/crypto.rs b/kanidmd/idm/src/crypto.rs index 419d472af..5bd96da7f 100644 --- a/kanidmd/idm/src/crypto.rs +++ b/kanidmd/idm/src/crypto.rs @@ -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, ErrorStack> { diff --git a/kanidmd/idm/src/entry.rs b/kanidmd/idm/src/entry.rs index 2c326d036..d6a5243d2 100644 --- a/kanidmd/idm/src/entry.rs +++ b/kanidmd/idm/src/entry.rs @@ -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: VALID, state: STATE, @@ -323,29 +317,28 @@ impl Entry { /// 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 { - 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 { #[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() { diff --git a/kanidmd/idm/src/event.rs b/kanidmd/idm/src/event.rs index 3ad205d8d..c059e3618 100644 --- a/kanidmd/idm/src/event.rs +++ b/kanidmd/idm/src/event.rs @@ -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 { diff --git a/kanidmd/idm/src/filter.rs b/kanidmd/idm/src/filter.rs index 85eafbd92..68b4d3961 100644 --- a/kanidmd/idm/src/filter.rs +++ b/kanidmd/idm/src/filter.rs @@ -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 { // 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 { - 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 { - 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 { - 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. diff --git a/kanidmd/idm/src/identity.rs b/kanidmd/idm/src/identity.rs index 5fed6db8a..8d9098b97 100644 --- a/kanidmd/idm/src/identity.rs +++ b/kanidmd/idm/src/identity.rs @@ -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 diff --git a/kanidmd/idm/src/idm/account.rs b/kanidmd/idm/src/idm/account.rs index 0bc432354..d8084d85e 100644 --- a/kanidmd/idm/src/idm/account.rs +++ b/kanidmd/idm/src/idm/account.rs @@ -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, qs: &mut QueryServerReadTransaction, ) -> Result { - 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, qs: &mut QueryServerWriteTransaction, ) -> Result { - 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, qs: &mut QueryServerReadTransaction, ) -> Result { - 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( diff --git a/kanidmd/idm/src/idm/authsession.rs b/kanidmd/idm/src/idm/authsession.rs index 915449e12..1b05d63ce 100644 --- a/kanidmd/idm/src/idm/authsession.rs +++ b/kanidmd/idm/src/idm/authsession.rs @@ -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 { let mut s = HashSet::new(); diff --git a/kanidmd/idm/src/idm/credupdatesession.rs b/kanidmd/idm/src/idm/credupdatesession.rs index deac3e93c..63a789f48 100644 --- a/kanidmd/idm/src/idm/credupdatesession.rs +++ b/kanidmd/idm/src/idm/credupdatesession.rs @@ -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, passkeys: Vec, @@ -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 { - spanned!("idm::server::credupdatesession", { - 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", { - 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"); diff --git a/kanidmd/idm/src/idm/event.rs b/kanidmd/idm/src/idm/event.rs index 082c1b234..523311d63 100644 --- a/kanidmd/idm/src/idm/event.rs +++ b/kanidmd/idm/src/idm/event.rs @@ -1,6 +1,7 @@ -use crate::prelude::*; use kanidm_proto::v1::OperationError; +use crate::prelude::*; + #[cfg(test)] pub(crate) struct PasswordChangeEvent { pub ident: Identity, diff --git a/kanidmd/idm/src/idm/group.rs b/kanidmd/idm/src/idm/group.rs index 86c324648..b90f33d2e 100644 --- a/kanidmd/idm/src/idm/group.rs +++ b/kanidmd/idm/src/idm/group.rs @@ -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"); diff --git a/kanidmd/idm/src/idm/mod.rs b/kanidmd/idm/src/idm/mod.rs index 36f4f52e9..76cb1ede8 100644 --- a/kanidmd/idm/src/idm/mod.rs +++ b/kanidmd/idm/src/idm/mod.rs @@ -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), Continue(Vec), diff --git a/kanidmd/idm/src/idm/oauth2.rs b/kanidmd/idm/src/idm/oauth2.rs index 84100d18a..7387902dc 100644 --- a/kanidmd/idm/src/idm/oauth2.rs +++ b/kanidmd/idm/src/idm/oauth2.rs @@ -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; diff --git a/kanidmd/idm/src/idm/radius.rs b/kanidmd/idm/src/idm/radius.rs index 9e064e3e0..6ea3eb794 100644 --- a/kanidmd/idm/src/idm/radius.rs +++ b/kanidmd/idm/src/idm/radius.rs @@ -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"); diff --git a/kanidmd/idm/src/idm/server.rs b/kanidmd/idm/src/idm/server.rs index 187189be1..c40bf598b 100644 --- a/kanidmd/idm/src/idm/server.rs +++ b/kanidmd/idm/src/idm/server.rs @@ -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>; type CredSoftLockMutex = Arc>; @@ -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 -> 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 "; diff --git a/kanidmd/idm/src/idm/serviceaccount.rs b/kanidmd/idm/src/idm/serviceaccount.rs index 18532df8f..67894bbb6 100644 --- a/kanidmd/idm/src/idm/serviceaccount.rs +++ b/kanidmd/idm/src/idm/serviceaccount.rs @@ -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, // qs: &mut QueryServerWriteTransaction, ) -> Result { - 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] diff --git a/kanidmd/idm/src/idm/unix.rs b/kanidmd/idm/src/idm/unix.rs index bc5831cd5..cb4bcb02c 100644 --- a/kanidmd/idm/src/idm/unix.rs +++ b/kanidmd/idm/src/idm/unix.rs @@ -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 { diff --git a/kanidmd/idm/src/interval.rs b/kanidmd/idm/src/interval.rs index db8737cd6..5a7f37329 100644 --- a/kanidmd/idm/src/interval.rs +++ b/kanidmd/idm/src/interval.rs @@ -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 { diff --git a/kanidmd/idm/src/ldap.rs b/kanidmd/idm/src/ldap.rs index f848bcd23..843977673 100644 --- a/kanidmd/idm/src/ldap.rs +++ b/kanidmd/idm/src/ldap.rs @@ -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", { - // 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", { - 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", { - let lres: Result, _> = 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, _> = 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😍"; diff --git a/kanidmd/idm/src/lib.rs b/kanidmd/idm/src/lib.rs index 1fe22dd86..3112e9ee8 100644 --- a/kanidmd/idm/src/lib.rs +++ b/kanidmd/idm/src/lib.rs @@ -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, - }; } diff --git a/kanidmd/idm/src/macros.rs b/kanidmd/idm/src/macros.rs index 88d16bccf..b497ddd8c 100644 --- a/kanidmd/idm/src/macros.rs +++ b/kanidmd/idm/src/macros.rs @@ -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 = 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); }}; } diff --git a/kanidmd/idm/src/modify.rs b/kanidmd/idm/src/modify.rs index 189be742f..c1fba5005 100644 --- a/kanidmd/idm/src/modify.rs +++ b/kanidmd/idm/src/modify.rs @@ -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 { } impl<'a> IntoIterator for &'a ModifyList { - type Item = &'a Modify; type IntoIter = slice::Iter<'a, Modify>; + type Item = &'a Modify; fn into_iter(self) -> Self::IntoIter { self.mods.iter() diff --git a/kanidmd/idm/src/plugins/attrunique.rs b/kanidmd/idm/src/plugins/attrunique.rs index edcc139d3..d74bc4891 100644 --- a/kanidmd/idm/src/plugins/attrunique.rs +++ b/kanidmd/idm/src/plugins/attrunique.rs @@ -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() { diff --git a/kanidmd/idm/src/plugins/base.rs b/kanidmd/idm/src/plugins/base.rs index 12ba64f7f..a60ced3c2 100644 --- a/kanidmd/idm/src/plugins/base.rs +++ b/kanidmd/idm/src/plugins/base.rs @@ -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": [ diff --git a/kanidmd/idm/src/plugins/domain.rs b/kanidmd/idm/src/plugins/domain.rs index eca3c41bb..1bd1774da 100644 --- a/kanidmd/idm/src/plugins/domain.rs +++ b/kanidmd/idm/src/plugins/domain.rs @@ -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); diff --git a/kanidmd/idm/src/plugins/dyngroup.rs b/kanidmd/idm/src/plugins/dyngroup.rs index c890216ee..d4956fea2 100644 --- a/kanidmd/idm/src/plugins/dyngroup.rs +++ b/kanidmd/idm/src/plugins/dyngroup.rs @@ -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] diff --git a/kanidmd/idm/src/plugins/gidnumber.rs b/kanidmd/idm/src/plugins/gidnumber.rs index b491f7c29..b81dbc073 100644 --- a/kanidmd/idm/src/plugins/gidnumber.rs +++ b/kanidmd/idm/src/plugins/gidnumber.rs @@ -1,11 +1,12 @@ // A plugin that generates gid numbers on types that require them for posix // support. +use std::iter::once; + use crate::event::{CreateEvent, ModifyEvent}; use crate::plugins::Plugin; use crate::prelude::*; use crate::utils::uuid_to_gid_u32; -use std::iter::once; /// Systemd dynamic units allocate between 61184–65519, most distros allocate /// system uids from 0 - 1000, and many others give user ids between 1000 to diff --git a/kanidmd/idm/src/plugins/jwskeygen.rs b/kanidmd/idm/src/plugins/jwskeygen.rs index b5974024e..6b9ed4056 100644 --- a/kanidmd/idm/src/plugins/jwskeygen.rs +++ b/kanidmd/idm/src/plugins/jwskeygen.rs @@ -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 = diff --git a/kanidmd/idm/src/plugins/memberof.rs b/kanidmd/idm/src/plugins/memberof.rs index ab1c37548..2485ea754 100644 --- a/kanidmd/idm/src/plugins/memberof.rs +++ b/kanidmd/idm/src/plugins/memberof.rs @@ -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"); diff --git a/kanidmd/idm/src/plugins/mod.rs b/kanidmd/idm/src/plugins/mod.rs index d49197430..f38597ca4 100644 --- a/kanidmd/idm/src/plugins/mod.rs +++ b/kanidmd/idm/src/plugins/mod.rs @@ -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>, 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], 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], 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>, 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>], cand: &[Entry], 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>, 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], 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>, ) { - 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); } } diff --git a/kanidmd/idm/src/plugins/password_import.rs b/kanidmd/idm/src/plugins/password_import.rs index cd1f31dad..50a1cd2bd 100644 --- a/kanidmd/idm/src/plugins/password_import.rs +++ b/kanidmd/idm/src/plugins/password_import.rs @@ -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 {} diff --git a/kanidmd/idm/src/plugins/protected.rs b/kanidmd/idm/src/plugins/protected.rs index edddc875c..53d028346 100644 --- a/kanidmd/idm/src/plugins/protected.rs +++ b/kanidmd/idm/src/plugins/protected.rs @@ -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 {} diff --git a/kanidmd/idm/src/plugins/refint.rs b/kanidmd/idm/src/plugins/refint.rs index be6730de4..f3d251589 100644 --- a/kanidmd/idm/src/plugins/refint.rs +++ b/kanidmd/idm/src/plugins/refint.rs @@ -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() { diff --git a/kanidmd/idm/src/plugins/spn.rs b/kanidmd/idm/src/plugins/spn.rs index b553503b5..70b9ad915 100644 --- a/kanidmd/idm/src/plugins/spn.rs +++ b/kanidmd/idm/src/plugins/spn.rs @@ -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 {} diff --git a/kanidmd/idm/src/repl/cid.rs b/kanidmd/idm/src/repl/cid.rs index 503d0f053..65f675903 100644 --- a/kanidmd/idm/src/repl/cid.rs +++ b/kanidmd/idm/src/repl/cid.rs @@ -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 diff --git a/kanidmd/idm/src/repl/entry.rs b/kanidmd/idm/src/repl/entry.rs index e2dc5e057..4eb2e3a88 100644 --- a/kanidmd/idm/src/repl/entry.rs +++ b/kanidmd/idm/src/repl/entry.rs @@ -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() { diff --git a/kanidmd/idm/src/repl/ruv.rs b/kanidmd/idm/src/repl/ruv.rs index e07064ab9..f02ad3efc 100644 --- a/kanidmd/idm/src/repl/ruv.rs +++ b/kanidmd/idm/src/repl/ruv.rs @@ -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 diff --git a/kanidmd/idm/src/schema.rs b/kanidmd/idm/src/schema.rs index c3924c763..8f027df5a 100644 --- a/kanidmd/idm/src/schema.rs +++ b/kanidmd/idm/src/schema.rs @@ -16,17 +16,17 @@ //! [`Attributes`]: struct.SchemaAttribute.html //! [`Classes`]: struct.SchemaClass.html +use std::collections::BTreeSet; + +use concread::cowcell::*; +use hashbrown::{HashMap, HashSet}; +use kanidm_proto::v1::{ConsistencyError, OperationError, SchemaError}; +use tracing::trace; +use uuid::Uuid; + use crate::be::IdxKey; use crate::prelude::*; use crate::valueset::ValueSet; -use kanidm_proto::v1::{ConsistencyError, OperationError, SchemaError}; -use tracing::trace; - -use hashbrown::{HashMap, HashSet}; -use std::collections::BTreeSet; -use uuid::Uuid; - -use concread::cowcell::*; // representations of schema that confines object types, classes // and attributes. This ties in deeply with "Entry". @@ -642,124 +642,124 @@ impl<'a> SchemaWriteTransaction<'a> { .collect() } + #[instrument(level = "debug", name = "schema::generate_in_memory", skip_all)] pub fn generate_in_memory(&mut self) -> Result<(), OperationError> { - spanned!("schema::generate_in_memory", { - // - self.classes.clear(); - self.attributes.clear(); - // Bootstrap in definitions of our own schema types - // First, add all the needed core attributes for schema parsing - self.attributes.insert( - AttrString::from("class"), - SchemaAttribute { - name: AttrString::from("class"), - uuid: UUID_SCHEMA_ATTR_CLASS, - description: String::from("The set of classes defining an object"), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality, IndexType::Presence], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("uuid"), - SchemaAttribute { - name: AttrString::from("uuid"), - uuid: UUID_SCHEMA_ATTR_UUID, - description: String::from("The universal unique id of the object"), - multivalue: false, - // Uniqueness is handled by base.rs, not attrunique here due to - // needing to check recycled objects too. - unique: false, - phantom: false, - index: vec![IndexType::Equality, IndexType::Presence], - syntax: SyntaxType::Uuid, - }, - ); - self.attributes.insert( - AttrString::from("last_modified_cid"), - SchemaAttribute { - name: AttrString::from("last_modified_cid"), - uuid: UUID_SCHEMA_ATTR_LAST_MOD_CID, - description: String::from("The cid of the last change to this object"), - multivalue: false, - // Uniqueness is handled by base.rs, not attrunique here due to - // needing to check recycled objects too. - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Cid, - }, - ); - self.attributes.insert( - AttrString::from("name"), - SchemaAttribute { - name: AttrString::from("name"), - uuid: UUID_SCHEMA_ATTR_NAME, - description: String::from("The shortform name of an object"), - multivalue: false, - unique: true, - phantom: false, - index: vec![IndexType::Equality, IndexType::Presence], - syntax: SyntaxType::Utf8StringIname, - }, - ); - self.attributes.insert( - AttrString::from("spn"), - SchemaAttribute { - name: AttrString::from("spn"), - uuid: UUID_SCHEMA_ATTR_SPN, - description: String::from( - "The Security Principal Name of an object, unique across all domain trusts", - ), - multivalue: false, - unique: true, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::SecurityPrincipalName, - }, - ); - self.attributes.insert( - AttrString::from("attributename"), - SchemaAttribute { - name: AttrString::from("attributename"), - uuid: UUID_SCHEMA_ATTR_ATTRIBUTENAME, - description: String::from("The name of a schema attribute"), - multivalue: false, - unique: true, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("classname"), - SchemaAttribute { - name: AttrString::from("classname"), - uuid: UUID_SCHEMA_ATTR_CLASSNAME, - description: String::from("The name of a schema class"), - multivalue: false, - unique: true, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("description"), - SchemaAttribute { - name: AttrString::from("description"), - uuid: UUID_SCHEMA_ATTR_DESCRIPTION, - description: String::from("A description of an attribute, object or class"), - multivalue: false, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8String, - }, - ); - self.attributes.insert(AttrString::from("multivalue"), SchemaAttribute { + // + self.classes.clear(); + self.attributes.clear(); + // Bootstrap in definitions of our own schema types + // First, add all the needed core attributes for schema parsing + self.attributes.insert( + AttrString::from("class"), + SchemaAttribute { + name: AttrString::from("class"), + uuid: UUID_SCHEMA_ATTR_CLASS, + description: String::from("The set of classes defining an object"), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality, IndexType::Presence], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("uuid"), + SchemaAttribute { + name: AttrString::from("uuid"), + uuid: UUID_SCHEMA_ATTR_UUID, + description: String::from("The universal unique id of the object"), + multivalue: false, + // Uniqueness is handled by base.rs, not attrunique here due to + // needing to check recycled objects too. + unique: false, + phantom: false, + index: vec![IndexType::Equality, IndexType::Presence], + syntax: SyntaxType::Uuid, + }, + ); + self.attributes.insert( + AttrString::from("last_modified_cid"), + SchemaAttribute { + name: AttrString::from("last_modified_cid"), + uuid: UUID_SCHEMA_ATTR_LAST_MOD_CID, + description: String::from("The cid of the last change to this object"), + multivalue: false, + // Uniqueness is handled by base.rs, not attrunique here due to + // needing to check recycled objects too. + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Cid, + }, + ); + self.attributes.insert( + AttrString::from("name"), + SchemaAttribute { + name: AttrString::from("name"), + uuid: UUID_SCHEMA_ATTR_NAME, + description: String::from("The shortform name of an object"), + multivalue: false, + unique: true, + phantom: false, + index: vec![IndexType::Equality, IndexType::Presence], + syntax: SyntaxType::Utf8StringIname, + }, + ); + self.attributes.insert( + AttrString::from("spn"), + SchemaAttribute { + name: AttrString::from("spn"), + uuid: UUID_SCHEMA_ATTR_SPN, + description: String::from( + "The Security Principal Name of an object, unique across all domain trusts", + ), + multivalue: false, + unique: true, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::SecurityPrincipalName, + }, + ); + self.attributes.insert( + AttrString::from("attributename"), + SchemaAttribute { + name: AttrString::from("attributename"), + uuid: UUID_SCHEMA_ATTR_ATTRIBUTENAME, + description: String::from("The name of a schema attribute"), + multivalue: false, + unique: true, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("classname"), + SchemaAttribute { + name: AttrString::from("classname"), + uuid: UUID_SCHEMA_ATTR_CLASSNAME, + description: String::from("The name of a schema class"), + multivalue: false, + unique: true, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("description"), + SchemaAttribute { + name: AttrString::from("description"), + uuid: UUID_SCHEMA_ATTR_DESCRIPTION, + description: String::from("A description of an attribute, object or class"), + multivalue: false, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8String, + }, + ); + self.attributes.insert(AttrString::from("multivalue"), SchemaAttribute { name: AttrString::from("multivalue"), uuid: UUID_SCHEMA_ATTR_MULTIVALUE, description: String::from("If true, this attribute is able to store multiple values rather than just a single value."), @@ -769,7 +769,7 @@ impl<'a> SchemaWriteTransaction<'a> { index: vec![], syntax: SyntaxType::Boolean, }); - self.attributes.insert(AttrString::from("phantom"), SchemaAttribute { + self.attributes.insert(AttrString::from("phantom"), SchemaAttribute { name: AttrString::from("phantom"), uuid: UUID_SCHEMA_ATTR_PHANTOM, description: String::from("If true, this attribute must NOT be present in any may/must sets of a class as. This represents generated attributes."), @@ -779,107 +779,112 @@ impl<'a> SchemaWriteTransaction<'a> { index: vec![], syntax: SyntaxType::Boolean, }); - self.attributes.insert(AttrString::from("unique"), SchemaAttribute { + self.attributes.insert( + AttrString::from("unique"), + SchemaAttribute { name: AttrString::from("unique"), uuid: UUID_SCHEMA_ATTR_UNIQUE, - description: String::from("If true, this attribute must store a unique value through out the database."), + description: String::from( + "If true, this attribute must store a unique value through out the database.", + ), multivalue: false, unique: false, phantom: false, index: vec![], syntax: SyntaxType::Boolean, - }); - self.attributes.insert( - AttrString::from("index"), - SchemaAttribute { - name: AttrString::from("index"), - uuid: UUID_SCHEMA_ATTR_INDEX, - description: String::from( - "Describe the indexes to apply to instances of this attribute.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::IndexId, - }, - ); - self.attributes.insert( - AttrString::from("syntax"), - SchemaAttribute { - name: AttrString::from("syntax"), - uuid: UUID_SCHEMA_ATTR_SYNTAX, - description: String::from( - "Describe the syntax of this attribute. This affects indexing and sorting.", - ), - multivalue: false, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::SyntaxId, - }, - ); - self.attributes.insert( - AttrString::from("systemmay"), - SchemaAttribute { - name: AttrString::from("systemmay"), - uuid: UUID_SCHEMA_ATTR_SYSTEMMAY, - description: String::from( - "A list of system provided optional attributes this class can store.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("may"), - SchemaAttribute { - name: AttrString::from("may"), - uuid: UUID_SCHEMA_ATTR_MAY, - description: String::from( - "A user modifiable list of optional attributes this class can store.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("systemmust"), - SchemaAttribute { - name: AttrString::from("systemmust"), - uuid: UUID_SCHEMA_ATTR_SYSTEMMUST, - description: String::from( - "A list of system provided required attributes this class must store.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("must"), - SchemaAttribute { - name: AttrString::from("must"), - uuid: UUID_SCHEMA_ATTR_MUST, - description: String::from( - "A user modifiable list of required attributes this class must store.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( + }, + ); + self.attributes.insert( + AttrString::from("index"), + SchemaAttribute { + name: AttrString::from("index"), + uuid: UUID_SCHEMA_ATTR_INDEX, + description: String::from( + "Describe the indexes to apply to instances of this attribute.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::IndexId, + }, + ); + self.attributes.insert( + AttrString::from("syntax"), + SchemaAttribute { + name: AttrString::from("syntax"), + uuid: UUID_SCHEMA_ATTR_SYNTAX, + description: String::from( + "Describe the syntax of this attribute. This affects indexing and sorting.", + ), + multivalue: false, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::SyntaxId, + }, + ); + self.attributes.insert( + AttrString::from("systemmay"), + SchemaAttribute { + name: AttrString::from("systemmay"), + uuid: UUID_SCHEMA_ATTR_SYSTEMMAY, + description: String::from( + "A list of system provided optional attributes this class can store.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("may"), + SchemaAttribute { + name: AttrString::from("may"), + uuid: UUID_SCHEMA_ATTR_MAY, + description: String::from( + "A user modifiable list of optional attributes this class can store.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("systemmust"), + SchemaAttribute { + name: AttrString::from("systemmust"), + uuid: UUID_SCHEMA_ATTR_SYSTEMMUST, + description: String::from( + "A list of system provided required attributes this class must store.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("must"), + SchemaAttribute { + name: AttrString::from("must"), + uuid: UUID_SCHEMA_ATTR_MUST, + description: String::from( + "A user modifiable list of required attributes this class must store.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( AttrString::from("systemsupplements"), SchemaAttribute { name: AttrString::from("systemsupplements"), @@ -894,7 +899,7 @@ impl<'a> SchemaWriteTransaction<'a> { syntax: SyntaxType::Utf8StringInsensitive, }, ); - self.attributes.insert( + self.attributes.insert( AttrString::from("supplements"), SchemaAttribute { name: AttrString::from("supplements"), @@ -909,22 +914,22 @@ impl<'a> SchemaWriteTransaction<'a> { syntax: SyntaxType::Utf8StringInsensitive, }, ); - self.attributes.insert( - AttrString::from("systemexcludes"), - SchemaAttribute { - name: AttrString::from("systemexcludes"), - uuid: UUID_SCHEMA_ATTR_SYSTEMEXCLUDES, - description: String::from( - "A set of classes that are denied presence in connection to this class", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( + self.attributes.insert( + AttrString::from("systemexcludes"), + SchemaAttribute { + name: AttrString::from("systemexcludes"), + uuid: UUID_SCHEMA_ATTR_SYSTEMEXCLUDES, + description: String::from( + "A set of classes that are denied presence in connection to this class", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( AttrString::from("excludes"), SchemaAttribute { name: AttrString::from("excludes"), @@ -940,9 +945,9 @@ impl<'a> SchemaWriteTransaction<'a> { }, ); - // SYSINFO attrs - // ACP attributes. - self.attributes.insert( + // SYSINFO attrs + // ACP attributes. + self.attributes.insert( AttrString::from("acp_enable"), SchemaAttribute { name: AttrString::from("acp_enable"), @@ -956,107 +961,111 @@ impl<'a> SchemaWriteTransaction<'a> { }, ); - self.attributes.insert( - AttrString::from("acp_receiver"), - SchemaAttribute { - name: AttrString::from("acp_receiver"), - uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER, - description: String::from( - "Who the ACP applies to, constraining or allowing operations.", - ), - multivalue: false, - unique: false, - phantom: false, - index: vec![IndexType::Equality, IndexType::SubString], - syntax: SyntaxType::JsonFilter, - }, - ); - self.attributes.insert( - AttrString::from("acp_targetscope"), - SchemaAttribute { - name: AttrString::from("acp_targetscope"), - uuid: UUID_SCHEMA_ATTR_ACP_TARGETSCOPE, - description: String::from( - "The effective targets of the ACP, IE what will be acted upon.", - ), - multivalue: false, - unique: false, - phantom: false, - index: vec![IndexType::Equality, IndexType::SubString], - syntax: SyntaxType::JsonFilter, - }, - ); - self.attributes.insert( - AttrString::from("acp_search_attr"), - SchemaAttribute { - name: AttrString::from("acp_search_attr"), - uuid: UUID_SCHEMA_ATTR_ACP_SEARCH_ATTR, - description: String::from("The attributes that may be viewed or searched by the reciever on targetscope."), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("acp_create_class"), - SchemaAttribute { - name: AttrString::from("acp_create_class"), - uuid: UUID_SCHEMA_ATTR_ACP_CREATE_CLASS, - description: String::from( - "The set of classes that can be created on a new entry.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("acp_create_attr"), - SchemaAttribute { - name: AttrString::from("acp_create_attr"), - uuid: UUID_SCHEMA_ATTR_ACP_CREATE_ATTR, - description: String::from( - "The set of attribute types that can be created on an entry.", - ), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); + self.attributes.insert( + AttrString::from("acp_receiver"), + SchemaAttribute { + name: AttrString::from("acp_receiver"), + uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER, + description: String::from( + "Who the ACP applies to, constraining or allowing operations.", + ), + multivalue: false, + unique: false, + phantom: false, + index: vec![IndexType::Equality, IndexType::SubString], + syntax: SyntaxType::JsonFilter, + }, + ); + self.attributes.insert( + AttrString::from("acp_targetscope"), + SchemaAttribute { + name: AttrString::from("acp_targetscope"), + uuid: UUID_SCHEMA_ATTR_ACP_TARGETSCOPE, + description: String::from( + "The effective targets of the ACP, IE what will be acted upon.", + ), + multivalue: false, + unique: false, + phantom: false, + index: vec![IndexType::Equality, IndexType::SubString], + syntax: SyntaxType::JsonFilter, + }, + ); + self.attributes.insert( + AttrString::from("acp_search_attr"), + SchemaAttribute { + name: AttrString::from("acp_search_attr"), + uuid: UUID_SCHEMA_ATTR_ACP_SEARCH_ATTR, + description: String::from( + "The attributes that may be viewed or searched by the reciever on targetscope.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("acp_create_class"), + SchemaAttribute { + name: AttrString::from("acp_create_class"), + uuid: UUID_SCHEMA_ATTR_ACP_CREATE_CLASS, + description: String::from("The set of classes that can be created on a new entry."), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("acp_create_attr"), + SchemaAttribute { + name: AttrString::from("acp_create_attr"), + uuid: UUID_SCHEMA_ATTR_ACP_CREATE_ATTR, + description: String::from( + "The set of attribute types that can be created on an entry.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); - self.attributes.insert( - AttrString::from("acp_modify_removedattr"), - SchemaAttribute { - name: AttrString::from("acp_modify_removedattr"), - uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVEDATTR, - description: String::from("The set of attribute types that could be removed or purged in a modification."), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("acp_modify_presentattr"), - SchemaAttribute { - name: AttrString::from("acp_modify_presentattr"), - uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENTATTR, - description: String::from("The set of attribute types that could be added or asserted in a modification."), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( + self.attributes.insert( + AttrString::from("acp_modify_removedattr"), + SchemaAttribute { + name: AttrString::from("acp_modify_removedattr"), + uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVEDATTR, + description: String::from( + "The set of attribute types that could be removed or purged in a modification.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("acp_modify_presentattr"), + SchemaAttribute { + name: AttrString::from("acp_modify_presentattr"), + uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENTATTR, + description: String::from( + "The set of attribute types that could be added or asserted in a modification.", + ), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( AttrString::from("acp_modify_class"), SchemaAttribute { name: AttrString::from("acp_modify_class"), @@ -1069,341 +1078,341 @@ impl<'a> SchemaWriteTransaction<'a> { syntax: SyntaxType::Utf8StringInsensitive, }, ); - // MO/Member - self.attributes.insert( - AttrString::from("memberof"), - SchemaAttribute { - name: AttrString::from("memberof"), - uuid: UUID_SCHEMA_ATTR_MEMBEROF, - description: String::from("reverse group membership of the object"), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::ReferenceUuid, - }, - ); - self.attributes.insert( - AttrString::from("directmemberof"), - SchemaAttribute { - name: AttrString::from("directmemberof"), - uuid: UUID_SCHEMA_ATTR_DIRECTMEMBEROF, - description: String::from("reverse direct group membership of the object"), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::ReferenceUuid, - }, - ); - self.attributes.insert( - AttrString::from("member"), - SchemaAttribute { - name: AttrString::from("member"), - uuid: UUID_SCHEMA_ATTR_MEMBER, - description: String::from("List of members of the group"), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::ReferenceUuid, - }, - ); - // Migration related - self.attributes.insert( - AttrString::from("version"), - SchemaAttribute { - name: AttrString::from("version"), - uuid: UUID_SCHEMA_ATTR_VERSION, - description: String::from( - "The systems internal migration version for provided objects", - ), - multivalue: false, - unique: false, - phantom: false, - index: vec![], - syntax: SyntaxType::Uint32, - }, - ); - // Domain for sysinfo - self.attributes.insert( - AttrString::from("domain"), - SchemaAttribute { - name: AttrString::from("domain"), - uuid: UUID_SCHEMA_ATTR_DOMAIN, - description: String::from("A DNS Domain name entry."), - multivalue: true, - unique: false, - phantom: false, - index: vec![IndexType::Equality], - syntax: SyntaxType::Utf8StringIname, - }, - ); - self.attributes.insert( - AttrString::from("claim"), - SchemaAttribute { - name: AttrString::from("claim"), - uuid: UUID_SCHEMA_ATTR_CLAIM, - description: String::from( - "The string identifier of an extracted claim that can be filtered", - ), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("scope"), - SchemaAttribute { - name: AttrString::from("scope"), - uuid: UUID_SCHEMA_ATTR_SCOPE, - description: String::from( - "The string identifier of a permission scope in a session", - ), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); + // MO/Member + self.attributes.insert( + AttrString::from("memberof"), + SchemaAttribute { + name: AttrString::from("memberof"), + uuid: UUID_SCHEMA_ATTR_MEMBEROF, + description: String::from("reverse group membership of the object"), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::ReferenceUuid, + }, + ); + self.attributes.insert( + AttrString::from("directmemberof"), + SchemaAttribute { + name: AttrString::from("directmemberof"), + uuid: UUID_SCHEMA_ATTR_DIRECTMEMBEROF, + description: String::from("reverse direct group membership of the object"), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::ReferenceUuid, + }, + ); + self.attributes.insert( + AttrString::from("member"), + SchemaAttribute { + name: AttrString::from("member"), + uuid: UUID_SCHEMA_ATTR_MEMBER, + description: String::from("List of members of the group"), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::ReferenceUuid, + }, + ); + // Migration related + self.attributes.insert( + AttrString::from("version"), + SchemaAttribute { + name: AttrString::from("version"), + uuid: UUID_SCHEMA_ATTR_VERSION, + description: String::from( + "The systems internal migration version for provided objects", + ), + multivalue: false, + unique: false, + phantom: false, + index: vec![], + syntax: SyntaxType::Uint32, + }, + ); + // Domain for sysinfo + self.attributes.insert( + AttrString::from("domain"), + SchemaAttribute { + name: AttrString::from("domain"), + uuid: UUID_SCHEMA_ATTR_DOMAIN, + description: String::from("A DNS Domain name entry."), + multivalue: true, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::Utf8StringIname, + }, + ); + self.attributes.insert( + AttrString::from("claim"), + SchemaAttribute { + name: AttrString::from("claim"), + uuid: UUID_SCHEMA_ATTR_CLAIM, + description: String::from( + "The string identifier of an extracted claim that can be filtered", + ), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("scope"), + SchemaAttribute { + name: AttrString::from("scope"), + uuid: UUID_SCHEMA_ATTR_SCOPE, + description: String::from( + "The string identifier of a permission scope in a session", + ), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); - self.attributes.insert( - AttrString::from("password_import"), - SchemaAttribute { - name: AttrString::from("password_import"), - uuid: UUID_SCHEMA_ATTR_PASSWORD_IMPORT, - description: String::from("An imported password hash from an external system."), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8String, - }, - ); + self.attributes.insert( + AttrString::from("password_import"), + SchemaAttribute { + name: AttrString::from("password_import"), + uuid: UUID_SCHEMA_ATTR_PASSWORD_IMPORT, + description: String::from("An imported password hash from an external system."), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8String, + }, + ); - // LDAP Masking Phantoms - self.attributes.insert( - AttrString::from("dn"), - SchemaAttribute { - name: AttrString::from("dn"), - uuid: UUID_SCHEMA_ATTR_DN, - description: String::from("An LDAP Compatible DN"), - multivalue: false, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("entrydn"), - SchemaAttribute { - name: AttrString::from("entrydn"), - uuid: UUID_SCHEMA_ATTR_ENTRYDN, - description: String::from("An LDAP Compatible EntryDN"), - multivalue: false, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("entryuuid"), - SchemaAttribute { - name: AttrString::from("entryuuid"), - uuid: UUID_SCHEMA_ATTR_ENTRYUUID, - description: String::from("An LDAP Compatible entryUUID"), - multivalue: false, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Uuid, - }, - ); - self.attributes.insert( - AttrString::from("objectclass"), - SchemaAttribute { - name: AttrString::from("objectclass"), - uuid: UUID_SCHEMA_ATTR_OBJECTCLASS, - description: String::from("An LDAP Compatible objectClass"), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringInsensitive, - }, - ); - self.attributes.insert( - AttrString::from("cn"), - SchemaAttribute { - name: AttrString::from("cn"), - uuid: UUID_SCHEMA_ATTR_CN, - description: String::from("An LDAP Compatible objectClass"), - multivalue: false, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Utf8StringIname, - }, - ); - self.attributes.insert( - AttrString::from("keys"), - SchemaAttribute { - name: AttrString::from("keys"), - uuid: UUID_SCHEMA_ATTR_KEYS, - description: String::from("An LDAP Compatible keys (ssh)"), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::SshKey, - }, - ); - self.attributes.insert( - AttrString::from("sshpublickey"), - SchemaAttribute { - name: AttrString::from("sshpublickey"), - uuid: UUID_SCHEMA_ATTR_SSHPUBLICKEY, - description: String::from("An LDAP Compatible sshPublicKey"), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::SshKey, - }, - ); - self.attributes.insert( - AttrString::from("email"), - SchemaAttribute { - name: AttrString::from("email"), - uuid: UUID_SCHEMA_ATTR_EMAIL, - description: String::from("An LDAP Compatible email"), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::EmailAddress, - }, - ); - self.attributes.insert( - AttrString::from("emailaddress"), - SchemaAttribute { - name: AttrString::from("emailaddress"), - uuid: UUID_SCHEMA_ATTR_EMAILADDRESS, - description: String::from("An LDAP Compatible emailAddress"), - multivalue: true, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::EmailAddress, - }, - ); - self.attributes.insert( - AttrString::from("uidnumber"), - SchemaAttribute { - name: AttrString::from("uidnumber"), - uuid: UUID_SCHEMA_ATTR_UIDNUMBER, - description: String::from("An LDAP Compatible uidNumber"), - multivalue: false, - unique: false, - phantom: true, - index: vec![], - syntax: SyntaxType::Uint32, - }, - ); - // end LDAP masking phantoms + // LDAP Masking Phantoms + self.attributes.insert( + AttrString::from("dn"), + SchemaAttribute { + name: AttrString::from("dn"), + uuid: UUID_SCHEMA_ATTR_DN, + description: String::from("An LDAP Compatible DN"), + multivalue: false, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("entrydn"), + SchemaAttribute { + name: AttrString::from("entrydn"), + uuid: UUID_SCHEMA_ATTR_ENTRYDN, + description: String::from("An LDAP Compatible EntryDN"), + multivalue: false, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("entryuuid"), + SchemaAttribute { + name: AttrString::from("entryuuid"), + uuid: UUID_SCHEMA_ATTR_ENTRYUUID, + description: String::from("An LDAP Compatible entryUUID"), + multivalue: false, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Uuid, + }, + ); + self.attributes.insert( + AttrString::from("objectclass"), + SchemaAttribute { + name: AttrString::from("objectclass"), + uuid: UUID_SCHEMA_ATTR_OBJECTCLASS, + description: String::from("An LDAP Compatible objectClass"), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringInsensitive, + }, + ); + self.attributes.insert( + AttrString::from("cn"), + SchemaAttribute { + name: AttrString::from("cn"), + uuid: UUID_SCHEMA_ATTR_CN, + description: String::from("An LDAP Compatible objectClass"), + multivalue: false, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Utf8StringIname, + }, + ); + self.attributes.insert( + AttrString::from("keys"), + SchemaAttribute { + name: AttrString::from("keys"), + uuid: UUID_SCHEMA_ATTR_KEYS, + description: String::from("An LDAP Compatible keys (ssh)"), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::SshKey, + }, + ); + self.attributes.insert( + AttrString::from("sshpublickey"), + SchemaAttribute { + name: AttrString::from("sshpublickey"), + uuid: UUID_SCHEMA_ATTR_SSHPUBLICKEY, + description: String::from("An LDAP Compatible sshPublicKey"), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::SshKey, + }, + ); + self.attributes.insert( + AttrString::from("email"), + SchemaAttribute { + name: AttrString::from("email"), + uuid: UUID_SCHEMA_ATTR_EMAIL, + description: String::from("An LDAP Compatible email"), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::EmailAddress, + }, + ); + self.attributes.insert( + AttrString::from("emailaddress"), + SchemaAttribute { + name: AttrString::from("emailaddress"), + uuid: UUID_SCHEMA_ATTR_EMAILADDRESS, + description: String::from("An LDAP Compatible emailAddress"), + multivalue: true, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::EmailAddress, + }, + ); + self.attributes.insert( + AttrString::from("uidnumber"), + SchemaAttribute { + name: AttrString::from("uidnumber"), + uuid: UUID_SCHEMA_ATTR_UIDNUMBER, + description: String::from("An LDAP Compatible uidNumber"), + multivalue: false, + unique: false, + phantom: true, + index: vec![], + syntax: SyntaxType::Uint32, + }, + ); + // end LDAP masking phantoms - self.classes.insert( - AttrString::from("attributetype"), - SchemaClass { - name: AttrString::from("attributetype"), - uuid: UUID_SCHEMA_CLASS_ATTRIBUTETYPE, - description: String::from("Definition of a schema attribute"), - systemmay: vec![AttrString::from("phantom"), AttrString::from("index")], - systemmust: vec![ - AttrString::from("class"), - AttrString::from("attributename"), - AttrString::from("multivalue"), - AttrString::from("unique"), - AttrString::from("syntax"), - AttrString::from("description"), - ], - systemexcludes: vec![AttrString::from("classtype")], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("classtype"), - SchemaClass { - name: AttrString::from("classtype"), - uuid: UUID_SCHEMA_CLASS_CLASSTYPE, - description: String::from("Definition of a schema classtype"), - systemmay: vec![ - AttrString::from("systemmay"), - AttrString::from("may"), - AttrString::from("systemmust"), - AttrString::from("must"), - AttrString::from("systemsupplements"), - AttrString::from("supplements"), - AttrString::from("systemexcludes"), - AttrString::from("excludes"), - ], - systemmust: vec![ - AttrString::from("class"), - AttrString::from("classname"), - AttrString::from("description"), - ], - systemexcludes: vec![AttrString::from("attributetype")], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("object"), - SchemaClass { - name: AttrString::from("object"), - uuid: UUID_SCHEMA_CLASS_OBJECT, - description: String::from( - "A system created class that all objects must contain", - ), - systemmay: vec![AttrString::from("description")], - systemmust: vec![ - AttrString::from("class"), - AttrString::from("uuid"), - AttrString::from("last_modified_cid"), - ], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("memberof"), - SchemaClass { - name: AttrString::from("memberof"), - uuid: UUID_SCHEMA_CLASS_MEMBEROF, - description: String::from("Class that is dynamically added to recepients of memberof or directmemberof"), - systemmay: vec![ - AttrString::from("memberof"), - AttrString::from("directmemberof") - ], - .. Default::default() - }, - ); - self.classes.insert( - AttrString::from("extensibleobject"), - SchemaClass { - name: AttrString::from("extensibleobject"), - uuid: UUID_SCHEMA_CLASS_EXTENSIBLEOBJECT, - description: String::from( - "A class type that has green hair and turns off all rules ...", - ), - ..Default::default() - }, - ); - /* These two classes are core to the entry lifecycle for recycling and tombstoning */ - self.classes.insert( + self.classes.insert( + AttrString::from("attributetype"), + SchemaClass { + name: AttrString::from("attributetype"), + uuid: UUID_SCHEMA_CLASS_ATTRIBUTETYPE, + description: String::from("Definition of a schema attribute"), + systemmay: vec![AttrString::from("phantom"), AttrString::from("index")], + systemmust: vec![ + AttrString::from("class"), + AttrString::from("attributename"), + AttrString::from("multivalue"), + AttrString::from("unique"), + AttrString::from("syntax"), + AttrString::from("description"), + ], + systemexcludes: vec![AttrString::from("classtype")], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("classtype"), + SchemaClass { + name: AttrString::from("classtype"), + uuid: UUID_SCHEMA_CLASS_CLASSTYPE, + description: String::from("Definition of a schema classtype"), + systemmay: vec![ + AttrString::from("systemmay"), + AttrString::from("may"), + AttrString::from("systemmust"), + AttrString::from("must"), + AttrString::from("systemsupplements"), + AttrString::from("supplements"), + AttrString::from("systemexcludes"), + AttrString::from("excludes"), + ], + systemmust: vec![ + AttrString::from("class"), + AttrString::from("classname"), + AttrString::from("description"), + ], + systemexcludes: vec![AttrString::from("attributetype")], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("object"), + SchemaClass { + name: AttrString::from("object"), + uuid: UUID_SCHEMA_CLASS_OBJECT, + description: String::from("A system created class that all objects must contain"), + systemmay: vec![AttrString::from("description")], + systemmust: vec![ + AttrString::from("class"), + AttrString::from("uuid"), + AttrString::from("last_modified_cid"), + ], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("memberof"), + SchemaClass { + name: AttrString::from("memberof"), + uuid: UUID_SCHEMA_CLASS_MEMBEROF, + description: String::from( + "Class that is dynamically added to recepients of memberof or directmemberof", + ), + systemmay: vec![ + AttrString::from("memberof"), + AttrString::from("directmemberof"), + ], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("extensibleobject"), + SchemaClass { + name: AttrString::from("extensibleobject"), + uuid: UUID_SCHEMA_CLASS_EXTENSIBLEOBJECT, + description: String::from( + "A class type that has green hair and turns off all rules ...", + ), + ..Default::default() + }, + ); + /* These two classes are core to the entry lifecycle for recycling and tombstoning */ + self.classes.insert( AttrString::from("recycled"), SchemaClass { name: AttrString::from("recycled"), @@ -1412,7 +1421,7 @@ impl<'a> SchemaWriteTransaction<'a> { .. Default::default() }, ); - self.classes.insert( + self.classes.insert( AttrString::from("tombstone"), SchemaClass { name: AttrString::from("tombstone"), @@ -1425,89 +1434,89 @@ impl<'a> SchemaWriteTransaction<'a> { .. Default::default() }, ); - // sysinfo - self.classes.insert( - AttrString::from("system_info"), - SchemaClass { - name: AttrString::from("system_info"), - uuid: UUID_SCHEMA_CLASS_SYSTEM_INFO, - description: String::from("System metadata object class"), - systemmust: vec![AttrString::from("version")], - ..Default::default() - }, - ); - // ACP - self.classes.insert( - AttrString::from("access_control_profile"), - SchemaClass { - name: AttrString::from("access_control_profile"), - uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_PROFILE, - description: String::from("System Access Control Profile Class"), - systemmay: vec![ - AttrString::from("acp_enable"), - AttrString::from("description"), - ], - systemmust: vec![ - AttrString::from("acp_receiver"), - AttrString::from("acp_targetscope"), - AttrString::from("name"), - ], - systemsupplements: vec![ - AttrString::from("access_control_search"), - AttrString::from("access_control_delete"), - AttrString::from("access_control_modify"), - AttrString::from("access_control_create"), - ], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("access_control_search"), - SchemaClass { - name: AttrString::from("access_control_search"), - uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_SEARCH, - description: String::from("System Access Control Search Class"), - systemmust: vec![AttrString::from("acp_search_attr")], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("access_control_delete"), - SchemaClass { - name: AttrString::from("access_control_delete"), - uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_DELETE, - description: String::from("System Access Control DELETE Class"), - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("access_control_modify"), - SchemaClass { - name: AttrString::from("access_control_modify"), - uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_MODIFY, - description: String::from("System Access Control Modify Class"), - systemmay: vec![ - AttrString::from("acp_modify_removedattr"), - AttrString::from("acp_modify_presentattr"), - AttrString::from("acp_modify_class"), - ], - ..Default::default() - }, - ); - self.classes.insert( - AttrString::from("access_control_create"), - SchemaClass { - name: AttrString::from("access_control_create"), - uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_CREATE, - description: String::from("System Access Control Create Class"), - systemmay: vec![ - AttrString::from("acp_create_class"), - AttrString::from("acp_create_attr"), - ], - ..Default::default() - }, - ); - self.classes.insert( + // sysinfo + self.classes.insert( + AttrString::from("system_info"), + SchemaClass { + name: AttrString::from("system_info"), + uuid: UUID_SCHEMA_CLASS_SYSTEM_INFO, + description: String::from("System metadata object class"), + systemmust: vec![AttrString::from("version")], + ..Default::default() + }, + ); + // ACP + self.classes.insert( + AttrString::from("access_control_profile"), + SchemaClass { + name: AttrString::from("access_control_profile"), + uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_PROFILE, + description: String::from("System Access Control Profile Class"), + systemmay: vec![ + AttrString::from("acp_enable"), + AttrString::from("description"), + ], + systemmust: vec![ + AttrString::from("acp_receiver"), + AttrString::from("acp_targetscope"), + AttrString::from("name"), + ], + systemsupplements: vec![ + AttrString::from("access_control_search"), + AttrString::from("access_control_delete"), + AttrString::from("access_control_modify"), + AttrString::from("access_control_create"), + ], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("access_control_search"), + SchemaClass { + name: AttrString::from("access_control_search"), + uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_SEARCH, + description: String::from("System Access Control Search Class"), + systemmust: vec![AttrString::from("acp_search_attr")], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("access_control_delete"), + SchemaClass { + name: AttrString::from("access_control_delete"), + uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_DELETE, + description: String::from("System Access Control DELETE Class"), + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("access_control_modify"), + SchemaClass { + name: AttrString::from("access_control_modify"), + uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_MODIFY, + description: String::from("System Access Control Modify Class"), + systemmay: vec![ + AttrString::from("acp_modify_removedattr"), + AttrString::from("acp_modify_presentattr"), + AttrString::from("acp_modify_class"), + ], + ..Default::default() + }, + ); + self.classes.insert( + AttrString::from("access_control_create"), + SchemaClass { + name: AttrString::from("access_control_create"), + uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_CREATE, + description: String::from("System Access Control Create Class"), + systemmay: vec![ + AttrString::from("acp_create_class"), + AttrString::from("acp_create_attr"), + ], + ..Default::default() + }, + ); + self.classes.insert( AttrString::from("system"), SchemaClass { name: AttrString::from("system"), @@ -1517,15 +1526,14 @@ impl<'a> SchemaWriteTransaction<'a> { }, ); - let r = self.validate(); - if r.is_empty() { - admin_debug!("schema validate -> passed"); - Ok(()) - } else { - admin_error!(err = ?r, "schema validate -> errors"); - Err(OperationError::ConsistencyError(r)) - } - }) + let r = self.validate(); + if r.is_empty() { + admin_debug!("schema validate -> passed"); + Ok(()) + } else { + admin_error!(err = ?r, "schema validate -> errors"); + Err(OperationError::ConsistencyError(r)) + } } } @@ -1624,12 +1632,14 @@ impl Schema { #[cfg(test)] mod tests { - use crate::prelude::*; - use crate::schema::SchemaTransaction; - use crate::schema::{IndexType, Schema, SchemaAttribute, SchemaClass, SyntaxType}; use kanidm_proto::v1::{ConsistencyError, SchemaError}; use uuid::Uuid; + use crate::prelude::*; + use crate::schema::{ + IndexType, Schema, SchemaAttribute, SchemaClass, SchemaTransaction, SyntaxType, + }; + // use crate::proto_v1::Filter as ProtoFilter; macro_rules! validate_schema { diff --git a/kanidmd/idm/src/server.rs b/kanidmd/idm/src/server.rs index c13c34aa2..5c69bbe6f 100644 --- a/kanidmd/idm/src/server.rs +++ b/kanidmd/idm/src/server.rs @@ -3,13 +3,15 @@ // This is really only used for long lived, high level types that need clone // that otherwise can't be cloned. Think Mutex. +use std::cell::Cell; +use std::sync::Arc; +use std::time::Duration; + use async_std::task; use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn}; use concread::cowcell::*; use hashbrown::{HashMap, HashSet}; -use std::cell::Cell; -use std::sync::Arc; -use std::time::Duration; +use kanidm_proto::v1::{ConsistencyError, SchemaError}; use tokio::sync::{Semaphore, SemaphorePermit}; use tracing::trace; @@ -19,7 +21,6 @@ use crate::access::{ AccessControlsWriteTransaction, }; use crate::be::{Backend, BackendReadTransaction, BackendTransaction, BackendWriteTransaction}; -use crate::prelude::*; // We use so many, we just import them all ... use crate::event::{ CreateEvent, DeleteEvent, ExistsEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent, @@ -29,13 +30,13 @@ use crate::identity::IdentityId; use crate::modify::{Modify, ModifyInvalid, ModifyList, ModifyValid}; use crate::plugins::dyngroup::{DynGroup, DynGroupCache}; use crate::plugins::Plugins; +use crate::prelude::*; use crate::repl::cid::Cid; use crate::schema::{ Schema, SchemaAttribute, SchemaClass, SchemaReadTransaction, SchemaTransaction, SchemaWriteTransaction, }; use crate::valueset::uuid_to_proto_string; -use kanidm_proto::v1::{ConsistencyError, SchemaError}; const RESOLVE_FILTER_CACHE_MAX: usize = 4096; const RESOLVE_FILTER_CACHE_LOCAL: usize = 0; @@ -177,107 +178,103 @@ pub trait QueryServerTransaction<'a> { /// [`SearchEvent`]: ../event/struct.SearchEvent.html /// [`access`]: ../access/index.html /// [`fn search`]: trait.QueryServerTransaction.html#method.search + #[instrument(level = "debug", skip_all)] fn search_ext( &self, se: &SearchEvent, ) -> Result>, OperationError> { - spanned!("server::search_ext", { - /* - * This just wraps search, but it's for the external interface - * so as a result it also reduces the entry set's attributes at - * the end. - */ - let entries = self.search(se)?; + /* + * This just wraps search, but it's for the external interface + * so as a result it also reduces the entry set's attributes at + * the end. + */ + let entries = self.search(se)?; - let access = self.get_accesscontrols(); - access - .search_filter_entry_attributes(se, entries) - .map_err(|e| { - // Log and fail if something went wrong. - admin_error!(?e, "Failed to filter entry attributes"); - e - }) - // This now returns the reduced vec. - }) + let access = self.get_accesscontrols(); + access + .search_filter_entry_attributes(se, entries) + .map_err(|e| { + // Log and fail if something went wrong. + admin_error!(?e, "Failed to filter entry attributes"); + e + }) + // This now returns the reduced vec. } + #[instrument(level = "debug", skip_all)] fn search(&self, se: &SearchEvent) -> Result>, OperationError> { - spanned!("server::search", { - if se.ident.is_internal() { - trace!(internal_filter = ?se.filter, "search"); - } else { - security_info!(initiator = %se.ident, "search"); - admin_info!(external_filter = ?se.filter, "search"); - } + if se.ident.is_internal() { + trace!(internal_filter = ?se.filter, "search"); + } else { + security_info!(initiator = %se.ident, "search"); + admin_info!(external_filter = ?se.filter, "search"); + } - // This is an important security step because it prevents us from - // performing un-indexed searches on attr's that don't exist in the - // server. This is why ExtensibleObject can only take schema that - // exists in the server, not arbitrary attr names. - // - // This normalises and validates in a single step. - // - // NOTE: Filters are validated in event conversion. + // This is an important security step because it prevents us from + // performing un-indexed searches on attr's that don't exist in the + // server. This is why ExtensibleObject can only take schema that + // exists in the server, not arbitrary attr names. + // + // This normalises and validates in a single step. + // + // NOTE: Filters are validated in event conversion. - let resolve_filter_cache = self.get_resolve_filter_cache(); + let resolve_filter_cache = self.get_resolve_filter_cache(); - let be_txn = self.get_be_txn(); - let idxmeta = be_txn.get_idxmeta_ref(); - // Now resolve all references and indexes. - let vfr = spanned!("server::search", { - se.filter - .resolve(&se.ident, Some(idxmeta), Some(resolve_filter_cache)) - }) + let be_txn = self.get_be_txn(); + let idxmeta = be_txn.get_idxmeta_ref(); + // Now resolve all references and indexes. + let vfr = se + .filter + .resolve(&se.ident, Some(idxmeta), Some(resolve_filter_cache)) .map_err(|e| { admin_error!(?e, "search filter resolve failure"); e })?; - let lims = se.get_limits(); + let lims = se.get_limits(); - // NOTE: We currently can't build search plugins due to the inability to hand - // the QS wr/ro to the plugin trait. However, there shouldn't be a need for search - // plugins, because all data transforms should be in the write path. + // NOTE: We currently can't build search plugins due to the inability to hand + // the QS wr/ro to the plugin trait. However, there shouldn't be a need for search + // plugins, because all data transforms should be in the write path. - let res = self.get_be_txn().search(lims, &vfr).map_err(|e| { - admin_error!(?e, "backend failure"); - OperationError::Backend - })?; + let res = self.get_be_txn().search(lims, &vfr).map_err(|e| { + admin_error!(?e, "backend failure"); + OperationError::Backend + })?; - // Apply ACP before we let the plugins "have at it". - // WARNING; for external searches this is NOT the only - // ACP application. There is a second application to reduce the - // attribute set on the entries! - // - let access = self.get_accesscontrols(); - access.search_filter_entries(se, res).map_err(|e| { - admin_error!(?e, "Unable to access filter entries"); - e - }) + // Apply ACP before we let the plugins "have at it". + // WARNING; for external searches this is NOT the only + // ACP application. There is a second application to reduce the + // attribute set on the entries! + // + let access = self.get_accesscontrols(); + access.search_filter_entries(se, res).map_err(|e| { + admin_error!(?e, "Unable to access filter entries"); + e }) } + #[instrument(level = "debug", skip_all)] fn exists(&self, ee: &ExistsEvent) -> Result { - spanned!("server::exists", { - let be_txn = self.get_be_txn(); - let idxmeta = be_txn.get_idxmeta_ref(); + let be_txn = self.get_be_txn(); + let idxmeta = be_txn.get_idxmeta_ref(); - let resolve_filter_cache = self.get_resolve_filter_cache(); + let resolve_filter_cache = self.get_resolve_filter_cache(); - let vfr = ee - .filter - .resolve(&ee.ident, Some(idxmeta), Some(resolve_filter_cache)) - .map_err(|e| { - admin_error!(?e, "Failed to resolve filter"); - e - })?; + let vfr = ee + .filter + .resolve(&ee.ident, Some(idxmeta), Some(resolve_filter_cache)) + .map_err(|e| { + admin_error!(?e, "Failed to resolve filter"); + e + })?; - let lims = ee.get_limits(); + let lims = ee.get_limits(); - self.get_be_txn().exists(lims, &vfr).map_err(|e| { - admin_error!(?e, "backend failure"); - OperationError::Backend - }) + self.get_be_txn().exists(lims, &vfr).map_err(|e| { + admin_error!(?e, "backend failure"); + OperationError::Backend }) } @@ -327,42 +324,39 @@ pub trait QueryServerTransaction<'a> { } /// From internal, generate an "exists" event and dispatch + #[instrument(level = "debug", skip_all)] fn internal_exists(&self, filter: Filter) -> Result { - spanned!("server::internal_exists", { - // Check the filter - let f_valid = filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - // Build an exists event - let ee = ExistsEvent::new_internal(f_valid); - // Submit it - self.exists(&ee) - }) + // Check the filter + let f_valid = filter + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + // Build an exists event + let ee = ExistsEvent::new_internal(f_valid); + // Submit it + self.exists(&ee) } + #[instrument(level = "debug", skip_all)] fn internal_search( &self, filter: Filter, ) -> Result>, OperationError> { - spanned!("server::internal_search", { - let f_valid = filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - let se = SearchEvent::new_internal(f_valid); - self.search(&se) - }) + let f_valid = filter + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + let se = SearchEvent::new_internal(f_valid); + self.search(&se) } + #[instrument(level = "debug", skip_all)] fn impersonate_search_valid( &self, f_valid: Filter, f_intent_valid: Filter, event: &Identity, ) -> Result>, OperationError> { - spanned!("server::internal_search_valid", { - let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid); - self.search(&se) - }) + let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid); + self.search(&se) } /// Applies ACP to filter result entries. @@ -392,78 +386,73 @@ pub trait QueryServerTransaction<'a> { self.impersonate_search_valid(f_valid, f_intent_valid, event) } + #[instrument(level = "debug", skip_all)] fn impersonate_search_ext( &self, filter: Filter, filter_intent: Filter, event: &Identity, ) -> Result>, OperationError> { - spanned!("server::internal_search_ext_valid", { - let f_valid = filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - let f_intent_valid = filter_intent - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - self.impersonate_search_ext_valid(f_valid, f_intent_valid, event) - }) + let f_valid = filter + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + let f_intent_valid = filter_intent + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + self.impersonate_search_ext_valid(f_valid, f_intent_valid, event) } /// Get a single entry by its UUID. This is used heavily for internal /// server operations, especially in login and ACP checks. + #[instrument(level = "debug", skip_all)] fn internal_search_uuid( &self, uuid: &Uuid, ) -> Result, OperationError> { - spanned!("server::internal_search_uuid", { - let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); - let f_valid = spanned!("server::internal_search_uuid", { - filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation) // I feel like we should log this... - })?; - let se = SearchEvent::new_internal(f_valid); + let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); + let f_valid = filter.validate(self.get_schema()).map_err(|e| { + error!(?e, "Filter Validate - SchemaViolation"); + OperationError::SchemaViolation(e) + })?; + let se = SearchEvent::new_internal(f_valid); - let mut vs = self.search(&se)?; - match vs.pop() { - Some(entry) if vs.is_empty() => Ok(entry), - _ => Err(OperationError::NoMatchingEntries), - } - }) + let mut vs = self.search(&se)?; + match vs.pop() { + Some(entry) if vs.is_empty() => Ok(entry), + _ => Err(OperationError::NoMatchingEntries), + } } + #[instrument(level = "debug", skip_all)] fn impersonate_search_ext_uuid( &self, uuid: &Uuid, event: &Identity, ) -> Result, OperationError> { - spanned!("server::internal_search_ext_uuid", { - let filter_intent = filter_all!(f_eq("uuid", PartialValue::new_uuid(*uuid))); - let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); + let filter_intent = filter_all!(f_eq("uuid", PartialValue::new_uuid(*uuid))); + let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); - let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?; - match vs.pop() { - Some(entry) if vs.is_empty() => Ok(entry), - _ => Err(OperationError::NoMatchingEntries), - } - }) + let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?; + match vs.pop() { + Some(entry) if vs.is_empty() => Ok(entry), + _ => Err(OperationError::NoMatchingEntries), + } } + #[instrument(level = "debug", skip_all)] fn impersonate_search_uuid( &self, uuid: &Uuid, event: &Identity, ) -> Result, OperationError> { - spanned!("server::internal_search_uuid", { - let filter_intent = filter_all!(f_eq("uuid", PartialValue::new_uuid(*uuid))); - let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); + let filter_intent = filter_all!(f_eq("uuid", PartialValue::new_uuid(*uuid))); + let filter = filter!(f_eq("uuid", PartialValue::new_uuid(*uuid))); - let mut vs = self.impersonate_search(filter, filter_intent, event)?; - match vs.pop() { - Some(entry) if vs.is_empty() => Ok(entry), - _ => Err(OperationError::NoMatchingEntries), - } - }) + let mut vs = self.impersonate_search(filter, filter_intent, event)?; + match vs.pop() { + Some(entry) if vs.is_empty() => Ok(entry), + _ => Err(OperationError::NoMatchingEntries), + } } /// Do a schema aware conversion from a String:String to String:Value for modification @@ -807,20 +796,18 @@ pub trait QueryServerTransaction<'a> { // This is the core of the server, as it processes the entire event // applies all parts required in order and more. impl<'a> QueryServerTransaction<'a> for QueryServerReadTransaction<'a> { + type AccessControlsTransactionType = AccessControlsReadTransaction<'a>; type BackendTransactionType = BackendReadTransaction<'a>; + type SchemaTransactionType = SchemaReadTransaction; fn get_be_txn(&self) -> &BackendReadTransaction<'a> { &self.be_txn } - type SchemaTransactionType = SchemaReadTransaction; - fn get_schema(&self) -> &SchemaReadTransaction { &self.schema } - type AccessControlsTransactionType = AccessControlsReadTransaction<'a>; - fn get_accesscontrols(&self) -> &AccessControlsReadTransaction<'a> { &self.accesscontrols } @@ -890,20 +877,18 @@ impl<'a> QueryServerReadTransaction<'a> { // the entry changelogs are consistent to their entries. let schema = self.get_schema(); - spanned!("server::verify", { - let filt_all = filter!(f_pres("class")); - let all_entries = match self.internal_search(filt_all) { - Ok(a) => a, - Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)], - }; + let filt_all = filter!(f_pres("class")); + let all_entries = match self.internal_search(filt_all) { + Ok(a) => a, + Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)], + }; - for e in all_entries { - e.verify(schema, &mut results) - } + for e in all_entries { + e.verify(schema, &mut results) + } - // Verify the RUV to the entry changelogs now. - self.get_be_txn().verify_ruv(&mut results); - }); + // Verify the RUV to the entry changelogs now. + self.get_be_txn().verify_ruv(&mut results); // Ok entries passed, lets move on to the content. // Most of our checks are in the plugins, so we let them @@ -918,20 +903,18 @@ impl<'a> QueryServerReadTransaction<'a> { } impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> { + type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>; type BackendTransactionType = BackendWriteTransaction<'a>; + type SchemaTransactionType = SchemaWriteTransaction<'a>; fn get_be_txn(&self) -> &BackendWriteTransaction<'a> { &self.be_txn } - type SchemaTransactionType = SchemaWriteTransaction<'a>; - fn get_schema(&self) -> &SchemaWriteTransaction<'a> { &self.schema } - type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>; - fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> { &self.accesscontrols } @@ -1221,859 +1204,827 @@ impl QueryServer { } impl<'a> QueryServerWriteTransaction<'a> { + #[instrument(level = "debug", skip_all)] pub fn create(&self, ce: &CreateEvent) -> Result<(), OperationError> { - spanned!("server::create", { - // The create event is a raw, read only representation of the request - // that was made to us, including information about the identity - // performing the request. - if !ce.ident.is_internal() { - security_info!(name = %ce.ident, "create initiator"); - } + // The create event is a raw, read only representation of the request + // that was made to us, including information about the identity + // performing the request. + if !ce.ident.is_internal() { + security_info!(name = %ce.ident, "create initiator"); + } - // Log the request + // Log the request - // TODO #67: Do we need limits on number of creates, or do we constraint - // based on request size in the frontend? + // TODO #67: Do we need limits on number of creates, or do we constraint + // based on request size in the frontend? - // Copy the entries to a writeable form, this involves assigning a - // change id so we can track what's happening. - let candidates: Vec> = ce.entries.clone(); + // Copy the entries to a writeable form, this involves assigning a + // change id so we can track what's happening. + let candidates: Vec> = ce.entries.clone(); - // Do we have rights to perform these creates? - // create_allow_operation - let access = self.get_accesscontrols(); - let op_allow = access - .create_allow_operation(ce, &candidates) - .map_err(|e| { - admin_error!("Failed to check create access {:?}", e); - e - })?; - if !op_allow { - return Err(OperationError::AccessDenied); - } - - // Before we assign replication metadata, we need to assert these entries - // are valid to create within the set of replication transitions. This - // means they *can not* be recycled or tombstones! - if candidates.iter().any(|e| e.mask_recycled_ts().is_none()) { - admin_warn!("Refusing to create invalid entries that are attempting to bypass replication state machine."); - return Err(OperationError::AccessDenied); - } - - // Assign our replication metadata now, since we can proceed with this operation. - let mut candidates: Vec> = candidates - .into_iter() - .map(|e| e.assign_cid(self.cid.clone(), &self.schema)) - .collect(); - - // run any pre plugins, giving them the list of mutable candidates. - // pre-plugins are defined here in their correct order of calling! - // I have no intent to make these dynamic or configurable. - - Plugins::run_pre_create_transform(self, &mut candidates, ce).map_err(|e| { - admin_error!("Create operation failed (pre_transform plugin), {:?}", e); + // Do we have rights to perform these creates? + // create_allow_operation + let access = self.get_accesscontrols(); + let op_allow = access + .create_allow_operation(ce, &candidates) + .map_err(|e| { + admin_error!("Failed to check create access {:?}", e); e })?; + if !op_allow { + return Err(OperationError::AccessDenied); + } - // NOTE: This is how you map from Vec> to Result> - // remember, that you only get the first error and the iter terminates. + // Before we assign replication metadata, we need to assert these entries + // are valid to create within the set of replication transitions. This + // means they *can not* be recycled or tombstones! + if candidates.iter().any(|e| e.mask_recycled_ts().is_none()) { + admin_warn!("Refusing to create invalid entries that are attempting to bypass replication state machine."); + return Err(OperationError::AccessDenied); + } - // eprintln!("{:?}", candidates); + // Assign our replication metadata now, since we can proceed with this operation. + let mut candidates: Vec> = candidates + .into_iter() + .map(|e| e.assign_cid(self.cid.clone(), &self.schema)) + .collect(); - // Now, normalise AND validate! + // run any pre plugins, giving them the list of mutable candidates. + // pre-plugins are defined here in their correct order of calling! + // I have no intent to make these dynamic or configurable. - let res: Result>, OperationError> = candidates - .into_iter() - .map(|e| { - e.validate(&self.schema) - .map_err(|e| { - admin_error!("Schema Violation in create validate {:?}", e); - OperationError::SchemaViolation(e) - }) - .map(|e| { - // Then seal the changes? - e.seal(&self.schema) - }) - }) - .collect(); + Plugins::run_pre_create_transform(self, &mut candidates, ce).map_err(|e| { + admin_error!("Create operation failed (pre_transform plugin), {:?}", e); + e + })?; - let norm_cand: Vec> = res?; + // NOTE: This is how you map from Vec> to Result> + // remember, that you only get the first error and the iter terminates. - // Run any pre-create plugins now with schema validated entries. - // This is important for normalisation of certain types IE class - // or attributes for these checks. - Plugins::run_pre_create(self, &norm_cand, ce).map_err(|e| { - admin_error!("Create operation failed (plugin), {:?}", e); - e - })?; + // eprintln!("{:?}", candidates); - // We may change from ce.entries later to something else? - let commit_cand = self.be_txn.create(&self.cid, norm_cand).map_err(|e| { - admin_error!("betxn create failure {:?}", e); - e - })?; + // Now, normalise AND validate! - // Run any post plugins + let res: Result>, OperationError> = candidates + .into_iter() + .map(|e| { + e.validate(&self.schema) + .map_err(|e| { + admin_error!("Schema Violation in create validate {:?}", e); + OperationError::SchemaViolation(e) + }) + .map(|e| { + // Then seal the changes? + e.seal(&self.schema) + }) + }) + .collect(); - Plugins::run_post_create(self, &commit_cand, ce).map_err(|e| { - admin_error!("Create operation failed (post plugin), {:?}", e); - e - })?; + let norm_cand: Vec> = res?; - // We have finished all plugs and now have a successful operation - flag if - // schema or acp requires reload. - if !self.changed_schema.get() { - self.changed_schema.set(commit_cand.iter().any(|e| { - e.attribute_equality("class", &PVCLASS_CLASSTYPE) - || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) - })) - } - if !self.changed_acp.get() { - self.changed_acp.set( - commit_cand - .iter() - .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), - ) - } - if !self.changed_oauth2.get() { - self.changed_oauth2.set( - commit_cand - .iter() - .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), - ) - } - if !self.changed_domain.get() { - self.changed_domain.set( - commit_cand - .iter() - .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), - ) - } + // Run any pre-create plugins now with schema validated entries. + // This is important for normalisation of certain types IE class + // or attributes for these checks. + Plugins::run_pre_create(self, &norm_cand, ce).map_err(|e| { + admin_error!("Create operation failed (plugin), {:?}", e); + e + })?; - let cu = self.changed_uuid.as_ptr(); - unsafe { - (*cu).extend(commit_cand.iter().map(|e| e.get_uuid())); - } - trace!( - schema_reload = ?self.changed_schema, - acp_reload = ?self.changed_acp, - oauth2_reload = ?self.changed_oauth2, - domain_reload = ?self.changed_domain, - ); + // We may change from ce.entries later to something else? + let commit_cand = self.be_txn.create(&self.cid, norm_cand).map_err(|e| { + admin_error!("betxn create failure {:?}", e); + e + })?; - // We are complete, finalise logging and return + // Run any post plugins - if ce.ident.is_internal() { - trace!("Create operation success"); - } else { - admin_info!("Create operation success"); - } - Ok(()) - }) + Plugins::run_post_create(self, &commit_cand, ce).map_err(|e| { + admin_error!("Create operation failed (post plugin), {:?}", e); + e + })?; + + // We have finished all plugs and now have a successful operation - flag if + // schema or acp requires reload. + if !self.changed_schema.get() { + self.changed_schema.set(commit_cand.iter().any(|e| { + e.attribute_equality("class", &PVCLASS_CLASSTYPE) + || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) + })) + } + if !self.changed_acp.get() { + self.changed_acp.set( + commit_cand + .iter() + .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), + ) + } + if !self.changed_oauth2.get() { + self.changed_oauth2.set( + commit_cand + .iter() + .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), + ) + } + if !self.changed_domain.get() { + self.changed_domain.set( + commit_cand + .iter() + .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), + ) + } + + let cu = self.changed_uuid.as_ptr(); + unsafe { + (*cu).extend(commit_cand.iter().map(|e| e.get_uuid())); + } + trace!( + schema_reload = ?self.changed_schema, + acp_reload = ?self.changed_acp, + oauth2_reload = ?self.changed_oauth2, + domain_reload = ?self.changed_domain, + ); + + // We are complete, finalise logging and return + + if ce.ident.is_internal() { + trace!("Create operation success"); + } else { + admin_info!("Create operation success"); + } + Ok(()) } #[allow(clippy::cognitive_complexity)] + #[instrument(level = "debug", skip_all)] pub fn delete(&self, de: &DeleteEvent) -> Result<(), OperationError> { - spanned!("server::delete", { - // Do you have access to view all the set members? Reduce based on your - // read permissions and attrs - // THIS IS PRETTY COMPLEX SEE THE DESIGN DOC - // In this case we need a search, but not INTERNAL to keep the same - // associated credentials. - // We only need to retrieve uuid though ... - if !de.ident.is_internal() { - security_info!(name = %de.ident, "delete initiator"); - } + // Do you have access to view all the set members? Reduce based on your + // read permissions and attrs + // THIS IS PRETTY COMPLEX SEE THE DESIGN DOC + // In this case we need a search, but not INTERNAL to keep the same + // associated credentials. + // We only need to retrieve uuid though ... + if !de.ident.is_internal() { + security_info!(name = %de.ident, "delete initiator"); + } - // Now, delete only what you can see - let pre_candidates = self - .impersonate_search_valid(de.filter.clone(), de.filter_orig.clone(), &de.ident) - .map_err(|e| { - admin_error!("delete: error in pre-candidate selection {:?}", e); - e - })?; - - // Apply access controls to reduce the set if required. - // delete_allow_operation - let access = self.get_accesscontrols(); - let op_allow = access - .delete_allow_operation(de, &pre_candidates) - .map_err(|e| { - admin_error!("Failed to check delete access {:?}", e); - e - })?; - if !op_allow { - return Err(OperationError::AccessDenied); - } - - // Is the candidate set empty? - if pre_candidates.is_empty() { - request_error!(filter = ?de.filter, "delete: no candidates match filter"); - return Err(OperationError::NoMatchingEntries); - }; - - if pre_candidates.iter().any(|e| e.mask_tombstone().is_none()) { - admin_warn!("Refusing to delete entries which may be an attempt to bypass replication state machine."); - return Err(OperationError::AccessDenied); - } - - let mut candidates: Vec> = pre_candidates - .iter() - // Invalidate and assign change id's - .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) - .collect(); - - trace!(?candidates, "delete: candidates"); - - // Pre delete plugs - Plugins::run_pre_delete(self, &mut candidates, de).map_err(|e| { - admin_error!("Delete operation failed (plugin), {:?}", e); + // Now, delete only what you can see + let pre_candidates = self + .impersonate_search_valid(de.filter.clone(), de.filter_orig.clone(), &de.ident) + .map_err(|e| { + admin_error!("delete: error in pre-candidate selection {:?}", e); e })?; - trace!(?candidates, "delete: now marking candidates as recycled"); + // Apply access controls to reduce the set if required. + // delete_allow_operation + let access = self.get_accesscontrols(); + let op_allow = access + .delete_allow_operation(de, &pre_candidates) + .map_err(|e| { + admin_error!("Failed to check delete access {:?}", e); + e + })?; + if !op_allow { + return Err(OperationError::AccessDenied); + } - let res: Result>, OperationError> = candidates - .into_iter() - .map(|e| { - e.to_recycled() - .validate(&self.schema) - .map_err(|e| { - admin_error!(err = ?e, "Schema Violation in delete validate"); - OperationError::SchemaViolation(e) - }) - // seal if it worked. - .map(|e| e.seal(&self.schema)) - }) - .collect(); + // Is the candidate set empty? + if pre_candidates.is_empty() { + request_error!(filter = ?de.filter, "delete: no candidates match filter"); + return Err(OperationError::NoMatchingEntries); + }; - let del_cand: Vec> = res?; + if pre_candidates.iter().any(|e| e.mask_tombstone().is_none()) { + admin_warn!("Refusing to delete entries which may be an attempt to bypass replication state machine."); + return Err(OperationError::AccessDenied); + } - self.be_txn - .modify(&self.cid, &pre_candidates, &del_cand) - .map_err(|e| { - // be_txn is dropped, ie aborted here. - admin_error!("Delete operation failed (backend), {:?}", e); - e - })?; + let mut candidates: Vec> = pre_candidates + .iter() + // Invalidate and assign change id's + .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) + .collect(); - // Post delete plugins - Plugins::run_post_delete(self, &del_cand, de).map_err(|e| { - admin_error!("Delete operation failed (plugin), {:?}", e); + trace!(?candidates, "delete: candidates"); + + // Pre delete plugs + Plugins::run_pre_delete(self, &mut candidates, de).map_err(|e| { + admin_error!("Delete operation failed (plugin), {:?}", e); + e + })?; + + trace!(?candidates, "delete: now marking candidates as recycled"); + + let res: Result>, OperationError> = candidates + .into_iter() + .map(|e| { + e.to_recycled() + .validate(&self.schema) + .map_err(|e| { + admin_error!(err = ?e, "Schema Violation in delete validate"); + OperationError::SchemaViolation(e) + }) + // seal if it worked. + .map(|e| e.seal(&self.schema)) + }) + .collect(); + + let del_cand: Vec> = res?; + + self.be_txn + .modify(&self.cid, &pre_candidates, &del_cand) + .map_err(|e| { + // be_txn is dropped, ie aborted here. + admin_error!("Delete operation failed (backend), {:?}", e); e })?; - // We have finished all plugs and now have a successful operation - flag if - // schema or acp requires reload. - if !self.changed_schema.get() { - self.changed_schema.set(del_cand.iter().any(|e| { - e.attribute_equality("class", &PVCLASS_CLASSTYPE) - || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) - })) - } - if !self.changed_acp.get() { - self.changed_acp.set( - del_cand - .iter() - .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), - ) - } - if !self.changed_oauth2.get() { - self.changed_oauth2.set( - del_cand - .iter() - .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), - ) - } - if !self.changed_domain.get() { - self.changed_domain.set( - del_cand - .iter() - .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), - ) - } + // Post delete plugins + Plugins::run_post_delete(self, &del_cand, de).map_err(|e| { + admin_error!("Delete operation failed (plugin), {:?}", e); + e + })?; - let cu = self.changed_uuid.as_ptr(); - unsafe { - (*cu).extend(del_cand.iter().map(|e| e.get_uuid())); - } + // We have finished all plugs and now have a successful operation - flag if + // schema or acp requires reload. + if !self.changed_schema.get() { + self.changed_schema.set(del_cand.iter().any(|e| { + e.attribute_equality("class", &PVCLASS_CLASSTYPE) + || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) + })) + } + if !self.changed_acp.get() { + self.changed_acp.set( + del_cand + .iter() + .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), + ) + } + if !self.changed_oauth2.get() { + self.changed_oauth2.set( + del_cand + .iter() + .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), + ) + } + if !self.changed_domain.get() { + self.changed_domain.set( + del_cand + .iter() + .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), + ) + } - trace!( - schema_reload = ?self.changed_schema, - acp_reload = ?self.changed_acp, - oauth2_reload = ?self.changed_oauth2, - domain_reload = ?self.changed_domain, - ); + let cu = self.changed_uuid.as_ptr(); + unsafe { + (*cu).extend(del_cand.iter().map(|e| e.get_uuid())); + } - // Send result - if de.ident.is_internal() { - trace!("Delete operation success"); - } else { - admin_info!("Delete operation success"); - } - Ok(()) - }) + trace!( + schema_reload = ?self.changed_schema, + acp_reload = ?self.changed_acp, + oauth2_reload = ?self.changed_oauth2, + domain_reload = ?self.changed_domain, + ); + + // Send result + if de.ident.is_internal() { + trace!("Delete operation success"); + } else { + admin_info!("Delete operation success"); + } + Ok(()) } + #[instrument(level = "debug", skip_all)] pub fn purge_tombstones(&self) -> Result<(), OperationError> { - spanned!("server::purge_tombstones", { - // purge everything that is a tombstone. - let cid = self.cid.sub_secs(CHANGELOG_MAX_AGE).map_err(|e| { - admin_error!("Unable to generate search cid {:?}", e); - e - })?; + // purge everything that is a tombstone. + let cid = self.cid.sub_secs(CHANGELOG_MAX_AGE).map_err(|e| { + admin_error!("Unable to generate search cid {:?}", e); + e + })?; - // Delete them - this is a TRUE delete, no going back now! - self.be_txn - .reap_tombstones(&cid) - .map_err(|e| { - admin_error!(err = ?e, "Tombstone purge operation failed (backend)"); - e - }) - .map(|_| { - admin_info!("Tombstone purge operation success"); - }) - }) + // Delete them - this is a TRUE delete, no going back now! + self.be_txn + .reap_tombstones(&cid) + .map_err(|e| { + admin_error!(err = ?e, "Tombstone purge operation failed (backend)"); + e + }) + .map(|_| { + admin_info!("Tombstone purge operation success"); + }) } + #[instrument(level = "debug", skip_all)] pub fn purge_recycled(&self) -> Result<(), OperationError> { - spanned!("server::purge_recycled", { - // Send everything that is recycled to tombstone - // Search all recycled - let cid = self.cid.sub_secs(RECYCLEBIN_MAX_AGE).map_err(|e| { - admin_error!(err = ?e, "Unable to generate search cid"); + // Send everything that is recycled to tombstone + // Search all recycled + let cid = self.cid.sub_secs(RECYCLEBIN_MAX_AGE).map_err(|e| { + admin_error!(err = ?e, "Unable to generate search cid"); + e + })?; + let rc = self.internal_search(filter_all!(f_and!([ + f_eq("class", PVCLASS_RECYCLED.clone()), + f_lt("last_modified_cid", PartialValue::new_cid(cid)), + ])))?; + + if rc.is_empty() { + admin_info!("No recycled present - purge operation success"); + return Ok(()); + } + + // Modify them to strip all avas except uuid + let tombstone_cand: Result, _> = rc + .iter() + .map(|e| { + e.to_tombstone(self.cid.clone()) + .validate(&self.schema) + .map_err(|e| { + admin_error!("Schema Violation in purge_recycled validate: {:?}", e); + OperationError::SchemaViolation(e) + }) + // seal if it worked. + .map(|e| e.seal(&self.schema)) + }) + .collect(); + + let tombstone_cand = tombstone_cand?; + + // Backend Modify + self.be_txn + .modify(&self.cid, &rc, &tombstone_cand) + .map_err(|e| { + admin_error!("Purge recycled operation failed (backend), {:?}", e); e - })?; - let rc = self.internal_search(filter_all!(f_and!([ - f_eq("class", PVCLASS_RECYCLED.clone()), - f_lt("last_modified_cid", PartialValue::new_cid(cid)), - ])))?; - - if rc.is_empty() { - admin_info!("No recycled present - purge operation success"); - return Ok(()); - } - - // Modify them to strip all avas except uuid - let tombstone_cand: Result, _> = rc - .iter() - .map(|e| { - e.to_tombstone(self.cid.clone()) - .validate(&self.schema) - .map_err(|e| { - admin_error!("Schema Violation in purge_recycled validate: {:?}", e); - OperationError::SchemaViolation(e) - }) - // seal if it worked. - .map(|e| e.seal(&self.schema)) - }) - .collect(); - - let tombstone_cand = tombstone_cand?; - - // Backend Modify - self.be_txn - .modify(&self.cid, &rc, &tombstone_cand) - .map_err(|e| { - admin_error!("Purge recycled operation failed (backend), {:?}", e); - e - }) - .map(|_| { - admin_info!("Purge recycled operation success"); - }) - }) + }) + .map(|_| { + admin_info!("Purge recycled operation success"); + }) } - // Should this take a revive event? + #[instrument(level = "debug", skip_all)] pub fn revive_recycled(&self, re: &ReviveRecycledEvent) -> Result<(), OperationError> { - spanned!("server::revive_recycled", { - // Revive an entry to live. This is a specialised function, and draws a lot of - // inspiration from modify. - // - // Access is granted by the ability to ability to search the class=recycled - // and the ability modify + remove that class from the object. - if !re.ident.is_internal() { - security_info!(name = %re.ident, "revive initiator"); + // Revive an entry to live. This is a specialised function, and draws a lot of + // inspiration from modify. + // + // Access is granted by the ability to ability to search the class=recycled + // and the ability modify + remove that class from the object. + if !re.ident.is_internal() { + security_info!(name = %re.ident, "revive initiator"); + } + + // Get the list of pre_candidates, using impersonate search. + let pre_candidates = + self.impersonate_search_valid(re.filter.clone(), re.filter.clone(), &re.ident)?; + + // Is the list empty? + if pre_candidates.is_empty() { + if re.ident.is_internal() { + trace!( + "revive: no candidates match filter ... continuing {:?}", + re.filter + ); + return Ok(()); + } else { + request_error!( + "revive: no candidates match filter, failure {:?}", + re.filter + ); + return Err(OperationError::NoMatchingEntries); } + }; - // Get the list of pre_candidates, using impersonate search. - let pre_candidates = - self.impersonate_search_valid(re.filter.clone(), re.filter.clone(), &re.ident)?; + trace!("revive: pre_candidates -> {:?}", pre_candidates); - // Is the list empty? - if pre_candidates.is_empty() { - if re.ident.is_internal() { - trace!( - "revive: no candidates match filter ... continuing {:?}", - re.filter - ); - return Ok(()); - } else { - request_error!( - "revive: no candidates match filter, failure {:?}", - re.filter - ); - return Err(OperationError::NoMatchingEntries); - } - }; + // Check access against a "fake" modify. + let modlist = ModifyList::new_list(vec![Modify::Removed( + AttrString::from("class"), + PVCLASS_RECYCLED.clone(), + )]); - trace!("revive: pre_candidates -> {:?}", pre_candidates); + let m_valid = modlist.validate(self.get_schema()).map_err(|e| { + admin_error!("revive recycled modlist Schema Violation {:?}", e); + OperationError::SchemaViolation(e) + })?; - // Check access against a "fake" modify. - let modlist = ModifyList::new_list(vec![Modify::Removed( - AttrString::from("class"), - PVCLASS_RECYCLED.clone(), - )]); + let me = + ModifyEvent::new_impersonate(&re.ident, re.filter.clone(), re.filter.clone(), m_valid); - let m_valid = modlist.validate(self.get_schema()).map_err(|e| { - admin_error!("revive recycled modlist Schema Violation {:?}", e); - OperationError::SchemaViolation(e) - })?; - - let me = ModifyEvent::new_impersonate( - &re.ident, - re.filter.clone(), - re.filter.clone(), - m_valid, - ); - - let access = self.get_accesscontrols(); - let op_allow = access - .modify_allow_operation(&me, &pre_candidates) - .map_err(|e| { - admin_error!("Unable to check modify access {:?}", e); - e - })?; - if !op_allow { - return Err(OperationError::AccessDenied); - } - - // Are all of the entries actually recycled? - if pre_candidates.iter().all(|e| e.mask_recycled().is_some()) { - admin_warn!("Refusing to revive entries that are already live!"); - return Err(OperationError::AccessDenied); - } - - // Build the list of mods from directmo, to revive memberships. - let mut dm_mods: HashMap> = - HashMap::with_capacity(pre_candidates.len()); - - for e in &pre_candidates { - // Get this entries uuid. - let u: Uuid = e.get_uuid(); - - if let Some(riter) = e.get_ava_as_refuuid("directmemberof") { - for g_uuid in riter { - dm_mods - .entry(g_uuid) - .and_modify(|mlist| { - let m = Modify::Present( - AttrString::from("member"), - Value::new_refer_r(&u), - ); - mlist.push_mod(m); - }) - .or_insert({ - let m = Modify::Present( - AttrString::from("member"), - Value::new_refer_r(&u), - ); - ModifyList::new_list(vec![m]) - }); - } - } - } - - // clone the writeable entries. - let mut candidates: Vec> = pre_candidates - .iter() - .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) - // Mutate to apply the revive. - .map(|er| er.to_revived()) - .collect(); - - // Are they all revived? - if candidates.iter().all(|e| e.mask_recycled().is_none()) { - admin_error!("Not all candidates were correctly revived, unable to proceed"); - return Err(OperationError::InvalidEntryState); - } - - // Do we need to apply pre-mod? - // Very likely, incase domain has renamed etc. - Plugins::run_pre_modify(self, &mut candidates, &me).map_err(|e| { - admin_error!("Revive operation failed (plugin), {:?}", e); + let access = self.get_accesscontrols(); + let op_allow = access + .modify_allow_operation(&me, &pre_candidates) + .map_err(|e| { + admin_error!("Unable to check modify access {:?}", e); e })?; + if !op_allow { + return Err(OperationError::AccessDenied); + } - // Schema validate - let res: Result>, OperationError> = candidates - .into_iter() - .map(|e| { - e.validate(&self.schema) - .map_err(|e| { - admin_error!("Schema Violation {:?}", e); - OperationError::SchemaViolation(e) + // Are all of the entries actually recycled? + if pre_candidates.iter().all(|e| e.mask_recycled().is_some()) { + admin_warn!("Refusing to revive entries that are already live!"); + return Err(OperationError::AccessDenied); + } + + // Build the list of mods from directmo, to revive memberships. + let mut dm_mods: HashMap> = + HashMap::with_capacity(pre_candidates.len()); + + for e in &pre_candidates { + // Get this entries uuid. + let u: Uuid = e.get_uuid(); + + if let Some(riter) = e.get_ava_as_refuuid("directmemberof") { + for g_uuid in riter { + dm_mods + .entry(g_uuid) + .and_modify(|mlist| { + let m = + Modify::Present(AttrString::from("member"), Value::new_refer_r(&u)); + mlist.push_mod(m); }) - .map(|e| e.seal(&self.schema)) - }) - .collect(); - - let norm_cand: Vec> = res?; - - // build the mod partial - let mp = ModifyPartial { - norm_cand, - pre_candidates, - me: &me, - }; - - // Call modify_apply - self.modify_apply(mp)?; - - // If and only if that succeeds, apply the direct membership modifications - // if possible. - for (g, mods) in dm_mods { - // I think the filter/filter_all shouldn't matter here because the only - // valid direct memberships should be still valid/live references, as refint - // removes anything that was deleted even from recycled entries. - let f = filter_all!(f_eq("uuid", PartialValue::new_uuid(g))); - self.internal_modify(&f, &mods)?; - } - - Ok(()) - }) - } - - // Should this take a revive event? - pub fn revive_recycled_legacy(&self, re: &ReviveRecycledEvent) -> Result<(), OperationError> { - spanned!("server::revive_recycled", { - // Revive an entry to live. This is a specialised function, and draws a lot of - // inspiration from modify. - // - // - // Access is granted by the ability to ability to search the class=recycled - // and the ability modify + remove that class from the object. - - // create the modify for access testing. - // tl;dr, remove the class=recycled - let modlist = ModifyList::new_list(vec![Modify::Removed( - AttrString::from("class"), - PVCLASS_RECYCLED.clone(), - )]); - - let m_valid = modlist.validate(self.get_schema()).map_err(|e| { - admin_error!( - "Schema Violation in revive recycled modlist validate: {:?}", - e - ); - OperationError::SchemaViolation(e) - })?; - - // Get the entries we are about to revive. - // we make a set of per-entry mod lists. A list of lists even ... - let revive_cands = - self.impersonate_search_valid(re.filter.clone(), re.filter.clone(), &re.ident)?; - - let mut dm_mods: HashMap> = - HashMap::with_capacity(revive_cands.len()); - - for e in revive_cands { - // Get this entries uuid. - let u: Uuid = e.get_uuid(); - - if let Some(riter) = e.get_ava_as_refuuid("directmemberof") { - for g_uuid in riter { - dm_mods - .entry(g_uuid) - .and_modify(|mlist| { - let m = Modify::Present( - AttrString::from("member"), - Value::new_refer_r(&u), - ); - mlist.push_mod(m); - }) - .or_insert({ - let m = Modify::Present( - AttrString::from("member"), - Value::new_refer_r(&u), - ); - ModifyList::new_list(vec![m]) - }); - } + .or_insert({ + let m = + Modify::Present(AttrString::from("member"), Value::new_refer_r(&u)); + ModifyList::new_list(vec![m]) + }); } } + } - // Now impersonate the modify - self.impersonate_modify_valid( - re.filter.clone(), - re.filter.clone(), - m_valid, - &re.ident, - )?; - // If and only if that succeeds, apply the direct membership modifications - // if possible. - for (g, mods) in dm_mods { - // I think the filter/filter_all shouldn't matter here because the only - // valid direct memberships should be still valid/live references. - let f = filter_all!(f_eq("uuid", PartialValue::new_uuid(g))); - self.internal_modify(&f, &mods)?; + // clone the writeable entries. + let mut candidates: Vec> = pre_candidates + .iter() + .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) + // Mutate to apply the revive. + .map(|er| er.to_revived()) + .collect(); + + // Are they all revived? + if candidates.iter().all(|e| e.mask_recycled().is_none()) { + admin_error!("Not all candidates were correctly revived, unable to proceed"); + return Err(OperationError::InvalidEntryState); + } + + // Do we need to apply pre-mod? + // Very likely, incase domain has renamed etc. + Plugins::run_pre_modify(self, &mut candidates, &me).map_err(|e| { + admin_error!("Revive operation failed (plugin), {:?}", e); + e + })?; + + // Schema validate + let res: Result>, OperationError> = candidates + .into_iter() + .map(|e| { + e.validate(&self.schema) + .map_err(|e| { + admin_error!("Schema Violation {:?}", e); + OperationError::SchemaViolation(e) + }) + .map(|e| e.seal(&self.schema)) + }) + .collect(); + + let norm_cand: Vec> = res?; + + // build the mod partial + let mp = ModifyPartial { + norm_cand, + pre_candidates, + me: &me, + }; + + // Call modify_apply + self.modify_apply(mp)?; + + // If and only if that succeeds, apply the direct membership modifications + // if possible. + for (g, mods) in dm_mods { + // I think the filter/filter_all shouldn't matter here because the only + // valid direct memberships should be still valid/live references, as refint + // removes anything that was deleted even from recycled entries. + let f = filter_all!(f_eq("uuid", PartialValue::new_uuid(g))); + self.internal_modify(&f, &mods)?; + } + + Ok(()) + } + + #[instrument(level = "debug", skip_all)] + pub fn revive_recycled_legacy(&self, re: &ReviveRecycledEvent) -> Result<(), OperationError> { + // Revive an entry to live. This is a specialised function, and draws a lot of + // inspiration from modify. + // + // + // Access is granted by the ability to ability to search the class=recycled + // and the ability modify + remove that class from the object. + + // create the modify for access testing. + // tl;dr, remove the class=recycled + let modlist = ModifyList::new_list(vec![Modify::Removed( + AttrString::from("class"), + PVCLASS_RECYCLED.clone(), + )]); + + let m_valid = modlist.validate(self.get_schema()).map_err(|e| { + admin_error!( + "Schema Violation in revive recycled modlist validate: {:?}", + e + ); + OperationError::SchemaViolation(e) + })?; + + // Get the entries we are about to revive. + // we make a set of per-entry mod lists. A list of lists even ... + let revive_cands = + self.impersonate_search_valid(re.filter.clone(), re.filter.clone(), &re.ident)?; + + let mut dm_mods: HashMap> = + HashMap::with_capacity(revive_cands.len()); + + for e in revive_cands { + // Get this entries uuid. + let u: Uuid = e.get_uuid(); + + if let Some(riter) = e.get_ava_as_refuuid("directmemberof") { + for g_uuid in riter { + dm_mods + .entry(g_uuid) + .and_modify(|mlist| { + let m = + Modify::Present(AttrString::from("member"), Value::new_refer_r(&u)); + mlist.push_mod(m); + }) + .or_insert({ + let m = + Modify::Present(AttrString::from("member"), Value::new_refer_r(&u)); + ModifyList::new_list(vec![m]) + }); + } } - Ok(()) - }) + } + + // Now impersonate the modify + self.impersonate_modify_valid(re.filter.clone(), re.filter.clone(), m_valid, &re.ident)?; + // If and only if that succeeds, apply the direct membership modifications + // if possible. + for (g, mods) in dm_mods { + // I think the filter/filter_all shouldn't matter here because the only + // valid direct memberships should be still valid/live references. + let f = filter_all!(f_eq("uuid", PartialValue::new_uuid(g))); + self.internal_modify(&f, &mods)?; + } + Ok(()) } /// Unsafety: This is unsafe because you need to be careful about how you handle and check /// the Ok(None) case which occurs during internal operations, and that you DO NOT re-order /// and call multiple pre-applies at the same time, else you can cause DB corruption. + #[instrument(level = "debug", skip_all)] pub(crate) unsafe fn modify_pre_apply<'x>( &self, me: &'x ModifyEvent, ) -> Result>, OperationError> { - spanned!("server::modify_pre_apply", { - // Get the candidates. - // Modify applies a modlist to a filter, so we need to internal search - // then apply. - if !me.ident.is_internal() { - security_info!(name = %me.ident, "modify initiator"); - } + // Get the candidates. + // Modify applies a modlist to a filter, so we need to internal search + // then apply. + if !me.ident.is_internal() { + security_info!(name = %me.ident, "modify initiator"); + } - // Validate input. + // Validate input. - // Is the modlist non zero? - if me.modlist.is_empty() { - request_error!("modify: empty modify request"); - return Err(OperationError::EmptyRequest); - } + // Is the modlist non zero? + if me.modlist.is_empty() { + request_error!("modify: empty modify request"); + return Err(OperationError::EmptyRequest); + } - // Is the modlist valid? - // This is now done in the event transform + // Is the modlist valid? + // This is now done in the event transform - // Is the filter invalid to schema? - // This is now done in the event transform + // Is the filter invalid to schema? + // This is now done in the event transform - // This also checks access controls due to use of the impersonation. - let pre_candidates = self - .impersonate_search_valid(me.filter.clone(), me.filter_orig.clone(), &me.ident) - .map_err(|e| { - admin_error!("modify: error in pre-candidate selection {:?}", e); - e - })?; - - if pre_candidates.is_empty() { - if me.ident.is_internal() { - trace!( - "modify: no candidates match filter ... continuing {:?}", - me.filter - ); - return Ok(None); - } else { - request_error!( - "modify: no candidates match filter, failure {:?}", - me.filter - ); - return Err(OperationError::NoMatchingEntries); - } - }; - - trace!("modify: pre_candidates -> {:?}", pre_candidates); - trace!("modify: modlist -> {:?}", me.modlist); - - // Are we allowed to make the changes we want to? - // modify_allow_operation - let access = self.get_accesscontrols(); - let op_allow = access - .modify_allow_operation(me, &pre_candidates) - .map_err(|e| { - admin_error!("Unable to check modify access {:?}", e); - e - })?; - if !op_allow { - return Err(OperationError::AccessDenied); - } - - // Clone a set of writeables. - // Apply the modlist -> Remember, we have a set of origs - // and the new modified ents. - let mut candidates: Vec> = pre_candidates - .iter() - .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) - .collect(); - - candidates - .iter_mut() - .for_each(|er| er.apply_modlist(&me.modlist)); - - trace!("modify: candidates -> {:?}", candidates); - - // Did any of the candidates now become masked? - if candidates.iter().any(|e| e.mask_recycled_ts().is_none()) { - admin_warn!("Refusing to apply modifications that are attempting to bypass replication state machine."); - return Err(OperationError::AccessDenied); - } - - // Pre mod plugins - // We should probably supply the pre-post cands here. - Plugins::run_pre_modify(self, &mut candidates, me).map_err(|e| { - admin_error!("Pre-Modify operation failed (plugin), {:?}", e); + // This also checks access controls due to use of the impersonation. + let pre_candidates = self + .impersonate_search_valid(me.filter.clone(), me.filter_orig.clone(), &me.ident) + .map_err(|e| { + admin_error!("modify: error in pre-candidate selection {:?}", e); e })?; - // NOTE: There is a potential optimisation here, where if - // candidates == pre-candidates, then we don't need to store anything - // because we effectively just did an assert. However, like all - // optimisations, this could be premature - so we for now, just - // do the CORRECT thing and recommit as we may find later we always - // want to add CSN's or other. - - let res: Result>, OperationError> = candidates - .into_iter() - .map(|entry| { - entry - .validate(&self.schema) - .map_err(|e| { - admin_error!( - "Schema Violation in validation of modify_pre_apply {:?}", - e - ); - OperationError::SchemaViolation(e) - }) - .map(|entry| entry.seal(&self.schema)) - }) - .collect(); - - let norm_cand: Vec> = res?; - - Ok(Some(ModifyPartial { - norm_cand, - pre_candidates, - me, - })) - }) - } - - pub(crate) fn modify_apply(&self, mp: ModifyPartial<'_>) -> Result<(), OperationError> { - spanned!("server::modify_apply", { - let ModifyPartial { - norm_cand, - pre_candidates, - me, - } = mp; - - // Backend Modify - self.be_txn - .modify(&self.cid, &pre_candidates, &norm_cand) - .map_err(|e| { - admin_error!("Modify operation failed (backend), {:?}", e); - e - })?; - - // Post Plugins - // - // memberOf actually wants the pre cand list and the norm_cand list to see what - // changed. Could be optimised, but this is correct still ... - Plugins::run_post_modify(self, &pre_candidates, &norm_cand, me).map_err(|e| { - admin_error!("Post-Modify operation failed (plugin), {:?}", e); - e - })?; - - // We have finished all plugs and now have a successful operation - flag if - // schema or acp requires reload. Remember, this is a modify, so we need to check - // pre and post cands. - if !self.changed_schema.get() { - self.changed_schema.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| { - e.attribute_equality("class", &PVCLASS_CLASSTYPE) - || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) - }), - ) - } - if !self.changed_acp.get() { - self.changed_acp.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), - ) - } - if !self.changed_oauth2.get() { - self.changed_oauth2.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), - ) - } - if !self.changed_domain.get() { - self.changed_domain.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), - ) - } - - let cu = self.changed_uuid.as_ptr(); - unsafe { - (*cu).extend( - norm_cand - .iter() - .map(|e| e.get_uuid()) - .chain(pre_candidates.iter().map(|e| e.get_uuid())), - ); - } - - trace!( - schema_reload = ?self.changed_schema, - acp_reload = ?self.changed_acp, - oauth2_reload = ?self.changed_oauth2, - domain_reload = ?self.changed_domain, - ); - - // return + if pre_candidates.is_empty() { if me.ident.is_internal() { - trace!("Modify operation success"); + trace!( + "modify: no candidates match filter ... continuing {:?}", + me.filter + ); + return Ok(None); } else { - admin_info!("Modify operation success"); + request_error!( + "modify: no candidates match filter, failure {:?}", + me.filter + ); + return Err(OperationError::NoMatchingEntries); } - Ok(()) - }) + }; + + trace!("modify: pre_candidates -> {:?}", pre_candidates); + trace!("modify: modlist -> {:?}", me.modlist); + + // Are we allowed to make the changes we want to? + // modify_allow_operation + let access = self.get_accesscontrols(); + let op_allow = access + .modify_allow_operation(me, &pre_candidates) + .map_err(|e| { + admin_error!("Unable to check modify access {:?}", e); + e + })?; + if !op_allow { + return Err(OperationError::AccessDenied); + } + + // Clone a set of writeables. + // Apply the modlist -> Remember, we have a set of origs + // and the new modified ents. + let mut candidates: Vec> = pre_candidates + .iter() + .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) + .collect(); + + candidates + .iter_mut() + .for_each(|er| er.apply_modlist(&me.modlist)); + + trace!("modify: candidates -> {:?}", candidates); + + // Did any of the candidates now become masked? + if candidates.iter().any(|e| e.mask_recycled_ts().is_none()) { + admin_warn!("Refusing to apply modifications that are attempting to bypass replication state machine."); + return Err(OperationError::AccessDenied); + } + + // Pre mod plugins + // We should probably supply the pre-post cands here. + Plugins::run_pre_modify(self, &mut candidates, me).map_err(|e| { + admin_error!("Pre-Modify operation failed (plugin), {:?}", e); + e + })?; + + // NOTE: There is a potential optimisation here, where if + // candidates == pre-candidates, then we don't need to store anything + // because we effectively just did an assert. However, like all + // optimisations, this could be premature - so we for now, just + // do the CORRECT thing and recommit as we may find later we always + // want to add CSN's or other. + + let res: Result>, OperationError> = candidates + .into_iter() + .map(|entry| { + entry + .validate(&self.schema) + .map_err(|e| { + admin_error!("Schema Violation in validation of modify_pre_apply {:?}", e); + OperationError::SchemaViolation(e) + }) + .map(|entry| entry.seal(&self.schema)) + }) + .collect(); + + let norm_cand: Vec> = res?; + + Ok(Some(ModifyPartial { + norm_cand, + pre_candidates, + me, + })) } + #[instrument(level = "debug", skip_all)] + pub(crate) fn modify_apply(&self, mp: ModifyPartial<'_>) -> Result<(), OperationError> { + let ModifyPartial { + norm_cand, + pre_candidates, + me, + } = mp; + + // Backend Modify + self.be_txn + .modify(&self.cid, &pre_candidates, &norm_cand) + .map_err(|e| { + admin_error!("Modify operation failed (backend), {:?}", e); + e + })?; + + // Post Plugins + // + // memberOf actually wants the pre cand list and the norm_cand list to see what + // changed. Could be optimised, but this is correct still ... + Plugins::run_post_modify(self, &pre_candidates, &norm_cand, me).map_err(|e| { + admin_error!("Post-Modify operation failed (plugin), {:?}", e); + e + })?; + + // We have finished all plugs and now have a successful operation - flag if + // schema or acp requires reload. Remember, this is a modify, so we need to check + // pre and post cands. + if !self.changed_schema.get() { + self.changed_schema.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| { + e.attribute_equality("class", &PVCLASS_CLASSTYPE) + || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) + }), + ) + } + if !self.changed_acp.get() { + self.changed_acp.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), + ) + } + if !self.changed_oauth2.get() { + self.changed_oauth2.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), + ) + } + if !self.changed_domain.get() { + self.changed_domain.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), + ) + } + + let cu = self.changed_uuid.as_ptr(); + unsafe { + (*cu).extend( + norm_cand + .iter() + .map(|e| e.get_uuid()) + .chain(pre_candidates.iter().map(|e| e.get_uuid())), + ); + } + + trace!( + schema_reload = ?self.changed_schema, + acp_reload = ?self.changed_acp, + oauth2_reload = ?self.changed_oauth2, + domain_reload = ?self.changed_domain, + ); + + // return + if me.ident.is_internal() { + trace!("Modify operation success"); + } else { + admin_info!("Modify operation success"); + } + Ok(()) + } + + #[instrument(level = "debug", skip_all)] pub fn modify(&self, me: &ModifyEvent) -> Result<(), OperationError> { - spanned!("server::modify", { - let mp = unsafe { self.modify_pre_apply(me)? }; - if let Some(mp) = mp { - self.modify_apply(mp) - } else { - // No action to apply, the pre-apply said nothing to be done. - Ok(()) - } - }) + let mp = unsafe { self.modify_pre_apply(me)? }; + if let Some(mp) = mp { + self.modify_apply(mp) + } else { + // No action to apply, the pre-apply said nothing to be done. + Ok(()) + } } /// Used in conjunction with internal_batch_modify, to get a pre/post /// pair, where post is pre-configured with metadata to allow /// modificiation before submit back to internal_batch_modify + #[instrument(level = "debug", skip_all)] pub(crate) fn internal_search_writeable( &self, filter: &Filter, ) -> Result, OperationError> { - spanned!("server::internal_search_writeable", { - let f_valid = filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - let se = SearchEvent::new_internal(f_valid); - self.search(&se).map(|vs| { - vs.into_iter() - .map(|e| { - let writeable = e.as_ref().clone().invalidate(self.cid.clone()); - (e, writeable) - }) - .collect() - }) + let f_valid = filter + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + let se = SearchEvent::new_internal(f_valid); + self.search(&se).map(|vs| { + vs.into_iter() + .map(|e| { + let writeable = e.as_ref().clone().invalidate(self.cid.clone()); + (e, writeable) + }) + .collect() }) } @@ -2083,113 +2034,112 @@ impl<'a> QueryServerWriteTransaction<'a> { /// uphold all other plugin and state rules that are important. You /// probably want modify instead. #[allow(clippy::needless_pass_by_value)] + #[instrument(level = "debug", skip_all)] pub(crate) fn internal_batch_modify( &self, pre_candidates: Vec>, candidates: Vec>, ) -> Result<(), OperationError> { - spanned!("server::internal_batch_modify", { - if pre_candidates.is_empty() && candidates.is_empty() { - // No action needed. - return Ok(()); - } + if pre_candidates.is_empty() && candidates.is_empty() { + // No action needed. + return Ok(()); + } - if pre_candidates.len() != candidates.len() { - admin_error!("internal_batch_modify - cand lengths differ"); - return Err(OperationError::InvalidRequestState); - } + if pre_candidates.len() != candidates.len() { + admin_error!("internal_batch_modify - cand lengths differ"); + return Err(OperationError::InvalidRequestState); + } - let res: Result>, OperationError> = candidates - .into_iter() - .map(|e| { - e.validate(&self.schema) - .map_err(|e| { - admin_error!( - "Schema Violation in internal_batch_modify validate: {:?}", - e - ); - OperationError::SchemaViolation(e) - }) - .map(|e| e.seal(&self.schema)) - }) - .collect(); + let res: Result>, OperationError> = candidates + .into_iter() + .map(|e| { + e.validate(&self.schema) + .map_err(|e| { + admin_error!( + "Schema Violation in internal_batch_modify validate: {:?}", + e + ); + OperationError::SchemaViolation(e) + }) + .map(|e| e.seal(&self.schema)) + }) + .collect(); - let norm_cand: Vec> = res?; + let norm_cand: Vec> = res?; - if cfg!(debug_assertions) { - pre_candidates - .iter() - .zip(norm_cand.iter()) - .try_for_each(|(pre, post)| { - if pre.get_uuid() == post.get_uuid() { - Ok(()) - } else { - admin_error!("modify - cand sets not correctly aligned"); - Err(OperationError::InvalidRequestState) - } - })?; - } - - // Backend Modify - self.be_txn - .modify(&self.cid, &pre_candidates, &norm_cand) - .map_err(|e| { - admin_error!("Modify operation failed (backend), {:?}", e); - e + if cfg!(debug_assertions) { + pre_candidates + .iter() + .zip(norm_cand.iter()) + .try_for_each(|(pre, post)| { + if pre.get_uuid() == post.get_uuid() { + Ok(()) + } else { + admin_error!("modify - cand sets not correctly aligned"); + Err(OperationError::InvalidRequestState) + } })?; + } - if !self.changed_schema.get() { - self.changed_schema.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| { - e.attribute_equality("class", &PVCLASS_CLASSTYPE) - || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) - }), - ) - } - if !self.changed_acp.get() { - self.changed_acp.set( - norm_cand - .iter() - .chain(pre_candidates.iter().map(|e| e.as_ref())) - .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), - ) - } - if !self.changed_oauth2.get() { - self.changed_oauth2.set( - norm_cand - .iter() - .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), - ) - } - if !self.changed_domain.get() { - self.changed_domain.set( - norm_cand - .iter() - .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), - ) - } - let cu = self.changed_uuid.as_ptr(); - unsafe { - (*cu).extend( - norm_cand - .iter() - .map(|e| e.get_uuid()) - .chain(pre_candidates.iter().map(|e| e.get_uuid())), - ); - } - trace!( - schema_reload = ?self.changed_schema, - acp_reload = ?self.changed_acp, - oauth2_reload = ?self.changed_oauth2, - domain_reload = ?self.changed_domain, + // Backend Modify + self.be_txn + .modify(&self.cid, &pre_candidates, &norm_cand) + .map_err(|e| { + admin_error!("Modify operation failed (backend), {:?}", e); + e + })?; + + if !self.changed_schema.get() { + self.changed_schema.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| { + e.attribute_equality("class", &PVCLASS_CLASSTYPE) + || e.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) + }), + ) + } + if !self.changed_acp.get() { + self.changed_acp.set( + norm_cand + .iter() + .chain(pre_candidates.iter().map(|e| e.as_ref())) + .any(|e| e.attribute_equality("class", &PVCLASS_ACP)), + ) + } + if !self.changed_oauth2.get() { + self.changed_oauth2.set( + norm_cand + .iter() + .any(|e| e.attribute_equality("class", &PVCLASS_OAUTH2_RS)), + ) + } + if !self.changed_domain.get() { + self.changed_domain.set( + norm_cand + .iter() + .any(|e| e.attribute_equality("uuid", &PVUUID_DOMAIN_INFO)), + ) + } + let cu = self.changed_uuid.as_ptr(); + unsafe { + (*cu).extend( + norm_cand + .iter() + .map(|e| e.get_uuid()) + .chain(pre_candidates.iter().map(|e| e.get_uuid())), ); + } + trace!( + schema_reload = ?self.changed_schema, + acp_reload = ?self.changed_acp, + oauth2_reload = ?self.changed_oauth2, + domain_reload = ?self.changed_domain, + ); - trace!("Modify operation success"); - Ok(()) - }) + trace!("Modify operation success"); + Ok(()) } pub(crate) fn get_dyngroup_cache(&self) -> &mut DynGroupCache { @@ -2200,149 +2150,143 @@ impl<'a> QueryServerWriteTransaction<'a> { } /// Migrate 2 to 3 changes the name, domain_name types from iutf8 to iname. + #[instrument(level = "debug", skip_all)] pub fn migrate_2_to_3(&self) -> Result<(), OperationError> { - spanned!("server::migrate_2_to_3", { - admin_warn!("starting 2 to 3 migration. THIS MAY TAKE A LONG TIME!"); - // Get all entries where pres name or domain_name. INCLUDE TS + RECYCLE. + admin_warn!("starting 2 to 3 migration. THIS MAY TAKE A LONG TIME!"); + // Get all entries where pres name or domain_name. INCLUDE TS + RECYCLE. - let filt = filter_all!(f_or!([f_pres("name"), f_pres("domain_name"),])); + let filt = filter_all!(f_or!([f_pres("name"), f_pres("domain_name"),])); - let pre_candidates = self.internal_search(filt).map_err(|e| { - admin_error!(err = ?e, "migrate_2_to_3 internal search failure"); - e - })?; + let pre_candidates = self.internal_search(filt).map_err(|e| { + admin_error!(err = ?e, "migrate_2_to_3 internal search failure"); + e + })?; - // If there is nothing, we donn't need to do anything. - if pre_candidates.is_empty() { - admin_info!("migrate_2_to_3 no entries to migrate, complete"); - return Ok(()); + // If there is nothing, we donn't need to do anything. + if pre_candidates.is_empty() { + admin_info!("migrate_2_to_3 no entries to migrate, complete"); + return Ok(()); + } + + // Change the value type. + let mut candidates: Vec> = pre_candidates + .iter() + .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) + .collect(); + + candidates.iter_mut().try_for_each(|er| { + let nvs = if let Some(vs) = er.get_ava_set("name") { + vs.migrate_iutf8_iname()? + } else { + None + }; + if let Some(nvs) = nvs { + er.set_ava_set("name", nvs) } - // Change the value type. - let mut candidates: Vec> = pre_candidates - .iter() - .map(|er| er.as_ref().clone().invalidate(self.cid.clone())) - .collect(); - - candidates.iter_mut().try_for_each(|er| { - let nvs = if let Some(vs) = er.get_ava_set("name") { - vs.migrate_iutf8_iname()? - } else { - None - }; - if let Some(nvs) = nvs { - er.set_ava_set("name", nvs) - } - - let nvs = if let Some(vs) = er.get_ava_set("domain_name") { - vs.migrate_iutf8_iname()? - } else { - None - }; - if let Some(nvs) = nvs { - er.set_ava_set("domain_name", nvs) - } - - Ok(()) - })?; - - // Schema check all. - let res: Result>, SchemaError> = candidates - .into_iter() - .map(|e| e.validate(&self.schema).map(|e| e.seal(&self.schema))) - .collect(); - - let norm_cand: Vec> = match res { - Ok(v) => v, - Err(e) => { - admin_error!("migrate_2_to_3 schema error -> {:?}", e); - return Err(OperationError::SchemaViolation(e)); - } + let nvs = if let Some(vs) = er.get_ava_set("domain_name") { + vs.migrate_iutf8_iname()? + } else { + None }; + if let Some(nvs) = nvs { + er.set_ava_set("domain_name", nvs) + } - // Write them back. - self.be_txn - .modify(&self.cid, &pre_candidates, &norm_cand) - .map_err(|e| { - admin_error!("migrate_2_to_3 modification failure -> {:?}", e); - e - }) - // Complete - }) + Ok(()) + })?; + + // Schema check all. + let res: Result>, SchemaError> = candidates + .into_iter() + .map(|e| e.validate(&self.schema).map(|e| e.seal(&self.schema))) + .collect(); + + let norm_cand: Vec> = match res { + Ok(v) => v, + Err(e) => { + admin_error!("migrate_2_to_3 schema error -> {:?}", e); + return Err(OperationError::SchemaViolation(e)); + } + }; + + // Write them back. + self.be_txn + .modify(&self.cid, &pre_candidates, &norm_cand) + .map_err(|e| { + admin_error!("migrate_2_to_3 modification failure -> {:?}", e); + e + }) + // Complete } /// Migrate 3 to 4 - this triggers a regen of the domains security token /// as we previously did not have it in the entry. + #[instrument(level = "debug", skip_all)] pub fn migrate_3_to_4(&self) -> Result<(), OperationError> { - spanned!("server::migrate_3_to_4", { - admin_warn!("starting 3 to 4 migration."); - let filter = filter!(f_eq("uuid", (*PVUUID_DOMAIN_INFO).clone())); - let modlist = ModifyList::new_purge("domain_token_key"); - self.internal_modify(&filter, &modlist) - // Complete - }) + admin_warn!("starting 3 to 4 migration."); + let filter = filter!(f_eq("uuid", (*PVUUID_DOMAIN_INFO).clone())); + let modlist = ModifyList::new_purge("domain_token_key"); + self.internal_modify(&filter, &modlist) + // Complete } /// Migrate 4 to 5 - this triggers a regen of all oauth2 RS es256 der keys /// as we previously did not generate them on entry creation. + #[instrument(level = "debug", skip_all)] pub fn migrate_4_to_5(&self) -> Result<(), OperationError> { - spanned!("server::migrate_4_to_5", { - admin_warn!("starting 4 to 5 migration."); - let filter = filter!(f_and!([ - f_eq("class", (*PVCLASS_OAUTH2_RS).clone()), - f_andnot(f_pres("es256_private_key_der")), - ])); - let modlist = ModifyList::new_purge("es256_private_key_der"); - self.internal_modify(&filter, &modlist) - // Complete - }) + admin_warn!("starting 4 to 5 migration."); + let filter = filter!(f_and!([ + f_eq("class", (*PVCLASS_OAUTH2_RS).clone()), + f_andnot(f_pres("es256_private_key_der")), + ])); + let modlist = ModifyList::new_purge("es256_private_key_der"); + self.internal_modify(&filter, &modlist) + // Complete } /// Migrate 5 to 6 - This updates the domain info item to reset the token /// keys based on the new encryption types. + #[instrument(level = "debug", skip_all)] pub fn migrate_5_to_6(&self) -> Result<(), OperationError> { - spanned!("server::migrate_5_to_6", { - admin_warn!("starting 5 to 6 migration."); - let filter = filter!(f_eq("uuid", (*PVUUID_DOMAIN_INFO).clone())); - let mut modlist = ModifyList::new_purge("domain_token_key"); - // We need to also push the version here so that we pass schema. - modlist.push_mod(Modify::Present( - AttrString::from("version"), - Value::Uint32(0), - )); - self.internal_modify(&filter, &modlist) - // Complete - }) + admin_warn!("starting 5 to 6 migration."); + let filter = filter!(f_eq("uuid", (*PVUUID_DOMAIN_INFO).clone())); + let mut modlist = ModifyList::new_purge("domain_token_key"); + // We need to also push the version here so that we pass schema. + modlist.push_mod(Modify::Present( + AttrString::from("version"), + Value::Uint32(0), + )); + self.internal_modify(&filter, &modlist) + // Complete } /// Migrate 6 to 7 /// /// Modify accounts that are not persons, to be service accounts so that the extension /// rules remain valid. + #[instrument(level = "debug", skip_all)] pub fn migrate_6_to_7(&self) -> Result<(), OperationError> { - spanned!("server::migrate_6_to_7", { - admin_warn!("starting 6 to 7 migration."); - let filter = filter!(f_and!([ - f_eq("class", (*PVCLASS_ACCOUNT).clone()), - f_andnot(f_eq("class", (*PVCLASS_PERSON).clone())), - ])); - let modlist = ModifyList::new_append("class", Value::new_class("service_account")); - self.internal_modify(&filter, &modlist) - // Complete - }) + admin_warn!("starting 6 to 7 migration."); + let filter = filter!(f_and!([ + f_eq("class", (*PVCLASS_ACCOUNT).clone()), + f_andnot(f_eq("class", (*PVCLASS_PERSON).clone())), + ])); + let modlist = ModifyList::new_append("class", Value::new_class("service_account")); + self.internal_modify(&filter, &modlist) + // Complete } /// Migrate 7 to 8 /// /// Touch all service accounts to trigger a regen of their es256 jws keys for api tokens + #[instrument(level = "debug", skip_all)] pub fn migrate_7_to_8(&self) -> Result<(), OperationError> { - spanned!("server::migrate_7_to_8", { - admin_warn!("starting 7 to 8 migration."); - let filter = filter!(f_eq("class", (*PVCLASS_SERVICE_ACCOUNT).clone())); - let modlist = ModifyList::new_append("class", Value::new_class("service_account")); - self.internal_modify(&filter, &modlist) - // Complete - }) + admin_warn!("starting 7 to 8 migration."); + let filter = filter!(f_eq("class", (*PVCLASS_SERVICE_ACCOUNT).clone())); + let modlist = ModifyList::new_append("class", Value::new_class("service_account")); + self.internal_modify(&filter, &modlist) + // Complete } // These are where searches and other actions are actually implemented. This @@ -2367,21 +2311,20 @@ impl<'a> QueryServerWriteTransaction<'a> { self.delete(&de) } + #[instrument(level = "debug", skip_all)] pub fn internal_modify( &self, filter: &Filter, modlist: &ModifyList, ) -> Result<(), OperationError> { - spanned!("server::internal_modify", { - let f_valid = filter - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - let m_valid = modlist - .validate(self.get_schema()) - .map_err(OperationError::SchemaViolation)?; - let me = ModifyEvent::new_internal(f_valid, m_valid); - self.modify(&me) - }) + let f_valid = filter + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + let m_valid = modlist + .validate(self.get_schema()) + .map_err(OperationError::SchemaViolation)?; + let me = ModifyEvent::new_internal(f_valid, m_valid); + self.modify(&me) } pub fn impersonate_modify_valid( @@ -2461,18 +2404,17 @@ impl<'a> QueryServerWriteTransaction<'a> { } */ + #[instrument(level = "debug", skip_all)] pub fn internal_migrate_or_create_str(&self, e_str: &str) -> Result<(), OperationError> { - let res = spanned!("server::internal_migrate_or_create_str", { - Entry::from_proto_entry_str(e_str, self) - /* - .and_then(|e: Entry| { - let schema = self.get_schema(); - e.validate(schema).map_err(OperationError::SchemaViolation) - }) - */ - .and_then(|e: Entry| self.internal_migrate_or_create(e)) - }); - trace!(?res, "internal_migrate_or_create_str -> result"); + let res = Entry::from_proto_entry_str(e_str, self) + /* + .and_then(|e: Entry| { + let schema = self.get_schema(); + e.validate(schema).map_err(OperationError::SchemaViolation) + }) + */ + .and_then(|e: Entry| self.internal_migrate_or_create(e)); + trace!(?res); debug_assert!(res.is_ok()); res } @@ -3024,25 +2966,24 @@ impl<'a> QueryServerWriteTransaction<'a> { } /// Pulls the domain name from the database and updates the DomainInfo data in memory + #[instrument(level = "debug", skip_all)] fn reload_domain_info(&mut self) -> Result<(), OperationError> { - spanned!("server::reload_domain_info", { - let domain_name = self.get_db_domain_name()?; - let display_name = self.get_db_domain_display_name()?; - let mut_d_info = self.d_info.get_mut(); - if mut_d_info.d_name != domain_name { - admin_warn!( - "Using domain name from the database {} - was {} in memory", - domain_name, - mut_d_info.d_name, - ); - admin_warn!( + let domain_name = self.get_db_domain_name()?; + let display_name = self.get_db_domain_display_name()?; + let mut_d_info = self.d_info.get_mut(); + if mut_d_info.d_name != domain_name { + admin_warn!( + "Using domain name from the database {} - was {} in memory", + domain_name, + mut_d_info.d_name, + ); + admin_warn!( "If you think this is an error, see https://kanidm.github.io/kanidm/stable/administrivia.html#rename-the-domain" ); - mut_d_info.d_name = domain_name; - } - mut_d_info.d_display = display_name; - Ok(()) - }) + mut_d_info.d_name = domain_name; + } + mut_d_info.d_display = display_name; + Ok(()) } /// Initiate a domain display name change process. This isn't particularly scary @@ -3183,13 +3124,15 @@ impl<'a> QueryServerWriteTransaction<'a> { #[cfg(test)] mod tests { + use std::sync::Arc; + use std::time::Duration; + + use kanidm_proto::v1::SchemaError; + use crate::credential::policy::CryptoPolicy; use crate::credential::Credential; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent}; use crate::prelude::*; - use kanidm_proto::v1::SchemaError; - use std::sync::Arc; - use std::time::Duration; #[test] fn test_qs_create_user() { diff --git a/kanidmd/idm/src/status.rs b/kanidmd/idm/src/status.rs index 2f700813f..9ea46dd41 100644 --- a/kanidmd/idm/src/status.rs +++ b/kanidmd/idm/src/status.rs @@ -1,8 +1,9 @@ //! An actor that shows the servers current status and statistics. (TODO). -use crate::prelude::*; use uuid::Uuid; +use crate::prelude::*; + pub struct StatusRequestEvent { pub eventid: Uuid, } diff --git a/kanidmd/idm/src/utils.rs b/kanidmd/idm/src/utils.rs index 70922a4ff..d28ceb75c 100644 --- a/kanidmd/idm/src/utils.rs +++ b/kanidmd/idm/src/utils.rs @@ -1,26 +1,23 @@ -use hashbrown::HashSet; -use std::io::ErrorKind; -use std::path::PathBuf; -use std::time::{Duration, SystemTime}; - -use filetime::FileTime; -use touch::file as touch_file; -use uuid::{Builder, Uuid}; - -use rand::distributions::Distribution; -use rand::{thread_rng, Rng}; - #[cfg(not(target_family = "windows"))] use std::fs::Metadata; +use std::io::ErrorKind; #[cfg(target_os = "linux")] use std::os::linux::fs::MetadataExt; #[cfg(target_os = "macos")] use std::os::macos::fs::MetadataExt; +use std::path::PathBuf; +use std::time::{Duration, SystemTime}; + +use filetime::FileTime; +use hashbrown::HashSet; +use rand::distributions::Distribution; +use rand::{thread_rng, Rng}; +use touch::file as touch_file; // #[cfg(target_os = "windows")] // use std::os::windows::fs::MetadataExt; - #[cfg(target_family = "unix")] use users::{get_current_gid, get_current_uid}; +use uuid::{Builder, Uuid}; #[derive(Debug)] pub struct DistinctAlpha; @@ -188,10 +185,12 @@ pub fn file_permissions_readonly(meta: &Metadata) -> bool { #[cfg(test)] mod tests { - use crate::utils::{uuid_from_duration, uuid_to_gid_u32}; use std::time::Duration; + use uuid::Uuid; + use crate::utils::{uuid_from_duration, uuid_to_gid_u32}; + #[test] fn test_utils_uuid_from_duration() { let u1 = uuid_from_duration(Duration::from_secs(1), [0xff; 4]); diff --git a/kanidmd/idm/src/value.rs b/kanidmd/idm/src/value.rs index 883f0b398..258a64672 100644 --- a/kanidmd/idm/src/value.rs +++ b/kanidmd/idm/src/value.rs @@ -3,29 +3,26 @@ //! typed values, allows their comparison, filtering and more. It also has the code for serialising //! these into a form for the backend that can be persistent into the [`Backend`](crate::be::Backend). -use crate::be::dbentry::DbIdentSpn; -use crate::credential::Credential; -use crate::identity::IdentityId; -use crate::repl::cid::Cid; -use kanidm_proto::v1::Filter as ProtoFilter; - -use compact_jwt::JwsSigner; -use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use std::convert::TryFrom; use std::fmt; use std::str::FromStr; use std::time::Duration; + +use compact_jwt::JwsSigner; +use kanidm_proto::v1::Filter as ProtoFilter; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use sshkeys::PublicKey as SshPublicKey; use time::OffsetDateTime; use url::Url; use uuid::Uuid; +use webauthn_rs::prelude::{DeviceKey as DeviceKeyV4, Passkey as PasskeyV4}; -use sshkeys::PublicKey as SshPublicKey; - -use regex::Regex; - -use webauthn_rs::prelude::DeviceKey as DeviceKeyV4; -use webauthn_rs::prelude::Passkey as PasskeyV4; +use crate::be::dbentry::DbIdentSpn; +use crate::credential::Credential; +use crate::identity::IdentityId; +use crate::repl::cid::Cid; lazy_static! { pub static ref SPN_RE: Regex = { diff --git a/kanidmd/idm/src/valueset/address.rs b/kanidmd/idm/src/valueset/address.rs index 7c0db4f7a..74c01c37d 100644 --- a/kanidmd/idm/src/valueset/address.rs +++ b/kanidmd/idm/src/valueset/address.rs @@ -1,12 +1,12 @@ +use std::collections::BTreeSet; + +use smolset::SmolSet; + use crate::be::dbvalue::DbValueAddressV1; use crate::prelude::*; use crate::schema::SchemaAttribute; use crate::value::Address; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; - -use std::collections::BTreeSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetAddress { diff --git a/kanidmd/idm/src/valueset/binary.rs b/kanidmd/idm/src/valueset/binary.rs index 2b2d65fec..8e31ad374 100644 --- a/kanidmd/idm/src/valueset/binary.rs +++ b/kanidmd/idm/src/valueset/binary.rs @@ -1,12 +1,12 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; - use std::collections::btree_map::Entry as BTreeEntry; use std::collections::BTreeMap; +use smolset::SmolSet; + +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; + #[derive(Debug, Clone)] pub struct ValueSetPrivateBinary { set: SmolSet<[Vec; 1]>, diff --git a/kanidmd/idm/src/valueset/bool.rs b/kanidmd/idm/src/valueset/bool.rs index 3f1efda3c..b5e8ba7a5 100644 --- a/kanidmd/idm/src/valueset/bool.rs +++ b/kanidmd/idm/src/valueset/bool.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetBool { diff --git a/kanidmd/idm/src/valueset/cid.rs b/kanidmd/idm/src/valueset/cid.rs index a864cbede..36aedacc9 100644 --- a/kanidmd/idm/src/valueset/cid.rs +++ b/kanidmd/idm/src/valueset/cid.rs @@ -1,11 +1,10 @@ +use smolset::SmolSet; + use crate::be::dbvalue::DbCidV1; use crate::prelude::*; use crate::repl::cid::Cid; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; - -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetCid { diff --git a/kanidmd/idm/src/valueset/cred.rs b/kanidmd/idm/src/valueset/cred.rs index 366149430..a22f0b108 100644 --- a/kanidmd/idm/src/valueset/cred.rs +++ b/kanidmd/idm/src/valueset/cred.rs @@ -1,18 +1,15 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use std::collections::btree_map::Entry as BTreeEntry; use std::collections::BTreeMap; +use webauthn_rs::prelude::{DeviceKey as DeviceKeyV4, Passkey as PasskeyV4}; + use crate::be::dbvalue::{ DbValueCredV1, DbValueDeviceKeyV1, DbValueIntentTokenStateV1, DbValuePasskeyV1, }; use crate::credential::Credential; -use crate::valueset::IntentTokenState; - -use webauthn_rs::prelude::DeviceKey as DeviceKeyV4; -use webauthn_rs::prelude::Passkey as PasskeyV4; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, IntentTokenState, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetCredential { diff --git a/kanidmd/idm/src/valueset/datetime.rs b/kanidmd/idm/src/valueset/datetime.rs index 8ea8ab6ee..4741fdf02 100644 --- a/kanidmd/idm/src/valueset/datetime.rs +++ b/kanidmd/idm/src/valueset/datetime.rs @@ -1,10 +1,10 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use smolset::SmolSet; use time::OffsetDateTime; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; + #[derive(Debug, Clone)] pub struct ValueSetDateTime { set: SmolSet<[OffsetDateTime; 1]>, diff --git a/kanidmd/idm/src/valueset/iname.rs b/kanidmd/idm/src/valueset/iname.rs index b44308a97..a9d35b99e 100644 --- a/kanidmd/idm/src/valueset/iname.rs +++ b/kanidmd/idm/src/valueset/iname.rs @@ -1,10 +1,9 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use std::collections::BTreeSet; +use crate::prelude::*; +use crate::schema::SchemaAttribute; use crate::value::INAME_RE; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetIname { diff --git a/kanidmd/idm/src/valueset/index.rs b/kanidmd/idm/src/valueset/index.rs index 1969b0b7e..7aebcb66a 100644 --- a/kanidmd/idm/src/valueset/index.rs +++ b/kanidmd/idm/src/valueset/index.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetIndex { diff --git a/kanidmd/idm/src/valueset/iutf8.rs b/kanidmd/idm/src/valueset/iutf8.rs index d6b4da6d8..8fd50d56e 100644 --- a/kanidmd/idm/src/valueset/iutf8.rs +++ b/kanidmd/idm/src/valueset/iutf8.rs @@ -1,10 +1,9 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use std::collections::BTreeSet; use super::iname::ValueSetIname; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetIutf8 { diff --git a/kanidmd/idm/src/valueset/json.rs b/kanidmd/idm/src/valueset/json.rs index 6f234892f..09449f302 100644 --- a/kanidmd/idm/src/valueset/json.rs +++ b/kanidmd/idm/src/valueset/json.rs @@ -1,10 +1,10 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use kanidm_proto::v1::Filter as ProtoFilter; use smolset::SmolSet; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; + #[derive(Debug, Clone)] pub struct ValueSetJsonFilter { set: SmolSet<[ProtoFilter; 1]>, diff --git a/kanidmd/idm/src/valueset/jws.rs b/kanidmd/idm/src/valueset/jws.rs index 3d85ffefd..063099050 100644 --- a/kanidmd/idm/src/valueset/jws.rs +++ b/kanidmd/idm/src/valueset/jws.rs @@ -1,10 +1,9 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; +use compact_jwt::{JwaAlg, JwsSigner}; use hashbrown::HashSet; -use compact_jwt::{JwaAlg, JwsSigner}; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetJwsKeyEs256 { diff --git a/kanidmd/idm/src/valueset/mod.rs b/kanidmd/idm/src/valueset/mod.rs index de9e054c0..65ce03f6f 100644 --- a/kanidmd/idm/src/valueset/mod.rs +++ b/kanidmd/idm/src/valueset/mod.rs @@ -1,27 +1,21 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use compact_jwt::JwsSigner; +use dyn_clone::DynClone; +use hashbrown::HashSet; +use kanidm_proto::v1::Filter as ProtoFilter; +use smolset::SmolSet; +use time::OffsetDateTime; +// use std::fmt::Debug; +use webauthn_rs::prelude::DeviceKey as DeviceKeyV4; +use webauthn_rs::prelude::Passkey as PasskeyV4; + +use crate::be::dbvalue::DbValueSetV2; use crate::credential::Credential; use crate::prelude::*; use crate::repl::cid::Cid; use crate::schema::SchemaAttribute; - -use crate::be::dbvalue::DbValueSetV2; -use crate::value::Address; -use crate::value::IntentTokenState; -use crate::value::Session; -use compact_jwt::JwsSigner; - -use kanidm_proto::v1::Filter as ProtoFilter; - -use std::collections::{BTreeMap, BTreeSet}; - -use dyn_clone::DynClone; -use hashbrown::HashSet; -use smolset::SmolSet; -// use std::fmt::Debug; - -use webauthn_rs::prelude::DeviceKey as DeviceKeyV4; -use webauthn_rs::prelude::Passkey as PasskeyV4; - -use time::OffsetDateTime; +use crate::value::{Address, IntentTokenState, Session}; mod address; mod binary; @@ -69,8 +63,7 @@ pub use self::syntax::ValueSetSyntax; pub use self::uint32::ValueSetUint32; pub use self::url::ValueSetUrl; pub use self::utf8::ValueSetUtf8; -pub use self::uuid::ValueSetRefer; -pub use self::uuid::ValueSetUuid; +pub use self::uuid::{ValueSetRefer, ValueSetUuid}; pub type ValueSet = Box; diff --git a/kanidmd/idm/src/valueset/nsuniqueid.rs b/kanidmd/idm/src/valueset/nsuniqueid.rs index ee1e69984..b2f06d403 100644 --- a/kanidmd/idm/src/valueset/nsuniqueid.rs +++ b/kanidmd/idm/src/valueset/nsuniqueid.rs @@ -1,9 +1,9 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; use crate::value::NSUNIQUEID_RE; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetNsUniqueId { diff --git a/kanidmd/idm/src/valueset/oauth.rs b/kanidmd/idm/src/valueset/oauth.rs index 10f7f6c7c..e6af6d08c 100644 --- a/kanidmd/idm/src/valueset/oauth.rs +++ b/kanidmd/idm/src/valueset/oauth.rs @@ -1,15 +1,11 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use std::collections::BTreeSet; +use std::collections::btree_map::Entry as BTreeEntry; +use std::collections::{BTreeMap, BTreeSet}; use crate::be::dbvalue::DbValueOauthScopeMapV1; -use crate::valueset::uuid_to_proto_string; -use std::collections::btree_map::Entry as BTreeEntry; -use std::collections::BTreeMap; - +use crate::prelude::*; +use crate::schema::SchemaAttribute; use crate::value::OAUTHSCOPE_RE; +use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetOauthScope { diff --git a/kanidmd/idm/src/valueset/restricted.rs b/kanidmd/idm/src/valueset/restricted.rs index 9a12bdc18..3f680977d 100644 --- a/kanidmd/idm/src/valueset/restricted.rs +++ b/kanidmd/idm/src/valueset/restricted.rs @@ -1,8 +1,8 @@ +use std::collections::BTreeSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use std::collections::BTreeSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetRestricted { diff --git a/kanidmd/idm/src/valueset/secret.rs b/kanidmd/idm/src/valueset/secret.rs index 2482b8b32..c906eeab2 100644 --- a/kanidmd/idm/src/valueset/secret.rs +++ b/kanidmd/idm/src/valueset/secret.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetSecret { diff --git a/kanidmd/idm/src/valueset/session.rs b/kanidmd/idm/src/valueset/session.rs index 0cd6353c3..49a26db99 100644 --- a/kanidmd/idm/src/valueset/session.rs +++ b/kanidmd/idm/src/valueset/session.rs @@ -1,15 +1,14 @@ +use std::collections::btree_map::Entry as BTreeEntry; +use std::collections::BTreeMap; + +use time::OffsetDateTime; + use crate::be::dbvalue::{DbValueIdentityId, DbValueSession}; use crate::identity::IdentityId; use crate::prelude::*; use crate::schema::SchemaAttribute; use crate::value::Session; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use std::collections::btree_map::Entry as BTreeEntry; -use std::collections::BTreeMap; -use time::OffsetDateTime; - -use crate::valueset::uuid_to_proto_string; +use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetSession { diff --git a/kanidmd/idm/src/valueset/spn.rs b/kanidmd/idm/src/valueset/spn.rs index 489e27cd5..6b001a71b 100644 --- a/kanidmd/idm/src/valueset/spn.rs +++ b/kanidmd/idm/src/valueset/spn.rs @@ -1,9 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; - -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetSpn { diff --git a/kanidmd/idm/src/valueset/ssh.rs b/kanidmd/idm/src/valueset/ssh.rs index f9d449ce9..eab58aad8 100644 --- a/kanidmd/idm/src/valueset/ssh.rs +++ b/kanidmd/idm/src/valueset/ssh.rs @@ -1,11 +1,10 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use std::collections::btree_map::Entry as BTreeEntry; use std::collections::BTreeMap; use crate::be::dbvalue::DbValueTaggedStringV1; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetSshKey { diff --git a/kanidmd/idm/src/valueset/syntax.rs b/kanidmd/idm/src/valueset/syntax.rs index 098211442..c094db468 100644 --- a/kanidmd/idm/src/valueset/syntax.rs +++ b/kanidmd/idm/src/valueset/syntax.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetSyntax { diff --git a/kanidmd/idm/src/valueset/uint32.rs b/kanidmd/idm/src/valueset/uint32.rs index f978de79b..f82a31b28 100644 --- a/kanidmd/idm/src/valueset/uint32.rs +++ b/kanidmd/idm/src/valueset/uint32.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetUint32 { diff --git a/kanidmd/idm/src/valueset/url.rs b/kanidmd/idm/src/valueset/url.rs index ec3993edd..b011425f8 100644 --- a/kanidmd/idm/src/valueset/url.rs +++ b/kanidmd/idm/src/valueset/url.rs @@ -1,8 +1,8 @@ +use smolset::SmolSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use smolset::SmolSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetUrl { diff --git a/kanidmd/idm/src/valueset/utf8.rs b/kanidmd/idm/src/valueset/utf8.rs index 04e18c4e2..6d04ae262 100644 --- a/kanidmd/idm/src/valueset/utf8.rs +++ b/kanidmd/idm/src/valueset/utf8.rs @@ -1,8 +1,8 @@ +use std::collections::BTreeSet; + use crate::prelude::*; use crate::schema::SchemaAttribute; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; -use std::collections::BTreeSet; +use crate::valueset::{DbValueSetV2, ValueSet}; #[derive(Debug, Clone)] pub struct ValueSetUtf8 { diff --git a/kanidmd/idm/src/valueset/uuid.rs b/kanidmd/idm/src/valueset/uuid.rs index dbdcac214..bac7933a2 100644 --- a/kanidmd/idm/src/valueset/uuid.rs +++ b/kanidmd/idm/src/valueset/uuid.rs @@ -1,12 +1,11 @@ -use crate::prelude::*; -use crate::schema::SchemaAttribute; -use crate::valueset::uuid_to_proto_string; -use crate::valueset::DbValueSetV2; -use crate::valueset::ValueSet; use std::collections::BTreeSet; use smolset::SmolSet; +use crate::prelude::*; +use crate::schema::SchemaAttribute; +use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ValueSet}; + #[derive(Debug, Clone)] pub struct ValueSetUuid { set: SmolSet<[Uuid; 1]>, diff --git a/kanidmd/score/Cargo.toml b/kanidmd/score/Cargo.toml index 3743627a2..ff5160d11 100644 --- a/kanidmd/score/Cargo.toml +++ b/kanidmd/score/Cargo.toml @@ -1,52 +1,52 @@ [package] name = "score" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-std = { version = "^1.12.0", features = ["tokio1"] } -async-trait = "^0.1.57" -compact_jwt = "^0.2.3" -futures-util = "^0.3.21" -http-types = "^2.12.0" -kanidm = { path = "../idm" } -kanidm_proto = { path = "../../kanidm_proto" } -ldap3_proto = "^0.2.3" -libc = "^0.2.127" -openssl = "^0.10.41" -regex = "1.5.6" -serde = { version = "^1.0.142", features = ["derive"] } -serde_json = "^1.0.83" -sketching = { path = "../../sketching" } -tide = "^0.16.0" +async-std = { workspace = true, features = ["tokio1"] } +async-trait.workspace = true +compact_jwt.workspace = true +futures-util.workspace = true +http-types.workspace = true +kanidm.workspace = true +kanidm_proto.workspace = true +ldap3_proto.workspace = true +libc.workspace = true +openssl.workspace = true +regex.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +sketching.workspace = true +tide.workspace = true # I tried including brotli and it didn't work, including "default" pulls a mime-type list from the internet on build -tide-compress = { version = "0.10.6", default-features = false, features = [ "deflate", "gzip", "regex-check" ] } -tide-openssl = "^0.1.1" -tokio = { version = "^1.21.1", features = ["net", "sync", "io-util", "macros"] } -tokio-openssl = "^0.6.3" -tokio-util = { version = "^0.7.4", features = ["codec"] } -tracing = { version = "^0.1.35", features = ["attributes"] } -uuid = { version = "^1.1.2", features = ["serde", "v4" ] } +tide-compress = { workspace = true, default-features = false, features = [ "deflate", "gzip", "regex-check" ] } +tide-openssl.workspace = true +tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] } +tokio-openssl.workspace = true +tokio-util = { workspace = true, features = ["codec"] } +tracing = { workspace = true, features = ["attributes"] } +uuid = { workspace = true, features = ["serde", "v4" ] } [build-dependencies] -profiles = { path = "../../profiles" } +profiles.workspace = true [dev-dependencies] -kanidm_client = { path = "../../kanidm_client" } -futures = "^0.3.21" +kanidm_client.workspace = true +futures.workspace = true webauthn-authenticator-rs.workspace = true -oauth2_ext = { package = "oauth2", version = "^4.1.0", default-features = false } -base64 = "^0.13.0" +oauth2_ext = { workspace = true, default-features = false } -url = { version = "^2.3.1", features = ["serde"] } -reqwest = { version = "0.11.11", features=["cookies", "json", "native-tls"] } +url = { workspace = true, features = ["serde"] } +reqwest = { workspace = true, features=["cookies", "json", "native-tls"] } diff --git a/kanidmd/score/src/https/manifest.rs b/kanidmd/score/src/https/manifest.rs index c76b348b6..d1634165d 100644 --- a/kanidmd/score/src/https/manifest.rs +++ b/kanidmd/score/src/https/manifest.rs @@ -1,7 +1,8 @@ +use serde::{Deserialize, Serialize}; + ///! Builds a Progressive Web App Manifest page. // Thanks to the webmanifest crate for a lot of this code use crate::https::{AppState, RequestExtensions}; -use serde::{Deserialize, Serialize}; /// The MIME type for `.webmanifest` files. const MIME_TYPE_MANIFEST: &str = "application/manifest+json;charset=utf-8"; diff --git a/kanidmd/score/src/https/middleware.rs b/kanidmd/score/src/https/middleware.rs index fc163768a..27c3b5114 100644 --- a/kanidmd/score/src/https/middleware.rs +++ b/kanidmd/score/src/https/middleware.rs @@ -1,6 +1,7 @@ +use regex::Regex; + ///! Custom tide middleware for Kanidm use crate::https::JavaScriptFile; -use regex::Regex; /// This is for the tide_compression middleware so that we only compress certain content types. /// diff --git a/kanidmd/score/src/https/mod.rs b/kanidmd/score/src/https/mod.rs index bc935bb03..c4a831220 100644 --- a/kanidmd/score/src/https/mod.rs +++ b/kanidmd/score/src/https/mod.rs @@ -4,11 +4,9 @@ mod oauth2; mod routemaps; mod v1; -use self::manifest::manifest; -use self::middleware::*; -use self::oauth2::*; -use self::routemaps::{RouteMap, RouteMaps}; -use self::v1::*; +use std::fs::canonicalize; +use std::path::PathBuf; +use std::str::FromStr; use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator}; use kanidm::actors::v1_read::QueryServerReadV1; @@ -17,14 +15,17 @@ use kanidm::config::{ServerRole, TlsConfiguration}; use kanidm::prelude::*; use kanidm::status::StatusActor; use serde::Serialize; -use std::fs::canonicalize; -use std::path::PathBuf; -use std::str::FromStr; use tide_compress::CompressMiddleware; use tide_openssl::TlsListener; use tracing::{error, info}; use uuid::Uuid; +use self::manifest::manifest; +use self::middleware::*; +use self::oauth2::*; +use self::routemaps::{RouteMap, RouteMaps}; +use self::v1::*; + #[derive(Clone)] pub struct JavaScriptFile { // Relative to the pkg/ dir diff --git a/kanidmd/score/src/https/oauth2.rs b/kanidmd/score/src/https/oauth2.rs index 85270b4ff..e40d65524 100644 --- a/kanidmd/score/src/https/oauth2.rs +++ b/kanidmd/score/src/https/oauth2.rs @@ -1,5 +1,3 @@ -use super::v1::{json_rest_event_get, json_rest_event_post}; -use super::{to_tide_response, AppState, RequestExtensions}; use kanidm::idm::oauth2::{ AccessTokenIntrospectRequest, AccessTokenRequest, AuthorisationRequest, AuthorisePermitSuccess, AuthoriseResponse, ErrorResponse, Oauth2Error, @@ -9,6 +7,9 @@ use kanidm_proto::oauth2::AuthorisationResponse; use kanidm_proto::v1::Entry as ProtoEntry; use serde::{Deserialize, Serialize}; +use super::v1::{json_rest_event_get, json_rest_event_post}; +use super::{to_tide_response, AppState, RequestExtensions}; + // == Oauth2 Configuration Endpoints == pub async fn oauth2_get(req: tide::Request) -> tide::Result { diff --git a/kanidmd/score/src/https/routemaps.rs b/kanidmd/score/src/https/routemaps.rs index cace10f13..8befd96c5 100644 --- a/kanidmd/score/src/https/routemaps.rs +++ b/kanidmd/score/src/https/routemaps.rs @@ -1,11 +1,11 @@ -use crate::https::AppState; ///! Route-mapping magic for tide /// /// Instead of adding routes with (for example) the .post method you add them with .mapped_post, pasing an instance of [RouteMap] and it'll do the rest... -/// use serde::{Deserialize, Serialize}; use tide::{Endpoint, Route}; +use crate::https::AppState; + // Extends the tide::Route for RouteMaps, this would really be nice if it was generic :( pub trait RouteMaps { fn mapped_method( @@ -43,21 +43,27 @@ impl RouteMaps for Route<'_, AppState> { routemap.routelist.push(RouteInfo { path, method }); self.method(method, ep) } + fn mapped_delete(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Delete, ep) } + fn mapped_get(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Get, ep) } + fn mapped_patch(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Patch, ep) } + fn mapped_post(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Post, ep) } + fn mapped_put(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Put, ep) } + fn mapped_update(&mut self, routemap: &mut RouteMap, ep: impl Endpoint) -> &mut Self { self.mapped_method(routemap, http_types::Method::Update, ep) } @@ -88,6 +94,7 @@ impl RouteMap { pub fn do_map(&self) -> String { serde_json::to_string_pretty(self).unwrap() } + // Inject the route for the routemap endpoint pub fn push_self(&mut self, path: String, method: http_types::Method) { self.routelist.push(RouteInfo { path, method }); diff --git a/kanidmd/score/src/https/v1.rs b/kanidmd/score/src/https/v1.rs index 464d8adba..3dbf6d482 100644 --- a/kanidmd/score/src/https/v1.rs +++ b/kanidmd/score/src/https/v1.rs @@ -1,23 +1,21 @@ +use std::str::FromStr; +use std::time::Duration; + +use async_std::task; +use compact_jwt::Jws; use kanidm::event::AuthResult; use kanidm::filter::{Filter, FilterInvalid}; use kanidm::idm::AuthState; use kanidm::prelude::*; use kanidm::status::StatusRequestEvent; - -use kanidm_proto::v1::Entry as ProtoEntry; use kanidm_proto::v1::{ AccountUnixExtend, ApiTokenGenerate, AuthRequest, AuthResponse, AuthState as ProtoAuthState, - CUIntentToken, CURequest, CUSessionToken, CreateRequest, DeleteRequest, GroupUnixExtend, - ModifyRequest, OperationError, SearchRequest, SingleStringRequest, + CUIntentToken, CURequest, CUSessionToken, CreateRequest, DeleteRequest, Entry as ProtoEntry, + GroupUnixExtend, ModifyRequest, OperationError, SearchRequest, SingleStringRequest, }; +use serde::{Deserialize, Serialize}; use super::{to_tide_response, AppState, RequestExtensions, RouteMap}; -use async_std::task; -use compact_jwt::Jws; -use std::str::FromStr; -use std::time::Duration; - -use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub(crate) struct SessionId { diff --git a/kanidmd/score/src/ldaps.rs b/kanidmd/score/src/ldaps.rs index 823a27b8f..90bb3408f 100644 --- a/kanidmd/score/src/ldaps.rs +++ b/kanidmd/score/src/ldaps.rs @@ -1,18 +1,19 @@ -use kanidm::actors::v1_read::QueryServerReadV1; -use kanidm::ldap::{LdapBoundToken, LdapResponseState}; -use kanidm::prelude::*; -use openssl::ssl::{Ssl, SslAcceptor, SslAcceptorBuilder}; +use std::marker::Unpin; +use std::net; use std::pin::Pin; -use tokio_openssl::SslStream; +use std::str::FromStr; use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; -use ldap3_proto::{proto::LdapMsg, LdapCodec}; -use std::marker::Unpin; -use std::net; -use std::str::FromStr; +use kanidm::actors::v1_read::QueryServerReadV1; +use kanidm::ldap::{LdapBoundToken, LdapResponseState}; +use kanidm::prelude::*; +use ldap3_proto::proto::LdapMsg; +use ldap3_proto::LdapCodec; +use openssl::ssl::{Ssl, SslAcceptor, SslAcceptorBuilder}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpListener; +use tokio_openssl::SslStream; use tokio_util::codec::{FramedRead, FramedWrite}; struct LdapSession { diff --git a/kanidmd/score/src/lib.rs b/kanidmd/score/src/lib.rs index c521864fe..fe381eb47 100644 --- a/kanidmd/score/src/lib.rs +++ b/kanidmd/score/src/lib.rs @@ -29,12 +29,10 @@ pub mod https; mod ldaps; // use crossbeam::channel::unbounded; +use std::sync::Arc; + use async_std::task; use compact_jwt::JwsSigner; -use kanidm::prelude::*; -#[cfg(not(target_family = "windows"))] -use libc::umask; - use kanidm::actors::v1_read::QueryServerReadV1; use kanidm::actors::v1_write::QueryServerWriteV1; use kanidm::be::{Backend, BackendConfig, BackendTransaction, FsType}; @@ -43,13 +41,14 @@ use kanidm::crypto::setup_tls; use kanidm::idm::server::{IdmServer, IdmServerDelayed}; use kanidm::interval::IntervalActor; use kanidm::ldap::LdapServer; +use kanidm::prelude::*; use kanidm::schema::Schema; use kanidm::status::StatusActor; use kanidm::utils::{duration_from_epoch_now, touch_file_or_quit}; use kanidm_proto::messages::{AccountChangeMessage, MessageStatus}; use kanidm_proto::v1::OperationError; - -use std::sync::Arc; +#[cfg(not(target_family = "windows"))] +use libc::umask; // === internal setup helpers diff --git a/kanidmd/score/tests/https_middleware.rs b/kanidmd/score/tests/https_middleware.rs index d81f0f5ad..708015734 100644 --- a/kanidmd/score/tests/https_middleware.rs +++ b/kanidmd/score/tests/https_middleware.rs @@ -1,14 +1,12 @@ use std::sync::atomic::Ordering; mod common; -use crate::common::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, PORT_ALLOC}; - use kanidm::audit::LogLevel; use kanidm::config::{Configuration, IntegrationTestConfig, ServerRole}; use score::create_server_core; use tokio::task; -use crate::common::is_free_port; +use crate::common::{is_free_port, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, PORT_ALLOC}; #[tokio::test] async fn test_https_middleware_headers() { diff --git a/kanidmd/score/tests/oauth2_test.rs b/kanidmd/score/tests/oauth2_test.rs index 88eb322fc..f43b3f305 100644 --- a/kanidmd/score/tests/oauth2_test.rs +++ b/kanidmd/score/tests/oauth2_test.rs @@ -1,6 +1,8 @@ #![deny(warnings)] mod common; -use crate::common::{setup_async_test, ADMIN_TEST_PASSWORD}; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::str::FromStr; use compact_jwt::{JwkKeySet, JwsValidator, OidcToken, OidcUnverified}; use kanidm_proto::oauth2::{ @@ -8,11 +10,10 @@ use kanidm_proto::oauth2::{ AccessTokenResponse, AuthorisationResponse, OidcDiscoveryResponse, }; use oauth2_ext::PkceCodeChallenge; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::str::FromStr; use url::Url; +use crate::common::{setup_async_test, ADMIN_TEST_PASSWORD}; + macro_rules! assert_no_cache { ($response:expr) => {{ // Check we have correct nocache headers. diff --git a/kanidmd/score/tests/proto_v1_test.rs b/kanidmd/score/tests/proto_v1_test.rs index f9c8e8f88..8ef10f64f 100644 --- a/kanidmd/score/tests/proto_v1_test.rs +++ b/kanidmd/score/tests/proto_v1_test.rs @@ -1,19 +1,20 @@ #![deny(warnings)] use std::time::SystemTime; -use tracing::debug; - use kanidm::credential::totp::Totp; use kanidm_proto::v1::{ ApiToken, CURegState, CredentialDetailType, Entry, Filter, Modify, ModifyList, }; +use tracing::debug; mod common; -use crate::common::{setup_async_test, ADMIN_TEST_PASSWORD}; -use compact_jwt::JwsUnverified; use std::str::FromStr; -use webauthn_authenticator_rs::{softpasskey::SoftPasskey, WebauthnAuthenticator}; +use compact_jwt::JwsUnverified; +use webauthn_authenticator_rs::softpasskey::SoftPasskey; +use webauthn_authenticator_rs::WebauthnAuthenticator; + +use crate::common::{setup_async_test, ADMIN_TEST_PASSWORD}; const UNIX_TEST_PASSWORD: &str = "unix test user password"; diff --git a/kanidmd_web_ui/Cargo.toml b/kanidmd_web_ui/Cargo.toml index 7acb584a0..9a4845e34 100644 --- a/kanidmd_web_ui/Cargo.toml +++ b/kanidmd_web_ui/Cargo.toml @@ -1,47 +1,43 @@ [package] name = "kanidmd_web_ui" -version = "1.1.0-alpha.9" -authors = [ - "William Brown ", - "James Hodgkinson ", - ] -rust-version = "1.64" -edition = "2021" -license = "MPL-2.0" description = "Kanidm Server Web User Interface" 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 # These are ignored because the crate is in a workspace #[profile.release] # less code to include into binary - [lib] crate-type = ["cdylib", "rlib"] [dependencies] -compact_jwt = { version = "^0.2.3", default-features = false, features = ["unsafe_release_without_verify"] } -# compact_jwt = { path = "../../compact_jwt" , default-features = false, features = ["unsafe_release_without_verify"] } -gloo = "^0.8.0" -gloo-net = "0.2.4" -js-sys = "^0.3.58" -kanidm_proto = { path = "../kanidm_proto", features = ["wasm"] } -qrcode = { version = "^0.12.0", default-features = false, features = ["svg"] } -serde = { version = "^1.0.142", features = ["derive"] } -serde_json = "^1.0.83" -serde-wasm-bindgen = "0.4" -uuid = "^1.1.2" -wasm-bindgen = { version = "^0.2.81" } -wasm-bindgen-futures = { version = "^0.4.30" } -wasm-bindgen-test = "0.3.33" -yew = "^0.19.3" -yew-agent = "^0.1.0" -yew-router = "^0.16.0" +compact_jwt = { workspace = true, default-features = false, features = ["unsafe_release_without_verify"] } +gloo.workspace = true +gloo-net.workspace = true +js-sys.workspace = true +kanidm_proto = { workspace = true, features = ["wasm"] } +qrcode = { workspace = true, default-features = false, features = ["svg"] } +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +serde-wasm-bindgen.workspace = true +uuid.workspace = true +wasm-bindgen.workspace = true +wasm-bindgen-futures.workspace = true +wasm-bindgen-test.workspace = true +yew.workspace = true +yew-agent.workspace = true +yew-router.workspace = true [dependencies.web-sys] -version = "^0.3.60" +workspace = true features = [ "AuthenticationExtensionsClientOutputs", "AuthenticatorResponse", diff --git a/kanidmd_web_ui/pkg/kanidmd_web_ui.js b/kanidmd_web_ui/pkg/kanidmd_web_ui.js deleted file mode 100644 index 36fc947f2..000000000 --- a/kanidmd_web_ui/pkg/kanidmd_web_ui.js +++ /dev/null @@ -1,1093 +0,0 @@ -import { modal_hide_by_id } from '/pkg/wasmloader.js'; - -let wasm; - -const heap = new Array(32).fill(undefined); - -heap.push(undefined, null, true, false); - -function getObject(idx) { return heap[idx]; } - -function isLikeNone(x) { - return x === undefined || x === null; -} - -let cachedFloat64Memory0 = new Float64Array(); - -function getFloat64Memory0() { - if (cachedFloat64Memory0.byteLength === 0) { - cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); - } - return cachedFloat64Memory0; -} - -let cachedInt32Memory0 = new Int32Array(); - -function getInt32Memory0() { - if (cachedInt32Memory0.byteLength === 0) { - cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachedInt32Memory0; -} - -let WASM_VECTOR_LEN = 0; - -let cachedUint8Memory0 = new Uint8Array(); - -function getUint8Memory0() { - if (cachedUint8Memory0.byteLength === 0) { - cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8Memory0; -} - -const cachedTextEncoder = new TextEncoder('utf-8'); - -const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); -} - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}); - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length); - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len); - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3); - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - -function dropObject(idx) { - if (idx < 36) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -function debugString(val) { - // primitive types - const type = typeof val; - if (type == 'number' || type == 'boolean' || val == null) { - return `${val}`; - } - if (type == 'string') { - return `"${val}"`; - } - if (type == 'symbol') { - const description = val.description; - if (description == null) { - return 'Symbol'; - } else { - return `Symbol(${description})`; - } - } - if (type == 'function') { - const name = val.name; - if (typeof name == 'string' && name.length > 0) { - return `Function(${name})`; - } else { - return 'Function'; - } - } - // objects - if (Array.isArray(val)) { - const length = val.length; - let debug = '['; - if (length > 0) { - debug += debugString(val[0]); - } - for(let i = 1; i < length; i++) { - debug += ', ' + debugString(val[i]); - } - debug += ']'; - return debug; - } - // Test for built-in - const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); - let className; - if (builtInMatches.length > 1) { - className = builtInMatches[1]; - } else { - // Failed to match the standard '[object ClassName]' - return toString.call(val); - } - if (className == 'Object') { - // we're a user defined class or Object - // JSON.stringify avoids problems with cycles, and is generally much - // easier than looping through ownProperties of `val`. - try { - return 'Object(' + JSON.stringify(val) + ')'; - } catch (_) { - return 'Object'; - } - } - // errors - if (val instanceof Error) { - return `${val.name}: ${val.message}\n${val.stack}`; - } - // TODO we could test for more things here, like `Set`s and `Map`s. - return className; -} - -function makeMutClosure(arg0, arg1, dtor, f) { - const state = { a: arg0, b: arg1, cnt: 1, dtor }; - const real = (...args) => { - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - const a = state.a; - state.a = 0; - try { - return f(a, state.b, ...args); - } finally { - if (--state.cnt === 0) { - wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); - - } else { - state.a = a; - } - } - }; - real.original = state; - - return real; -} - -let stack_pointer = 32; - -function addBorrowedObject(obj) { - if (stack_pointer == 1) throw new Error('out of js stack'); - heap[--stack_pointer] = obj; - return stack_pointer; -} -function __wbg_adapter_36(arg0, arg1, arg2) { - try { - wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hbcc7dfc6c2687f89(arg0, arg1, addBorrowedObject(arg2)); - } finally { - heap[stack_pointer++] = undefined; - } -} - -function makeClosure(arg0, arg1, dtor, f) { - const state = { a: arg0, b: arg1, cnt: 1, dtor }; - const real = (...args) => { - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - try { - return f(state.a, state.b, ...args); - } finally { - if (--state.cnt === 0) { - wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b); - state.a = 0; - - } - } - }; - real.original = state; - - return real; -} -function __wbg_adapter_39(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6d98df24b306b11b(arg0, arg1, addHeapObject(arg2)); -} - -function __wbg_adapter_42(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h93ba8e63e7e4f60b(arg0, arg1, addHeapObject(arg2)); -} - -/** -*/ -export function run_app() { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.run_app(retptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - if (r1) { - throw takeObject(r0); - } - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } -} - -let cachedUint32Memory0 = new Uint32Array(); - -function getUint32Memory0() { - if (cachedUint32Memory0.byteLength === 0) { - cachedUint32Memory0 = new Uint32Array(wasm.memory.buffer); - } - return cachedUint32Memory0; -} - -function getArrayJsValueFromWasm0(ptr, len) { - const mem = getUint32Memory0(); - const slice = mem.subarray(ptr / 4, ptr / 4 + len); - const result = []; - for (let i = 0; i < slice.length; i++) { - result.push(takeObject(slice[i])); - } - return result; -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - wasm.__wbindgen_exn_store(addHeapObject(e)); - } -} - -async function load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - - } catch (e) { - if (module.headers.get('Content-Type') != 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { - throw e; - } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - - } else { - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - - } else { - return instance; - } - } -} - -function getImports() { - const imports = {}; - imports.wbg = {}; - imports.wbg.__wbindgen_is_bigint = function(arg0) { - const ret = typeof(getObject(arg0)) === 'bigint'; - return ret; - }; - imports.wbg.__wbindgen_is_undefined = function(arg0) { - const ret = getObject(arg0) === undefined; - return ret; - }; - imports.wbg.__wbindgen_number_get = function(arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof(obj) === 'number' ? obj : undefined; - getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; - getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); - }; - imports.wbg.__wbindgen_boolean_get = function(arg0) { - const v = getObject(arg0); - const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; - return ret; - }; - imports.wbg.__wbindgen_string_get = function(arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof(obj) === 'string' ? obj : undefined; - var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbindgen_is_object = function(arg0) { - const val = getObject(arg0); - const ret = typeof(val) === 'object' && val !== null; - return ret; - }; - imports.wbg.__wbindgen_is_string = function(arg0) { - const ret = typeof(getObject(arg0)) === 'string'; - return ret; - }; - imports.wbg.__wbindgen_object_clone_ref = function(arg0) { - const ret = getObject(arg0); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; - imports.wbg.__wbindgen_cb_drop = function(arg0) { - const obj = takeObject(arg0).original; - if (obj.cnt-- == 1) { - obj.a = 0; - return true; - } - const ret = false; - return ret; - }; - imports.wbg.__wbg_modalhidebyid_3090e1f0ff737387 = function(arg0, arg1) { - modal_hide_by_id(getStringFromWasm0(arg0, arg1)); - }; - imports.wbg.__wbg_BigInt_d0c7d465bfa30d3b = function(arg0) { - const ret = BigInt(arg0); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_number_new = function(arg0) { - const ret = arg0; - return addHeapObject(ret); - }; - imports.wbg.__wbg_BigInt_1fab4952b6c4a499 = function(arg0) { - const ret = BigInt(BigInt.asUintN(64, arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_null = function(arg0) { - const ret = getObject(arg0) === null; - return ret; - }; - imports.wbg.__wbg_BigInt_06819bca5a5bedef = function(arg0) { - const ret = BigInt(getObject(arg0)); - return ret; - }; - imports.wbg.__wbg_BigInt_67359e71cae1c6c9 = function(arg0) { - const ret = BigInt(getObject(arg0)); - return ret; - }; - imports.wbg.__wbg_get_2268d91a19a98b92 = function(arg0, arg1) { - const ret = getObject(arg0)[takeObject(arg1)]; - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_c943d600fa71e4dd = function(arg0, arg1, arg2) { - getObject(arg0)[takeObject(arg1)] = takeObject(arg2); - }; - imports.wbg.__wbg_new_abda76e883ba8a5f = function() { - const ret = new Error(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { - const ret = getObject(arg1).stack; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { - try { - console.error(getStringFromWasm0(arg0, arg1)); - } finally { - wasm.__wbindgen_free(arg0, arg1); - } - }; - imports.wbg.__wbg_debug_783a3d4910bc24c7 = function(arg0, arg1) { - var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); - wasm.__wbindgen_free(arg0, arg1 * 4); - console.debug(...v0); - }; - imports.wbg.__wbg_error_71d6845bf00a930f = function(arg0, arg1) { - var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); - wasm.__wbindgen_free(arg0, arg1 * 4); - console.error(...v0); - }; - imports.wbg.__wbg_log_1f7f93998ab961f7 = function(arg0, arg1) { - var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); - wasm.__wbindgen_free(arg0, arg1 * 4); - console.log(...v0); - }; - imports.wbg.__wbg_warn_0b90a269a514ae1d = function(arg0, arg1) { - var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); - wasm.__wbindgen_free(arg0, arg1 * 4); - console.warn(...v0); - }; - imports.wbg.__wbg_instanceof_Window_acc97ff9f5d2c7b4 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Window; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_document_3ead31dbcad65886 = function(arg0) { - const ret = getObject(arg0).document; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_location_8cc8ccf27e342c0a = function(arg0) { - const ret = getObject(arg0).location; - return addHeapObject(ret); - }; - imports.wbg.__wbg_history_2a104346a1208269 = function() { return handleError(function (arg0) { - const ret = getObject(arg0).history; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_navigator_d1dcf282b97e2495 = function(arg0) { - const ret = getObject(arg0).navigator; - return addHeapObject(ret); - }; - imports.wbg.__wbg_localStorage_753b6d15a844c3dc = function() { return handleError(function (arg0) { - const ret = getObject(arg0).localStorage; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_sessionStorage_4ab60c7f3cb9633b = function() { return handleError(function (arg0) { - const ret = getObject(arg0).sessionStorage; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_fetch_0fe04905cccfc2aa = function(arg0, arg1) { - const ret = getObject(arg0).fetch(getObject(arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_body_3cb4b4042b9a632b = function(arg0) { - const ret = getObject(arg0).body; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_createElement_976dbb84fe1661b5 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_createElementNS_1561aca8ee3693c0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - const ret = getObject(arg0).createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_createTextNode_300f845fab76642f = function(arg0, arg1, arg2) { - const ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_getElementById_3a708b83e4f034d7 = function(arg0, arg1, arg2) { - const ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2)); - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_querySelector_3628dc2c3319e7e0 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2)); - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_getItem_845e475f85f593e4 = function() { return handleError(function (arg0, arg1, arg2, arg3) { - const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3)); - var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }, arguments) }; - imports.wbg.__wbg_removeItem_9da69ede4eea3326 = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).removeItem(getStringFromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_setItem_9c469d634d0c321c = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - }, arguments) }; - imports.wbg.__wbg_pathname_78a642e573bf8169 = function(arg0, arg1) { - const ret = getObject(arg1).pathname; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_search_afb25c63fe262036 = function(arg0, arg1) { - const ret = getObject(arg1).search; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_setsearch_40007c2a91333011 = function(arg0, arg1, arg2) { - getObject(arg0).search = getStringFromWasm0(arg1, arg2); - }; - imports.wbg.__wbg_new_7d95b89914e4d377 = function() { return handleError(function (arg0, arg1) { - const ret = new URL(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_new_ca4d3a3eca340210 = function() { return handleError(function () { - const ret = new URLSearchParams(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_new_2d0053ee81e4dd2a = function() { return handleError(function () { - const ret = new Headers(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_get_31b57952dfc2c6cc = function() { return handleError(function (arg0, arg1, arg2, arg3) { - const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3)); - var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }, arguments) }; - imports.wbg.__wbg_set_992c1d31586b2957 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - }, arguments) }; - imports.wbg.__wbg_value_ccb32485ee1b3928 = function(arg0, arg1) { - const ret = getObject(arg1).value; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_setvalue_df64bc6794c098f2 = function(arg0, arg1, arg2) { - getObject(arg0).value = getStringFromWasm0(arg1, arg2); - }; - imports.wbg.__wbg_url_1c013f0875e97715 = function(arg0, arg1) { - const ret = getObject(arg1).url; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_headers_85824e993aa739bf = function(arg0) { - const ret = getObject(arg0).headers; - return addHeapObject(ret); - }; - imports.wbg.__wbg_newwithstr_fdce36db91ec5f92 = function() { return handleError(function (arg0, arg1) { - const ret = new Request(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_add_89a4f3b0846cf0aa = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).add(getStringFromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_remove_1a26eb5d822902ed = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).remove(getStringFromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_instanceof_HtmlFormElement_1c489ff7e99e43d3 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof HTMLFormElement; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_instanceof_HtmlInputElement_970e4026de0fccff = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof HTMLInputElement; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_setchecked_f1e1f3e62cdca8e7 = function(arg0, arg1) { - getObject(arg0).checked = arg1 !== 0; - }; - imports.wbg.__wbg_value_b2a620d34c663701 = function(arg0, arg1) { - const ret = getObject(arg1).value; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_setvalue_e5b519cca37d82a7 = function(arg0, arg1, arg2) { - getObject(arg0).value = getStringFromWasm0(arg1, arg2); - }; - imports.wbg.__wbg_instanceof_Element_33bd126d58f2021b = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Element; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_namespaceURI_e19c7be2c60e5b5c = function(arg0, arg1) { - const ret = getObject(arg1).namespaceURI; - var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_classList_8a97f5e2e1bc3fa9 = function(arg0) { - const ret = getObject(arg0).classList; - return addHeapObject(ret); - }; - imports.wbg.__wbg_setinnerHTML_32081d8a164e6dc4 = function(arg0, arg1, arg2) { - getObject(arg0).innerHTML = getStringFromWasm0(arg1, arg2); - }; - imports.wbg.__wbg_removeAttribute_beaed7727852af78 = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).removeAttribute(getStringFromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_setAttribute_d8436c14a59ab1af = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - }, arguments) }; - imports.wbg.__wbg_instanceof_HtmlElement_eff00d16af7bd6e7 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof HTMLElement; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_focus_adfe4cc61e2c09bc = function() { return handleError(function (arg0) { - getObject(arg0).focus(); - }, arguments) }; - imports.wbg.__wbg_create_53c6ddb068a22172 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).create(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_get_da97585bbb5a63bb = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).get(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_href_90ff36b5040e3b76 = function(arg0, arg1) { - const ret = getObject(arg1).href; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbg_credentials_eab5c0bffc3e9cc5 = function(arg0) { - const ret = getObject(arg0).credentials; - return addHeapObject(ret); - }; - imports.wbg.__wbg_getClientExtensionResults_0381c2792f96b9fa = function(arg0) { - const ret = getObject(arg0).getClientExtensionResults(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_instanceof_Event_1009dd203d9055ee = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Event; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_target_bf704b7db7ad1387 = function(arg0) { - const ret = getObject(arg0).target; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_cancelBubble_8c0bdf21c08f1717 = function(arg0) { - const ret = getObject(arg0).cancelBubble; - return ret; - }; - imports.wbg.__wbg_preventDefault_3209279b490de583 = function(arg0) { - getObject(arg0).preventDefault(); - }; - imports.wbg.__wbg_addEventListener_1fc744729ac6dc27 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4)); - }, arguments) }; - imports.wbg.__wbg_removeEventListener_b10f1a66647f3aa0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0); - }, arguments) }; - imports.wbg.__wbg_newwithform_6b545e9ddaccc455 = function() { return handleError(function (arg0) { - const ret = new FormData(getObject(arg0)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_get_f1d748260e3dfd1f = function(arg0, arg1, arg2) { - const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_parentElement_0cffb3ceb0f107bd = function(arg0) { - const ret = getObject(arg0).parentElement; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_lastChild_a2f5ed739809bb31 = function(arg0) { - const ret = getObject(arg0).lastChild; - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; - imports.wbg.__wbg_setnodeValue_4077cafeefd0725e = function(arg0, arg1, arg2) { - getObject(arg0).nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2); - }; - imports.wbg.__wbg_appendChild_e513ef0e5098dfdd = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).appendChild(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_insertBefore_9f2d2defb9471006 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).insertBefore(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_removeChild_6751e9ca5d9aaf00 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).removeChild(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_pushState_38917fb88b4add30 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { - getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5)); - }, arguments) }; - imports.wbg.__wbg_pathname_4441d4d8fc4aba51 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg1).pathname; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }, arguments) }; - imports.wbg.__wbg_search_4aac147f005678e5 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg1).search; - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }, arguments) }; - imports.wbg.__wbg_replace_ab0ff56e84982ad2 = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).replace(getStringFromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_instanceof_Response_eaa426220848a39e = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Response; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_status_c4ef3dd591e63435 = function(arg0) { - const ret = getObject(arg0).status; - return ret; - }; - imports.wbg.__wbg_headers_fd64ad685cf22e5d = function(arg0) { - const ret = getObject(arg0).headers; - return addHeapObject(ret); - }; - imports.wbg.__wbg_json_eb16b12f372e850c = function() { return handleError(function (arg0) { - const ret = getObject(arg0).json(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_text_1169d752cc697903 = function() { return handleError(function (arg0) { - const ret = getObject(arg0).text(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_get_57245cc7d7c7619d = function(arg0, arg1) { - const ret = getObject(arg0)[arg1 >>> 0]; - return addHeapObject(ret); - }; - imports.wbg.__wbg_length_6e3bbe7c8bd4dbd8 = function(arg0) { - const ret = getObject(arg0).length; - return ret; - }; - imports.wbg.__wbg_new_1d9a920c6bfc44a8 = function() { - const ret = new Array(); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_function = function(arg0) { - const ret = typeof(getObject(arg0)) === 'function'; - return ret; - }; - imports.wbg.__wbg_newnoargs_b5b063fc6c2f0376 = function(arg0, arg1) { - const ret = new Function(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_268f7b7dd3430798 = function() { - const ret = new Map(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_next_579e583d33566a86 = function(arg0) { - const ret = getObject(arg0).next; - return addHeapObject(ret); - }; - imports.wbg.__wbg_next_aaef7c8aa5e212ac = function() { return handleError(function (arg0) { - const ret = getObject(arg0).next(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_done_1b73b0672e15f234 = function(arg0) { - const ret = getObject(arg0).done; - return ret; - }; - imports.wbg.__wbg_value_1ccc36bc03462d71 = function(arg0) { - const ret = getObject(arg0).value; - return addHeapObject(ret); - }; - imports.wbg.__wbg_iterator_6f9d4f28845f426c = function() { - const ret = Symbol.iterator; - return addHeapObject(ret); - }; - imports.wbg.__wbg_get_765201544a2b6869 = function() { return handleError(function (arg0, arg1) { - const ret = Reflect.get(getObject(arg0), getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_call_97ae9d8645dc388b = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).call(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_new_0b9bfdd97583284e = function() { - const ret = new Object(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_self_6d479506f72c6a71 = function() { return handleError(function () { - const ret = self.self; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_window_f2557cc78490aceb = function() { return handleError(function () { - const ret = window.window; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_globalThis_7f206bda628d5286 = function() { return handleError(function () { - const ret = globalThis.globalThis; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_global_ba75c50d1cf384f4 = function() { return handleError(function () { - const ret = global.global; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_set_a68214f35c417fa9 = function(arg0, arg1, arg2) { - getObject(arg0)[arg1 >>> 0] = takeObject(arg2); - }; - imports.wbg.__wbg_isArray_27c46c67f498e15d = function(arg0) { - const ret = Array.isArray(getObject(arg0)); - return ret; - }; - imports.wbg.__wbg_push_740e4b286702d964 = function(arg0, arg1) { - const ret = getObject(arg0).push(getObject(arg1)); - return ret; - }; - imports.wbg.__wbg_instanceof_ArrayBuffer_e5e48f4762c5610b = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof ArrayBuffer; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_instanceof_Error_56b496a10a56de66 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Error; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_new_8d2af00bc1e329ee = function(arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_message_fe2af63ccc8985bc = function(arg0) { - const ret = getObject(arg0).message; - return addHeapObject(ret); - }; - imports.wbg.__wbg_name_48eda3ae6aa697ca = function(arg0) { - const ret = getObject(arg0).name; - return addHeapObject(ret); - }; - imports.wbg.__wbg_toString_73c9b562dccf34bd = function(arg0) { - const ret = getObject(arg0).toString(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_933729cf5b66ac11 = function(arg0, arg1, arg2) { - const ret = getObject(arg0).set(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_isSafeInteger_dfa0593e8d7ac35a = function(arg0) { - const ret = Number.isSafeInteger(getObject(arg0)); - return ret; - }; - imports.wbg.__wbg_valueOf_6b6effad03e5c546 = function(arg0) { - const ret = getObject(arg0).valueOf(); - return ret; - }; - imports.wbg.__wbg_entries_65a76a413fc91037 = function(arg0) { - const ret = Object.entries(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_is_40a66842732708e7 = function(arg0, arg1) { - const ret = Object.is(getObject(arg0), getObject(arg1)); - return ret; - }; - imports.wbg.__wbg_toString_7be108a12ef03bc2 = function(arg0) { - const ret = getObject(arg0).toString(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_resolve_99fe17964f31ffc0 = function(arg0) { - const ret = Promise.resolve(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_then_11f7a54d67b4bfad = function(arg0, arg1) { - const ret = getObject(arg0).then(getObject(arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_then_cedad20fbbd9418a = function(arg0, arg1, arg2) { - const ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_buffer_3f3d764d4747d564 = function(arg0) { - const ret = getObject(arg0).buffer; - return addHeapObject(ret); - }; - imports.wbg.__wbg_newwithbyteoffsetandlength_d9aa266703cb98be = function(arg0, arg1, arg2) { - const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_8c3f0052272a457a = function(arg0) { - const ret = new Uint8Array(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_83db9690f9353e79 = function(arg0, arg1, arg2) { - getObject(arg0).set(getObject(arg1), arg2 >>> 0); - }; - imports.wbg.__wbg_length_9e1ae1900cb0fbd5 = function(arg0) { - const ret = getObject(arg0).length; - return ret; - }; - imports.wbg.__wbg_instanceof_Uint8Array_971eeda69eb75003 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Uint8Array; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_has_8359f114ce042f5a = function() { return handleError(function (arg0, arg1) { - const ret = Reflect.has(getObject(arg0), getObject(arg1)); - return ret; - }, arguments) }; - imports.wbg.__wbg_set_bf3f89b92d5a34bf = function() { return handleError(function (arg0, arg1, arg2) { - const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); - return ret; - }, arguments) }; - imports.wbg.__wbg_stringify_d6471d300ded9b68 = function() { return handleError(function (arg0) { - const ret = JSON.stringify(getObject(arg0)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { - const ret = debugString(getObject(arg1)); - const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbindgen_throw = function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }; - imports.wbg.__wbindgen_memory = function() { - const ret = wasm.memory; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_closure_wrapper6332 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1615, __wbg_adapter_36); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_closure_wrapper6493 = function(arg0, arg1, arg2) { - const ret = makeClosure(arg0, arg1, 1650, __wbg_adapter_39); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_closure_wrapper6732 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1711, __wbg_adapter_42); - return addHeapObject(ret); - }; - - return imports; -} - -function initMemory(imports, maybe_memory) { - -} - -function finalizeInit(instance, module) { - wasm = instance.exports; - init.__wbindgen_wasm_module = module; - cachedFloat64Memory0 = new Float64Array(); - cachedInt32Memory0 = new Int32Array(); - cachedUint32Memory0 = new Uint32Array(); - cachedUint8Memory0 = new Uint8Array(); - - - return wasm; -} - -function initSync(module) { - const imports = getImports(); - - initMemory(imports); - - if (!(module instanceof WebAssembly.Module)) { - module = new WebAssembly.Module(module); - } - - const instance = new WebAssembly.Instance(module, imports); - - return finalizeInit(instance, module); -} - -async function init(input) { - if (typeof input === 'undefined') { - input = new URL('kanidmd_web_ui_bg.wasm', import.meta.url); - } - const imports = getImports(); - - if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { - input = fetch(input); - } - - initMemory(imports); - - const { instance, module } = await load(await input, imports); - - return finalizeInit(instance, module); -} - -export { initSync } -export default init; diff --git a/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm b/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm deleted file mode 100644 index 6f144858da117621ff3a730270ebcddee23b7651..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1599068 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�+A2i<({$9-%^WN$$=7HwX@P^u(%onR~ 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

K=blcrxTN`aIoZrxS^H_z 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Ÿ>FKeSoZAA=XLL8h8eUltyGcveUB!DI1%rXgJ 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%0Dy=lw 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 U-@{wzd< for AdminListAccountsMsg { fn from(ge: GetError) -> Self { @@ -476,6 +478,7 @@ pub enum AdminViewServiceAccountMsg { impl Component for AdminViewServiceAccount { type Message = AdminViewServiceAccountMsg; type Properties = AdminViewAccountProps; + fn create(ctx: &Context) -> Self { let token = match models::get_bearer_token() { Some(value) => value, diff --git a/kanidmd_web_ui/src/components/admin_groups.rs b/kanidmd_web_ui/src/components/admin_groups.rs index 5949d8b67..951ea055b 100644 --- a/kanidmd_web_ui/src/components/admin_groups.rs +++ b/kanidmd_web_ui/src/components/admin_groups.rs @@ -1,14 +1,15 @@ +use std::collections::BTreeMap; + +use gloo::console; +use yew::{html, Component, Context, Html, Properties}; +use yew_router::prelude::Link; + use crate::components::adminmenu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; -use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE}; -use crate::constants::{CSS_CELL, CSS_TABLE}; +use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; use crate::models; use crate::utils::{do_alert_error, do_page_header, init_request}; use crate::views::AdminRoute; -use gloo::console; -use std::collections::BTreeMap; -use yew::{html, Component, Context, Html, Properties}; -use yew_router::prelude::Link; impl From for AdminListGroupsMsg { fn from(ge: GetError) -> Self { @@ -282,7 +283,6 @@ pub struct AdminViewGroup { impl Component for AdminViewGroup { type Message = AdminViewGroupMsg; - type Properties = AdminViewGroupProps; fn create(ctx: &Context) -> Self { diff --git a/kanidmd_web_ui/src/components/admin_oauth2.rs b/kanidmd_web_ui/src/components/admin_oauth2.rs index fb13f1aaa..c57a90525 100644 --- a/kanidmd_web_ui/src/components/admin_oauth2.rs +++ b/kanidmd_web_ui/src/components/admin_oauth2.rs @@ -1,14 +1,15 @@ +use std::collections::BTreeMap; + +use gloo::console; +use yew::{html, Component, Context, Html, Properties}; +use yew_router::prelude::Link; + use crate::components::adminmenu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; -use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE}; -use crate::constants::{CSS_CELL, CSS_TABLE}; +use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; use crate::models; use crate::utils::{do_alert_error, do_page_header, init_request}; use crate::views::AdminRoute; -use gloo::console; -use std::collections::BTreeMap; -use yew::{html, Component, Context, Html, Properties}; -use yew_router::prelude::Link; impl From for AdminListOAuth2Msg { fn from(ge: GetError) -> Self { diff --git a/kanidmd_web_ui/src/components/adminmenu.rs b/kanidmd_web_ui/src/components/adminmenu.rs index 61f84bbf6..aee95b581 100644 --- a/kanidmd_web_ui/src/components/adminmenu.rs +++ b/kanidmd_web_ui/src/components/adminmenu.rs @@ -1,13 +1,12 @@ +use serde::{Deserialize, Serialize}; +use yew::{html, Component, Context, Html, Properties}; +use yew_router::prelude::Link; + use crate::components::alpha_warning_banner; use crate::constants::{CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER}; // use crate::error::FetchError; use crate::views::AdminRoute; -use serde::{Deserialize, Serialize}; - -use yew::{html, Component, Context, Html, Properties}; -use yew_router::prelude::Link; - const CSS_CARD: &str = "card text-center"; const CSS_CARD_BODY: &str = "card-body text-center"; diff --git a/kanidmd_web_ui/src/components/change_unix_password.rs b/kanidmd_web_ui/src/components/change_unix_password.rs index 61a00de26..2ac1bea12 100644 --- a/kanidmd_web_ui/src/components/change_unix_password.rs +++ b/kanidmd_web_ui/src/components/change_unix_password.rs @@ -1,11 +1,10 @@ +use std::str::FromStr; + use compact_jwt::{Jws, JwsUnverified}; use kanidm_proto::v1::{SingleStringRequest, UserAuthToken}; -use std::str::FromStr; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; -use web_sys::{FormData, HtmlFormElement}; - -use web_sys::{Request, RequestInit, RequestMode, Response}; +use web_sys::{FormData, HtmlFormElement, Request, RequestInit, RequestMode, Response}; use yew::prelude::*; use crate::error::*; @@ -85,6 +84,7 @@ impl Component for ChangeUnixPassword { pw_check_val: "".to_string(), } } + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { match msg { Msg::Submit(data) => { @@ -303,6 +303,7 @@ impl ChangeUnixPassword { Ok(Msg::Error { emsg, kopid }) } } + fn reset(&mut self) { self.pw_val = "".to_string(); self.pw_check_val = "".to_string(); diff --git a/kanidmd_web_ui/src/credential/delete.rs b/kanidmd_web_ui/src/credential/delete.rs index caaf0142f..fc5d07fd5 100644 --- a/kanidmd_web_ui/src/credential/delete.rs +++ b/kanidmd_web_ui/src/credential/delete.rs @@ -1,19 +1,16 @@ -use crate::error::*; -use crate::utils; - -use super::eventbus::{EventBus, EventBusMsg}; -use super::reset::ModalProps; - #[cfg(debug)] use gloo::console; -use yew::prelude::*; -use yew_agent::Dispatched; - +use kanidm_proto::v1::{CURequest, CUSessionToken, CUStatus}; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_agent::Dispatched; -use kanidm_proto::v1::{CURequest, CUSessionToken, CUStatus}; +use super::eventbus::{EventBus, EventBusMsg}; +use super::reset::ModalProps; +use crate::error::*; +use crate::utils; enum State { Init, diff --git a/kanidmd_web_ui/src/credential/eventbus.rs b/kanidmd_web_ui/src/credential/eventbus.rs index 1f07c85b2..ac40c14ba 100644 --- a/kanidmd_web_ui/src/credential/eventbus.rs +++ b/kanidmd_web_ui/src/credential/eventbus.rs @@ -1,9 +1,8 @@ -use serde::{Deserialize, Serialize}; use std::collections::HashSet; -use yew_agent::{Agent, AgentLink, Context, HandlerId}; - use kanidm_proto::v1::CUStatus; +use serde::{Deserialize, Serialize}; +use yew_agent::{Agent, AgentLink, Context, HandlerId}; #[derive(Serialize, Deserialize, Debug, Clone)] #[allow(clippy::large_enum_variant)] @@ -18,10 +17,10 @@ pub struct EventBus { } impl Agent for EventBus { - type Reach = Context; - type Message = (); type Input = EventBusMsg; + type Message = (); type Output = EventBusMsg; + type Reach = Context; fn create(link: AgentLink) -> Self { Self { diff --git a/kanidmd_web_ui/src/credential/passkey.rs b/kanidmd_web_ui/src/credential/passkey.rs index d8cca8066..f87908c31 100644 --- a/kanidmd_web_ui/src/credential/passkey.rs +++ b/kanidmd_web_ui/src/credential/passkey.rs @@ -1,19 +1,16 @@ -use crate::error::*; -use crate::utils; - -use super::eventbus::{EventBus, EventBusMsg}; -use super::reset::ModalProps; - use gloo::console; -use yew::prelude::*; -use yew_agent::Dispatched; - +use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus}; +use kanidm_proto::webauthn::{CreationChallengeResponse, RegisterPublicKeyCredential}; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_agent::Dispatched; -use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus}; -use kanidm_proto::webauthn::{CreationChallengeResponse, RegisterPublicKeyCredential}; +use super::eventbus::{EventBus, EventBusMsg}; +use super::reset::ModalProps; +use crate::error::*; +use crate::utils; pub struct PasskeyModalApp { state: State, diff --git a/kanidmd_web_ui/src/credential/passkeyremove.rs b/kanidmd_web_ui/src/credential/passkeyremove.rs index 4aab0b9be..292e418b6 100644 --- a/kanidmd_web_ui/src/credential/passkeyremove.rs +++ b/kanidmd_web_ui/src/credential/passkeyremove.rs @@ -1,21 +1,17 @@ -use crate::error::*; -use crate::utils; - -use super::eventbus::{EventBus, EventBusMsg}; -use super::reset::PasskeyRemoveModalProps; - #[cfg(debug)] use gloo::console; -use yew::prelude::*; -use yew_agent::Dispatched; - +use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus}; +use uuid::Uuid; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_agent::Dispatched; -use uuid::Uuid; - -use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus}; +use super::eventbus::{EventBus, EventBusMsg}; +use super::reset::PasskeyRemoveModalProps; +use crate::error::*; +use crate::utils; pub struct PasskeyRemoveModalApp { state: State, diff --git a/kanidmd_web_ui/src/credential/pwmodal.rs b/kanidmd_web_ui/src/credential/pwmodal.rs index 9e42be8de..5aaa808b7 100644 --- a/kanidmd_web_ui/src/credential/pwmodal.rs +++ b/kanidmd_web_ui/src/credential/pwmodal.rs @@ -1,18 +1,15 @@ -use crate::error::*; -use crate::utils; - -use super::eventbus::{EventBus, EventBusMsg}; -use super::reset::ModalProps; - use gloo::console; -use yew::prelude::*; -use yew_agent::Dispatched; - +use kanidm_proto::v1::{CURequest, CUSessionToken, CUStatus, OperationError, PasswordFeedback}; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_agent::Dispatched; -use kanidm_proto::v1::{CURequest, CUSessionToken, CUStatus, OperationError, PasswordFeedback}; +use super::eventbus::{EventBus, EventBusMsg}; +use super::reset::ModalProps; +use crate::error::*; +use crate::utils; enum PwState { Init, diff --git a/kanidmd_web_ui/src/credential/reset.rs b/kanidmd_web_ui/src/credential/reset.rs index c1f1b1c15..28f1cacde 100644 --- a/kanidmd_web_ui/src/credential/reset.rs +++ b/kanidmd_web_ui/src/credential/reset.rs @@ -1,19 +1,14 @@ -use crate::error::*; -use crate::models; -use crate::utils; - use gloo::console; -use yew::prelude::*; -use yew_agent::{Bridge, Bridged}; -use yew_router::prelude::*; - use kanidm_proto::v1::{ CUIntentToken, CUSessionToken, CUStatus, CredentialDetail, CredentialDetailType, }; - +use uuid::Uuid; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_agent::{Bridge, Bridged}; +use yew_router::prelude::*; use super::delete::DeleteApp; use super::eventbus::{EventBus, EventBusMsg}; @@ -21,8 +16,8 @@ use super::passkey::PasskeyModalApp; use super::passkeyremove::PasskeyRemoveModalApp; use super::pwmodal::PwModalApp; use super::totpmodal::TotpModalApp; - -use uuid::Uuid; +use crate::error::*; +use crate::{models, utils}; #[derive(PartialEq, Eq, Properties)] pub struct ModalProps { diff --git a/kanidmd_web_ui/src/credential/totpmodal.rs b/kanidmd_web_ui/src/credential/totpmodal.rs index 0ad6920fa..3616ea04c 100644 --- a/kanidmd_web_ui/src/credential/totpmodal.rs +++ b/kanidmd_web_ui/src/credential/totpmodal.rs @@ -1,21 +1,18 @@ -use crate::error::*; -use crate::utils; - -use super::eventbus::{EventBus, EventBusMsg}; -use super::reset::ModalProps; - #[cfg(debug)] use gloo::console; -use web_sys::Node; +use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus, TotpSecret}; +use qrcode::render::svg; +use qrcode::QrCode; +use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; +use wasm_bindgen_futures::JsFuture; +use web_sys::{Node, Request, RequestInit, RequestMode, Response}; use yew::prelude::*; use yew_agent::Dispatched; -use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; -use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestInit, RequestMode, Response}; - -use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus, TotpSecret}; -use qrcode::{render::svg, QrCode}; +use super::eventbus::{EventBus, EventBusMsg}; +use super::reset::ModalProps; +use crate::error::*; +use crate::utils; enum TotpState { Init, diff --git a/kanidmd_web_ui/src/error.rs b/kanidmd_web_ui/src/error.rs index 538c6284c..f24725c32 100644 --- a/kanidmd_web_ui/src/error.rs +++ b/kanidmd_web_ui/src/error.rs @@ -1,5 +1,6 @@ use std::error::Error; use std::fmt; + use wasm_bindgen::JsValue; #[derive(Debug, Clone, PartialEq)] diff --git a/kanidmd_web_ui/src/login.rs b/kanidmd_web_ui/src/login.rs index 974eae65b..681cd831f 100644 --- a/kanidmd_web_ui/src/login.rs +++ b/kanidmd_web_ui/src/login.rs @@ -1,5 +1,9 @@ // use anyhow::Error; use gloo::console; +use kanidm_proto::v1::{ + AuthAllowed, AuthCredential, AuthMech, AuthRequest, AuthResponse, AuthState, AuthStep, +}; +use kanidm_proto::webauthn::PublicKeyCredential; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::{spawn_local, JsFuture}; @@ -10,13 +14,7 @@ use yew_router::prelude::*; use crate::constants::{CLASS_BUTTON_DARK, CLASS_DIV_LOGIN_BUTTON, CLASS_DIV_LOGIN_FIELD}; use crate::error::FetchError; -use crate::models; -use crate::utils; - -use kanidm_proto::v1::{ - AuthAllowed, AuthCredential, AuthMech, AuthRequest, AuthResponse, AuthState, AuthStep, -}; -use kanidm_proto::webauthn::PublicKeyCredential; +use crate::{models, utils}; pub struct LoginApp { inputvalue: String, diff --git a/kanidmd_web_ui/src/manager.rs b/kanidmd_web_ui/src/manager.rs index de6b9e32e..2adeeb228 100644 --- a/kanidmd_web_ui/src/manager.rs +++ b/kanidmd_web_ui/src/manager.rs @@ -5,6 +5,7 @@ //! will allow you to proceed with the oauth flow. use gloo::console; +use serde::{Deserialize, Serialize}; use wasm_bindgen::UnwrapThrowExt; use yew::functional::*; use yew::prelude::*; @@ -14,7 +15,6 @@ use crate::credential::reset::CredentialResetApp; use crate::login::LoginApp; use crate::oauth2::Oauth2App; use crate::views::{ViewRoute, ViewsApp}; -use serde::{Deserialize, Serialize}; // router to decide on state. #[derive(Routable, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] diff --git a/kanidmd_web_ui/src/models/mod.rs b/kanidmd_web_ui/src/models/mod.rs index 066a8ff8e..c3d6e2e88 100644 --- a/kanidmd_web_ui/src/models/mod.rs +++ b/kanidmd_web_ui/src/models/mod.rs @@ -1,18 +1,16 @@ -use kanidm_proto::oauth2::AuthorisationRequest; - #[cfg(debug)] use gloo::console; -use gloo::storage::LocalStorage as PersistentStorage; -use gloo::storage::SessionStorage as TemporaryStorage; -use gloo::storage::Storage; +use gloo::storage::{ + LocalStorage as PersistentStorage, SessionStorage as TemporaryStorage, Storage, +}; +use kanidm_proto::oauth2::AuthorisationRequest; +use kanidm_proto::v1::{CUSessionToken, CUStatus}; +use serde::{Deserialize, Serialize}; use wasm_bindgen::UnwrapThrowExt; use yew_router::prelude::{AnyHistory, History}; use crate::manager::Route; use crate::views::ViewRoute; -use serde::{Deserialize, Serialize}; - -use kanidm_proto::v1::{CUSessionToken, CUStatus}; pub fn get_bearer_token() -> Option { let prev_session: Result = PersistentStorage::get("kanidm_bearer_token"); diff --git a/kanidmd_web_ui/src/oauth2.rs b/kanidmd_web_ui/src/oauth2.rs index 1f20407bd..4c5e45d84 100644 --- a/kanidmd_web_ui/src/oauth2.rs +++ b/kanidmd_web_ui/src/oauth2.rs @@ -1,8 +1,10 @@ // use anyhow::Error; use gloo::console; -use wasm_bindgen::JsCast; -use wasm_bindgen::JsValue; -use wasm_bindgen::UnwrapThrowExt; +pub use kanidm_proto::oauth2::{ + AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisationResponse, + CodeChallengeMethod, ErrorResponse, +}; +use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, RequestRedirect, Response}; use yew::prelude::*; @@ -10,13 +12,7 @@ use yew_router::prelude::*; use crate::error::*; use crate::manager::Route; -use crate::models; -use crate::utils; - -pub use kanidm_proto::oauth2::{ - AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisationResponse, - CodeChallengeMethod, ErrorResponse, -}; +use crate::{models, utils}; enum State { // We don't have a token, or something is invalid. diff --git a/kanidmd_web_ui/src/views/apps.rs b/kanidmd_web_ui/src/views/apps.rs index 878e9b5e9..2a9ae6f7b 100644 --- a/kanidmd_web_ui/src/views/apps.rs +++ b/kanidmd_web_ui/src/views/apps.rs @@ -1,9 +1,10 @@ -use crate::components::alpha_warning_banner; -use crate::constants::{CSS_CELL, CSS_PAGE_HEADER, CSS_TABLE}; #[cfg(debug)] use gloo::console; use yew::prelude::*; +use crate::components::alpha_warning_banner; +use crate::constants::{CSS_CELL, CSS_PAGE_HEADER, CSS_TABLE}; + pub enum Msg { // Nothing } diff --git a/kanidmd_web_ui/src/views/mod.rs b/kanidmd_web_ui/src/views/mod.rs index 3ad1aac11..315ca1afa 100644 --- a/kanidmd_web_ui/src/views/mod.rs +++ b/kanidmd_web_ui/src/views/mod.rs @@ -1,18 +1,19 @@ -use crate::components::{admin_accounts, admin_groups, admin_oauth2, adminmenu}; -use crate::error::*; -use crate::manager::Route; -use crate::models; -use crate::utils; +use std::str::FromStr; + use compact_jwt::{Jws, JwsUnverified}; use kanidm_proto::v1::UserAuthToken; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use wasm_bindgen::{JsCast, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; use yew::prelude::*; use yew_router::prelude::*; +use crate::components::{admin_accounts, admin_groups, admin_oauth2, adminmenu}; +use crate::error::*; +use crate::manager::Route; +use crate::{models, utils}; + mod apps; mod components; mod profile; @@ -339,6 +340,7 @@ impl ViewsApp { } } + async fn check_token_valid(token: String) -> Result { let mut opts = RequestInit::new(); opts.method("GET"); diff --git a/kanidmd_web_ui/src/views/profile.rs b/kanidmd_web_ui/src/views/profile.rs index 215499ed1..d68801137 100644 --- a/kanidmd_web_ui/src/views/profile.rs +++ b/kanidmd_web_ui/src/views/profile.rs @@ -1,10 +1,10 @@ -use crate::constants::CSS_PAGE_HEADER; -use crate::views::ViewProps; - use gloo::console; use wasm_bindgen::UnwrapThrowExt; use yew::prelude::*; +use crate::constants::CSS_PAGE_HEADER; +use crate::views::ViewProps; + // User Profile UI pub struct ProfileApp {} diff --git a/kanidmd_web_ui/src/views/security.rs b/kanidmd_web_ui/src/views/security.rs index f19b539c9..33d3083f3 100644 --- a/kanidmd_web_ui/src/views/security.rs +++ b/kanidmd_web_ui/src/views/security.rs @@ -1,24 +1,21 @@ -use crate::constants::CSS_PAGE_HEADER; -use crate::error::*; -use crate::models; -use crate::utils; - -use crate::components::change_unix_password::ChangeUnixPassword; -use crate::manager::Route; -use crate::views::{ViewProps, ViewRoute}; +use std::str::FromStr; use compact_jwt::{Jws, JwsUnverified}; #[cfg(debug)] use gloo::console; -use std::str::FromStr; -use yew::prelude::*; -use yew_router::prelude::*; - use kanidm_proto::v1::{CUSessionToken, CUStatus, UiHint, UserAuthToken}; - use wasm_bindgen::{JsCast, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, RequestMode, Response}; +use yew::prelude::*; +use yew_router::prelude::*; + +use crate::components::change_unix_password::ChangeUnixPassword; +use crate::constants::CSS_PAGE_HEADER; +use crate::error::*; +use crate::manager::Route; +use crate::views::{ViewProps, ViewRoute}; +use crate::{models, utils}; #[allow(clippy::large_enum_variant)] // Page state diff --git a/orca/Cargo.toml b/orca/Cargo.toml index dd5864809..37698aede 100644 --- a/orca/Cargo.toml +++ b/orca/Cargo.toml @@ -1,44 +1,44 @@ [package] name = "orca" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -rust-version = "1.59" -edition = "2021" -license = "MPL-2.0" description = "Orca - load testing for LDAP and Kanidm" documentation = "https://docs.rs/kanidm/latest/kanidm/" -homepage = "https://github.com/kanidm/kanidm/" -repository = "https://github.com/kanidm/kanidm/" + +version.workspace = true +authors.workspace = true +rust-version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true [[bin]] name = "orca" path = "src/main.rs" [dependencies] -clap = { version = "^3.2", features = ["derive"] } -crossbeam = "0.8.1" -csv = "1.1.6" -dialoguer = "0.10.1" -futures-util = { version = "^0.3.21", features = ["sink"] } -kanidm_client = { path = "../kanidm_client" } -kanidm_proto = { path = "../kanidm_proto" } -ldap3_proto = "^0.2.3" -mathru = "^0.13.0" -openssl = "^0.10.41" -rand = "^0.8.5" -serde = { version = "^1.0.142", features = ["derive"] } -serde_json = "^1.0.83" -tokio = { version = "^1.21.1", features = ["rt-multi-thread"] } -tokio-openssl = "^0.6.3" -tokio-util = { version = "^0.7.4", features = ["codec"] } -toml = "^0.5.9" -tracing = "^0.1.35" -tracing-subscriber = "^0.3.14" -uuid = { version = "^1.1.2", features = ["serde", "v4" ] } +clap.workspace = true +crossbeam.workspace = true +csv.workspace = true +dialoguer.workspace = true +futures-util = { workspace = true, features = ["sink"] } +kanidm_client.workspace = true +kanidm_proto.workspace = true +ldap3_proto.workspace = true +mathru.workspace = true +openssl.workspace = true +rand.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +tokio = { workspace = true, features = ["rt-multi-thread"] } +tokio-openssl.workspace = true +tokio-util = { workspace = true, features = ["codec"] } +toml.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +uuid = { workspace = true, features = ["serde", "v4" ] } [target.'cfg(not(target_family = "windows"))'.dependencies] -tikv-jemallocator = "0.5" - +tikv-jemallocator.workspace = true [build-dependencies] -profiles = { path = "../profiles" } +profiles.workspace = true diff --git a/orca/src/data.rs b/orca/src/data.rs index f0ad49458..665b1994d 100644 --- a/orca/src/data.rs +++ b/orca/src/data.rs @@ -1,10 +1,10 @@ use std::collections::{HashMap, HashSet}; use std::time::Duration; -use uuid::Uuid; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; pub fn readable_password_from_random() -> String { let mut trng = thread_rng(); diff --git a/orca/src/ds.rs b/orca/src/ds.rs index fa8db1979..5239ef54b 100644 --- a/orca/src/ds.rs +++ b/orca/src/ds.rs @@ -1,11 +1,13 @@ +use std::collections::{HashMap, HashSet}; +use std::time::{Duration, Instant}; + +use ldap3_proto::proto::*; +use uuid::Uuid; + use crate::data::*; use crate::ldap::{LdapClient, LdapSchema}; use crate::profile::DsConfig; use crate::{TargetServer, TargetServerBuilder}; -use ldap3_proto::proto::*; -use std::collections::{HashMap, HashSet}; -use std::time::{Duration, Instant}; -use uuid::Uuid; #[derive(Debug)] pub struct DirectoryServer { diff --git a/orca/src/kani.rs b/orca/src/kani.rs index 75e57c319..a35a698ae 100644 --- a/orca/src/kani.rs +++ b/orca/src/kani.rs @@ -1,12 +1,14 @@ +use std::collections::{HashMap, HashSet}; +use std::time::{Duration, Instant}; + +use kanidm_client::{ClientError, KanidmClient, KanidmClientBuilder, StatusCode}; +use kanidm_proto::v1::*; +use uuid::Uuid; + use crate::data::*; use crate::ldap::{LdapClient, LdapSchema}; use crate::profile::{KaniHttpConfig, KaniLdapConfig}; use crate::{TargetServer, TargetServerBuilder}; -use kanidm_client::{ClientError, KanidmClient, KanidmClientBuilder, StatusCode}; -use kanidm_proto::v1::*; -use std::collections::{HashMap, HashSet}; -use std::time::{Duration, Instant}; -use uuid::Uuid; #[derive(Debug)] pub struct KaniHttpServer { diff --git a/orca/src/ldap.rs b/orca/src/ldap.rs index d4ba09870..1079ac54b 100644 --- a/orca/src/ldap.rs +++ b/orca/src/ldap.rs @@ -1,9 +1,11 @@ +use core::pin::Pin; use std::net::{SocketAddr, ToSocketAddrs}; use std::time::{Duration, Instant}; -use core::pin::Pin; use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; +use ldap3_proto::proto::*; +use ldap3_proto::LdapCodec; use openssl::ssl::{Ssl, SslConnector, SslMethod, SslVerifyMode}; // use std::sync::atomic::{AtomicUsize, Ordering}; use tokio::net::TcpStream; @@ -11,9 +13,6 @@ use tokio::sync::Mutex; use tokio_openssl::SslStream; use tokio_util::codec::Framed; -use ldap3_proto::proto::*; -use ldap3_proto::LdapCodec; - struct LdapInner { pub framed: Framed, LdapCodec>, pub msgid: i32, diff --git a/orca/src/main.rs b/orca/src/main.rs index 218b8d63d..890411d97 100644 --- a/orca/src/main.rs +++ b/orca/src/main.rs @@ -15,14 +15,16 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[macro_use] extern crate tracing; -use crate::ds::DirectoryServer; -use crate::kani::{KaniHttpServer, KaniLdapServer}; -use clap::{Parser, Subcommand}; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::time::{Duration, Instant}; + +use clap::{Parser, Subcommand}; use uuid::Uuid; +use crate::ds::DirectoryServer; +use crate::kani::{KaniHttpServer, KaniLdapServer}; + mod data; mod ds; mod kani; diff --git a/orca/src/preprocess.rs b/orca/src/preprocess.rs index 1e6cc196b..24ca190e3 100644 --- a/orca/src/preprocess.rs +++ b/orca/src/preprocess.rs @@ -1,7 +1,3 @@ -use crate::data::*; -use rand::seq::SliceRandom; -use rand::Rng; -use serde::Deserialize; use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; use std::convert::TryFrom; @@ -10,8 +6,14 @@ use std::io::BufReader; use std::path::Path; use std::str::FromStr; use std::time::Duration; + +use rand::seq::SliceRandom; +use rand::Rng; +use serde::Deserialize; use uuid::Uuid; +use crate::data::*; + #[derive(Debug, Deserialize)] struct RawRecord { conn: String, diff --git a/orca/src/runner/mod.rs b/orca/src/runner/mod.rs index d35efec3d..76d592291 100644 --- a/orca/src/runner/mod.rs +++ b/orca/src/runner/mod.rs @@ -1,8 +1,10 @@ -use crate::setup::config; -use crate::{TargetOpt, TestTypeOpt}; -use dialoguer::Confirm; use std::fs::create_dir_all; use std::path::{Path, PathBuf}; + +use dialoguer::Confirm; + +use crate::setup::config; +use crate::{TargetOpt, TestTypeOpt}; mod search; pub(crate) async fn doit( diff --git a/orca/src/runner/search.rs b/orca/src/runner/search.rs index b00730ba1..a31f63c68 100644 --- a/orca/src/runner/search.rs +++ b/orca/src/runner/search.rs @@ -1,20 +1,20 @@ -use crate::data::{Entity, OpType, TestData}; -use crate::profile::Profile; -use crate::{TargetServer, TargetServerBuilder}; -use crossbeam::channel::{unbounded, RecvTimeoutError}; -use mathru::statistics::distrib::Continuous; -use mathru::statistics::distrib::Normal; -use rand::seq::IteratorRandom; -use rand::seq::SliceRandom; -use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::BufWriter; use std::path::PathBuf; use std::sync::Arc; use std::time::{Duration, Instant}; + +use crossbeam::channel::{unbounded, RecvTimeoutError}; +use mathru::statistics::distrib::{Continuous, Normal}; +use rand::seq::{IteratorRandom, SliceRandom}; +use serde::{Deserialize, Serialize}; use tokio::sync::broadcast; use tokio::task; +use crate::data::{Entity, OpType, TestData}; +use crate::profile::Profile; +use crate::{TargetServer, TargetServerBuilder}; + #[derive(Debug, Clone)] enum TestPhase { WarmUp, diff --git a/orca/src/setup.rs b/orca/src/setup.rs index b8a5c8210..2bb23bcd6 100644 --- a/orca/src/setup.rs +++ b/orca/src/setup.rs @@ -1,14 +1,14 @@ +use std::fs::File; +use std::io::{BufReader, Read}; +use std::path::{Path, PathBuf}; + +use uuid::Uuid; + use crate::data::TestData; use crate::ds::DirectoryServer; use crate::kani::{KaniHttpServer, KaniLdapServer}; use crate::profile::Profile; -use crate::TargetOpt; -use crate::TargetServer; -use std::fs::File; -use std::io::BufReader; -use std::io::Read; -use std::path::{Path, PathBuf}; -use uuid::Uuid; +use crate::{TargetOpt, TargetServer}; pub(crate) fn config( target: &TargetOpt, diff --git a/profiles/Cargo.toml b/profiles/Cargo.toml index 5ea1b10f6..bde4c7c98 100644 --- a/profiles/Cargo.toml +++ b/profiles/Cargo.toml @@ -1,23 +1,24 @@ [package] name = "profiles" -version = "1.1.0-alpha.9" -authors = ["William Brown "] -rust-version = "1.64" -edition = "2021" -license = "MPL-2.0" description = "Kanidm Build System Profiles" 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 = "profiles" path = "src/lib.rs" [dependencies] -serde = { version = "^1.0.142", features = ["derive"] } -toml = "^0.5.9" -base64 = "^0.13.0" +serde = { workspace = true, features = ["derive"] } +toml.workspace = true +base64.workspace = true [build-dependencies] -base64 = "^0.13.0" +base64.workspace = true diff --git a/profiles/build.rs b/profiles/build.rs index 6fc63d576..115b2c1e0 100644 --- a/profiles/build.rs +++ b/profiles/build.rs @@ -1,6 +1,5 @@ -use std::env; -use std::fs; use std::path::PathBuf; +use std::{env, fs}; fn main() { println!("cargo:rerun-if-env-changed=KANIDM_BUILD_PROFILE"); diff --git a/profiles/src/lib.rs b/profiles/src/lib.rs index 6b692d060..27858e728 100644 --- a/profiles/src/lib.rs +++ b/profiles/src/lib.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::env; +use serde::Deserialize; + #[derive(Debug, Deserialize)] #[allow(non_camel_case_types)] enum CpuOptLevel { diff --git a/sketching/Cargo.toml b/sketching/Cargo.toml index 4801c4982..a1e9d627b 100644 --- a/sketching/Cargo.toml +++ b/sketching/Cargo.toml @@ -1,18 +1,19 @@ [package] name = "sketching" -version = "0.1.0" -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] -async-trait = "^0.1.57" -tide = "^0.16.0" -num_enum = "^0.5.7" - -tracing = { version = "^0.1.35", features = ["attributes", "max_level_trace", "release_max_level_debug"] } -tracing-subscriber = { version = "^0.3.14", features = ["env-filter"] } - -# tracing-forest = { version = "0.1.4", features = ["uuid", "smallvec", "tokio", "env-filter"] } -tracing-forest = { git = "https://github.com/QnnOkabayashi/tracing-forest.git", rev = "48d78f7294ceee47a22eee5c80964143c4fb3fe1", features = ["uuid", "smallvec", "tokio", "env-filter"] } +async-trait.workspace = true +num_enum.workspace = true +tide.workspace = true +tracing = { workspace = true, features = ["attributes"] } +tracing-subscriber = { workspace = true, features = ["env-filter"] } +tracing-forest = { workspace = true, features = ["uuid", "smallvec", "tokio", "env-filter"] } diff --git a/sketching/src/lib.rs b/sketching/src/lib.rs index f84d28db5..778e5e21f 100644 --- a/sketching/src/lib.rs +++ b/sketching/src/lib.rs @@ -2,14 +2,13 @@ #![warn(unused_extern_crates)] use num_enum::{IntoPrimitive, TryFromPrimitive}; -use tracing_forest::{util::*, Tag}; +use tracing_forest::util::*; +use tracing_forest::Tag; pub mod macros; pub mod middleware; -pub use tracing; -pub use tracing_forest; -pub use tracing_subscriber; +pub use {tracing, tracing_forest, tracing_subscriber}; pub fn test_init() { // tracing_subscriber::fmt::try_init() diff --git a/sketching/src/middleware.rs b/sketching/src/middleware.rs index b6a4b09d3..5dd8c779c 100644 --- a/sketching/src/middleware.rs +++ b/sketching/src/middleware.rs @@ -1,8 +1,7 @@ -use crate::{request_error, request_info, request_warn, security_info}; use tide::{self, Middleware, Next, Request}; use tracing::{self, instrument}; -use crate::*; +use crate::{request_error, request_info, request_warn, security_info, *}; #[derive(Default)] pub struct TreeMiddleware {}