kanidm/server/testkit/src/lib.rs

396 lines
13 KiB
Rust
Raw Normal View History

2022-10-24 01:50:31 +02:00
#![deny(warnings)]
#![warn(unused_extern_crates)]
#![deny(clippy::todo)]
#![deny(clippy::unimplemented)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![deny(clippy::panic)]
#![deny(clippy::unreachable)]
#![deny(clippy::await_holding_lock)]
#![deny(clippy::needless_pass_by_value)]
#![deny(clippy::trivially_copy_pass_by_ref)]
use std::net::TcpStream;
use std::sync::atomic::{AtomicU16, Ordering};
2022-04-29 05:23:46 +02:00
use kanidm_client::{KanidmClient, KanidmClientBuilder};
use kanidm_proto::v1::{Filter, Modify, ModifyList};
use kanidmd_core::config::{Configuration, IntegrationTestConfig};
use kanidmd_core::{create_server_core, CoreHandle};
use kanidmd_lib::prelude::{
Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1,
IDM_PEOPLE_EXTEND_PRIV_V1,
};
use tokio::task;
2021-04-25 03:35:56 +02:00
pub const ADMIN_TEST_USER: &str = "admin";
pub const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
pub const NOT_ADMIN_TEST_USERNAME: &str = "krab_test_user";
pub const NOT_ADMIN_TEST_PASSWORD: &str = "eicieY7ahchaoCh0eeTa";
pub static PORT_ALLOC: AtomicU16 = AtomicU16::new(18080);
2022-10-24 01:50:31 +02:00
pub use testkit_macros::test;
2022-05-26 06:58:53 +02:00
pub fn is_free_port(port: u16) -> bool {
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
TcpStream::connect(("0.0.0.0", port)).is_err()
}
// Test external behaviours of the service.
2022-05-26 06:58:53 +02:00
// allowed because the use of this function is behind a test gate
#[allow(dead_code)]
pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreHandle) {
2023-01-28 04:52:44 +01:00
sketching::test_init();
2020-12-08 02:06:13 +01:00
let mut counter = 0;
let port = loop {
let possible_port = PORT_ALLOC.fetch_add(1, Ordering::SeqCst);
if is_free_port(possible_port) {
break possible_port;
}
2020-12-08 02:06:13 +01:00
counter += 1;
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
#[allow(clippy::panic)]
2020-12-08 02:06:13 +01:00
if counter >= 5 {
tracing::error!("Unable to allocate port!");
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
panic!();
2020-12-08 02:06:13 +01:00
}
};
let int_config = Box::new(IntegrationTestConfig {
2021-04-25 03:35:56 +02:00
admin_user: ADMIN_TEST_USER.to_string(),
admin_password: ADMIN_TEST_PASSWORD.to_string(),
});
let addr = format!("http://localhost:{}", port);
// Setup the address and origin..
config.address = format!("127.0.0.1:{}", port);
config.integration_test_config = Some(int_config);
config.domain = "localhost".to_string();
config.origin = addr.clone();
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
let core_handle = match create_server_core(config, false).await {
Ok(val) => val,
#[allow(clippy::panic)]
Err(_) => panic!("failed to start server core"),
};
// We have to yield now to guarantee that the elements are setup.
task::yield_now().await;
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
#[allow(clippy::panic)]
let rsclient = match KanidmClientBuilder::new()
2022-10-24 01:50:31 +02:00
.address(addr.clone())
2021-05-19 23:58:11 +02:00
.no_proxy()
2022-04-29 05:23:46 +02:00
.build()
Converting from tide to axum (#1797) * Starting to chase down testing * commenting out unused/inactive endpoints, adding more tests * clippyism * making clippy happy v2 * testing when things are not right * moar checkpoint * splitting up testkit things a bit * moving https -> tide * mad lad be crabbin * spawning like a frog * something something different spawning * woot it works ish * more server things * adding version header to requests * adding kopid_middleware * well that was supposed to be an hour... four later * more nonsense * carrying on with the conversion * first pass through the conversion is DONE! * less pub more better * session storage works better, fixed some paths * axum-csp version thing * try a typedheader * better openssl config things * updating lockfile * http2 * actually sending JSON when we say we will! * just about to do something dumb * flargl * more yak shaving * So many clippy-isms, fixing up a query handler bleep bloop * So many clippy-isms, fixing up a query handler bleep bloop * fmt * all tests pass including basic web logins and nav * so much clippyism * stripping out old comments * fmt * commenty things * stripping out tide * updates * de-tiding things * fmt * adding optional header matching ,thanks @cuberoot74088 * oauth2 stuff to match #1807 but in axum * CLIPPY IS FINALLY SATED * moving scim from /v1/scim to /scim * one day clippy will make sense * cleanups * removing sketching middleware * cleanup, strip a broken test endpoint (routemap), more clippy * docs fmt * pulling axum-csp from the wrong cargo.toml * docs fmt * fmt fixes
2023-07-05 14:26:39 +02:00
{
Ok(val) => val,
Err(_) => panic!("failed to build client"),
};
2022-10-24 01:50:31 +02:00
tracing::info!("Testkit server setup complete - {}", addr);
(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::panic)]
if rsclient
.idm_group_get(group_name)
.await
.unwrap_or_else(|_| panic!("Failed to get group {}", group_name))
.is_none()
{
#[allow(clippy::panic)]
rsclient
.idm_group_create(group_name)
.await
.unwrap_or_else(|_| panic!("Failed to create group {}", group_name));
}
#[allow(clippy::panic)]
rsclient
.idm_group_add_members(group_name, &[id])
.await
.unwrap_or_else(|_| panic!("Failed to add user {} to group {}", id, group_name));
}
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 [Attribute::SshPublicKey, Attribute::Mail].into_iter() {
println!("Checking writable for {}", attr);
#[allow(clippy::expect_used)]
let res = is_attr_writable(rsclient, id, attr)
.await
.expect("Failed to get writable status for attribute");
assert!(res);
}
if let Some(legalname) = legalname {
#[allow(clippy::expect_used)]
let res = is_attr_writable(rsclient, legalname, Attribute::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: Attribute) -> Option<bool> {
println!("writing to attribute: {}", attr);
match attr {
Attribute::RadiusSecret => Some(
rsclient
.idm_account_radius_credential_regenerate(id)
.await
.is_ok(),
),
Attribute::PrimaryCredential => Some(
rsclient
.idm_person_account_primary_credential_set_password(id, "dsadjasiodqwjk12asdl")
.await
.is_ok(),
),
Attribute::SshPublicKey => Some(
rsclient
.idm_person_account_post_ssh_pubkey(
id,
"k1",
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0\
L1EyR30CwoP william@amethyst",
)
.await
.is_ok(),
),
Attribute::UnixPassword => Some(
rsclient
.idm_person_account_unix_cred_put(id, "dsadjasiodqwjk12asdl")
.await
.is_ok(),
),
Attribute::LegalName => Some(
rsclient
.idm_person_account_set_attr(
id,
Attribute::LegalName.as_ref(),
&["test legal name"],
)
.await
.is_ok(),
),
Attribute::Mail => Some(
rsclient
.idm_person_account_set_attr(
id,
Attribute::Mail.as_ref(),
&[&format!("{}@example.com", id)],
)
.await
.is_ok(),
),
entry => {
let new_value = match entry {
Attribute::AcpReceiverGroup => "00000000-0000-0000-0000-000000000011".to_string(),
Attribute::AcpTargetScope => "{\"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(Attribute::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_V1.name,
&[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_V1.name, &[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: &[Attribute],
is_readable: bool,
) {
println!("Test read to {}, is readable: {}", id, is_readable);
#[allow(clippy::expect_used)]
let rset = rsclient
.search(Filter::Eq(Attribute::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 {
Attribute::RadiusSecret => rsclient
.idm_account_radius_credential_get(id)
.await
.unwrap()
.is_some(),
_ => e.attrs.get(attr.as_ref()).is_some(),
};
dbg!(is_ok, is_readable);
assert!(is_ok == is_readable)
}
}
pub async fn test_write_attrs(
rsclient: &KanidmClient,
id: &str,
attrs: &[Attribute],
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 [Attribute::Description, Attribute::Name].into_iter() {
#[allow(clippy::unwrap_used)]
let is_writable = is_attr_writable(rsclient, group, attr).await.unwrap();
dbg!(group, attr, is_writable, can_be_modified);
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(BUILTIN_GROUP_IDM_ADMINS_V1.name, &[ADMIN_TEST_USER])
.await
.expect("Failed to add admin user to idm_admins")
}