headless webdriver testing, starting on brotli feature (#1844)

* headless chromedriver testing
* updating build scripts
This commit is contained in:
James Hodgkinson 2023-07-10 16:49:09 +10:00 committed by GitHub
parent 43d5577895
commit 749522418c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 802 additions and 394 deletions

View file

@ -7,6 +7,7 @@ name: GitHub Pages
- "master"
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:
id-token: write
@ -36,7 +37,11 @@ jobs:
fetch-depth: 0
- name: Latest branch
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
- name: Move redirector page
run: |
@ -53,6 +58,7 @@ jobs:
fanout:
uses: './.github/workflows/kanidm_individual_book.yml'
needs: pre_deploy
# yamllint disable-line rule:line-length
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
strategy:
@ -78,6 +84,7 @@ jobs:
- fanout
- docs_master
runs-on: ubuntu-latest
# yamllint disable-line rule:line-length
if: ${{ github.action_ref == 'refs/heads/master' && github.repository == 'kanidm/kanidm' && github.event == 'merge' }}
steps:
- name: Setup Pages
@ -110,7 +117,10 @@ jobs:
find $(pwd) -name '*.tar.gz' -ls -exec tar zxvf "{}" \;
echo "Carrying on..."
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/
echo "Cleaning up docs archives"
rm docs/*.tar.gz

View file

@ -41,9 +41,15 @@ jobs:
# Optional: do not specify to match Chrome's version
# chromedriver-version: '88.0.4324.96'
# docs here:
# https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html
- run: make webui
# - run: make webui
- name: "Run wasm-pack test"
# https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/browsers.html
run: make webui/test
continue-on-error: true
- name: "Run webdriver tests"
run: |
chromedriver &
cargo test -p kanidmd_testkit --features webdriver
env:
DISPLAY: ":99"

7
.yamllint Normal file
View file

@ -0,0 +1,7 @@
extends: default
rules:
line-length:
max: 120
level: warning

49
Cargo.lock generated
View file

@ -1510,6 +1510,28 @@ dependencies = [
"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]]
name = "fastrand"
version = "1.9.0"
@ -2704,7 +2726,9 @@ name = "kanidmd_testkit"
version = "1.1.0-beta.13-dev"
dependencies = [
"compact_jwt",
"fantoccini",
"futures",
"hyper-tls",
"kanidm_client",
"kanidm_proto",
"kanidmd_core",
@ -5273,6 +5297,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.10"
@ -5577,6 +5607,25 @@ dependencies = [
"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]]
name = "which"
version = "4.4.0"

View file

@ -241,6 +241,7 @@ pub async fn oauth2_id_delete(
// valid Kanidm instance in the topology can handle these request.
//
#[instrument(level = "debug", skip(state, kopid))]
pub async fn oauth2_authorise_post(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
@ -256,6 +257,7 @@ pub async fn oauth2_authorise_post(
res
}
#[instrument(level = "debug", skip(state, kopid))]
pub async fn oauth2_authorise_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,

View file

@ -1408,9 +1408,10 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
};
if consent_previously_granted {
let pretty_scopes: Vec<String> = granted_scopes.iter().map(|s| s.to_owned()).collect();
admin_info!(
"User has previously consented, permitting. {:?}",
granted_scopes
"User has previously consented, permitting with scopes: {}",
pretty_scopes.join(",")
);
// Setup for the permit success

View file

@ -15,11 +15,21 @@ repository = { workspace = true }
name = "kanidmd_testkit"
path = "src/lib.rs"
[features]
default = []
# Enables webdriver tests, you need to be running a webdriver server
webdriver = ["fantoccini"]
[dependencies]
kanidm_client = { workspace = true }
kanidm_proto = { workspace = true }
kanidmd_core = { 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"] }

View file

@ -14,6 +14,7 @@ use std::net::TcpStream;
use std::sync::atomic::{AtomicU16, Ordering};
use kanidm_client::{KanidmClient, KanidmClientBuilder};
use kanidm_proto::v1::{Filter, Modify, ModifyList};
use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole};
use kanidmd_core::{create_server_core, CoreHandle};
use tokio::task;
@ -92,3 +93,289 @@ pub async fn setup_async_test() -> (KanidmClient, CoreHandle) {
(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")
}

View file

@ -2,9 +2,8 @@
use std::collections::HashSet;
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] = [
"name",
@ -55,240 +54,13 @@ static DEFAULT_HP_GROUP_NAMES: [&str; 24] = [
static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
["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
// - Read to all self attributes (within security constraints).
// - Write to a limited set of self attributes, such as:
// name, displayname, legalname, ssh-keys, credentials etc.
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).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;
@ -321,21 +93,20 @@ async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
// ability to lock and unlock accounts, excluding high access members.
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).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;
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] = [
"name",
"displayname",
@ -343,11 +114,29 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
"ssh_publickey",
"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"];
test_read_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false).await;
test_write_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false).await;
test_read_attrs(
&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
}
@ -356,18 +145,11 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
// write group but not high access
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
// 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;
@ -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_add_members("test_group", &["test"])
.idm_group_add_members("test_group", &[NOT_ADMIN_TEST_USERNAME])
.await
.unwrap();
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.
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_admins_access_control_entries(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
static ACP_COMMON_ATTRS: [&str; 4] = [
"name",
@ -459,14 +234,7 @@ async fn test_default_entries_rbac_admins_access_control_entries(rsclient: Kanid
// TODO #252: write schema entries
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
let default_classnames: HashSet<String> = [
"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).
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_admins_group_entries(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
create_user(&rsclient, "test", "test_group").await;
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "test_group").await;
let default_group_names =
[&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.
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_admins_ha_accounts(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
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
#[kanidmd_testkit::test]
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
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.idm_person_account_delete(NOT_ADMIN_TEST_USERNAME)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.recycle_bin_revive(NOT_ADMIN_TEST_USERNAME)
.await
.unwrap();
create_user(&rsclient, "test", "test_group").await;
rsclient.idm_person_account_delete("test").await.unwrap();
rsclient.recycle_bin_revive("test").await.unwrap();
let acc = rsclient.idm_person_account_get("test").await.unwrap();
let acc = rsclient
.idm_person_account_get(NOT_ADMIN_TEST_USERNAME)
.await
.unwrap();
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
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).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 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;
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, false).await;
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
test_read_attrs(
&rsclient,
NOT_ADMIN_TEST_USERNAME,
&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();
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;
login_account(&rsclient, "write_people_manager").await;
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true).await;
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false).await;
test_read_attrs(
&rsclient,
NOT_ADMIN_TEST_USERNAME,
&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
// read memberof, unix attrs, name, displayname, class
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).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;
rsclient
.idm_group_add_members("test_group", &["anonymous"])
.await
@ -694,9 +466,21 @@ async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
let _ = rsclient.logout();
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_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;
}
@ -705,50 +489,57 @@ async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
// Read other needed attributes to fulfil radius functions.
#[kanidmd_testkit::test]
async fn test_default_entries_rbac_radius_servers(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).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;
static RADIUS_NECESSARY_ATTRS: [&str; 4] = ["name", "spn", "uuid", "radius_secret"];
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true).await;
test_read_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, true).await;
test_write_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, false).await;
test_read_attrs(
&rsclient,
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]
async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
rsclient
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
.await
.unwrap();
rsclient
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
.await
.unwrap();
login_put_admin_idm_admins(&rsclient).await;
// 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;
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
.unwrap();
// a non-person, they can't write to themselves even with the priv
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
test_write_attrs(&rsclient, "test", &["mail"], true).await;
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &["mail"], true).await;
// not someone elses
test_write_attrs(&rsclient, "other", &["mail"], false).await;

View 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;
}

View file

@ -14,21 +14,11 @@ license = "MPL-2.0"
homepage = "https://github.com/kanidm/kanidm/"
repository = "https://github.com/kanidm/kanidm/"
# version = { workspace = true }
# authors = { workspace = true }
# rust-version = { workspace = true }
# edition = { workspace = true }
# license = { workspace = true }
# homepage = { workspace = true }
# repository = { workspace = true }
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
compact_jwt = { workspace = true }
# gloo = "^0.8.0"
gloo = { workspace = true }
js-sys = { workspace = true }
kanidm_proto = { workspace = true, features = ["wasm"] }
@ -45,7 +35,6 @@ yew = { workspace = true, features = ["csr"] }
yew-router = { workspace = true }
time = { workspace = true }
[dependencies.web-sys]
workspace = true
features = [
@ -76,4 +65,3 @@ features = [
"Response",
"Window",
]

View file

@ -1,5 +1,6 @@
#!/bin/sh
set -e
# This builds the assets for the Web UI, defaulting to a release build.
if [ ! -f build_wasm.sh ]; then
@ -22,7 +23,7 @@ if [ -z "$(which wasm-pack)" ]; then
fi
if [ "$(find ./pkg/ -name 'kanidmd*' | wc -l)" -gt 0 ]; then
echo "Cleaning up"
echo "Cleaning up WASM files before build..."
rm pkg/kanidmd*
fi
@ -35,3 +36,7 @@ touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \
cp ../../README.md ./pkg/
cp ../../LICENSE.md ./pkg/
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

View 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"

View file

@ -234,7 +234,7 @@ 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__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 {
heap[stack_pointer++] = undefined;
}
@ -242,14 +242,14 @@ function __wbg_adapter_48(arg0, arg1, arg2) {
function __wbg_adapter_51(arg0, arg1, arg2) {
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 {
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__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);
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) {
getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
};
@ -1125,16 +1121,16 @@ function __wbg_get_imports() {
const ret = wasm.memory;
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);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper3416 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1503, __wbg_adapter_51);
imports.wbg.__wbindgen_closure_wrapper3389 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1499, __wbg_adapter_51);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper4520 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1577, __wbg_adapter_54);
imports.wbg.__wbindgen_closure_wrapper4500 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1574, __wbg_adapter_54);
return addHeapObject(ret);
};

Binary file not shown.

View file

@ -244,7 +244,7 @@ impl LoginApp {
<>
<div class="container">
<label for="username" class="form-label">{ "Username" }</label>
<form
<form id="login"
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
#[cfg(debug_assertions)]
console::debug!("login::view_state -> Init - prevent_default()".to_string());
@ -295,7 +295,7 @@ impl LoginApp {
<>
<div class="container">
<p>{ msg }</p>
<form
<form id="login"
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
#[cfg(debug_assertions)]
console::debug!("login::view_state -> Init - prevent_default()".to_string());
@ -359,7 +359,7 @@ impl LoginApp {
<>
<div class="container">
<label for="password" class="form-label">{ "Password" }</label>
<form
<form id="login"
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
console::debug!("login::view_state -> Password - prevent_default()".to_string());
e.prevent_default();
@ -397,7 +397,7 @@ impl LoginApp {
<label for="backup_code" class="form-label">
{"Backup Code"}
</label>
<form
<form id="login"
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
console::debug!("login::view_state -> BackupCode - prevent_default()".to_string());
e.prevent_default();
@ -429,7 +429,7 @@ impl LoginApp {
<>
<div class="container">
<label for="totp" class="form-label">{"TOTP"}</label>
<form
<form id="login"
onsubmit={ ctx.link().callback(|e: SubmitEvent| {
console::debug!("login::view_state -> Totp - prevent_default()".to_string());
e.prevent_default();

View file

@ -1,17 +1,19 @@
/// Test harnesses for WASM things.
///
/// Here be crabs with troubling pasts.
///
/// Run this on a mac with Safari using the following command:
///
/// ```shell
/// wasm-pack test --safari
/// ```
//! Test harnesses for WASM things.
//!
//! Here be crabs with troubling pasts.
//!
//! Run this on a mac with Safari using the following command:
//!
//! ```shell
//! wasm-pack test --chrome --headless
//!```
//!
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
fn if_this_fails_then_oh_no() {
assert_eq!(1, 1);
}