diff --git a/.github/workflows/debian_package_kanidm.yml b/.github/workflows/debian_package_kanidm.yml index 7d8e280bf..c8639b33b 100644 --- a/.github/workflows/debian_package_kanidm.yml +++ b/.github/workflows/debian_package_kanidm.yml @@ -1,5 +1,5 @@ --- -name: "Build Debian Packages" +name: "Build Deb Packages" "on": push: @@ -31,25 +31,33 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Install dependencies + - name: install curl run: | - apt-get update && \ - apt-get install -y \ - lsb-release \ - libpam0g-dev \ - libudev-dev \ - libssl-dev \ - libsqlite3-dev \ - pkg-config \ - make \ - curl \ - sudo - - name: Install Rust - uses: dtolnay/rust-toolchain@stable + apt-get update && apt-get install -y curl - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.3 with: version: "v0.4.2" + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install dependencies + run: | + scripts/install_ubuntu_dependencies.sh + # apt-get update && \ + # apt-get install -y \ + # lsb-release \ + # libpam0g-dev \ + # libudev-dev \ + # libssl-dev \ + # libsqlite3-dev \ + # pkg-config \ + # make \ + # curl \ + # sudo \ + # build-essential \ + # rsync + - name: Install wasm-pack + run: cargo install wasm-pack - name: Build packages run: | make -f platform/debian/Makefile debs/all diff --git a/.github/workflows/wasm_test.yml b/.github/workflows/wasm_test.yml index c98d25ef1..bbf7b5588 100644 --- a/.github/workflows/wasm_test.yml +++ b/.github/workflows/wasm_test.yml @@ -16,21 +16,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install dependencies - run: | - sudo apt-get update && - sudo apt-get install -y \ - libpam0g-dev \ - libudev-dev \ - libssl-dev \ - libsqlite3-dev \ - pkg-config + # - name: Check arch + # run: | + # uname -a - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.3 with: version: "v0.4.2" + - name: Install Rust + uses: dtolnay/rust-toolchain@stable - name: Install wasm-pack run: cargo install wasm-pack + - name: Install dependencies + run: | + scripts/install_ubuntu_dependencies.sh # https://github.com/browser-actions/setup-chrome - name: Install Chrome Headless uses: browser-actions/setup-chrome@latest @@ -45,5 +44,6 @@ jobs: # docs here: # https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html - run: make webui - - run: wasm-pack test --headless --chrome + - name: "Run wasm-pack test" + run: make webui/test continue-on-error: true diff --git a/Cargo.lock b/Cargo.lock index 660aa9e5e..e6248ebcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,6 +1215,7 @@ dependencies = [ "clap", "clap_complete", "fs2", + "is-terminal", "kanidm_lib_file_permissions", "kanidm_proto", "kanidmd_core", @@ -2383,6 +2384,18 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -2718,7 +2731,6 @@ version = "1.1.0-beta.13" dependencies = [ "compact_jwt", "gloo", - "gloo-net", "js-sys", "kanidm_proto", "qrcode", diff --git a/Makefile b/Makefile index b8435c4c0..27abc0fe0 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ codespell: --skip='./book/book/*' \ --skip='./docs/*,./.git' \ --skip='./rlm_python/mods-available/eap' \ - --skip='./server/web_ui/src/external,./server/web_ui/pkg/external' \ + --skip='./server/web_ui/static/external,./server/web_ui/pkg/external' \ --skip='./server/lib/src/constants/system_config.rs,./pykanidm/site,./server/lib/src/constants/*.json' .PHONY: test/pykanidm/pytest @@ -255,3 +255,7 @@ cert/clean: .PHONY: webui webui: ## Build the WASM web frontend cd server/web_ui && ./build_wasm_release.sh + +.PHONY: webui/test +webui/test: ## Run wasm-pack test + cd server/web_ui && wasm-pack test --headless --chrome diff --git a/book/src/DEVELOPER_README.md b/book/src/DEVELOPER_README.md index 03548839b..b9da9e386 100644 --- a/book/src/DEVELOPER_README.md +++ b/book/src/DEVELOPER_README.md @@ -19,6 +19,8 @@ git and compiler tools. You should install this first. You will need [rustup](https://rustup.rs/) to install a Rust toolchain. +To build the Web UI you'll need [wasm-pack](https://rustwasm.github.io/wasm-pack/) (`cargo install wasm-pack`). + #### SUSE / OpenSUSE You will need to install rustup and our build dependencies with: diff --git a/book/src/installing_client_tools.md b/book/src/installing_client_tools.md index 4fd63e364..a465ef39e 100644 --- a/book/src/installing_client_tools.md +++ b/book/src/installing_client_tools.md @@ -128,8 +128,8 @@ alias kanidm="docker run ..." ## Initializing the configuration -The client requires a configuration file to connect to the server. -This should be at `/etc/kanidm/config` or `~/.config/kanidm`, and configures the kanidm command line tool. +The client requires a configuration file to connect to the server. This should be at +`/etc/kanidm/config` or `~/.config/kanidm`, and configures the kanidm command line tool. Here is a minimal example: diff --git a/examples/config_localhost b/examples/config_localhost index 59b9aa2cb..9d6953580 100644 --- a/examples/config_localhost +++ b/examples/config_localhost @@ -1,5 +1,4 @@ # This should be at /etc/kanidm/config or ~/.config/kanidm, and configures the kanidm command line tool -# to point at the local dev server and ignore TLS security. -uri = "https://localhost:8443" -verify_ca = false -verify_hostnames = false +# to point at the local dev server and trust the CA security. +uri="https://localhost:8443" +verify_ca="/tmp/kanidm/ca.pem" diff --git a/examples/insecure_server.toml b/examples/insecure_server.toml index 8c4ae0f4e..0178714fe 100644 --- a/examples/insecure_server.toml +++ b/examples/insecure_server.toml @@ -11,8 +11,8 @@ tls_key = "/tmp/kanidm/key.pem" # NOTE: this is overridden by environment variables at runtime # Defaults to "info" # -log_level = "info" -# log_level = "debug" +# log_level = "info" +log_level = "debug" # log_level = "trace" domain = "localhost" diff --git a/libs/client/Cargo.toml b/libs/client/Cargo.toml index f51ff6f3f..26fcfa4c1 100644 --- a/libs/client/Cargo.toml +++ b/libs/client/Cargo.toml @@ -3,23 +3,23 @@ name = "kanidm_client" description = "Kanidm Client Library" documentation = "https://docs.rs/kanidm_client/latest/kanidm_client/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [dependencies] -tracing.workspace = true +tracing = { workspace = true } reqwest = { workspace = true, default-features = false } -kanidm_proto.workspace = true +kanidm_proto = { workspace = true } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true +serde_json = { workspace = true } time = { workspace = true, features = ["serde", "std"] } tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] } -toml.workspace = true +toml = { workspace = true } uuid = { workspace = true, features = ["serde", "v4"] } url = { workspace = true, features = ["serde"] } webauthn-rs-proto = { workspace = true, features = ["wasm"] } diff --git a/libs/crypto/Cargo.toml b/libs/crypto/Cargo.toml index e88191046..6a902ba66 100644 --- a/libs/crypto/Cargo.toml +++ b/libs/crypto/Cargo.toml @@ -7,20 +7,20 @@ edition = "2021" tpm = ["dep:tss-esapi"] [dependencies] -argon2.workspace = true -base64.workspace = true -base64urlsafedata.workspace = true -hex.workspace = true -kanidm_proto.workspace = true +argon2 = { workspace = true } +base64 = { workspace = true } +base64urlsafedata = { workspace = true } +hex = { workspace = true } +kanidm_proto = { workspace = true } # We need to explicitly ask for openssl-sys so that we get the version propagated # into the build.rs for legacy feature checks. -openssl-sys.workspace = true -openssl.workspace = true -rand.workspace = true +openssl-sys = { workspace = true } +openssl = { workspace = true } +rand = { workspace = true } serde = { workspace = true, features = ["derive"] } -tracing.workspace = true +tracing = { workspace = true } tss-esapi = { workspace = true, optional = true } [dev-dependencies] -sketching.workspace = true +sketching = { workspace = true } diff --git a/libs/profiles/Cargo.toml b/libs/profiles/Cargo.toml index bdbeffe09..8c9617013 100644 --- a/libs/profiles/Cargo.toml +++ b/libs/profiles/Cargo.toml @@ -5,13 +5,13 @@ documentation = "https://docs.rs/kanidm/latest/kanidm/" # We do not have tests in this pkg autotests = false -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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" @@ -19,8 +19,8 @@ path = "src/lib.rs" [dependencies] serde = { workspace = true, features = ["derive"] } -toml.workspace = true -base64.workspace = true +toml = { workspace = true } +base64 = { workspace = true } [build-dependencies] -base64.workspace = true +base64 = { workspace = true } diff --git a/libs/sketching/Cargo.toml b/libs/sketching/Cargo.toml index 566199807..07b5b4973 100644 --- a/libs/sketching/Cargo.toml +++ b/libs/sketching/Cargo.toml @@ -3,18 +3,18 @@ name = "sketching" # We do not have tests in this pkg autotests = false -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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.workspace = true -num_enum.workspace = true -tide.workspace = true +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/libs/sketching/src/middleware.rs b/libs/sketching/src/middleware.rs index 26b844fe9..171feb5b9 100644 --- a/libs/sketching/src/middleware.rs +++ b/libs/sketching/src/middleware.rs @@ -76,6 +76,12 @@ impl TreeMiddleware { status = format_args!("{} - {}", status as u16, status.canonical_reason()), "Client error --> Response sent" ); + } else if status == 404 { + // because not-found isn't really an error, is it? + request_info!( + status = format_args!("{} - {}", status as u16, status.canonical_reason()), + "--> Response sent" + ); } else { request_warn!( status = format_args!("{} - {}", status as u16, status.canonical_reason()), diff --git a/proto/Cargo.toml b/proto/Cargo.toml index a43ef55f1..b872696dd 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -3,31 +3,31 @@ name = "kanidm_proto" description = "Kanidm Protocol Bindings for serde" documentation = "https://docs.rs/kanidm_proto/latest/kanidm_proto/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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.workspace = true -base64urlsafedata.workspace = true -num_enum.workspace = true -scim_proto.workspace = true +base32 = { workspace = true } +base64urlsafedata = { workspace = true } +num_enum = { workspace = true } +scim_proto = { workspace = true } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true +serde_json = { workspace = true } time = { workspace = true, features = ["serde", "std"] } -tracing.workspace = true +tracing = { workspace = true } url = { workspace = true, features = ["serde"] } -urlencoding.workspace = true +urlencoding = { workspace = true } uuid = { workspace = true, features = ["serde"] } -webauthn-rs-proto.workspace = true +webauthn-rs-proto = { workspace = true } [target.'cfg(not(target_family = "wasm"))'.dependencies] -last-git-commit.workspace = true +last-git-commit = { workspace = true } diff --git a/proto/src/v1.rs b/proto/src/v1.rs index 3aecabf9f..8fe5478ca 100644 --- a/proto/src/v1.rs +++ b/proto/src/v1.rs @@ -1099,7 +1099,7 @@ pub struct CUSessionToken { pub token: String, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum CURequest { PrimaryRemove, diff --git a/scripts/install_macos_dependencies.sh b/scripts/install_macos_dependencies.sh new file mode 100755 index 000000000..207a679d4 --- /dev/null +++ b/scripts/install_macos_dependencies.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +ERROR=0 +if [ -z "$(which cargo)" ]; then + echo "You don't have cargo / rust installed!" + echo "Go to for instructions!" + ERROR=1 +fi + +if [ -z "$(which wasm-pack)" ]; then + echo "You don't have wasm-pack installed! Installing it now..." + cargo install wasm-pack +fi + +if [ $ERROR -eq 1 ]; then + exit 1 +fi diff --git a/scripts/install_ubuntu_dependencies.sh b/scripts/install_ubuntu_dependencies.sh new file mode 100755 index 000000000..4ed47e893 --- /dev/null +++ b/scripts/install_ubuntu_dependencies.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -e + +if [ "$(whoami)" == "root" ]; then + SUDOCMD="" +else + SUDOCMD="sudo " +fi + +${SUDOCMD} apt-get update && +${SUDOCMD} apt-get install -y \ + libpam0g-dev \ + libudev-dev \ + libssl-dev \ + libsqlite3-dev \ + pkg-config \ + curl \ + rsync \ + build-essential + +if [ -z "$(which cargo)" ]; then + if [ -f "$HOME/.cargo/env" ]; then + #shellcheck disable=SC1091 + source "$HOME/.cargo/env" + elif [ "${INSTALL_RUST}" == "1" ]; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + #shellcheck disable=SC1091 + source "$HOME/.cargo/env" + fi + +fi + +ERROR=0 +if [ -z "$(which cargo)" ]; then + echo "You don't have cargo / rust installed!" + echo "Go to for instructions!" + echo "" + echo "Or run this script with INSTALL_RUST=1 to install it for you." + ERROR=1 +fi + +if [ $ERROR -eq 0 ] && [ -z "$(which wasm-pack)" ]; then + echo "You don't have wasm-pack installed! Installing it now..." + cargo install wasm-pack +fi +if [ $ERROR -eq 0 ] && [ -z "$(which wasm-bindgen)" ]; then + echo "You don't have wasm-bindgen installed! Installing it now..." + cargo install -f wasm-bindgen-cli +fi + + +if [ $ERROR -eq 1 ]; then + exit 1 +fi + +echo "Woo, all ready to go!" + +#shellcheck disable=SC2016 +echo 'You might need to load the env: source "$HOME/.cargo/env"' diff --git a/scripts/setup_dev_environment.sh b/scripts/setup_dev_environment.sh new file mode 100755 index 000000000..b0adfce6a --- /dev/null +++ b/scripts/setup_dev_environment.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# run this, it'll set up a bunch of default stuff +# - reset the admin and idm_admin users +# - set up a test user +# - set up a test group +# - set up a test oauth2 rp (https://kanidm.com) +# - prompt to reset testuser's creds online + +set -e + +# if they passed --help then output the help +if [ "${1}" == "--help" ]; then + echo "Usage: $0 [--remove-db]" + echo " --remove-db: remove the existing DB before running" + exit 0 +fi + +# if --remove-db is in the command line args then remove the DB +if [ -z "${REMOVE_TEST_DB}" ]; then + if [ "${1}" == "--remove-db" ]; then + REMOVE_TEST_DB=1 + else + REMOVE_TEST_DB=0 + fi +fi + + +if [ ! -f run_insecure_dev_server.sh ]; then + echo "Please run from the server/daemon dir!" + exit 1 +fi + +# wait for them to shut down the server if it's running... +while true +do + if [ "$(pgrep kanidmd | wc -l )" -eq 0 ]; then + break + fi + echo "Stop the kanidmd server first please!" + sleep 1 +done + +# defaults +KANIDM_CONFIG="../../examples/insecure_server.toml" +KANIDM_URL="$(rg origin "${KANIDM_CONFIG}" | awk '{print $NF}' | tr -d '"')" +KANIDM_CA_PATH="/tmp/kanidm/ca.pem" + +# needed for the CLI tools to do their thing +export KANIDM_URL +export KANIDM_CA_PATH +export KANIDM_CONFIG + +# string things +TEST_USER_NAME="testuser" +TEST_USER_DISPLAY="Test Crab" +TEST_GROUP="test_users" +OAUTH2_RP_ID="test_oauth2" +OAUTH2_RP_DISPLAY="test_oauth2" + +# commands to run things +KANIDM="cargo run --manifest-path ../../Cargo.toml --bin kanidm -- " +KANIDMD="cargo run -p daemon --bin kanidmd -- " + +if [ "${REMOVE_TEST_DB}" -eq 1 ]; then + echo "Removing the existing DB!" + rm /tmp/kanidm/kanidm.db || true +fi + +echo "Reset the admin user" +ADMIN_PASS=$(${KANIDMD} recover-account admin -o json 2>&1 | rg recovery | rg result | jq -r .result ) +echo "admin pass: '${ADMIN_PASS}'" +echo "Reset the idm_admin user" +IDM_ADMIN_PASS=$(${KANIDMD} recover-account idm_admin -o json 2>&1 | rg recovery | rg result | jq -r .result) +echo "idm_admin pass: '${IDM_ADMIN_PASS}'" + +while true +do + echo "Waiting for you to start the server... testing ${KANIDM_URL}" + curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" > /dev/null && break + sleep 2 +done + +echo "login with admin" +${KANIDM} login -D admin --password "${ADMIN_PASS}" +echo "login with idm_admin" +${KANIDM} login -D idm_admin --password "${IDM_ADMIN_PASS}" + +# create group test_users +${KANIDM} group create "${TEST_GROUP}" -D idm_admin + +# create testuser (person) +${KANIDM} person create "${TEST_USER_NAME}" "${TEST_USER_DISPLAY}" -D idm_admin + +echo "Adding ${TEST_USER_NAME} to ${TEST_GROUP}" +${KANIDM} group add-members "${TEST_GROUP}" "${TEST_USER_NAME}" -D idm_admin + +echo "Enable experimental UI for admin idm_admin ${TEST_USER_NAME}" +${KANIDM} group add-members idm_ui_enable_experimental_features admin idm_admin "${TEST_USER_NAME}" + +# create oauth2 rp +echo "Creating the OAuth2 RP" +${KANIDM} system oauth2 create "${OAUTH2_RP_ID}" "${OAUTH2_RP_DISPLAY}" "https://kanidm.com" -D admin + +echo "Creating the OAuth2 RP Scope Map" +${KANIDM} system oauth2 update-scope-map "${OAUTH2_RP_ID}" "${TEST_GROUP}" openid -D admin +echo "Creating the OAuth2 RP Supplemental Scope Map" +${KANIDM} system oauth2 update-sup-scope-map "${OAUTH2_RP_ID}" "${TEST_GROUP}" admin -D admin +echo "Creating the OAuth2 RP Secondary Supplemental Crab-baite Scope Map.... wait, no that's not a thing." + + +# config auth2 +echo "Pulling secret for the OAuth2 RP" +${KANIDM} system oauth2 show-basic-secret -o json "${OAUTH2_RP_ID}" -D admin + +echo "Creating cred reset link for ${TEST_USER_NAME}" +${KANIDM} person credential create-reset-token "${TEST_USER_NAME}" -D idm_admin + +echo "Done!" + +echo "###################################" +echo "admin password: ${ADMIN_PASS}" +echo "idm_admin password: ${IDM_ADMIN_PASS}" +echo "UI URL: ${KANIDM_URL}" +echo "###################################" diff --git a/server/core/Cargo.toml b/server/core/Cargo.toml index 14010bac7..ae7493625 100644 --- a/server/core/Cargo.toml +++ b/server/core/Cargo.toml @@ -38,7 +38,7 @@ tokio-openssl = { workspace = true } tokio-util = { workspace = true, features = ["codec"] } toml = {workspace = true} tracing = { workspace = true, features = ["attributes"] } -urlencoding.workspace = true +urlencoding = { workspace = true } uuid = { workspace = true, features = ["serde", "v4" ] } [build-dependencies] diff --git a/server/core/src/config.rs b/server/core/src/config.rs index b0e78d68c..783ba18ee 100644 --- a/server/core/src/config.rs +++ b/server/core/src/config.rs @@ -77,7 +77,7 @@ impl ServerConfig { } } -#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq)] pub enum ServerRole { #[default] WriteReplica, diff --git a/server/core/src/https/middleware.rs b/server/core/src/https/middleware.rs index 0fe2697d2..eadf4281c 100644 --- a/server/core/src/https/middleware.rs +++ b/server/core/src/https/middleware.rs @@ -200,6 +200,8 @@ impl tide::Middleware "base-uri 'self'", // nobody wants to be in a frame "frame-ancestors 'none'", + // allow inline images because bootstrap + "img-src 'self' data:", ] .join(";"), ); diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs index 362c9b026..c003c6693 100644 --- a/server/core/src/https/mod.rs +++ b/server/core/src/https/mod.rs @@ -52,16 +52,42 @@ impl JavaScriptFile { /// returns a `"#, - self.filepath, self.hash, typeattr, + r#""#, + self.filepath, &self.hash, &typeattr, ) } } +#[test] +fn test_javscriptfile() { + // make sure it outputs what we think it does + use JavaScriptFile; + let jsf = JavaScriptFile { + filepath: "wasmloader.js", + hash: "sha384-1234567890".to_string(), + filetype: Some("module".to_string()), + }; + assert_eq!( + jsf.as_tag(), + r#""# + ); + let jsf = JavaScriptFile { + filepath: "wasmloader.js", + hash: "sha384-1234567890".to_string(), + filetype: None, + }; + assert_eq!( + jsf.as_tag(), + r#""# + ); +} + #[derive(Clone)] pub struct AppState { pub status_ref: &'static StatusActor, @@ -352,8 +378,7 @@ pub fn generate_integrity_hash(filename: String) -> Result { pub async fn create_https_server( address: String, - domain: String, - // opt_tls_params: Option, + domain: &String, opt_tls_params: Option<&TlsConfiguration>, role: ServerRole, trust_x_forward_for: bool, diff --git a/server/core/src/lib.rs b/server/core/src/lib.rs index ac1566961..1828e49bd 100644 --- a/server/core/src/lib.rs +++ b/server/core/src/lib.rs @@ -51,7 +51,7 @@ use tokio::sync::broadcast; use crate::actors::v1_read::QueryServerReadV1; use crate::actors::v1_write::QueryServerWriteV1; -use crate::config::Configuration; +use crate::config::{Configuration, ServerRole}; use crate::interval::IntervalActor; // === internal setup helpers @@ -914,7 +914,7 @@ pub async fn create_server_core( // ⚠️ only start the sockets and listeners in non-config-test modes. let h = self::https::create_https_server( config.address, - config.domain, + &config.domain, config.tls_config.as_ref(), config.role, config.trust_x_forward_for, @@ -927,7 +927,11 @@ pub async fn create_server_core( ) .await?; - admin_info!("ready to rock! 🪨 "); + if config.role != ServerRole::WriteReplicaNoUI { + admin_info!("ready to rock! 🪨 UI available at: {}", config.origin); + } else { + admin_info!("ready to rock! 🪨"); + } Some(h) }; diff --git a/server/daemon/Cargo.toml b/server/daemon/Cargo.toml index 829b78282..f9fb08988 100644 --- a/server/daemon/Cargo.toml +++ b/server/daemon/Cargo.toml @@ -3,13 +3,13 @@ name = "daemon" description = "Kanidm Server Daemon" documentation = "https://docs.rs/kanidm/latest/kanidm/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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 @@ -18,27 +18,28 @@ name = "kanidmd" path = "src/main.rs" [dependencies] -kanidm_proto.workspace = true -kanidmd_core.workspace = true -kanidm_lib_file_permissions.workspace = true -sketching.workspace = true -fs2.workspace = true +kanidm_proto = { workspace = true } +kanidmd_core = { workspace = true } +kanidm_lib_file_permissions = { workspace = true } +sketching = { workspace = true } +fs2 = { workspace = true } clap = { workspace = true, features = ["env"] } reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] } toml = { workspace = true } +is-terminal = "0.4.7" [target.'cfg(target_family = "windows")'.dependencies] -whoami.workspace = true +whoami = { workspace = true } [target.'cfg(not(target_family = "windows"))'.dependencies] -users.workspace = true -tikv-jemallocator.workspace = true +users = { workspace = true } +tikv-jemallocator = { workspace = true } [build-dependencies] serde = { workspace = true, features = ["derive"] } clap = { workspace = true, features = ["derive"] } -clap_complete.workspace = true -profiles.workspace = true +clap_complete = { workspace = true } +profiles = { workspace = true } diff --git a/server/daemon/src/main.rs b/server/daemon/src/main.rs index af0250c5c..4f32ced73 100644 --- a/server/daemon/src/main.rs +++ b/server/daemon/src/main.rs @@ -126,7 +126,7 @@ async fn main() -> ExitCode { }; // if they specified it in the environment then that overrides everything - let log_level = match EnvFilter::try_from_default_env() { + let log_filter = match EnvFilter::try_from_default_env() { Ok(val) => val, Err(_e) => { // we couldn't get it from the env, so we'll try the config file! @@ -140,14 +140,25 @@ async fn main() -> ExitCode { .into() } }; + + // TODO: only send to stderr when we're not in a TTY tracing_forest::worker_task() .set_global(true) .set_tag(sketching::event_tagger) // Fall back to stderr .map_sender(|sender| sender.or_stderr()) - .build_on(|subscriber| subscriber - .with(log_level) - ) + .build_on(|subscriber|{ + let sub = subscriber.with(log_filter); + // this does NOT work, it just adds a layer. + // if std::io::stdout().is_terminal() { + // println!("Stdout is a terminal"); + // sub.with(sketching::tracing_subscriber::fmt::layer().with_writer(std::io::stderr)) + // } else { + // println!("Stdout is not a terminal"); + // sub.with(sketching::tracing_subscriber::fmt::layer().with_writer(std::io::stderr)) + // } + sub + }) .on(async { // Get information on the windows username #[cfg(target_family = "windows")] diff --git a/server/lib-macros/Cargo.toml b/server/lib-macros/Cargo.toml index a3390a5a5..517149937 100644 --- a/server/lib-macros/Cargo.toml +++ b/server/lib-macros/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" proc-macro = true [dependencies] -proc-macro2.workspace = true -quote.workspace = true -syn.workspace = true +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true } diff --git a/server/lib/Cargo.toml b/server/lib/Cargo.toml index c68bfd260..616f0ce3d 100644 --- a/server/lib/Cargo.toml +++ b/server/lib/Cargo.toml @@ -3,13 +3,13 @@ name = "kanidmd_lib" description = "Kanidm Server Backend Library" documentation = "https://docs.rs/kanidm/latest/kanidm/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [lib] name = "kanidmd_lib" @@ -20,73 +20,73 @@ name = "scaling_10k" harness = false [dependencies] -async-trait.workspace = true -base64.workspace = true -base64urlsafedata.workspace = true +async-trait = { workspace = true } +base64 = { workspace = true } +base64urlsafedata = { workspace = true } compact_jwt = { workspace = true, features = ["openssl"] } -concread.workspace = true -dyn-clone.workspace = true +concread = { workspace = true } +dyn-clone = { workspace = true } fernet = { workspace = true, features = ["fernet_danger_timestamps"] } -filetime.workspace = true -futures-util.workspace = true -hashbrown.workspace = true -idlset.workspace = true -kanidm_proto.workspace = true -kanidm_lib_crypto.workspace = true -lazy_static.workspace = true -ldap3_proto.workspace = true -libc.workspace = true -libsqlite3-sys.workspace = true -num_enum.workspace = true +filetime = { workspace = true } +futures-util = { workspace = true } +hashbrown = { workspace = true } +idlset = { workspace = true } +kanidm_proto = { workspace = true } +kanidm_lib_crypto = { workspace = true } +lazy_static = { workspace = true } +ldap3_proto = { workspace = true } +libc = { workspace = true } +libsqlite3-sys = { workspace = true } +num_enum = { workspace = true } # We need to explicitly ask for openssl-sys so that we get the version propagated # into the build.rs for legacy feature checks. -openssl-sys.workspace = true -openssl.workspace = true -rand.workspace = true +openssl-sys = { workspace = true } +openssl = { workspace = true } +rand = { workspace = true } regex = { workspace = true, features = ["std", "perf", "perf-inline", "unicode", "unicode-gencat"] } serde = { workspace = true, features = ["derive"] } -serde_cbor.workspace = true -serde_json.workspace = true -sketching.workspace = true +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 +smolset = { workspace = true } +sshkeys = { workspace = true } +tide = { workspace = true } time = { workspace = true, features = ["serde", "std"] } tokio = { workspace = true, features = ["net", "sync", "time", "rt"] } tokio-util = { workspace = true, features = ["codec"] } -toml.workspace = true -touch.workspace = true +toml = { workspace = true } +touch = { workspace = true } nonempty = { workspace = true, features = ["serialize"] } tracing = { workspace = true, features = ["attributes"] } url = { workspace = true, features = ["serde"] } -urlencoding.workspace = true +urlencoding = { workspace = true } uuid = { workspace = true, features = ["serde", "v4" ] } webauthn-rs = { workspace = true, features = ["resident-key-support", "preview-features", "danger-credential-internals"] } -webauthn-rs-core.workspace = true -zxcvbn.workspace = true +webauthn-rs-core = { workspace = true } +zxcvbn = { workspace = true } # because windows really can't build without the bundled one [target.'cfg(target_family = "windows")'.dependencies] rusqlite = { workspace = true, features = ["bundled"] } -whoami.workspace = true +whoami = { workspace = true } [target.'cfg(not(target_family = "windows"))'.dependencies] -rusqlite.workspace = true -users.workspace = true +rusqlite = { workspace = true } +users = { workspace = true } [features] # default = [ "libsqlite3-sys/bundled", "openssl/vendored" ] [dev-dependencies] criterion = { workspace = true, features = ["html_reports"] } -webauthn-authenticator-rs.workspace = true +webauthn-authenticator-rs = { workspace = true } -futures.workspace = true -kanidmd_lib_macros.workspace = true +futures = { workspace = true } +kanidmd_lib_macros = { workspace = true } [build-dependencies] -profiles.workspace = true +profiles = { workspace = true } diff --git a/server/lib/src/be/mod.rs b/server/lib/src/be/mod.rs index 6e952a13a..b65c92919 100644 --- a/server/lib/src/be/mod.rs +++ b/server/lib/src/be/mod.rs @@ -1254,7 +1254,7 @@ impl<'a> BackendWriteTransaction<'a> { })?; if entries.is_empty() { - admin_info!("No entries affected - reap_tombstones operation success"); + admin_debug!("No entries affected - reap_tombstones operation success"); return Ok(0); } diff --git a/server/lib/src/idm/authsession.rs b/server/lib/src/idm/authsession.rs index 8ed402639..85d67f185 100644 --- a/server/lib/src/idm/authsession.rs +++ b/server/lib/src/idm/authsession.rs @@ -815,7 +815,7 @@ impl AuthSession { // of what's next, or ordering. let valid_mechs = auth_session.valid_auth_mechs(); - security_info!(?valid_mechs, "Offering auth mechanisms"); + security_debug!(?valid_mechs, "Offering auth mechanisms"); let as_state = AuthState::Choose(valid_mechs); (Some(auth_session), as_state) } diff --git a/server/lib/src/server/recycle.rs b/server/lib/src/server/recycle.rs index 34d95958a..c69621709 100644 --- a/server/lib/src/server/recycle.rs +++ b/server/lib/src/server/recycle.rs @@ -39,7 +39,7 @@ impl<'a> QueryServerWriteTransaction<'a> { ])))?; if rc.is_empty() { - admin_info!("No recycled present - purge operation success"); + admin_info!("No recycled items present - purge operation success"); return Ok(()); } diff --git a/server/testkit-macros/Cargo.toml b/server/testkit-macros/Cargo.toml index 8193f6fe5..2264ded80 100644 --- a/server/testkit-macros/Cargo.toml +++ b/server/testkit-macros/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" proc-macro = true [dependencies] -proc-macro2.workspace = true -quote.workspace = true -syn.workspace = true +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { workspace = true } diff --git a/server/testkit/Cargo.toml b/server/testkit/Cargo.toml index 43d20c44a..037a4037e 100644 --- a/server/testkit/Cargo.toml +++ b/server/testkit/Cargo.toml @@ -3,40 +3,40 @@ name = "kanidmd_testkit" description = "Kanidm Server Test Framework" documentation = "https://docs.rs/kanidm/latest/kanidm/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [lib] name = "kanidmd_testkit" path = "src/lib.rs" [dependencies] -kanidm_client.workspace = true -kanidm_proto.workspace = true -kanidmd_core.workspace = true -kanidmd_lib.workspace = true +kanidm_client = { workspace = true } +kanidm_proto = { workspace = true } +kanidmd_core = { workspace = true } +kanidmd_lib = { workspace = true } url = { workspace = true, features = ["serde"] } reqwest = { workspace = true, default-features = false } -sketching.workspace = true -testkit-macros.workspace = true +sketching = { workspace = true } +testkit-macros = { workspace = true } tracing = { workspace = true, features = ["attributes"] } tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] } [build-dependencies] -profiles.workspace = true +profiles = { workspace = true } [dev-dependencies] -compact_jwt.workspace = true -serde_json.workspace = true -webauthn-authenticator-rs.workspace = true +compact_jwt = { workspace = true } +serde_json = { workspace = true } +webauthn-authenticator-rs = { workspace = true } oauth2_ext = { workspace = true, default-features = false } -futures.workspace = true -time.workspace = true +futures = { workspace = true } +time = { workspace = true } diff --git a/server/web_ui/Cargo.toml b/server/web_ui/Cargo.toml index 79770a1cf..19ea27729 100644 --- a/server/web_ui/Cargo.toml +++ b/server/web_ui/Cargo.toml @@ -22,9 +22,6 @@ repository = "https://github.com/kanidm/kanidm/" # 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"] @@ -33,7 +30,6 @@ crate-type = ["cdylib", "rlib"] compact_jwt = { workspace = true, default-features = false, features = ["unsafe_release_without_verify"] } # gloo = "^0.8.0" gloo = { workspace = true } -gloo-net = { workspace = true } js-sys = { workspace = true } kanidm_proto = { workspace = true, features = ["wasm"] } qrcode = { workspace = true, features = ["svg"] } diff --git a/server/web_ui/build_wasm.sh b/server/web_ui/build_wasm.sh index 373aebc5e..de411cee3 100755 --- a/server/web_ui/build_wasm.sh +++ b/server/web_ui/build_wasm.sh @@ -16,6 +16,11 @@ if [ -z "$(which rsync)" ]; then exit 1 fi +if [ -z "$(which wasm-pack)" ]; then + echo "Cannot find wasm-pack which is needed to build the UI, quitting!" + exit 1 +fi + if [ "$(find ./pkg/ -name 'kanidmd*' | wc -l)" -gt 0 ]; then echo "Cleaning up" rm pkg/kanidmd* @@ -23,13 +28,10 @@ fi # we can disable this since we want it to expand # shellcheck disable=SC2086 -wasm-pack build ${BUILD_FLAGS} --target web || exit 1 +wasm-pack build ${BUILD_FLAGS} --target web --mode no-install --no-pack || exit 1 touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \ - rsync --delete-after -r --copy-links -v ./src/img/ ./pkg/img/ && \ - rsync --delete-after -r --copy-links -v ./src/external/ ./pkg/external/ && \ + rsync --delete-after -r --copy-links -v ./static/* ./pkg/ && \ cp ../../README.md ./pkg/ cp ../../LICENSE.md ./pkg/ - cp ./src/style.css ./pkg/style.css && \ - cp ./src/wasmloader.js ./pkg/wasmloader.js && \ rm ./pkg/.gitignore diff --git a/server/web_ui/pkg/kanidmd_web_ui.js b/server/web_ui/pkg/kanidmd_web_ui.js index 9a0250a2e..16216932f 100644 --- a/server/web_ui/pkg/kanidmd_web_ui.js +++ b/server/web_ui/pkg/kanidmd_web_ui.js @@ -234,24 +234,24 @@ function addBorrowedObject(obj) { } function __wbg_adapter_48(arg0, arg1, arg2) { try { - wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hd63bd1cb9b35210b(arg0, arg1, addBorrowedObject(arg2)); + wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hd11765386bfbbb1b(arg0, arg1, addBorrowedObject(arg2)); } finally { heap[stack_pointer++] = undefined; } } function __wbg_adapter_51(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h4a4d23d6ebf5b8fa(arg0, arg1, addHeapObject(arg2)); -} - -function __wbg_adapter_54(arg0, arg1, arg2) { try { - wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf7e9e605a1806538(arg0, arg1, addBorrowedObject(arg2)); + wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h13182848512d5ace(arg0, arg1, addBorrowedObject(arg2)); } finally { heap[stack_pointer++] = undefined; } } +function __wbg_adapter_54(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9e05cfbc2276f207(arg0, arg1, addHeapObject(arg2)); +} + /** */ export function run_app() { @@ -330,10 +330,6 @@ async function __wbg_load(module, imports) { function __wbg_get_imports() { const imports = {}; imports.wbg = {}; - imports.wbg.__wbindgen_is_undefined = function(arg0) { - const ret = getObject(arg0) === undefined; - return ret; - }; imports.wbg.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); const ret = typeof(obj) === 'string' ? obj : undefined; @@ -369,6 +365,10 @@ function __wbg_get_imports() { const ret = getObject(arg0); return addHeapObject(ret); }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; imports.wbg.__wbindgen_in = function(arg0, arg1) { const ret = getObject(arg0) in getObject(arg1); return ret; @@ -643,20 +643,6 @@ function __wbg_get_imports() { imports.wbg.__wbg_remove_8ae45e50cb58bb66 = function() { return handleError(function (arg0, arg1, arg2) { getObject(arg0).remove(getStringFromWasm0(arg1, arg2)); }, arguments) }; - imports.wbg.__wbg_instanceof_WorkerGlobalScope_d9d741da0fb130ce = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof WorkerGlobalScope; - } catch { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_fetch_8eaf01857a5bb21f = function(arg0, arg1) { - const ret = getObject(arg0).fetch(getObject(arg1)); - return addHeapObject(ret); - }; imports.wbg.__wbg_instanceof_Element_4622f5da1249a3eb = function(arg0) { let result; try { @@ -711,10 +697,6 @@ function __wbg_get_imports() { imports.wbg.__wbg_focus_dbcbbbb2a04c0e1f = function() { return handleError(function (arg0) { getObject(arg0).focus(); }, arguments) }; - imports.wbg.__wbg_new_1eead62f64ca15ce = function() { return handleError(function () { - const ret = new Headers(); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_get_2e9aab260014946d = function() { return handleError(function (arg0, arg1, arg2, arg3) { const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3)); var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -725,29 +707,14 @@ function __wbg_get_imports() { imports.wbg.__wbg_set_b34caba58723c454 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); }, arguments) }; - imports.wbg.__wbg_url_fda63503ced387ff = function(arg0, arg1) { - const ret = getObject(arg1).url; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }; imports.wbg.__wbg_headers_b439dcff02e808e5 = function(arg0) { const ret = getObject(arg0).headers; return addHeapObject(ret); }; - imports.wbg.__wbg_newwithstr_3d9bc779603a93c7 = function() { return handleError(function (arg0, arg1) { - const ret = new Request(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_newwithstrandinit_cad5cd6038c7ff5d = function() { return handleError(function (arg0, arg1, arg2) { const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); return addHeapObject(ret); }, arguments) }; - imports.wbg.__wbg_new_2a98b9c4a51bdc04 = function() { return handleError(function () { - const ret = new URLSearchParams(); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_create_c7e40b6b88186cbf = function() { return handleError(function (arg0, arg1) { const ret = getObject(arg0).create(getObject(arg1)); return addHeapObject(ret); @@ -782,10 +749,6 @@ function __wbg_get_imports() { const ret = getObject(arg0).json(); return addHeapObject(ret); }, arguments) }; - imports.wbg.__wbg_text_a667ac1770538491 = function() { return handleError(function (arg0) { - const ret = getObject(arg0).text(); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_log_1d3ae0273d8f4f8a = function(arg0) { console.log(getObject(arg0)); }; @@ -1090,10 +1053,6 @@ function __wbg_get_imports() { const ret = Object.is(getObject(arg0), getObject(arg1)); return ret; }; - imports.wbg.__wbg_toString_a8e343996af880e9 = function(arg0) { - const ret = getObject(arg0).toString(); - return addHeapObject(ret); - }; imports.wbg.__wbg_resolve_53698b95aaf7fcf8 = function(arg0) { const ret = Promise.resolve(getObject(arg0)); return addHeapObject(ret); @@ -1159,16 +1118,16 @@ function __wbg_get_imports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper4681 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1097, __wbg_adapter_48); + imports.wbg.__wbindgen_closure_wrapper4628 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1074, __wbg_adapter_48); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper5482 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1396, __wbg_adapter_51); + imports.wbg.__wbindgen_closure_wrapper5401 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1357, __wbg_adapter_51); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper5489 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1400, __wbg_adapter_54); + imports.wbg.__wbindgen_closure_wrapper6520 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1431, __wbg_adapter_54); return addHeapObject(ret); }; diff --git a/server/web_ui/pkg/kanidmd_web_ui_bg.wasm b/server/web_ui/pkg/kanidmd_web_ui_bg.wasm index 932abddbf..3160d9f66 100644 Binary files a/server/web_ui/pkg/kanidmd_web_ui_bg.wasm and b/server/web_ui/pkg/kanidmd_web_ui_bg.wasm differ diff --git a/server/web_ui/pkg/package.json b/server/web_ui/pkg/package.json deleted file mode 100644 index 41a046c91..000000000 --- a/server/web_ui/pkg/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "kanidmd_web_ui", - "collaborators": [ - "William Brown ", - "James Hodgkinson " - ], - "description": "Kanidm Server Web User Interface", - "version": "1.1.0-beta.13", - "license": "MPL-2.0", - "repository": { - "type": "git", - "url": "https://github.com/kanidm/kanidm/" - }, - "files": [ - "kanidmd_web_ui_bg.wasm", - "kanidmd_web_ui.js", - "LICENSE.md" - ], - "module": "kanidmd_web_ui.js", - "homepage": "https://github.com/kanidm/kanidm/", - "sideEffects": [ - "./snippets/*" - ] -} \ No newline at end of file diff --git a/server/web_ui/src/components/admin_accounts.rs b/server/web_ui/src/components/admin_accounts.rs index ee5f9f3f5..b0d1e3df1 100644 --- a/server/web_ui/src/components/admin_accounts.rs +++ b/server/web_ui/src/components/admin_accounts.rs @@ -9,8 +9,9 @@ use crate::components::alpha_warning_banner; use crate::constants::{ CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_DT, CSS_TABLE, }; -use crate::utils::{do_alert_error, do_page_header, init_request}; +use crate::utils::{do_alert_error, do_page_header}; use crate::views::AdminRoute; +use crate::{do_request, RequestMethod}; impl From for AdminListAccountsMsg { fn from(ge: GetError) -> Self { @@ -93,24 +94,21 @@ pub async fn get_accounts() -> Result { ]; for (endpoint, object_type) in endpoints { - let request = init_request(endpoint); - let response = match request.send().await { - Ok(value) => value, + let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None).await { + Ok(val) => val, Err(error) => { return Err(GetError { err: format!("{:?}", error), }) } }; - #[allow(clippy::panic)] - let data: Vec = match response.json().await { + // TODO: this kind of thing comes back when you're logged out: SerdeError(Error("invalid type: string \"sessionexpired\", expected a sequence", line: 1, column: 16))', server/web_ui/src/components/admin_accounts.rs:107:27 + let data: Vec = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, - - // TODO: this kind of thing comes back when you're logged out: SerdeError(Error("invalid type: string \"sessionexpired\", expected a sequence", line: 1, column: 16))', server/web_ui/src/components/admin_accounts.rs:107:27 Err(error) => { return Err(GetError { err: format!("Failed to grab the account data into JSON: {:?}", error), - }) + }); } }; @@ -544,41 +542,59 @@ impl Component for AdminViewServiceAccount { /// pull the details for a single person by UUID pub async fn get_person(uuid: &str) -> Result { - let request = init_request(format!("/v1/person/{}", uuid).as_str()); - let response = match request.send().await { - Ok(value) => value, + let (_, _, value, _) = match do_request( + format!("/v1/person/{}", uuid).as_str(), + RequestMethod::GET, + None, + ) + .await + { + Ok(val) => val, Err(error) => { return Err(GetError { err: format!("{:?}", error), }) } }; - #[allow(clippy::panic)] - let data: Entity = match response.json().await { + + let data: Entity = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, - Err(error) => panic!("Failed to grab the person data into JSON: {:?}", error), + Err(error) => { + return Err(GetError { + err: format!("{:?}", error), + }); + } }; Ok(AdminViewPersonMsg::Responded { response: data }) } /// pull the details for a single service_account by UUID pub async fn get_service_account(uuid: &str) -> Result { - let request = init_request(format!("/v1/service_account/{}", uuid).as_str()); - let response = match request.send().await { + let (_, _, value, _) = match do_request( + format!("/v1/service_account/{}", uuid).as_str(), + RequestMethod::GET, + None, + ) + .await + { + Ok(val) => val, + Err(error) => { + return Err(GetError { + err: format!( + "Failed to grab the service_account data into JSON: {:?}", + error + ), + }) + } + }; + + let data: Entity = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, Err(error) => { return Err(GetError { err: format!("{:?}", error), - }) + }); } }; - #[allow(clippy::panic)] - let data: Entity = match response.json().await { - Ok(value) => value, - Err(error) => panic!( - "Failed to grab the service account data into JSON: {:?}", - error - ), - }; Ok(AdminViewServiceAccountMsg::Responded { response: data }) } diff --git a/server/web_ui/src/components/admin_groups.rs b/server/web_ui/src/components/admin_groups.rs index ebcf08bc9..203056f98 100644 --- a/server/web_ui/src/components/admin_groups.rs +++ b/server/web_ui/src/components/admin_groups.rs @@ -7,8 +7,9 @@ use yew_router::prelude::Link; use crate::components::admin_menu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; -use crate::utils::{do_alert_error, do_page_header, init_request}; +use crate::utils::{do_alert_error, do_page_header}; use crate::views::AdminRoute; +use crate::{do_request, RequestMethod}; impl From for AdminListGroupsMsg { fn from(ge: GetError) -> Self { @@ -70,20 +71,23 @@ pub async fn get_groups() -> Result { let endpoints = [("/v1/group", EntityType::Group)]; for (endpoint, object_type) in endpoints { - let request = init_request(endpoint); - let response = match request.send().await { + let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None).await { + Ok(val) => val, + Err(error) => { + return Err(GetError { + err: format!("Failed to grab the group data into JSON: {:?}", error), + }) + } + }; + + let data: Vec = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, Err(error) => { return Err(GetError { err: format!("{:?}", error), - }) + }); } }; - #[allow(clippy::panic)] - let data: Vec = match response.json().await { - Ok(value) => value, - Err(error) => panic!("Failed to grab the group data into JSON: {:?}", error), - }; for entity in data.iter() { let mut new_entity = entity.to_owned(); @@ -360,19 +364,23 @@ impl Component for AdminViewGroup { /// pull the details for a single group by UUID pub async fn get_group(groupid: &str) -> Result { - let request = init_request(format!("/v1/group/{}", groupid).as_str()); - let response = match request.send().await { - Ok(value) => value, + let endpoint = format!("/v1/group/{}", groupid); + let (_, _, value, _) = match do_request(&endpoint, RequestMethod::GET, None).await { + Ok(val) => val, Err(error) => { return Err(GetError { err: format!("{:?}", error), }) } }; - #[allow(clippy::panic)] - let data: Entity = match response.json().await { + + let data: Entity = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, - Err(error) => panic!("Failed to grab the group data into JSON: {:?}", error), + Err(error) => { + return Err(GetError { + err: format!("{:?}", error), + }); + } }; Ok(AdminViewGroupMsg::Responded { response: data }) } diff --git a/server/web_ui/src/components/admin_oauth2.rs b/server/web_ui/src/components/admin_oauth2.rs index c11348d7d..9921aa28e 100644 --- a/server/web_ui/src/components/admin_oauth2.rs +++ b/server/web_ui/src/components/admin_oauth2.rs @@ -7,8 +7,9 @@ use yew_router::prelude::Link; use crate::components::admin_menu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; -use crate::utils::{do_alert_error, do_page_header, init_request}; +use crate::utils::{do_alert_error, do_page_header}; use crate::views::AdminRoute; +use crate::{do_request, RequestMethod}; impl From for AdminListOAuth2Msg { fn from(ge: GetError) -> Self { @@ -71,22 +72,25 @@ pub async fn get_entities() -> Result { let endpoints = [("/v1/oauth2", EntityType::OAuth2RP)]; for (endpoint, object_type) in endpoints { - let request = init_request(endpoint); - let response = match request.send().await { - Ok(value) => value, + let (_, _, value, _) = match do_request(endpoint, RequestMethod::GET, None).await { + Ok(val) => val, Err(error) => { return Err(GetError { err: format!("{:?}", error), }) } }; - #[allow(clippy::panic)] - let data: Vec = match response.json().await { + + let data: Vec = match serde_wasm_bindgen::from_value(value) { Ok(value) => value, - Err(error) => panic!("Failed to grab the OAuth2 RP data into JSON: {:?}", error), + Err(error) => { + return Err(GetError { + err: format!("Failed to grab the oauth2 data into JSON: {:?}", error), + }); + } }; - for entity in data.iter() { + for entity in data.into_iter() { let mut new_entity = entity.to_owned(); new_entity.object_type = object_type.clone(); @@ -96,8 +100,9 @@ pub async fn get_entities() -> Result { .attrs .uuid .first() - .expect("Failed to grab the SPN for an account."); - oauth2_objects.insert(entity_id.to_string(), new_entity); + .map(|e| e.to_owned()) + .unwrap_or(String::from("Unknown entity name!")); + oauth2_objects.insert(entity_id, new_entity); } } @@ -401,17 +406,17 @@ impl Component for AdminViewOAuth2 { } pub async fn get_oauth2_rp(rs_name: &str) -> Result { - let request = init_request(format!("/v1/oauth2/{}", rs_name).as_str()); - let response = match request.send().await { - Ok(value) => value, + let endpoint = format!("/v1/oauth2/{}", rs_name); + let (_, _, value, _) = match do_request(&endpoint, RequestMethod::GET, None).await { + Ok(val) => val, Err(error) => { return Err(GetError { err: format!("{:?}", error), }) } }; - #[allow(clippy::panic)] - let data: Entity = match response.json().await { + + let data: Entity = match serde_wasm_bindgen::from_value(value) { Ok(value) => { console::log!(format!("{:?}", value)); value diff --git a/server/web_ui/src/components/change_unix_password.rs b/server/web_ui/src/components/change_unix_password.rs index 408cd75cd..4230db88e 100644 --- a/server/web_ui/src/components/change_unix_password.rs +++ b/server/web_ui/src/components/change_unix_password.rs @@ -1,8 +1,7 @@ use kanidm_proto::v1::{SingleStringRequest, UserAuthToken}; use uuid::Uuid; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; -use wasm_bindgen_futures::JsFuture; -use web_sys::{FormData, HtmlFormElement, Request, RequestInit, RequestMode, Response}; +use web_sys::{FormData, HtmlFormElement}; use yew::prelude::*; use crate::error::*; @@ -250,32 +249,14 @@ impl ChangeUnixPassword { }) .map(|s| JsValue::from(&s)) .expect_throw("Failed to change request"); - let mut opts = RequestInit::new(); - opts.method("PUT"); - opts.mode(RequestMode::SameOrigin); - opts.body(Some(&changereq_jsvalue)); - let uri = format!("/v1/person/{}/_unix/_credential", id); - - let request = Request::new_with_str_and_init(uri.as_str(), &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + crate::do_request(&uri, crate::RequestMethod::PUT, Some(changereq_jsvalue)).await?; if status == 200 { Ok(Msg::Success) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/components/create_reset_code.rs b/server/web_ui/src/components/create_reset_code.rs index dd022190b..1f952a4ac 100644 --- a/server/web_ui/src/components/create_reset_code.rs +++ b/server/web_ui/src/components/create_reset_code.rs @@ -10,9 +10,6 @@ use wasm_bindgen::UnwrapThrowExt; use web_sys::Node; use crate::error::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; enum State { Valid, @@ -237,35 +234,17 @@ impl Component for CreateResetCode { impl CreateResetCode { async fn credential_get_update_intent_token(id: String) -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - let uri = format!("/v1/person/{}/_credential/_update_intent?ttl=0", id); - let request = Request::new_with_str_and_init(uri.as_str(), &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + crate::do_request(&uri, crate::RequestMethod::GET, None).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let token: CUIntentToken = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); Ok(Msg::Ready { token }) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); // let jsval_json = JsFuture::from(resp.json()?).await?; Ok(Msg::Error { emsg, kopid }) } diff --git a/server/web_ui/src/credential/delete.rs b/server/web_ui/src/credential/delete.rs index 2d26bd8fb..8d8f3b1ad 100644 --- a/server/web_ui/src/credential/delete.rs +++ b/server/web_ui/src/credential/delete.rs @@ -1,14 +1,14 @@ #[cfg(debug_assertions)] use gloo::console; 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 wasm_bindgen::{JsValue, UnwrapThrowExt}; use yew::prelude::*; use super::reset::{EventBusMsg, ModalProps}; +use crate::do_request; use crate::error::*; use crate::utils; +use crate::RequestMethod; enum State { Init, @@ -50,37 +50,21 @@ impl DeleteApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + let custatus: CUStatus = + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); - cb.emit(EventBusMsg::UpdateStatus { status }); + cb.emit(EventBusMsg::UpdateStatus { status: custatus }); Ok(Msg::Success) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/passkey.rs b/server/web_ui/src/credential/passkey.rs index 6df666cec..cfc490161 100644 --- a/server/web_ui/src/credential/passkey.rs +++ b/server/web_ui/src/credential/passkey.rs @@ -1,14 +1,13 @@ use gloo::console; use kanidm_proto::v1::{CURegState, CURequest, CUSessionToken, CUStatus}; use kanidm_proto::webauthn::{CreationChallengeResponse, RegisterPublicKeyCredential}; -use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; +use wasm_bindgen::{JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestInit, RequestMode, Response}; use yew::prelude::*; use super::reset::{EventBusMsg, ModalProps}; -use crate::error::*; use crate::utils; +use crate::{do_request, error::*, RequestMethod}; pub struct PasskeyModalApp { state: State, @@ -61,30 +60,16 @@ impl PasskeyModalApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - - let kopid = headers.get("x-kanidm-opid").ok().flatten(); + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); cb.emit(EventBusMsg::UpdateStatus { status: status.clone(), @@ -102,8 +87,7 @@ impl PasskeyModalApp { CURegState::None => Msg::Success, }) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/passkeyremove.rs b/server/web_ui/src/credential/passkeyremove.rs index a4c8a4e04..41d666631 100644 --- a/server/web_ui/src/credential/passkeyremove.rs +++ b/server/web_ui/src/credential/passkeyremove.rs @@ -2,14 +2,13 @@ use gloo::console; 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 wasm_bindgen::{JsValue, UnwrapThrowExt}; use yew::prelude::*; use super::reset::{EventBusMsg, PasskeyRemoveModalProps}; use crate::error::*; use crate::utils; +use crate::{do_request, RequestMethod}; pub struct PasskeyRemoveModalApp { state: State, @@ -70,30 +69,17 @@ impl PasskeyRemoveModalApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - - let kopid = headers.get("x-kanidm-opid").ok().flatten(); + // this really should require a DELETE not a post! + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); cb.emit(EventBusMsg::UpdateStatus { status: status.clone(), @@ -111,8 +97,7 @@ impl PasskeyRemoveModalApp { CURegState::None => Msg::Success, }) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/pwmodal.rs b/server/web_ui/src/credential/pwmodal.rs index b783a3507..0b3498ab7 100644 --- a/server/web_ui/src/credential/pwmodal.rs +++ b/server/web_ui/src/credential/pwmodal.rs @@ -1,13 +1,13 @@ use gloo::console; 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 wasm_bindgen::{JsValue, UnwrapThrowExt}; + use yew::prelude::*; use super::reset::{EventBusMsg, ModalProps}; use crate::error::*; use crate::utils; +use crate::{do_request, RequestMethod}; enum PwState { Init, @@ -57,38 +57,24 @@ impl PwModalApp { } async fn submit_password_update(token: CUSessionToken, pw: String) -> Result { - let intentreq_jsvalue = serde_json::to_string(&(CURequest::Password(pw), token)) + let req_jsvalue = serde_json::to_string(&(CURequest::Password(pw), token)) .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&intentreq_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); Ok(Msg::PasswordResponseSuccess { status }) } else if status == 400 { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let jsval = JsFuture::from(resp.json()?).await?; let status: OperationError = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); match status { OperationError::PasswordQuality(feedback) => { Ok(Msg::PasswordResponseQuality { feedback }) @@ -99,9 +85,7 @@ impl PwModalApp { }), } } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/reset.rs b/server/web_ui/src/credential/reset.rs index dd1957ce1..c0cb223f7 100644 --- a/server/web_ui/src/credential/reset.rs +++ b/server/web_ui/src/credential/reset.rs @@ -3,9 +3,7 @@ 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 wasm_bindgen::{JsValue, UnwrapThrowExt}; use yew::prelude::*; use yew_router::prelude::*; @@ -15,7 +13,7 @@ use super::passkeyremove::PasskeyRemoveModalApp; use super::pwmodal::PwModalApp; use super::totpmodal::TotpModalApp; use super::totpremove::TotpRemoveComp; -use crate::error::*; +use crate::{do_request, error::*, RequestMethod}; use crate::{models, utils}; // use std::rc::Rc; @@ -577,37 +575,23 @@ impl CredentialResetApp { } async fn exchange_intent_token(token: String) -> Result { - let intentreq_jsvalue = serde_json::to_string(&CUIntentToken { token }) + let req_jsvalue = serde_json::to_string(&CUIntentToken { token }) .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise intent request"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&intentreq_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_exchange_intent", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); + let (kopid, status, value, _) = do_request( + "/v1/credential/_exchange_intent", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let (token, status): (CUSessionToken, CUStatus) = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); Ok(Msg::BeginSession { token, status }) } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } @@ -617,30 +601,13 @@ impl CredentialResetApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise session token"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init(url, &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); + let (kopid, status, value, _) = + do_request(url, RequestMethod::POST, Some(req_jsvalue)).await?; if status == 200 { Ok(Msg::Success) } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/totpmodal.rs b/server/web_ui/src/credential/totpmodal.rs index a9ceb0ed0..84b7ded10 100644 --- a/server/web_ui/src/credential/totpmodal.rs +++ b/server/web_ui/src/credential/totpmodal.rs @@ -3,14 +3,14 @@ use gloo::console; 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, RequestCredentials, RequestInit, RequestMode, Response}; +use wasm_bindgen::{JsValue, UnwrapThrowExt}; +use web_sys::Node; use yew::prelude::*; use super::reset::{EventBusMsg, ModalProps}; use crate::error::*; use crate::utils; +use crate::{do_request, RequestMethod}; enum TotpState { Init, @@ -74,31 +74,15 @@ impl TotpModalApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); cb.emit(EventBusMsg::UpdateStatus { status: status.clone(), @@ -115,8 +99,7 @@ impl TotpModalApp { CURegState::TotpInvalidSha1 => Msg::TotpInvalidSha1, }) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/credential/totpremove.rs b/server/web_ui/src/credential/totpremove.rs index bff40cf1c..32ffe39ee 100644 --- a/server/web_ui/src/credential/totpremove.rs +++ b/server/web_ui/src/credential/totpremove.rs @@ -2,12 +2,11 @@ use super::reset::{EventBusMsg, TotpRemoveProps}; #[cfg(debug_assertions)] use gloo::console; 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 wasm_bindgen::{JsValue, UnwrapThrowExt}; +use crate::do_request; use crate::error::*; -use crate::utils; +use crate::RequestMethod; use yew::prelude::*; pub enum Msg { @@ -108,37 +107,21 @@ impl TotpRemoveComp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise pw curequest"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - - opts.body(Some(&req_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/credential/_update", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - + let (kopid, status, value, _) = do_request( + "/v1/credential/_update", + RequestMethod::POST, + Some(req_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let status: CUStatus = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); cb.emit(EventBusMsg::UpdateStatus { status }); Ok(Msg::Success) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/lib.rs b/server/web_ui/src/lib.rs index 56416d13a..20fbbb946 100644 --- a/server/web_ui/src/lib.rs +++ b/server/web_ui/src/lib.rs @@ -11,7 +11,11 @@ #![deny(clippy::needless_pass_by_value)] #![deny(clippy::trivially_copy_pass_by_ref)] +use error::FetchError; +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; +use web_sys::{Headers, Request, RequestInit, RequestMode, Response}; #[macro_use] mod macros; @@ -35,3 +39,57 @@ pub fn run_app() -> Result<(), JsValue> { gloo::console::debug!(kanidm_proto::utils::get_version("kanidmd_web_ui")); Ok(()) } + +#[derive(Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum RequestMethod { + GET, + POST, + PUT, +} + +impl ToString for RequestMethod { + fn to_string(&self) -> String { + match self { + RequestMethod::PUT => "PUT".to_string(), + RequestMethod::POST => "POST".to_string(), + RequestMethod::GET => "GET".to_string(), + } + } +} + +/// Build and send a request to the backend, with some standard headers and pull back +/// (kopid, status, json, headers) +pub async fn do_request( + uri: &str, + method: RequestMethod, + body: Option, +) -> Result<(Option, u16, JsValue, Headers), FetchError> { + let mut opts = RequestInit::new(); + opts.method(&method.to_string()); + opts.mode(RequestMode::SameOrigin); + opts.credentials(web_sys::RequestCredentials::SameOrigin); + if let Some(body) = body { + #[cfg(debug_assertions)] + if method == RequestMethod::GET { + gloo::console::debug!("This seems odd, you've supplied a body with a GET request?") + } + opts.body(Some(&body)); + } + + let request = Request::new_with_str_and_init(uri, &opts)?; + request + .headers() + .set("content-type", "application/json") + .expect_throw("failed to set header"); + + let window = utils::window(); + let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; + let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); + let status = resp.status(); + let headers: Headers = resp.headers(); + + let kopid = headers.get("x-kanidm-opid").ok().flatten(); + + Ok((kopid, status, JsFuture::from(resp.json()?).await?, headers)) +} diff --git a/server/web_ui/src/login/mod.rs b/server/web_ui/src/login/mod.rs index 7cb938137..fee3627a2 100644 --- a/server/web_ui/src/login/mod.rs +++ b/server/web_ui/src/login/mod.rs @@ -6,18 +6,15 @@ use kanidm_proto::v1::{ }; use kanidm_proto::webauthn::PublicKeyCredential; use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; use wasm_bindgen_futures::{spawn_local, JsFuture}; -use web_sys::{ - CredentialRequestOptions, Request, RequestCredentials, RequestInit, RequestMode, Response, -}; +use web_sys::CredentialRequestOptions; use yew::prelude::*; use yew::virtual_dom::VNode; 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, utils}; +use crate::{do_request, models, utils, RequestMethod}; pub struct LoginApp { state: LoginState, @@ -105,48 +102,26 @@ impl LoginApp { issue: AuthIssueSession::Cookie, }, }; - let authreq_jsvalue = serde_json::to_string(&authreq) + let req_jsvalue = serde_json::to_string(&authreq) .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise authreq"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - opts.body(Some(&authreq_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/auth", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value - .dyn_into() - .expect_throw("Invalid response type - auth_init::Response"); - let status = resp.status(); - let headers = resp.headers(); + let (kopid, status, value, _) = + do_request("/v1/auth", RequestMethod::POST, Some(req_jsvalue)).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let state: AuthResponse = serde_wasm_bindgen::from_value(jsval) + let state: AuthResponse = serde_wasm_bindgen::from_value(value) .expect_throw("Invalid response type - auth_init::AuthResponse"); Ok(LoginAppMsg::Start(state)) } else if status == 404 { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; console::error!(format!( "User not found: {:?}. Operation ID: {:?}", - text, kopid + value.as_string().unwrap_or_default(), + kopid )); Ok(LoginAppMsg::UnknownUser) } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(LoginAppMsg::Error { emsg, kopid }) } } @@ -156,45 +131,23 @@ impl LoginApp { let authreq_jsvalue = serde_json::to_string(&issue) .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise authreq"); - - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - opts.body(Some(&authreq_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/reauth", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value - .dyn_into() - .expect_throw("Invalid response type - reauth_init::Response"); - let status = resp.status(); - let headers = resp.headers(); + let url = "/v1/reauth"; + let (kopid, status, value, _) = + do_request(url, RequestMethod::POST, Some(authreq_jsvalue)).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let state: AuthResponse = serde_wasm_bindgen::from_value(jsval) + let state: AuthResponse = serde_wasm_bindgen::from_value(value) .expect_throw("Invalid response type - auth_init::AuthResponse"); Ok(LoginAppMsg::Next(state)) } else if status == 404 { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; console::error!(format!( "User not found: {:?}. Operation ID: {:?}", - text, kopid + value.as_string(), + kopid )); Ok(LoginAppMsg::UnknownUser) } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(LoginAppMsg::Error { emsg, kopid }) } } @@ -204,30 +157,11 @@ impl LoginApp { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise authreq"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - opts.body(Some(&authreq_jsvalue)); - - let request = Request::new_with_str_and_init("/v1/auth", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set content-type header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value - .dyn_into() - .expect_throw("Invalid response type - auth_step::Response"); - let status = resp.status(); - let headers = resp.headers(); + let (kopid, status, value, _) = + do_request("/v1/auth", RequestMethod::POST, Some(authreq_jsvalue)).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let state: AuthResponse = serde_wasm_bindgen::from_value(jsval) + let state: AuthResponse = serde_wasm_bindgen::from_value(value) .map_err(|e| { console::error!(format!("auth_step::AuthResponse: {:?}", e)); e @@ -235,9 +169,7 @@ impl LoginApp { .expect_throw("Invalid response type - auth_step::AuthResponse"); Ok(LoginAppMsg::Next(state)) } else { - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string() + let emsg = value.as_string() .unwrap_or_else(|| "Unhandled error, please report this along with the operation ID below to your administrator. 😔".to_string()); Ok(LoginAppMsg::Error { emsg, kopid }) } @@ -247,6 +179,7 @@ impl LoginApp { fn button_start_again(&self, ctx: &Context) -> VNode { html! {
+ // TODO: this doesn't seem to work if you failed to login
} diff --git a/server/web_ui/src/oauth2.rs b/server/web_ui/src/oauth2.rs index db1891eb6..342c4bbf1 100644 --- a/server/web_ui/src/oauth2.rs +++ b/server/web_ui/src/oauth2.rs @@ -1,4 +1,3 @@ -// use anyhow::Error; use gloo::console; pub use kanidm_proto::oauth2::{ AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisationResponse, @@ -6,12 +5,12 @@ pub use kanidm_proto::oauth2::{ }; use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, RequestRedirect, Response}; +use web_sys::{Request, RequestInit, RequestMode, RequestRedirect, Response}; use yew::prelude::*; use yew_router::prelude::*; -use crate::error::*; use crate::manager::Route; +use crate::{do_request, error::*, RequestMethod}; use crate::{models, utils}; use std::collections::BTreeSet; @@ -70,31 +69,15 @@ impl From for Oauth2Msg { impl Oauth2App { async fn fetch_session_valid() -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - let request = Request::new_with_str_and_init("/v1/auth/valid", &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + do_request("/v1/auth/valid", RequestMethod::GET, None).await?; if status == 200 { Ok(Oauth2Msg::TokenValid) } else if status == 401 { Ok(Oauth2Msg::LoginRequired) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); // let jsval_json = JsFuture::from(resp.json()?).await?; Ok(Oauth2Msg::Error { emsg, kopid }) } @@ -105,29 +88,15 @@ impl Oauth2App { .map(|s| JsValue::from(&s)) .expect_throw("Failed to serialise authreq"); - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - opts.body(Some(&authreq_jsvalue)); - - let request = Request::new_with_str_and_init("/oauth2/authorise", &opts)?; - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); + let (kopid, status, value, headers) = do_request( + "/oauth2/authorise", + RequestMethod::POST, + Some(authreq_jsvalue), + ) + .await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let state: AuthorisationResponse = serde_wasm_bindgen::from_value(jsval) + let state: AuthorisationResponse = serde_wasm_bindgen::from_value(value) .map_err(|e| { let e_msg = format!("serde error -> {:?}", e); console::error!(e_msg.as_str()); @@ -159,8 +128,7 @@ impl Oauth2App { } else if status == 403 { Ok(Oauth2Msg::AccessDenied { kopid }) } else { - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Oauth2Msg::Error { emsg, kopid }) } } @@ -173,8 +141,7 @@ impl Oauth2App { let mut opts = RequestInit::new(); opts.method("POST"); opts.mode(RequestMode::SameOrigin); - opts.redirect(RequestRedirect::Manual); - opts.credentials(RequestCredentials::SameOrigin); + opts.redirect(RequestRedirect::Manual); // can't replace with do_request because of this opts.body(Some(&consentreq_jsvalue)); diff --git a/server/web_ui/src/utils.rs b/server/web_ui/src/utils.rs index ce86a1138..3c189844b 100644 --- a/server/web_ui/src/utils.rs +++ b/server/web_ui/src/utils.rs @@ -1,10 +1,9 @@ use gloo::console; -use gloo_net::http::Request; use url::Url; use wasm_bindgen::prelude::*; use wasm_bindgen::{JsCast, UnwrapThrowExt}; pub use web_sys::InputEvent; -use web_sys::{Document, HtmlElement, HtmlInputElement, RequestCredentials, RequestMode, Window}; +use web_sys::{Document, HtmlElement, HtmlInputElement, Window}; use yew::virtual_dom::VNode; use yew::{html, Html}; @@ -94,14 +93,6 @@ pub fn do_footer() -> VNode { } } -/// Builds a request object to a server-local endpoint with the usual requirements -pub fn init_request(endpoint: &str) -> gloo_net::http::Request { - Request::new(endpoint) - .mode(RequestMode::SameOrigin) - .credentials(RequestCredentials::SameOrigin) - .header("content-type", "application/json") -} - pub fn do_alert_error(alert_title: &str, alert_message: Option<&str>) -> Html { html! {
diff --git a/server/web_ui/src/views/apps.rs b/server/web_ui/src/views/apps.rs index fac3eab4c..96c9dac70 100644 --- a/server/web_ui/src/views/apps.rs +++ b/server/web_ui/src/views/apps.rs @@ -4,11 +4,8 @@ use yew::prelude::*; use crate::constants::{CSS_CARD, CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER}; use crate::error::FetchError; -use crate::utils; +use crate::{do_request, RequestMethod}; use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; use kanidm_proto::internal::AppLink; @@ -182,33 +179,15 @@ impl AppsApp { } async fn fetch_user_apps() -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - let request = Request::new_with_str_and_init("/v1/self/_applinks", &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + do_request("/v1/self/_applinks", RequestMethod::GET, None).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let apps: Vec = serde_wasm_bindgen::from_value(jsval) + let apps: Vec = serde_wasm_bindgen::from_value(value) .expect_throw("Invalid response type - auth_init::AuthResponse"); Ok(Msg::Ready { apps }) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(Msg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/views/mod.rs b/server/web_ui/src/views/mod.rs index fe397a667..02cd6c06f 100644 --- a/server/web_ui/src/views/mod.rs +++ b/server/web_ui/src/views/mod.rs @@ -1,16 +1,14 @@ use gloo::console; use kanidm_proto::v1::{UiHint, UserAuthToken}; use serde::{Deserialize, Serialize}; -use wasm_bindgen::{JsCast, UnwrapThrowExt}; -use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; +use wasm_bindgen::UnwrapThrowExt; use yew::prelude::*; use yew_router::prelude::*; use crate::components::{admin_accounts, admin_groups, admin_menu, admin_oauth2}; -use crate::error::*; use crate::manager::Route; -use crate::{models, utils}; +use crate::models; +use crate::{do_request, error::*, RequestMethod}; mod apps; mod profile; @@ -328,62 +326,25 @@ impl ViewsApp { } async fn check_session_valid() -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - let request = Request::new_with_str_and_init("/v1/auth/valid", &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)) - .await - .map_err(|e| { - console::error!(&format!("fetch request failed {:?}", e)); - e - })?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + do_request("/v1/auth/valid", RequestMethod::GET, None).await?; if status == 200 { Ok(ViewsMsg::Verified) } else if status == 401 { Ok(ViewsMsg::LogoutComplete) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(ViewsMsg::Error { emsg, kopid }) } } async fn fetch_user_data() -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - let request = Request::new_with_str_and_init("/v1/self/_uat", &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = + do_request("/v1/self/_uat", RequestMethod::GET, None).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; - let uat: UserAuthToken = serde_wasm_bindgen::from_value(jsval) + let uat: UserAuthToken = serde_wasm_bindgen::from_value(value) .map_err(|e| { let e_msg = format!("serde error -> {:?}", e); console::error!(e_msg.as_str()); @@ -392,39 +353,18 @@ impl ViewsApp { Ok(ViewsMsg::ProfileInfoReceived { uat }) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(ViewsMsg::Error { emsg, kopid }) } } async fn fetch_logout() -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - - let request = Request::new_with_str_and_init("/v1/logout", &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _) = do_request("/v1/logout", RequestMethod::GET, None).await?; if status == 200 { Ok(ViewsMsg::LogoutComplete) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); Ok(ViewsMsg::Error { emsg, kopid }) } } diff --git a/server/web_ui/src/views/profile.rs b/server/web_ui/src/views/profile.rs index efbe4230e..72d12877d 100644 --- a/server/web_ui/src/views/profile.rs +++ b/server/web_ui/src/views/profile.rs @@ -2,9 +2,7 @@ use gloo::console; use kanidm_proto::v1::{CUSessionToken, CUStatus, UiHint, UserAuthToken}; use time::format_description::well_known::Rfc3339; -use wasm_bindgen::{JsCast, UnwrapThrowExt}; -use wasm_bindgen_futures::JsFuture; -use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; +use wasm_bindgen::UnwrapThrowExt; use yew::prelude::*; use yew_router::prelude::*; @@ -13,8 +11,8 @@ use crate::components::create_reset_code::CreateResetCode; use crate::constants::CSS_PAGE_HEADER; use crate::error::*; use crate::manager::Route; +use crate::models; use crate::views::{ViewProps, ViewRoute}; -use crate::{models, utils}; #[allow(clippy::large_enum_variant)] // Page state @@ -240,35 +238,16 @@ impl ProfileApp { } async fn request_credential_update(id: String) -> Result { - let mut opts = RequestInit::new(); - opts.method("GET"); - opts.mode(RequestMode::SameOrigin); - opts.credentials(RequestCredentials::SameOrigin); - let uri = format!("/v1/person/{}/_credential/_update", id); - - let request = Request::new_with_str_and_init(uri.as_str(), &opts)?; - - request - .headers() - .set("content-type", "application/json") - .expect_throw("failed to set header"); - - let window = utils::window(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); - let status = resp.status(); + let (kopid, status, value, _headers) = + crate::do_request(&uri, crate::RequestMethod::GET, None).await?; if status == 200 { - let jsval = JsFuture::from(resp.json()?).await?; let (token, status): (CUSessionToken, CUStatus) = - serde_wasm_bindgen::from_value(jsval).expect_throw("Invalid response type"); + serde_wasm_bindgen::from_value(value).expect_throw("Invalid response type"); Ok(Msg::BeginCredentialUpdate { token, status }) } else { - let headers = resp.headers(); - let kopid = headers.get("x-kanidm-opid").ok().flatten(); - let text = JsFuture::from(resp.text()?).await?; - let emsg = text.as_string().unwrap_or_default(); + let emsg = value.as_string().unwrap_or_default(); // let jsval_json = JsFuture::from(resp.json()?).await?; Ok(Msg::Error { emsg, kopid }) } diff --git a/server/web_ui/src/external/bootstrap.bundle.min.js b/server/web_ui/static/external/bootstrap.bundle.min.js similarity index 100% rename from server/web_ui/src/external/bootstrap.bundle.min.js rename to server/web_ui/static/external/bootstrap.bundle.min.js diff --git a/server/web_ui/src/external/bootstrap.bundle.min.js.map b/server/web_ui/static/external/bootstrap.bundle.min.js.map similarity index 100% rename from server/web_ui/src/external/bootstrap.bundle.min.js.map rename to server/web_ui/static/external/bootstrap.bundle.min.js.map diff --git a/server/web_ui/src/external/bootstrap.min.css b/server/web_ui/static/external/bootstrap.min.css similarity index 100% rename from server/web_ui/src/external/bootstrap.min.css rename to server/web_ui/static/external/bootstrap.min.css diff --git a/server/web_ui/src/external/bootstrap.min.css.map b/server/web_ui/static/external/bootstrap.min.css.map similarity index 100% rename from server/web_ui/src/external/bootstrap.min.css.map rename to server/web_ui/static/external/bootstrap.min.css.map diff --git a/server/web_ui/src/external/confetti.js b/server/web_ui/static/external/confetti.js similarity index 100% rename from server/web_ui/src/external/confetti.js rename to server/web_ui/static/external/confetti.js diff --git a/server/web_ui/src/img/apple-touch-icon.png b/server/web_ui/static/img/apple-touch-icon.png similarity index 100% rename from server/web_ui/src/img/apple-touch-icon.png rename to server/web_ui/static/img/apple-touch-icon.png diff --git a/server/web_ui/src/img/favicon.png b/server/web_ui/static/img/favicon.png similarity index 100% rename from server/web_ui/src/img/favicon.png rename to server/web_ui/static/img/favicon.png diff --git a/server/web_ui/src/img/icon-accounts.svg b/server/web_ui/static/img/icon-accounts.svg similarity index 100% rename from server/web_ui/src/img/icon-accounts.svg rename to server/web_ui/static/img/icon-accounts.svg diff --git a/server/web_ui/src/img/icon-groups.svg b/server/web_ui/static/img/icon-groups.svg similarity index 100% rename from server/web_ui/src/img/icon-groups.svg rename to server/web_ui/static/img/icon-groups.svg diff --git a/server/web_ui/src/img/icon-oauth2.svg b/server/web_ui/static/img/icon-oauth2.svg similarity index 100% rename from server/web_ui/src/img/icon-oauth2.svg rename to server/web_ui/static/img/icon-oauth2.svg diff --git a/server/web_ui/src/img/icon-person.svg b/server/web_ui/static/img/icon-person.svg similarity index 100% rename from server/web_ui/src/img/icon-person.svg rename to server/web_ui/static/img/icon-person.svg diff --git a/server/web_ui/src/img/icon-robot.svg b/server/web_ui/static/img/icon-robot.svg similarity index 100% rename from server/web_ui/src/img/icon-robot.svg rename to server/web_ui/static/img/icon-robot.svg diff --git a/server/web_ui/src/img/kani-waving.svg b/server/web_ui/static/img/kani-waving.svg similarity index 100% rename from server/web_ui/src/img/kani-waving.svg rename to server/web_ui/static/img/kani-waving.svg diff --git a/server/web_ui/src/img/logo-180.png b/server/web_ui/static/img/logo-180.png similarity index 100% rename from server/web_ui/src/img/logo-180.png rename to server/web_ui/static/img/logo-180.png diff --git a/server/web_ui/src/img/logo-192.png b/server/web_ui/static/img/logo-192.png similarity index 100% rename from server/web_ui/src/img/logo-192.png rename to server/web_ui/static/img/logo-192.png diff --git a/server/web_ui/src/img/logo-256.png b/server/web_ui/static/img/logo-256.png similarity index 100% rename from server/web_ui/src/img/logo-256.png rename to server/web_ui/static/img/logo-256.png diff --git a/server/web_ui/src/img/logo-512.png b/server/web_ui/static/img/logo-512.png similarity index 100% rename from server/web_ui/src/img/logo-512.png rename to server/web_ui/static/img/logo-512.png diff --git a/server/web_ui/src/img/logo-square.svg b/server/web_ui/static/img/logo-square.svg similarity index 100% rename from server/web_ui/src/img/logo-square.svg rename to server/web_ui/static/img/logo-square.svg diff --git a/server/web_ui/src/style.css b/server/web_ui/static/style.css similarity index 100% rename from server/web_ui/src/style.css rename to server/web_ui/static/style.css diff --git a/server/web_ui/src/wasmloader.js b/server/web_ui/static/wasmloader.js similarity index 100% rename from server/web_ui/src/wasmloader.js rename to server/web_ui/static/wasmloader.js diff --git a/tools/iam_migrations/freeipa/Cargo.toml b/tools/iam_migrations/freeipa/Cargo.toml index 04827de22..1f9cd7890 100644 --- a/tools/iam_migrations/freeipa/Cargo.toml +++ b/tools/iam_migrations/freeipa/Cargo.toml @@ -3,39 +3,39 @@ name = "kanidm-ipa-sync" description = "Kanidm Client Tools" documentation = "https://kanidm.github.io/kanidm/stable/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [dependencies] -base64urlsafedata.workspace = true +base64urlsafedata = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } -chrono.workspace = true -cron.workspace = true -kanidm_client.workspace = true -kanidm_proto.workspace = true +chrono = { workspace = true } +cron = { workspace = true } +kanidm_client = { workspace = true } +kanidm_proto = { workspace = true } tokio = { workspace = true, features = ["rt", "macros", "net"] } -tracing.workspace = true +tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } -ldap3_client.workspace = true +ldap3_client = { workspace = true } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true -toml.workspace = true +serde_json = { workspace = true } +toml = { workspace = true } url = { workspace = true, features = ["serde"] } uuid = { workspace = true, features = ["serde"] } # For file metadata, should this me moved out? -kanidmd_lib.workspace = true +kanidmd_lib = { workspace = true } [target.'cfg(target_family = "unix")'.dependencies] -users.workspace = true +users = { workspace = true } [build-dependencies] clap = { workspace = true, features = ["derive"] } -clap_complete.workspace = true +clap_complete = { workspace = true } diff --git a/tools/iam_migrations/ldap/Cargo.toml b/tools/iam_migrations/ldap/Cargo.toml index cc1ca9061..a65e6fa8e 100644 --- a/tools/iam_migrations/ldap/Cargo.toml +++ b/tools/iam_migrations/ldap/Cargo.toml @@ -3,39 +3,39 @@ name = "kanidm-ldap-sync" description = "Kanidm Client Tools" documentation = "https://kanidm.github.io/kanidm/stable/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [dependencies] -base64urlsafedata.workspace = true +base64urlsafedata = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } -chrono.workspace = true -cron.workspace = true -kanidm_client.workspace = true -kanidm_proto.workspace = true +chrono = { workspace = true } +cron = { workspace = true } +kanidm_client = { workspace = true } +kanidm_proto = { workspace = true } tokio = { workspace = true, features = ["rt", "macros", "net"] } -tracing.workspace = true +tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } -ldap3_client.workspace = true +ldap3_client = { workspace = true } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true -toml.workspace = true +serde_json = { workspace = true } +toml = { workspace = true } url = { workspace = true, features = ["serde"] } uuid = { workspace = true, features = ["serde"] } # For file metadata, should this me moved out? -kanidmd_lib.workspace = true +kanidmd_lib = { workspace = true } [target.'cfg(target_family = "unix")'.dependencies] -users.workspace = true +users = { workspace = true } [build-dependencies] clap = { workspace = true, features = ["derive"] } -clap_complete.workspace = true +clap_complete = { workspace = true } diff --git a/tools/orca/Cargo.toml b/tools/orca/Cargo.toml index 37698aede..de95db821 100644 --- a/tools/orca/Cargo.toml +++ b/tools/orca/Cargo.toml @@ -3,42 +3,42 @@ name = "orca" description = "Orca - load testing for LDAP and Kanidm" documentation = "https://docs.rs/kanidm/latest/kanidm/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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.workspace = true -crossbeam.workspace = true -csv.workspace = true -dialoguer.workspace = true +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 +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 +serde_json = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread"] } -tokio-openssl.workspace = true +tokio-openssl = { workspace = true } tokio-util = { workspace = true, features = ["codec"] } -toml.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true +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.workspace = true +tikv-jemallocator = { workspace = true } [build-dependencies] -profiles.workspace = true +profiles = { workspace = true } diff --git a/unix_integration/Cargo.toml b/unix_integration/Cargo.toml index 37f98304f..adb692c4e 100644 --- a/unix_integration/Cargo.toml +++ b/unix_integration/Cargo.toml @@ -3,13 +3,13 @@ name = "kanidm_unix_int" description = "Kanidm Unix Integration Clients" documentation = "https://docs.rs/kanidm/latest/kanidm/" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [features] default = ["unix"] @@ -42,40 +42,40 @@ name = "kanidm_unix_common" path = "src/lib.rs" [dependencies] -bytes.workspace = true +bytes = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } -csv.workspace = true -futures.workspace = true -libc.workspace = true -libsqlite3-sys.workspace = true -lru.workspace = true -kanidm_client.workspace = true -kanidm_proto.workspace = true -kanidm_lib_crypto.workspace = true -kanidm_lib_file_permissions.workspace = true -notify-debouncer-full.workspace = true -rpassword.workspace = true -rusqlite.workspace = true +csv = { workspace = true } +futures = { workspace = true } +libc = { workspace = true } +libsqlite3-sys = { workspace = true } +lru = { workspace = true } +kanidm_client = { workspace = true } +kanidm_proto = { workspace = true } +kanidm_lib_crypto = { workspace = true } +kanidm_lib_file_permissions = { workspace = true } +notify-debouncer-full = { workspace = true } +rpassword = { workspace = true } +rusqlite = { workspace = true } selinux = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true -sketching.workspace = true +serde_json = { workspace = true } +sketching = { workspace = true } -toml.workspace = true +toml = { workspace = true } tokio = { workspace = true, features = ["rt", "fs", "macros", "sync", "time", "net", "io-util"] } tokio-util = { workspace = true, features = ["codec"] } -tracing.workspace = true +tracing = { workspace = true } tss-esapi = { workspace = true, optional = true } reqwest = { workspace = true, default-features = false } -walkdir.workspace = true +walkdir = { workspace = true } [target.'cfg(not(target_family = "windows"))'.dependencies] -users.workspace = true +users = { workspace = true } [dev-dependencies] -kanidmd_core.workspace = true +kanidmd_core = { workspace = true } [build-dependencies] clap = { workspace = true, features = ["derive"] } -clap_complete.workspace = true -profiles.workspace = true +clap_complete = { workspace = true } +profiles = { workspace = true } diff --git a/unix_integration/pam_kanidm/Cargo.toml b/unix_integration/pam_kanidm/Cargo.toml index d7931c623..759d82973 100644 --- a/unix_integration/pam_kanidm/Cargo.toml +++ b/unix_integration/pam_kanidm/Cargo.toml @@ -2,13 +2,13 @@ name = "pam_kanidm" links = "pam" -version.workspace = true -authors.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true +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" @@ -16,8 +16,8 @@ crate-type = [ "cdylib" ] path = "src/lib.rs" [dependencies] -kanidm_unix_int.workspace = true -libc.workspace = true +kanidm_unix_int = { workspace = true } +libc = { workspace = true } [build-dependencies] -pkg-config.workspace = true +pkg-config = { workspace = true }