mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 04:27:02 +01:00
headless webdriver testing, starting on brotli feature (#1844)
* headless chromedriver testing * updating build scripts
This commit is contained in:
parent
43d5577895
commit
749522418c
14
.github/workflows/kanidm_book.yml
vendored
14
.github/workflows/kanidm_book.yml
vendored
|
@ -7,6 +7,7 @@ name: GitHub Pages
|
||||||
- "master"
|
- "master"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
# yamllint disable-line rule:line-length
|
||||||
# permissions list: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
# permissions list: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
|
@ -36,7 +37,11 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Latest branch
|
- name: Latest branch
|
||||||
run: |
|
run: |
|
||||||
echo "latest=$(git branch -a | awk '{print $1}' | sort -t. -k3n,3 -k4n,4 | awk -F'/' '{print $NF}' | tail -n1)" >> $GITHUB_OUTPUT
|
echo "latest=$(git branch -a \
|
||||||
|
| awk '{print $1}' \
|
||||||
|
| sort -t. -k3n,3 -k4n,4 \
|
||||||
|
| awk -F'/' '{print $NF}' \
|
||||||
|
| tail -n1)" >> $GITHUB_OUTPUT
|
||||||
id: branchname
|
id: branchname
|
||||||
- name: Move redirector page
|
- name: Move redirector page
|
||||||
run: |
|
run: |
|
||||||
|
@ -53,6 +58,7 @@ jobs:
|
||||||
fanout:
|
fanout:
|
||||||
uses: './.github/workflows/kanidm_individual_book.yml'
|
uses: './.github/workflows/kanidm_individual_book.yml'
|
||||||
needs: pre_deploy
|
needs: pre_deploy
|
||||||
|
# yamllint disable-line rule:line-length
|
||||||
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
|
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -78,6 +84,7 @@ jobs:
|
||||||
- fanout
|
- fanout
|
||||||
- docs_master
|
- docs_master
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# yamllint disable-line rule:line-length
|
||||||
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
|
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
|
@ -110,7 +117,10 @@ jobs:
|
||||||
find $(pwd) -name '*.tar.gz' -ls -exec tar zxvf "{}" \;
|
find $(pwd) -name '*.tar.gz' -ls -exec tar zxvf "{}" \;
|
||||||
echo "Carrying on..."
|
echo "Carrying on..."
|
||||||
mkdir -p docs
|
mkdir -p docs
|
||||||
cd docs && cp -R "$(git branch -a | awk '{print $1}' | sort -t. -k3n,3 -k4n,4 | awk -F'/' '{print $NF}' | tail -n1)/" stable && cd ..
|
cd docs && cp -R "$(git branch -a \
|
||||||
|
| awk '{print $1}' \
|
||||||
|
| sort -t. -k3n,3 -k4n,4 \
|
||||||
|
| awk -F'/' '{print $NF}' | tail -n1)/" stable && cd ..
|
||||||
ls -la docs/
|
ls -la docs/
|
||||||
echo "Cleaning up docs archives"
|
echo "Cleaning up docs archives"
|
||||||
rm docs/*.tar.gz
|
rm docs/*.tar.gz
|
||||||
|
|
12
.github/workflows/wasm_test.yml
vendored
12
.github/workflows/wasm_test.yml
vendored
|
@ -41,9 +41,15 @@ jobs:
|
||||||
# Optional: do not specify to match Chrome's version
|
# Optional: do not specify to match Chrome's version
|
||||||
# chromedriver-version: '88.0.4324.96'
|
# chromedriver-version: '88.0.4324.96'
|
||||||
|
|
||||||
# docs here:
|
# - run: make webui
|
||||||
# https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html
|
|
||||||
- run: make webui
|
|
||||||
- name: "Run wasm-pack test"
|
- name: "Run wasm-pack test"
|
||||||
|
# https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html
|
||||||
run: make webui/test
|
run: make webui/test
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: "Run webdriver tests"
|
||||||
|
run: |
|
||||||
|
chromedriver &
|
||||||
|
cargo test -p kanidmd_testkit --features webdriver
|
||||||
|
env:
|
||||||
|
DISPLAY: ":99"
|
||||||
|
|
7
.yamllint
Normal file
7
.yamllint
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
extends: default
|
||||||
|
|
||||||
|
rules:
|
||||||
|
line-length:
|
||||||
|
max: 120
|
||||||
|
level: warning
|
||||||
|
|
49
Cargo.lock
generated
49
Cargo.lock
generated
|
@ -1510,6 +1510,28 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fantoccini"
|
||||||
|
version = "0.19.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65f0fbe245d714b596ba5802b46f937f5ce68dcae0f32f9a70b5c3b04d3c6f64"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
"cookie 0.16.2",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-tls",
|
||||||
|
"mime",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.3.22",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"webdriver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
|
@ -2704,7 +2726,9 @@ name = "kanidmd_testkit"
|
||||||
version = "1.1.0-beta.13-dev"
|
version = "1.1.0-beta.13-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"compact_jwt",
|
"compact_jwt",
|
||||||
|
"fantoccini",
|
||||||
"futures",
|
"futures",
|
||||||
|
"hyper-tls",
|
||||||
"kanidm_client",
|
"kanidm_client",
|
||||||
"kanidm_proto",
|
"kanidm_proto",
|
||||||
"kanidmd_core",
|
"kanidmd_core",
|
||||||
|
@ -5273,6 +5297,12 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
|
@ -5577,6 +5607,25 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webdriver"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9973cb72c8587d5ad5efdb91e663d36177dc37725e6c90ca86c626b0cc45c93f"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
"bytes",
|
||||||
|
"cookie 0.16.2",
|
||||||
|
"http",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.3.22",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "4.4.0"
|
version = "4.4.0"
|
||||||
|
|
|
@ -241,6 +241,7 @@ pub async fn oauth2_id_delete(
|
||||||
// valid Kanidm instance in the topology can handle these request.
|
// valid Kanidm instance in the topology can handle these request.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(state, kopid))]
|
||||||
pub async fn oauth2_authorise_post(
|
pub async fn oauth2_authorise_post(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
|
@ -256,6 +257,7 @@ pub async fn oauth2_authorise_post(
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(state, kopid))]
|
||||||
pub async fn oauth2_authorise_get(
|
pub async fn oauth2_authorise_get(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
|
|
|
@ -1408,9 +1408,10 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if consent_previously_granted {
|
if consent_previously_granted {
|
||||||
|
let pretty_scopes: Vec<String> = granted_scopes.iter().map(|s| s.to_owned()).collect();
|
||||||
admin_info!(
|
admin_info!(
|
||||||
"User has previously consented, permitting. {:?}",
|
"User has previously consented, permitting with scopes: {}",
|
||||||
granted_scopes
|
pretty_scopes.join(",")
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup for the permit success
|
// Setup for the permit success
|
||||||
|
|
|
@ -15,11 +15,21 @@ repository = { workspace = true }
|
||||||
name = "kanidmd_testkit"
|
name = "kanidmd_testkit"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
# Enables webdriver tests, you need to be running a webdriver server
|
||||||
|
webdriver = ["fantoccini"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kanidm_client = { workspace = true }
|
kanidm_client = { workspace = true }
|
||||||
kanidm_proto = { workspace = true }
|
kanidm_proto = { workspace = true }
|
||||||
kanidmd_core = { workspace = true }
|
kanidmd_core = { workspace = true }
|
||||||
kanidmd_lib = { workspace = true }
|
kanidmd_lib = { workspace = true }
|
||||||
|
# used for webdriver testing
|
||||||
|
hyper-tls = { workspace = true }
|
||||||
|
# used for webdriver testing
|
||||||
|
fantoccini = { version="0.19.3", optional=true}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
|
|
|
@ -14,6 +14,7 @@ use std::net::TcpStream;
|
||||||
use std::sync::atomic::{AtomicU16, Ordering};
|
use std::sync::atomic::{AtomicU16, Ordering};
|
||||||
|
|
||||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||||
|
use kanidm_proto::v1::{Filter, Modify, ModifyList};
|
||||||
use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole};
|
use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole};
|
||||||
use kanidmd_core::{create_server_core, CoreHandle};
|
use kanidmd_core::{create_server_core, CoreHandle};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
@ -92,3 +93,289 @@ pub async fn setup_async_test() -> (KanidmClient, CoreHandle) {
|
||||||
|
|
||||||
(rsclient, core_handle)
|
(rsclient, core_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// creates a user (username: `id`) and puts them into a group, creating it if need be.
|
||||||
|
pub async fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) {
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_create(id, id)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create the user");
|
||||||
|
|
||||||
|
// Create group and add to user to test read attr: member_of
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
if rsclient
|
||||||
|
.idm_group_get(group_name)
|
||||||
|
.await
|
||||||
|
.expect("Failed to get group")
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_create(group_name)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create group");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members(group_name, &[id])
|
||||||
|
.await
|
||||||
|
.expect("Failed to set group membership for user");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_user_with_all_attrs(
|
||||||
|
rsclient: &KanidmClient,
|
||||||
|
id: &str,
|
||||||
|
optional_group: Option<&str>,
|
||||||
|
) {
|
||||||
|
let group_format = format!("{}_group", id);
|
||||||
|
let group_name = optional_group.unwrap_or(&group_format);
|
||||||
|
|
||||||
|
create_user(rsclient, id, group_name).await;
|
||||||
|
add_all_attrs(rsclient, id, group_name, Some(id)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_all_attrs(
|
||||||
|
rsclient: &KanidmClient,
|
||||||
|
id: &str,
|
||||||
|
group_name: &str,
|
||||||
|
legalname: Option<&str>,
|
||||||
|
) {
|
||||||
|
// Extend with posix attrs to test read attr: gidnumber and loginshell
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_unix_extend(id, None, Some("/bin/sh"))
|
||||||
|
.await
|
||||||
|
.expect("Failed to set shell to /bin/sh for user");
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_unix_extend(group_name, None)
|
||||||
|
.await
|
||||||
|
.expect("Failed to extend user group");
|
||||||
|
|
||||||
|
for attr in ["ssh_publickey", "mail"].iter() {
|
||||||
|
println!("Checking writable for {}", attr);
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let res = is_attr_writable(rsclient, id, attr)
|
||||||
|
.await
|
||||||
|
.expect("Failed to get wriable status for attribute");
|
||||||
|
assert!(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(legalname) = legalname {
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let res = is_attr_writable(rsclient, legalname, "legalname")
|
||||||
|
.await
|
||||||
|
.expect("Failed to get writable status for legalname field");
|
||||||
|
assert!(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write radius credentials
|
||||||
|
if id != "anonymous" {
|
||||||
|
login_account(rsclient, id).await;
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let _ = rsclient
|
||||||
|
.idm_account_radius_credential_regenerate(id)
|
||||||
|
.await
|
||||||
|
.expect("Failed to regen password for user");
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to auth with password as admin!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) -> Option<bool> {
|
||||||
|
println!("writing to attribute: {}", attr);
|
||||||
|
match attr {
|
||||||
|
"radius_secret" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_account_radius_credential_regenerate(id)
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"primary_credential" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_primary_credential_set_password(id, "dsadjasiodqwjk12asdl")
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"ssh_publickey" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_post_ssh_pubkey(
|
||||||
|
id,
|
||||||
|
"k1",
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0\
|
||||||
|
L1EyR30CwoP william@amethyst",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"unix_password" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_unix_cred_put(id, "dsadjasiodqwjk12asdl")
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"legalname" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_set_attr(id, "legalname", &["test legal name"])
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"mail" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_set_attr(id, "mail", &[&format!("{}@example.com", id)])
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
entry => {
|
||||||
|
let new_value = match entry {
|
||||||
|
"acp_receiver_group" => "00000000-0000-0000-0000-000000000011".to_string(),
|
||||||
|
"acp_targetscope" => "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}".to_string(),
|
||||||
|
_ => id.to_string(),
|
||||||
|
};
|
||||||
|
let m = ModifyList::new_list(vec![
|
||||||
|
Modify::Purged(attr.to_string()),
|
||||||
|
Modify::Present(attr.to_string(), new_value),
|
||||||
|
]);
|
||||||
|
let f = Filter::Eq("name".to_string(), id.to_string());
|
||||||
|
Some(rsclient.modify(f.clone(), m.clone()).await.is_ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn login_account(rsclient: &KanidmClient, id: &str) {
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members(
|
||||||
|
"idm_people_account_password_import_priv",
|
||||||
|
&[ADMIN_TEST_USER],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to add user to idm_people_account_password_import_priv");
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("idm_people_extend_priv", &[ADMIN_TEST_USER])
|
||||||
|
.await
|
||||||
|
.expect("Failed to add user to idm_people_extend_priv");
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_person_account_primary_credential_set_password(id, NOT_ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to set password for user");
|
||||||
|
|
||||||
|
let _ = rsclient.logout().await;
|
||||||
|
let res = rsclient
|
||||||
|
.auth_simple_password(id, NOT_ADMIN_TEST_PASSWORD)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Setup privs
|
||||||
|
println!("{} logged in", id);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let res = rsclient
|
||||||
|
.reauth_simple_password(NOT_ADMIN_TEST_PASSWORD)
|
||||||
|
.await;
|
||||||
|
println!("{} priv granted for", id);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login to the given account, but first login with default admin credentials.
|
||||||
|
// This is necessary when switching between unprivileged accounts, but adds extra calls which
|
||||||
|
// create extra debugging noise, so should be avoided when unnecessary.
|
||||||
|
pub async fn login_account_via_admin(rsclient: &KanidmClient, id: &str) {
|
||||||
|
let _ = rsclient.logout().await;
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to login as admin!");
|
||||||
|
login_account(rsclient, id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_read_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str], is_readable: bool) {
|
||||||
|
println!("Test read to {}, is readable: {}", id, is_readable);
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let rset = rsclient
|
||||||
|
.search(Filter::Eq("name".to_string(), id.to_string()))
|
||||||
|
.await
|
||||||
|
.expect("Can't get user from search");
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let e = rset.first().expect("Failed to get first user from set");
|
||||||
|
|
||||||
|
for attr in attrs.iter() {
|
||||||
|
println!("Reading {}", attr);
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let is_ok = match *attr {
|
||||||
|
"radius_secret" => rsclient
|
||||||
|
.idm_account_radius_credential_get(id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_some(),
|
||||||
|
_ => e.attrs.get(*attr).is_some(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(is_ok == is_readable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_write_attrs(
|
||||||
|
rsclient: &KanidmClient,
|
||||||
|
id: &str,
|
||||||
|
attrs: &[&str],
|
||||||
|
is_writeable: bool,
|
||||||
|
) {
|
||||||
|
println!("Test write to {}, is writeable: {}", id, is_writeable);
|
||||||
|
for attr in attrs.iter() {
|
||||||
|
println!("Writing to {} - ex {}", attr, is_writeable);
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let is_ok = is_attr_writable(rsclient, id, attr).await.unwrap();
|
||||||
|
assert!(is_ok == is_writeable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_modify_group(
|
||||||
|
rsclient: &KanidmClient,
|
||||||
|
group_names: &[&str],
|
||||||
|
can_be_modified: bool,
|
||||||
|
) {
|
||||||
|
// need user test created to be added as test part
|
||||||
|
for group in group_names.iter() {
|
||||||
|
println!("Testing group: {}", group);
|
||||||
|
for attr in ["description", "name"].iter() {
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let is_writable = is_attr_writable(rsclient, group, attr).await.unwrap();
|
||||||
|
assert!(is_writable == can_be_modified)
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members(group, &[NOT_ADMIN_TEST_USERNAME])
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
== can_be_modified
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs in with the admin user and puts them in idm_admins so they can do admin things
|
||||||
|
pub async fn login_put_admin_idm_admins(rsclient: &KanidmClient) {
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to authenticate as admin!");
|
||||||
|
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
||||||
|
.await
|
||||||
|
.expect("Failed to add admin user to idm_admins")
|
||||||
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use kanidm_client::KanidmClient;
|
use kanidm_client::KanidmClient;
|
||||||
use kanidm_proto::v1::{Filter, Modify, ModifyList};
|
|
||||||
|
|
||||||
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, NOT_ADMIN_TEST_PASSWORD};
|
use kanidmd_testkit::*;
|
||||||
|
|
||||||
static USER_READABLE_ATTRS: [&str; 9] = [
|
static USER_READABLE_ATTRS: [&str; 9] = [
|
||||||
"name",
|
"name",
|
||||||
|
@ -55,240 +54,13 @@ static DEFAULT_HP_GROUP_NAMES: [&str; 24] = [
|
||||||
static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
|
static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
|
||||||
["idm_account_unix_extend_priv", "idm_group_unix_extend_priv"];
|
["idm_account_unix_extend_priv", "idm_group_unix_extend_priv"];
|
||||||
|
|
||||||
async fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) {
|
|
||||||
rsclient.idm_person_account_create(id, id).await.unwrap();
|
|
||||||
|
|
||||||
// Create group and add to user to test read attr: member_of
|
|
||||||
if rsclient.idm_group_get(group_name).await.unwrap().is_none() {
|
|
||||||
rsclient.idm_group_create(group_name).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members(group_name, &[id])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) -> Option<bool> {
|
|
||||||
println!("writing to attribute: {}", attr);
|
|
||||||
match attr {
|
|
||||||
"radius_secret" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_account_radius_credential_regenerate(id)
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
"primary_credential" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_primary_credential_set_password(id, "dsadjasiodqwjk12asdl")
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
"ssh_publickey" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_post_ssh_pubkey(
|
|
||||||
id,
|
|
||||||
"k1",
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0\
|
|
||||||
L1EyR30CwoP william@amethyst",
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
"unix_password" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_unix_cred_put(id, "dsadjasiodqwjk12asdl")
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
"legalname" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_set_attr(id, "legalname", &["test legal name"])
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
"mail" => Some(
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_set_attr(id, "mail", &[&format!("{}@example.com", id)])
|
|
||||||
.await
|
|
||||||
.is_ok(),
|
|
||||||
),
|
|
||||||
entry => {
|
|
||||||
let new_value = match entry {
|
|
||||||
"acp_receiver_group" => "00000000-0000-0000-0000-000000000011".to_string(),
|
|
||||||
"acp_targetscope" => "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}".to_string(),
|
|
||||||
_ => id.to_string(),
|
|
||||||
};
|
|
||||||
let m = ModifyList::new_list(vec![
|
|
||||||
Modify::Purged(attr.to_string()),
|
|
||||||
Modify::Present(attr.to_string(), new_value),
|
|
||||||
]);
|
|
||||||
let f = Filter::Eq("name".to_string(), id.to_string());
|
|
||||||
Some(rsclient.modify(f.clone(), m.clone()).await.is_ok())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_all_attrs(
|
|
||||||
rsclient: &KanidmClient,
|
|
||||||
id: &str,
|
|
||||||
group_name: &str,
|
|
||||||
legalname: Option<&str>,
|
|
||||||
) {
|
|
||||||
// Extend with posix attrs to test read attr: gidnumber and loginshell
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_unix_extend(id, None, Some("/bin/sh"))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_unix_extend(group_name, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for attr in ["ssh_publickey", "mail"].iter() {
|
|
||||||
assert!(is_attr_writable(rsclient, id, attr).await.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(legalname) = legalname {
|
|
||||||
assert!(is_attr_writable(rsclient, legalname, "legalname")
|
|
||||||
.await
|
|
||||||
.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write radius credentials
|
|
||||||
if id != "anonymous" {
|
|
||||||
login_account(rsclient, id).await;
|
|
||||||
let _ = rsclient
|
|
||||||
.idm_account_radius_credential_regenerate(id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_user_with_all_attrs(
|
|
||||||
rsclient: &KanidmClient,
|
|
||||||
id: &str,
|
|
||||||
optional_group: Option<&str>,
|
|
||||||
) {
|
|
||||||
let group_format = format!("{}_group", id);
|
|
||||||
let group_name = optional_group.unwrap_or(&group_format);
|
|
||||||
|
|
||||||
create_user(rsclient, id, group_name).await;
|
|
||||||
add_all_attrs(rsclient, id, group_name, Some(id)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn login_account(rsclient: &KanidmClient, id: &str) {
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members(
|
|
||||||
"idm_people_account_password_import_priv",
|
|
||||||
&[ADMIN_TEST_USER],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_people_extend_priv", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
rsclient
|
|
||||||
.idm_person_account_primary_credential_set_password(id, NOT_ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = rsclient.logout();
|
|
||||||
let res = rsclient
|
|
||||||
.auth_simple_password(id, NOT_ADMIN_TEST_PASSWORD)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Setup privs
|
|
||||||
println!("{} logged in", id);
|
|
||||||
assert!(res.is_ok());
|
|
||||||
|
|
||||||
let res = rsclient
|
|
||||||
.reauth_simple_password(NOT_ADMIN_TEST_PASSWORD)
|
|
||||||
.await;
|
|
||||||
println!("{} priv granted for", id);
|
|
||||||
assert!(res.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login to the given account, but first login with default admin credentials.
|
|
||||||
// This is necessary when switching between unprivileged accounts, but adds extra calls which
|
|
||||||
// create extra debugging noise, so should be avoided when unnecessary.
|
|
||||||
async fn login_account_via_admin(rsclient: &KanidmClient, id: &str) {
|
|
||||||
let _ = rsclient.logout();
|
|
||||||
rsclient
|
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
login_account(rsclient, id).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_read_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str], is_readable: bool) {
|
|
||||||
println!("Test read to {}, is readable: {}", id, is_readable);
|
|
||||||
let rset = rsclient
|
|
||||||
.search(Filter::Eq("name".to_string(), id.to_string()))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let e = rset.first().unwrap();
|
|
||||||
for attr in attrs.iter() {
|
|
||||||
println!("Reading {}", attr);
|
|
||||||
let is_ok = match *attr {
|
|
||||||
"radius_secret" => rsclient
|
|
||||||
.idm_account_radius_credential_get(id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.is_some(),
|
|
||||||
_ => e.attrs.get(*attr).is_some(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(is_ok == is_readable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_write_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str], is_writeable: bool) {
|
|
||||||
println!("Test write to {}, is writeable: {}", id, is_writeable);
|
|
||||||
for attr in attrs.iter() {
|
|
||||||
println!("Writing to {} - ex {}", attr, is_writeable);
|
|
||||||
let is_ok = is_attr_writable(rsclient, id, attr).await.unwrap();
|
|
||||||
assert!(is_ok == is_writeable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_modify_group(rsclient: &KanidmClient, group_names: &[&str], is_modificable: bool) {
|
|
||||||
// need user test created to be added as test part
|
|
||||||
for group in group_names.iter() {
|
|
||||||
println!("Testing group: {}", group);
|
|
||||||
for attr in ["description", "name"].iter() {
|
|
||||||
assert!(is_attr_writable(rsclient, group, attr).await.unwrap() == is_modificable)
|
|
||||||
}
|
|
||||||
assert!(
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members(group, &["test"])
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
== is_modificable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
// - Read to all self attributes (within security constraints).
|
// - Read to all self attributes (within security constraints).
|
||||||
// - Write to a limited set of self attributes, such as:
|
// - Write to a limited set of self attributes, such as:
|
||||||
// name, displayname, legalname, ssh-keys, credentials etc.
|
// name, displayname, legalname, ssh-keys, credentials etc.
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user_with_all_attrs(&rsclient, "self_account", Some("self_group")).await;
|
create_user_with_all_attrs(&rsclient, "self_account", Some("self_group")).await;
|
||||||
create_user_with_all_attrs(&rsclient, "other_account", Some("other_group")).await;
|
create_user_with_all_attrs(&rsclient, "other_account", Some("other_group")).await;
|
||||||
|
@ -321,21 +93,20 @@ async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
|
||||||
// ability to lock and unlock accounts, excluding high access members.
|
// ability to lock and unlock accounts, excluding high access members.
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user(&rsclient, "account_manager", "idm_account_manage_priv").await;
|
create_user(&rsclient, "account_manager", "idm_account_manage_priv").await;
|
||||||
create_user_with_all_attrs(&rsclient, "test", Some("test_group")).await;
|
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||||
|
|
||||||
login_account(&rsclient, "account_manager").await;
|
login_account(&rsclient, "account_manager").await;
|
||||||
|
|
||||||
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true).await;
|
test_read_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&USER_READABLE_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
static ACCOUNT_MANAGER_ATTRS: [&str; 5] = [
|
static ACCOUNT_MANAGER_ATTRS: [&str; 5] = [
|
||||||
"name",
|
"name",
|
||||||
"displayname",
|
"displayname",
|
||||||
|
@ -343,11 +114,29 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
||||||
"ssh_publickey",
|
"ssh_publickey",
|
||||||
"mail",
|
"mail",
|
||||||
];
|
];
|
||||||
test_write_attrs(&rsclient, "test", &ACCOUNT_MANAGER_ATTRS, true).await;
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&ACCOUNT_MANAGER_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
static PRIVATE_DATA_ATTRS: [&str; 1] = ["legalname"];
|
static PRIVATE_DATA_ATTRS: [&str; 1] = ["legalname"];
|
||||||
test_read_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false).await;
|
test_read_attrs(
|
||||||
test_write_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false).await;
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&PRIVATE_DATA_ATTRS,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&PRIVATE_DATA_ATTRS,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
// TODO #59: lock and _unlock, except high access members
|
// TODO #59: lock and _unlock, except high access members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,18 +145,11 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
||||||
// write group but not high access
|
// write group but not high access
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
|
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
|
||||||
// create test user without creating new groups
|
// create test user without creating new groups
|
||||||
create_user(&rsclient, "test", "idm_admins").await;
|
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "idm_admins").await;
|
||||||
|
|
||||||
login_account(&rsclient, "group_manager").await;
|
login_account(&rsclient, "group_manager").await;
|
||||||
|
|
||||||
|
@ -391,7 +173,7 @@ async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
||||||
|
|
||||||
rsclient.idm_group_create("test_group").await.unwrap();
|
rsclient.idm_group_create("test_group").await.unwrap();
|
||||||
rsclient
|
rsclient
|
||||||
.idm_group_add_members("test_group", &["test"])
|
.idm_group_add_members("test_group", &[NOT_ADMIN_TEST_USERNAME])
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(is_attr_writable(&rsclient, "test_group", "description")
|
assert!(is_attr_writable(&rsclient, "test_group", "description")
|
||||||
|
@ -403,14 +185,7 @@ async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
||||||
// read and write access control entries.
|
// read and write access control entries.
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_admins_access_control_entries(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_admins_access_control_entries(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ACP_COMMON_ATTRS: [&str; 4] = [
|
static ACP_COMMON_ATTRS: [&str; 4] = [
|
||||||
"name",
|
"name",
|
||||||
|
@ -459,14 +234,7 @@ async fn test_default_entries_rbac_admins_access_control_entries(rsclient: Kanid
|
||||||
// TODO #252: write schema entries
|
// TODO #252: write schema entries
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let default_classnames: HashSet<String> = [
|
let default_classnames: HashSet<String> = [
|
||||||
"access_control_create",
|
"access_control_create",
|
||||||
|
@ -574,16 +342,9 @@ async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient)
|
||||||
// create new accounts (to bootstrap the system).
|
// create new accounts (to bootstrap the system).
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_admins_group_entries(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_admins_group_entries(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user(&rsclient, "test", "test_group").await;
|
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "test_group").await;
|
||||||
|
|
||||||
let default_group_names =
|
let default_group_names =
|
||||||
[&DEFAULT_HP_GROUP_NAMES[..], &DEFAULT_NOT_HP_GROUP_NAMES[..]].concat();
|
[&DEFAULT_HP_GROUP_NAMES[..], &DEFAULT_NOT_HP_GROUP_NAMES[..]].concat();
|
||||||
|
@ -594,14 +355,7 @@ async fn test_default_entries_rbac_admins_group_entries(rsclient: KanidmClient)
|
||||||
// modify high access accounts as an escalation for security sensitive accounts.
|
// modify high access accounts as an escalation for security sensitive accounts.
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_admins_ha_accounts(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_admins_ha_accounts(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
|
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
|
||||||
test_write_attrs(&rsclient, "idm_admin", &MAIN_ATTRS, true).await;
|
test_write_attrs(&rsclient, "idm_admin", &MAIN_ATTRS, true).await;
|
||||||
|
@ -610,21 +364,23 @@ async fn test_default_entries_rbac_admins_ha_accounts(rsclient: KanidmClient) {
|
||||||
// recover from the recycle bin
|
// recover from the recycle bin
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_admins_recycle_accounts(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_admins_recycle_accounts(rsclient: KanidmClient) {
|
||||||
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
|
|
||||||
|
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "test_group").await;
|
||||||
|
|
||||||
rsclient
|
rsclient
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
.idm_person_account_delete(NOT_ADMIN_TEST_USERNAME)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
rsclient
|
rsclient
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
.recycle_bin_revive(NOT_ADMIN_TEST_USERNAME)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
create_user(&rsclient, "test", "test_group").await;
|
let acc = rsclient
|
||||||
|
.idm_person_account_get(NOT_ADMIN_TEST_USERNAME)
|
||||||
rsclient.idm_person_account_delete("test").await.unwrap();
|
.await
|
||||||
rsclient.recycle_bin_revive("test").await.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let acc = rsclient.idm_person_account_get("test").await.unwrap();
|
|
||||||
assert!(acc.is_some());
|
assert!(acc.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,29 +389,40 @@ async fn test_default_entries_rbac_admins_recycle_accounts(rsclient: KanidmClien
|
||||||
// write private or sensitive data of persons, IE legalName
|
// write private or sensitive data of persons, IE legalName
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user(&rsclient, "read_people_manager", "idm_people_read_priv").await;
|
create_user(&rsclient, "read_people_manager", "idm_people_read_priv").await;
|
||||||
create_user_with_all_attrs(&rsclient, "test", Some("test_group")).await;
|
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||||
|
|
||||||
static PEOPLE_MANAGER_ATTRS: [&str; 2] = ["legalname", "mail"];
|
static PEOPLE_MANAGER_ATTRS: [&str; 2] = ["legalname", "mail"];
|
||||||
|
|
||||||
static TECHNICAL_ATTRS: [&str; 3] = ["primary_credential", "radius_secret", "unix_password"];
|
static TECHNICAL_ATTRS: [&str; 3] = ["primary_credential", "radius_secret", "unix_password"];
|
||||||
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
|
test_read_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&PEOPLE_MANAGER_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
login_account(&rsclient, "read_people_manager").await;
|
login_account(&rsclient, "read_people_manager").await;
|
||||||
|
|
||||||
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
|
test_read_attrs(
|
||||||
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
|
&rsclient,
|
||||||
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, false).await;
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
|
&PEOPLE_MANAGER_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_read_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &TECHNICAL_ATTRS, false).await;
|
||||||
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&PEOPLE_MANAGER_ATTRS,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &TECHNICAL_ATTRS, false).await;
|
||||||
|
|
||||||
let _ = rsclient.logout();
|
let _ = rsclient.logout();
|
||||||
rsclient
|
rsclient
|
||||||
|
@ -665,26 +432,31 @@ async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
|
||||||
create_user(&rsclient, "write_people_manager", "idm_people_write_priv").await;
|
create_user(&rsclient, "write_people_manager", "idm_people_write_priv").await;
|
||||||
login_account(&rsclient, "write_people_manager").await;
|
login_account(&rsclient, "write_people_manager").await;
|
||||||
|
|
||||||
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
|
test_read_attrs(
|
||||||
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
|
&rsclient,
|
||||||
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
|
&PEOPLE_MANAGER_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_read_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &TECHNICAL_ATTRS, false).await;
|
||||||
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&PEOPLE_MANAGER_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &TECHNICAL_ATTRS, false).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anonymous Clients + Everyone Else
|
// Anonymous Clients + Everyone Else
|
||||||
// read memberof, unix attrs, name, displayname, class
|
// read memberof, unix attrs, name, displayname, class
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user_with_all_attrs(&rsclient, "test", Some("test_group")).await;
|
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||||
rsclient
|
rsclient
|
||||||
.idm_group_add_members("test_group", &["anonymous"])
|
.idm_group_add_members("test_group", &["anonymous"])
|
||||||
.await
|
.await
|
||||||
|
@ -694,9 +466,21 @@ async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
|
||||||
let _ = rsclient.logout();
|
let _ = rsclient.logout();
|
||||||
rsclient.auth_anonymous().await.unwrap();
|
rsclient.auth_anonymous().await.unwrap();
|
||||||
|
|
||||||
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true).await;
|
test_read_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&USER_READABLE_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
test_read_attrs(&rsclient, "anonymous", &USER_READABLE_ATTRS, true).await;
|
test_read_attrs(&rsclient, "anonymous", &USER_READABLE_ATTRS, true).await;
|
||||||
test_write_attrs(&rsclient, "test", &SELF_WRITEABLE_ATTRS, false).await;
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&SELF_WRITEABLE_ATTRS,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
test_write_attrs(&rsclient, "anonymous", &SELF_WRITEABLE_ATTRS, false).await;
|
test_write_attrs(&rsclient, "anonymous", &SELF_WRITEABLE_ATTRS, false).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,50 +489,57 @@ async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
|
||||||
// Read other needed attributes to fulfil radius functions.
|
// Read other needed attributes to fulfil radius functions.
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_default_entries_rbac_radius_servers(rsclient: KanidmClient) {
|
async fn test_default_entries_rbac_radius_servers(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
create_user(&rsclient, "radius_server", "idm_radius_servers").await;
|
create_user(&rsclient, "radius_server", "idm_radius_servers").await;
|
||||||
create_user_with_all_attrs(&rsclient, "test", Some("test_group")).await;
|
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||||
|
|
||||||
login_account(&rsclient, "radius_server").await;
|
login_account(&rsclient, "radius_server").await;
|
||||||
static RADIUS_NECESSARY_ATTRS: [&str; 4] = ["name", "spn", "uuid", "radius_secret"];
|
static RADIUS_NECESSARY_ATTRS: [&str; 4] = ["name", "spn", "uuid", "radius_secret"];
|
||||||
|
|
||||||
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true).await;
|
test_read_attrs(
|
||||||
test_read_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, true).await;
|
&rsclient,
|
||||||
test_write_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, false).await;
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&USER_READABLE_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_read_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&RADIUS_NECESSARY_ATTRS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
test_write_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
&RADIUS_NECESSARY_ATTRS,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[kanidmd_testkit::test]
|
#[kanidmd_testkit::test]
|
||||||
async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
|
async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
|
||||||
rsclient
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
rsclient
|
|
||||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// test and other, each can write to themselves, but not each other
|
// test and other, each can write to themselves, but not each other
|
||||||
create_user_with_all_attrs(&rsclient, "test", None).await;
|
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, None).await;
|
||||||
create_user_with_all_attrs(&rsclient, "other", None).await;
|
create_user_with_all_attrs(&rsclient, "other", None).await;
|
||||||
rsclient
|
rsclient
|
||||||
.idm_group_add_members("idm_people_self_write_mail_priv", &["other", "test"])
|
.idm_group_add_members(
|
||||||
|
"idm_people_self_write_mail_priv",
|
||||||
|
&["other", NOT_ADMIN_TEST_USERNAME],
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// a non-person, they can't write to themselves even with the priv
|
// a non-person, they can't write to themselves even with the priv
|
||||||
create_user(&rsclient, "nonperson", "nonperson_group").await;
|
create_user(&rsclient, "nonperson", "nonperson_group").await;
|
||||||
|
|
||||||
login_account(&rsclient, "test").await;
|
login_account(&rsclient, NOT_ADMIN_TEST_USERNAME).await;
|
||||||
// can write to own mail
|
// can write to own mail
|
||||||
test_write_attrs(&rsclient, "test", &["mail"], true).await;
|
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &["mail"], true).await;
|
||||||
// not someone elses
|
// not someone elses
|
||||||
test_write_attrs(&rsclient, "other", &["mail"], false).await;
|
test_write_attrs(&rsclient, "other", &["mail"], false).await;
|
||||||
|
|
||||||
|
|
191
server/testkit/tests/integration.rs
Normal file
191
server/testkit/tests/integration.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
//! Integration tests using browser automation
|
||||||
|
|
||||||
|
/// Tries to handle closing the webdriver session if there's an error
|
||||||
|
#[allow(unused_macros)]
|
||||||
|
macro_rules! handle_error {
|
||||||
|
($client:ident, $e:expr, $msg:expr) => {
|
||||||
|
match $e {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
$client.close().await.unwrap();
|
||||||
|
panic!("{:?}: {:?}", $msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to get the webdriver client, trying the default chromedriver port if the default selenium port doesn't work
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[cfg(feature = "webdriver")]
|
||||||
|
async fn get_webdriver_client() -> fantoccini::Client {
|
||||||
|
use fantoccini::wd::Capabilities;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
// check if the env var "CI" is set
|
||||||
|
let in_ci = match std::env::var("CI") {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(_) => false,
|
||||||
|
};
|
||||||
|
if !in_ci {
|
||||||
|
match fantoccini::ClientBuilder::native()
|
||||||
|
.connect("http://localhost:4444")
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(_) => {
|
||||||
|
// trying the default chromedriver port
|
||||||
|
eprintln!("Couldn't connect on 4444, trying 9515");
|
||||||
|
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).connect("http://localhost:9515")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("In CI setting headless and assuming Chrome");
|
||||||
|
let cap = json!({
|
||||||
|
"goog:chromeOptions" : {
|
||||||
|
"args" : ["--headless", "--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage", "--window-size=1280,1024"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let cap: Capabilities = serde_json::from_value(cap).unwrap();
|
||||||
|
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).capabilities(cap).connect("http://localhost:9515").await.unwrap()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[kanidmd_testkit::test]
|
||||||
|
#[cfg(feature = "webdriver")]
|
||||||
|
async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
|
||||||
|
if !cfg!(feature = "webdriver") {
|
||||||
|
println!("Skipping test as webdriver feature is not enabled!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
use fantoccini::elements::Element;
|
||||||
|
use fantoccini::Locator;
|
||||||
|
use kanidmd_testkit::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
login_put_admin_idm_admins(&rsclient).await;
|
||||||
|
|
||||||
|
create_user_with_all_attrs(
|
||||||
|
&rsclient,
|
||||||
|
NOT_ADMIN_TEST_USERNAME,
|
||||||
|
Some(NOT_ADMIN_TEST_PASSWORD),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let c = get_webdriver_client().await;
|
||||||
|
|
||||||
|
handle_error!(c, c.goto(rsclient.get_url()).await, "Couldn't get URL");
|
||||||
|
|
||||||
|
println!("Waiting for page to load");
|
||||||
|
let mut wait_attempts = 0;
|
||||||
|
while wait_attempts < 10 {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_micros(200)).await;
|
||||||
|
c.wait();
|
||||||
|
|
||||||
|
if c.find(Locator::Id("username")).await.is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wait_attempts += 1;
|
||||||
|
if wait_attempts > 10 {
|
||||||
|
panic!("Couldn't find username field after 10 attempts!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = handle_error!(
|
||||||
|
c,
|
||||||
|
c.find(Locator::Id("username")).await,
|
||||||
|
"Couldn't find input id=username"
|
||||||
|
);
|
||||||
|
handle_error!(c, id.click().await, "Couldn't click the username input?");
|
||||||
|
|
||||||
|
handle_error!(
|
||||||
|
c,
|
||||||
|
id.send_keys(NOT_ADMIN_TEST_USERNAME).await,
|
||||||
|
"Couldn't type the password?"
|
||||||
|
);
|
||||||
|
|
||||||
|
let username_form = handle_error!(
|
||||||
|
c,
|
||||||
|
c.form(Locator::Id("login")).await,
|
||||||
|
"Coudln't find login form"
|
||||||
|
);
|
||||||
|
handle_error!(
|
||||||
|
c,
|
||||||
|
username_form.submit().await,
|
||||||
|
"Couldn't submit username-login form"
|
||||||
|
);
|
||||||
|
c.wait();
|
||||||
|
tokio::time::sleep(Duration::from_millis(300)).await;
|
||||||
|
|
||||||
|
let password_form = handle_error!(
|
||||||
|
c,
|
||||||
|
c.form(Locator::Id("login")).await,
|
||||||
|
"Coudln't find login form"
|
||||||
|
);
|
||||||
|
let id = handle_error!(
|
||||||
|
c,
|
||||||
|
c.find(Locator::Id("password")).await,
|
||||||
|
"Couldn't find input id=password"
|
||||||
|
);
|
||||||
|
handle_error!(c, id.click().await, "Couldn't click the username input?");
|
||||||
|
|
||||||
|
handle_error!(
|
||||||
|
c,
|
||||||
|
id.send_keys(NOT_ADMIN_TEST_PASSWORD).await,
|
||||||
|
"Couldn't type the password?"
|
||||||
|
);
|
||||||
|
handle_error!(
|
||||||
|
c,
|
||||||
|
password_form.submit().await,
|
||||||
|
"Couldn't submit password-login form"
|
||||||
|
);
|
||||||
|
c.wait();
|
||||||
|
|
||||||
|
// try clicking the nav links
|
||||||
|
let mut navlinks: Vec<Element> = vec![];
|
||||||
|
let mut navlinks_attempts = 0;
|
||||||
|
while navlinks.is_empty() {
|
||||||
|
navlinks = handle_error!(
|
||||||
|
c,
|
||||||
|
c.find_all(Locator::Css(".nav-link")).await,
|
||||||
|
"Couldn't find nav-link CSS items"
|
||||||
|
);
|
||||||
|
navlinks_attempts += 1;
|
||||||
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||||
|
if navlinks_attempts > 10 {
|
||||||
|
panic!("Couldn't find navlinks after 2 seconds!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Found navlinks: {:?}", navlinks);
|
||||||
|
|
||||||
|
for link in navlinks {
|
||||||
|
println!("Clicking {:?}", link.text().await);
|
||||||
|
handle_error!(c, link.click().await, &format!("Couldn't click {:?}", link));
|
||||||
|
if let Ok(text) = link.text().await {
|
||||||
|
if text.to_lowercase() == "sign out" {
|
||||||
|
println!("looking for the sign out modal to click the cancel button...");
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
println!("Found the sign out modal, clicking the cancel button");
|
||||||
|
// find the cancel button and click it
|
||||||
|
let buttons = handle_error!(
|
||||||
|
c,
|
||||||
|
c.find_all(Locator::Css(".btn")).await,
|
||||||
|
"Couldn't find CSS 'btn' items"
|
||||||
|
);
|
||||||
|
println!("Found the following buttons: {:?}", buttons);
|
||||||
|
for button in buttons {
|
||||||
|
if let Ok(text) = button.text().await {
|
||||||
|
if text == "Cancel" {
|
||||||
|
println!("Found the sign out cancel button, clicking it");
|
||||||
|
handle_error!(c, button.click().await, "Couldn't click cancel button");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tokio::time::sleep(Duration::from_millis(3000)).await;
|
||||||
|
}
|
|
@ -14,21 +14,11 @@ license = "MPL-2.0"
|
||||||
homepage = "https://github.com/kanidm/kanidm/"
|
homepage = "https://github.com/kanidm/kanidm/"
|
||||||
repository = "https://github.com/kanidm/kanidm/"
|
repository = "https://github.com/kanidm/kanidm/"
|
||||||
|
|
||||||
# version = { workspace = true }
|
|
||||||
# authors = { workspace = true }
|
|
||||||
# rust-version = { workspace = true }
|
|
||||||
# edition = { workspace = true }
|
|
||||||
# license = { workspace = true }
|
|
||||||
# homepage = { workspace = true }
|
|
||||||
# repository = { workspace = true }
|
|
||||||
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
compact_jwt = { workspace = true }
|
compact_jwt = { workspace = true }
|
||||||
# gloo = "^0.8.0"
|
|
||||||
gloo = { workspace = true }
|
gloo = { workspace = true }
|
||||||
js-sys = { workspace = true }
|
js-sys = { workspace = true }
|
||||||
kanidm_proto = { workspace = true, features = ["wasm"] }
|
kanidm_proto = { workspace = true, features = ["wasm"] }
|
||||||
|
@ -45,7 +35,6 @@ yew = { workspace = true, features = ["csr"] }
|
||||||
yew-router = { workspace = true }
|
yew-router = { workspace = true }
|
||||||
time = { workspace = true }
|
time = { workspace = true }
|
||||||
|
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
workspace = true
|
workspace = true
|
||||||
features = [
|
features = [
|
||||||
|
@ -76,4 +65,3 @@ features = [
|
||||||
"Response",
|
"Response",
|
||||||
"Window",
|
"Window",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
# This builds the assets for the Web UI, defaulting to a release build.
|
# This builds the assets for the Web UI, defaulting to a release build.
|
||||||
|
|
||||||
if [ ! -f build_wasm.sh ]; then
|
if [ ! -f build_wasm.sh ]; then
|
||||||
|
@ -22,7 +23,7 @@ if [ -z "$(which wasm-pack)" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$(find ./pkg/ -name 'kanidmd*' | wc -l)" -gt 0 ]; then
|
if [ "$(find ./pkg/ -name 'kanidmd*' | wc -l)" -gt 0 ]; then
|
||||||
echo "Cleaning up"
|
echo "Cleaning up WASM files before build..."
|
||||||
rm pkg/kanidmd*
|
rm pkg/kanidmd*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -35,3 +36,7 @@ touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \
|
||||||
cp ../../README.md ./pkg/
|
cp ../../README.md ./pkg/
|
||||||
cp ../../LICENSE.md ./pkg/
|
cp ../../LICENSE.md ./pkg/
|
||||||
rm ./pkg/.gitignore
|
rm ./pkg/.gitignore
|
||||||
|
|
||||||
|
# updates the brotli-compressed files
|
||||||
|
echo "brotli-compressing the WASM file..."
|
||||||
|
find ./pkg -name '*.wasm' -exec ./find_best_brotli.sh "{}" \; || exit 1
|
||||||
|
|
63
server/web_ui/find_best_brotli.sh
Executable file
63
server/web_ui/find_best_brotli.sh
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# check brotli's installed
|
||||||
|
if ! command -v brotli &> /dev/null
|
||||||
|
then
|
||||||
|
echo "brotli tool could not be found, please make sure you have it in your path!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit if no argument is provided
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "No filename provided"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
filename=$1
|
||||||
|
echo "Compressing $1"
|
||||||
|
|
||||||
|
# Exit if the file doesn't exist
|
||||||
|
if [ ! -f "${filename}" ]; then
|
||||||
|
echo "File ${filename} not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
original_size_kb=$(du -k "${filename}" | cut -f1)
|
||||||
|
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
|
||||||
|
for num in {10..24}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
size=$(brotli --lgwin="${num}" -c "${filename}" | wc -c)
|
||||||
|
echo "${num} ${size}" >> "${tmpfile}"
|
||||||
|
} &
|
||||||
|
done
|
||||||
|
|
||||||
|
# Wait for all background jobs to finish
|
||||||
|
wait
|
||||||
|
|
||||||
|
# Process results
|
||||||
|
{
|
||||||
|
read -r min_num min_size
|
||||||
|
while read -r num size; do
|
||||||
|
if (( size < min_size )); then
|
||||||
|
min_size=$size
|
||||||
|
min_num=$num
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Original size was ${original_size_kb}KB"
|
||||||
|
echo "Smallest compressed size was ${min_size} bytes for NUM=${min_num}"
|
||||||
|
} < "${tmpfile}"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm "${tmpfile}"
|
||||||
|
|
||||||
|
# Use the smallest NUM value found in the test command
|
||||||
|
brotli --force --lgwin="${min_num}" "${filename}" -o "${filename}.br"
|
||||||
|
|
||||||
|
# find the difference in the file sizes
|
||||||
|
compressed_size_kb=$(du -k "${filename}.br" | cut -f1)
|
||||||
|
size_difference=$(bc <<< "${original_size_kb} - ${compressed_size_kb}")
|
||||||
|
echo "The compressed file is ${size_difference}KB smaller than the original file"
|
|
@ -234,7 +234,7 @@ function addBorrowedObject(obj) {
|
||||||
}
|
}
|
||||||
function __wbg_adapter_48(arg0, arg1, arg2) {
|
function __wbg_adapter_48(arg0, arg1, arg2) {
|
||||||
try {
|
try {
|
||||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6181404b47c1b27d(arg0, arg1, addBorrowedObject(arg2));
|
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h91fc06edd1d61942(arg0, arg1, addBorrowedObject(arg2));
|
||||||
} finally {
|
} finally {
|
||||||
heap[stack_pointer++] = undefined;
|
heap[stack_pointer++] = undefined;
|
||||||
}
|
}
|
||||||
|
@ -242,14 +242,14 @@ function __wbg_adapter_48(arg0, arg1, arg2) {
|
||||||
|
|
||||||
function __wbg_adapter_51(arg0, arg1, arg2) {
|
function __wbg_adapter_51(arg0, arg1, arg2) {
|
||||||
try {
|
try {
|
||||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0469109c0dc279df(arg0, arg1, addBorrowedObject(arg2));
|
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h69b5a7e71157cf78(arg0, arg1, addBorrowedObject(arg2));
|
||||||
} finally {
|
} finally {
|
||||||
heap[stack_pointer++] = undefined;
|
heap[stack_pointer++] = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function __wbg_adapter_54(arg0, arg1, arg2) {
|
function __wbg_adapter_54(arg0, arg1, arg2) {
|
||||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h23ae592972fec7fc(arg0, arg1, addHeapObject(arg2));
|
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h2d5c8ecfb4968ae0(arg0, arg1, addHeapObject(arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -463,10 +463,6 @@ function __wbg_get_imports() {
|
||||||
const ret = getObject(arg0) == getObject(arg1);
|
const ret = getObject(arg0) == getObject(arg1);
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
imports.wbg.__wbg_getwithrefkey_15c62c2b8546208d = function(arg0, arg1) {
|
|
||||||
const ret = getObject(arg0)[getObject(arg1)];
|
|
||||||
return addHeapObject(ret);
|
|
||||||
};
|
|
||||||
imports.wbg.__wbg_set_20cbc34131e76824 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbg_set_20cbc34131e76824 = function(arg0, arg1, arg2) {
|
||||||
getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
|
getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
|
||||||
};
|
};
|
||||||
|
@ -1125,16 +1121,16 @@ function __wbg_get_imports() {
|
||||||
const ret = wasm.memory;
|
const ret = wasm.memory;
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper2575 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper2571 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 1196, __wbg_adapter_48);
|
const ret = makeMutClosure(arg0, arg1, 1196, __wbg_adapter_48);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper3416 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper3389 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 1503, __wbg_adapter_51);
|
const ret = makeMutClosure(arg0, arg1, 1499, __wbg_adapter_51);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper4520 = function(arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper4500 = function(arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 1577, __wbg_adapter_54);
|
const ret = makeMutClosure(arg0, arg1, 1574, __wbg_adapter_54);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Binary file not shown.
BIN
server/web_ui/pkg/kanidmd_web_ui_bg.wasm.br
Normal file
BIN
server/web_ui/pkg/kanidmd_web_ui_bg.wasm.br
Normal file
Binary file not shown.
|
@ -244,7 +244,7 @@ impl LoginApp {
|
||||||
<>
|
<>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<label for="username" class="form-label">{ "Username" }</label>
|
<label for="username" class="form-label">{ "Username" }</label>
|
||||||
<form
|
<form id="login"
|
||||||
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
console::debug!("login::view_state -> Init - prevent_default()".to_string());
|
console::debug!("login::view_state -> Init - prevent_default()".to_string());
|
||||||
|
@ -295,7 +295,7 @@ impl LoginApp {
|
||||||
<>
|
<>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p>{ msg }</p>
|
<p>{ msg }</p>
|
||||||
<form
|
<form id="login"
|
||||||
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
console::debug!("login::view_state -> Init - prevent_default()".to_string());
|
console::debug!("login::view_state -> Init - prevent_default()".to_string());
|
||||||
|
@ -359,7 +359,7 @@ impl LoginApp {
|
||||||
<>
|
<>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<label for="password" class="form-label">{ "Password" }</label>
|
<label for="password" class="form-label">{ "Password" }</label>
|
||||||
<form
|
<form id="login"
|
||||||
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
||||||
console::debug!("login::view_state -> Password - prevent_default()".to_string());
|
console::debug!("login::view_state -> Password - prevent_default()".to_string());
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
|
@ -397,7 +397,7 @@ impl LoginApp {
|
||||||
<label for="backup_code" class="form-label">
|
<label for="backup_code" class="form-label">
|
||||||
{"Backup Code"}
|
{"Backup Code"}
|
||||||
</label>
|
</label>
|
||||||
<form
|
<form id="login"
|
||||||
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
||||||
console::debug!("login::view_state -> BackupCode - prevent_default()".to_string());
|
console::debug!("login::view_state -> BackupCode - prevent_default()".to_string());
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
|
@ -429,7 +429,7 @@ impl LoginApp {
|
||||||
<>
|
<>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<label for="totp" class="form-label">{"TOTP"}</label>
|
<label for="totp" class="form-label">{"TOTP"}</label>
|
||||||
<form
|
<form id="login"
|
||||||
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
|
||||||
console::debug!("login::view_state -> Totp - prevent_default()".to_string());
|
console::debug!("login::view_state -> Totp - prevent_default()".to_string());
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
/// Test harnesses for WASM things.
|
//! Test harnesses for WASM things.
|
||||||
///
|
//!
|
||||||
/// Here be crabs with troubling pasts.
|
//! Here be crabs with troubling pasts.
|
||||||
///
|
//!
|
||||||
/// Run this on a mac with Safari using the following command:
|
//! Run this on a mac with Safari using the following command:
|
||||||
///
|
//!
|
||||||
/// ```shell
|
//! ```shell
|
||||||
/// wasm-pack test --safari
|
//! wasm-pack test --chrome --headless
|
||||||
/// ```
|
//!```
|
||||||
|
//!
|
||||||
|
|
||||||
use wasm_bindgen_test::*;
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
wasm_bindgen_test_configure!(run_in_browser);
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
fn pass() {
|
fn if_this_fails_then_oh_no() {
|
||||||
assert_eq!(1, 1);
|
assert_eq!(1, 1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue