mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-24 04:57:00 +01:00
Part one of #461 - this adds the syntax to support email addresses and validation of their content, and a method to serialise to the DB that can be extended with attribute tagging in the future. Part two will address administration of these values.
995 lines
33 KiB
Rust
995 lines
33 KiB
Rust
#![deny(warnings)]
|
|
use std::time::SystemTime;
|
|
|
|
use log::debug;
|
|
|
|
use kanidm::credential::totp::Totp;
|
|
use kanidm_client::KanidmClient;
|
|
use kanidm_proto::v1::{CredentialDetailType, Entry, Filter, Modify, ModifyList};
|
|
|
|
mod common;
|
|
use crate::common::{run_test, ADMIN_TEST_PASSWORD};
|
|
|
|
use webauthn_authenticator_rs::{softtok::U2FSoft, WebauthnAuthenticator};
|
|
|
|
const ADMIN_TEST_PASSWORD_CHANGE: &str = "integration test admin new🎉";
|
|
const UNIX_TEST_PASSWORD: &str = "unix test user password";
|
|
|
|
#[test]
|
|
fn test_server_create() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let e: Entry = serde_json::from_str(
|
|
r#"{
|
|
"attrs": {
|
|
"class": ["account"],
|
|
"name": ["testperson"],
|
|
"displayname": ["testperson"]
|
|
}
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
// Not logged in - should fail!
|
|
let res = rsclient.create(vec![e.clone()]);
|
|
assert!(res.is_err());
|
|
|
|
let a_res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(a_res.is_ok());
|
|
|
|
let res = rsclient.create(vec![e]);
|
|
assert!(res.is_ok());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_modify() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
// Build a self mod.
|
|
|
|
let f = Filter::SelfUuid;
|
|
let m = ModifyList::new_list(vec![
|
|
Modify::Purged("displayname".to_string()),
|
|
Modify::Present("displayname".to_string(), "test".to_string()),
|
|
]);
|
|
|
|
// Not logged in - should fail!
|
|
let res = rsclient.modify(f.clone(), m.clone());
|
|
assert!(res.is_err());
|
|
|
|
let a_res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(a_res.is_ok());
|
|
|
|
let res = rsclient.modify(f, m);
|
|
println!("{:?}", res);
|
|
assert!(res.is_ok());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_whoami_anonymous() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
// First show we are un-authenticated.
|
|
let pre_res = rsclient.whoami();
|
|
// This means it was okay whoami, but no uat attached.
|
|
assert!(pre_res.unwrap().is_none());
|
|
|
|
// Now login as anonymous
|
|
let res = rsclient.auth_anonymous();
|
|
assert!(res.is_ok());
|
|
|
|
// Now do a whoami.
|
|
let (_e, uat) = match rsclient.whoami().unwrap() {
|
|
Some((e, uat)) => (e, uat),
|
|
None => panic!(),
|
|
};
|
|
debug!("{}", uat);
|
|
assert!(uat.name == "anonymous");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_whoami_admin_simple_password() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
// First show we are un-authenticated.
|
|
let pre_res = rsclient.whoami();
|
|
// This means it was okay whoami, but no uat attached.
|
|
assert!(pre_res.unwrap().is_none());
|
|
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Now do a whoami.
|
|
let (_e, uat) = match rsclient.whoami().unwrap() {
|
|
Some((e, uat)) => (e, uat),
|
|
None => panic!(),
|
|
};
|
|
debug!("{}", uat);
|
|
assert!(uat.name == "admin");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_search() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
// First show we are un-authenticated.
|
|
let pre_res = rsclient.whoami();
|
|
// This means it was okay whoami, but no uat attached.
|
|
assert!(pre_res.unwrap().is_none());
|
|
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
let rset = rsclient
|
|
.search(Filter::Eq("name".to_string(), "admin".to_string()))
|
|
.unwrap();
|
|
println!("{:?}", rset);
|
|
let e = rset.first().unwrap();
|
|
// Check it's admin.
|
|
println!("{:?}", e);
|
|
let name = e.attrs.get("name").unwrap();
|
|
assert!(name == &vec!["admin".to_string()]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_admin_change_simple_password() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
// First show we are un-authenticated.
|
|
let pre_res = rsclient.whoami();
|
|
// This means it was okay whoami, but no uat attached.
|
|
assert!(pre_res.unwrap().is_none());
|
|
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Now change the password.
|
|
rsclient
|
|
.idm_account_set_password(ADMIN_TEST_PASSWORD_CHANGE.to_string())
|
|
.unwrap();
|
|
|
|
// Now "reset" the client.
|
|
let _ = rsclient.logout();
|
|
// New password works!
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD_CHANGE);
|
|
assert!(res.is_ok());
|
|
|
|
// On the admin, show our credential state.
|
|
let cred_state = rsclient.idm_account_get_credential_status("admin").unwrap();
|
|
// Check the creds are what we expect.
|
|
if cred_state.creds.len() != 1 {
|
|
assert!(false);
|
|
}
|
|
|
|
if let Some(cred) = cred_state.creds.get(0) {
|
|
assert!(cred.type_ == CredentialDetailType::Password)
|
|
} else {
|
|
assert!(false);
|
|
}
|
|
|
|
// Old password fails, check after to prevent soft-locking.
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_err());
|
|
});
|
|
}
|
|
|
|
// Add a test for resetting another accounts pws via the rest api
|
|
#[test]
|
|
fn test_server_admin_reset_simple_password() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
// Create a diff account
|
|
let e: Entry = serde_json::from_str(
|
|
r#"{
|
|
"attrs": {
|
|
"class": ["account"],
|
|
"name": ["testperson"],
|
|
"displayname": ["testperson"]
|
|
}
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
// Not logged in - should fail!
|
|
let res = rsclient.create(vec![e]);
|
|
assert!(res.is_ok());
|
|
// By default, admin's can't actually administer accounts, so mod them into
|
|
// the account admin group.
|
|
let f = Filter::Eq("name".to_string(), "idm_admins".to_string());
|
|
let m = ModifyList::new_list(vec![Modify::Present(
|
|
"member".to_string(),
|
|
"system_admins".to_string(),
|
|
)]);
|
|
let res = rsclient.modify(f, m);
|
|
assert!(res.is_ok());
|
|
|
|
// Now set it's password - should be rejected based on low quality
|
|
let res = rsclient.idm_account_primary_credential_set_password("testperson", "password");
|
|
assert!(res.is_err());
|
|
// Set the password to ensure it's good
|
|
let res = rsclient.idm_account_primary_credential_set_password(
|
|
"testperson",
|
|
"tai4eCohtae9aegheo3Uw0oobahVighaig6heeli",
|
|
);
|
|
assert!(res.is_ok());
|
|
// Check it stuck.
|
|
let tclient = rsclient.new_session().expect("failed to build new session");
|
|
assert!(tclient
|
|
.auth_simple_password("testperson", "tai4eCohtae9aegheo3Uw0oobahVighaig6heeli")
|
|
.is_ok());
|
|
|
|
// Generate a pw instead
|
|
let res = rsclient.idm_account_primary_credential_set_generated("testperson");
|
|
assert!(res.is_ok());
|
|
let gpw = res.unwrap();
|
|
let tclient = rsclient.new_session().expect("failed to build new session");
|
|
assert!(tclient
|
|
.auth_simple_password("testperson", gpw.as_str())
|
|
.is_ok());
|
|
});
|
|
}
|
|
|
|
// test the rest group endpoint.
|
|
#[test]
|
|
fn test_server_rest_group_read() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// List the groups
|
|
let g_list = rsclient.idm_group_list().unwrap();
|
|
assert!(!g_list.is_empty());
|
|
|
|
let g = rsclient.idm_group_get("idm_admins").unwrap();
|
|
assert!(g.is_some());
|
|
println!("{:?}", g);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_group_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// List the groups
|
|
let g_list = rsclient.idm_group_list().unwrap();
|
|
assert!(!g_list.is_empty());
|
|
|
|
// Create a new group
|
|
rsclient.idm_group_create("demo_group").unwrap();
|
|
|
|
// List again, ensure one more.
|
|
let g_list_2 = rsclient.idm_group_list().unwrap();
|
|
assert!(g_list_2.len() > g_list.len());
|
|
|
|
// Test modifications to the group
|
|
|
|
// Add a member.
|
|
rsclient
|
|
.idm_group_add_members("demo_group", &["admin"])
|
|
.unwrap();
|
|
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
|
assert!(members == Some(vec!["admin@example.com".to_string()]));
|
|
|
|
// Set the list of members
|
|
rsclient
|
|
.idm_group_set_members("demo_group", &["admin", "demo_group"])
|
|
.unwrap();
|
|
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
|
assert!(
|
|
members
|
|
== Some(vec![
|
|
"admin@example.com".to_string(),
|
|
"demo_group@example.com".to_string()
|
|
])
|
|
);
|
|
|
|
// Remove a member from the group
|
|
rsclient
|
|
.idm_group_remove_members("demo_group", &["demo_group"])
|
|
.unwrap();
|
|
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
|
assert!(members == Some(vec!["admin@example.com".to_string()]));
|
|
|
|
// purge members
|
|
rsclient.idm_group_purge_members("demo_group").unwrap();
|
|
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
|
assert!(members == None);
|
|
|
|
// Delete the group
|
|
rsclient.idm_group_delete("demo_group").unwrap();
|
|
let g_list_3 = rsclient.idm_group_list().unwrap();
|
|
assert!(g_list_3.len() == g_list.len());
|
|
|
|
// Check we can get an exact group
|
|
let g = rsclient.idm_group_get("idm_admins").unwrap();
|
|
assert!(g.is_some());
|
|
println!("{:?}", g);
|
|
|
|
// They should have members
|
|
let members = rsclient.idm_group_get_members("idm_admins").unwrap();
|
|
println!("{:?}", members);
|
|
assert!(members == Some(vec!["idm_admin@example.com".to_string()]));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_account_read() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// List the accounts
|
|
let a_list = rsclient.idm_account_list().unwrap();
|
|
assert!(!a_list.is_empty());
|
|
|
|
let a = rsclient.idm_account_get("admin").unwrap();
|
|
assert!(a.is_some());
|
|
println!("{:?}", a);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_schema_read() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// List the schema
|
|
let s_list = rsclient.idm_schema_list().unwrap();
|
|
assert!(!s_list.is_empty());
|
|
|
|
let a_list = rsclient.idm_schema_attributetype_list().unwrap();
|
|
assert!(!a_list.is_empty());
|
|
|
|
let c_list = rsclient.idm_schema_classtype_list().unwrap();
|
|
assert!(!c_list.is_empty());
|
|
|
|
// Get an attr/class
|
|
let a = rsclient.idm_schema_attributetype_get("name").unwrap();
|
|
assert!(a.is_some());
|
|
println!("{:?}", a);
|
|
|
|
let c = rsclient.idm_schema_classtype_get("account").unwrap();
|
|
assert!(c.is_some());
|
|
println!("{:?}", c);
|
|
});
|
|
}
|
|
|
|
// Test resetting a radius cred, and then checking/viewing it.
|
|
#[test]
|
|
fn test_server_radius_credential_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Should have no radius secret
|
|
let n_sec = rsclient.idm_account_radius_credential_get("admin").unwrap();
|
|
assert!(n_sec.is_none());
|
|
|
|
// Set one
|
|
let sec1 = rsclient
|
|
.idm_account_radius_credential_regenerate("admin")
|
|
.unwrap();
|
|
|
|
// Should be able to get it.
|
|
let r_sec = rsclient.idm_account_radius_credential_get("admin").unwrap();
|
|
assert!(sec1 == r_sec.unwrap());
|
|
|
|
// test getting the token - we can do this as self or the radius server
|
|
let r_tok = rsclient.idm_account_radius_token_get("admin").unwrap();
|
|
assert!(sec1 == r_tok.secret);
|
|
assert!(r_tok.name == "admin");
|
|
|
|
// Reset it
|
|
let sec2 = rsclient
|
|
.idm_account_radius_credential_regenerate("admin")
|
|
.unwrap();
|
|
|
|
// Should be different
|
|
println!("s1 {} != s2 {}", sec1, sec2);
|
|
assert!(sec1 != sec2);
|
|
|
|
// Delete it
|
|
let res = rsclient.idm_account_radius_credential_delete("admin");
|
|
assert!(res.is_ok());
|
|
|
|
// No secret
|
|
let n_sec = rsclient.idm_account_radius_credential_get("admin").unwrap();
|
|
assert!(n_sec.is_none());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_account_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
// To enable the admin to actually make some of these changes, we have
|
|
// to make them a people admin. NOT recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_account_write_priv", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("demo_account", "Deeeeemo")
|
|
.unwrap();
|
|
|
|
// View the account
|
|
rsclient.idm_account_get("demo_account").unwrap();
|
|
|
|
// change the name?
|
|
rsclient
|
|
.idm_account_set_displayname("demo_account", "Demo Account")
|
|
.unwrap();
|
|
|
|
// Test adding some mail addrs
|
|
rsclient
|
|
.idm_account_add_attr("demo_account", "mail", &["demo@example.com"])
|
|
.unwrap();
|
|
|
|
let r = rsclient
|
|
.idm_account_get_attr("demo_account", "mail")
|
|
.unwrap();
|
|
|
|
assert!(r == Some(vec!["demo@example.com".to_string()]));
|
|
|
|
// Delete the account
|
|
rsclient.idm_account_delete("demo_account").unwrap();
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_sshkey_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Get the keys, should be empty vec.
|
|
let sk1 = rsclient.idm_account_get_ssh_pubkeys("admin").unwrap();
|
|
assert!(sk1.is_empty());
|
|
|
|
// idm_account_get_ssh_pubkeys
|
|
// idm_account_post_ssh_pubkey
|
|
// idm_account_get_ssh_pubkey
|
|
// idm_account_delete_ssh_pubkey
|
|
|
|
// Post an invalid key (should error)
|
|
let r1 = rsclient.idm_account_post_ssh_pubkey("admin", "inv", "invalid key");
|
|
assert!(r1.is_err());
|
|
|
|
// Post a valid key
|
|
let r2 = rsclient
|
|
.idm_account_post_ssh_pubkey("admin", "k1", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0L1EyR30CwoP william@amethyst");
|
|
println!("{:?}", r2);
|
|
assert!(r2.is_ok());
|
|
|
|
// Get, should have the key
|
|
let sk2 = rsclient.idm_account_get_ssh_pubkeys("admin").unwrap();
|
|
assert!(sk2.len() == 1);
|
|
|
|
// Post a valid key
|
|
let r3 = rsclient
|
|
.idm_account_post_ssh_pubkey("admin", "k2", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx4TpJYQjd0YI5lQIHqblIsCIK5NKVFURYS/eM3o6/Z william@amethyst");
|
|
assert!(r3.is_ok());
|
|
|
|
// Get, should have both keys.
|
|
let sk3 = rsclient.idm_account_get_ssh_pubkeys("admin").unwrap();
|
|
assert!(sk3.len() == 2);
|
|
|
|
// Delete a key (by tag)
|
|
let r4 = rsclient.idm_account_delete_ssh_pubkey("admin", "k1");
|
|
assert!(r4.is_ok());
|
|
|
|
// Get, should have remaining key.
|
|
let sk4 = rsclient.idm_account_get_ssh_pubkeys("admin").unwrap();
|
|
assert!(sk4.len() == 1);
|
|
|
|
// get by tag
|
|
let skn = rsclient.idm_account_get_ssh_pubkey("admin", "k2");
|
|
assert!(skn.is_ok());
|
|
assert!(skn.unwrap() == Some("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx4TpJYQjd0YI5lQIHqblIsCIK5NKVFURYS/eM3o6/Z william@amethyst".to_string()));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_domain_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
let mut dlist = rsclient.idm_domain_list().unwrap();
|
|
assert!(dlist.len() == 1);
|
|
|
|
let dlocal = rsclient.idm_domain_get("domain_local").unwrap();
|
|
// There should be one, and it's the domain_local
|
|
assert!(dlist.pop().unwrap().attrs == dlocal.attrs);
|
|
|
|
// Change the ssid
|
|
rsclient
|
|
.idm_domain_set_ssid("domain_local", "new_ssid")
|
|
.unwrap();
|
|
// check get and get the ssid and domain info
|
|
let nssid = rsclient.idm_domain_get_ssid("domain_local").unwrap();
|
|
assert!(nssid == "new_ssid");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_posix_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("posix_account", "Posix Demo Account")
|
|
.unwrap();
|
|
|
|
// Extend the account with posix attrs.
|
|
rsclient
|
|
.idm_account_unix_extend("posix_account", None, None)
|
|
.unwrap();
|
|
|
|
// Create a group
|
|
|
|
// Extend the group with posix attrs
|
|
rsclient.idm_group_create("posix_group").unwrap();
|
|
rsclient
|
|
.idm_group_add_members("posix_group", &["posix_account"])
|
|
.unwrap();
|
|
rsclient.idm_group_unix_extend("posix_group", None).unwrap();
|
|
|
|
// Open a new connection as anonymous
|
|
let res = rsclient.auth_anonymous();
|
|
assert!(res.is_ok());
|
|
|
|
// Get the account by name
|
|
let r = rsclient
|
|
.idm_account_unix_token_get("posix_account")
|
|
.unwrap();
|
|
// Get the account by gidnumber
|
|
let r1 = rsclient
|
|
.idm_account_unix_token_get(r.gidnumber.to_string().as_str())
|
|
.unwrap();
|
|
// get the account by spn
|
|
let r2 = rsclient.idm_account_unix_token_get(r.spn.as_str()).unwrap();
|
|
// get the account by uuid
|
|
let r3 = rsclient
|
|
.idm_account_unix_token_get(r.uuid.as_str())
|
|
.unwrap();
|
|
|
|
println!("{:?}", r);
|
|
assert!(r.name == "posix_account");
|
|
assert!(r1.name == "posix_account");
|
|
assert!(r2.name == "posix_account");
|
|
assert!(r3.name == "posix_account");
|
|
|
|
// get the group by nam
|
|
let r = rsclient.idm_group_unix_token_get("posix_group").unwrap();
|
|
// Get the group by gidnumber
|
|
let r1 = rsclient
|
|
.idm_group_unix_token_get(r.gidnumber.to_string().as_str())
|
|
.unwrap();
|
|
// get the group spn
|
|
let r2 = rsclient.idm_group_unix_token_get(r.spn.as_str()).unwrap();
|
|
// get the group by uuid
|
|
let r3 = rsclient.idm_group_unix_token_get(r.uuid.as_str()).unwrap();
|
|
|
|
println!("{:?}", r);
|
|
assert!(r.name == "posix_group");
|
|
assert!(r1.name == "posix_group");
|
|
assert!(r2.name == "posix_group");
|
|
assert!(r3.name == "posix_group");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_posix_auth_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
// Get an anon connection
|
|
let anon_rsclient = rsclient.new_session().unwrap();
|
|
assert!(anon_rsclient.auth_anonymous().is_ok());
|
|
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Setup a unix user
|
|
rsclient
|
|
.idm_account_create("posix_account", "Posix Demo Account")
|
|
.unwrap();
|
|
|
|
// Extend the account with posix attrs.
|
|
rsclient
|
|
.idm_account_unix_extend("posix_account", None, None)
|
|
.unwrap();
|
|
|
|
// add their password (unix self)
|
|
rsclient
|
|
.idm_account_unix_cred_put("posix_account", UNIX_TEST_PASSWORD)
|
|
.unwrap();
|
|
|
|
// attempt to verify (good, anon-conn)
|
|
let r1 = anon_rsclient.idm_account_unix_cred_verify("posix_account", UNIX_TEST_PASSWORD);
|
|
match r1 {
|
|
Ok(Some(_tok)) => {}
|
|
_ => assert!(false),
|
|
};
|
|
|
|
// attempt to verify (bad, anon-conn)
|
|
let r2 = anon_rsclient.idm_account_unix_cred_verify("posix_account", "ntaotnhuohtsuoehtsu");
|
|
match r2 {
|
|
Ok(None) => {}
|
|
_ => assert!(false),
|
|
};
|
|
|
|
// lock? (admin-conn)
|
|
// attempt to verify (good pw, should fail, anon-conn)
|
|
// status? (self-conn)
|
|
|
|
// clear password? (unix self)
|
|
rsclient
|
|
.idm_account_unix_cred_delete("posix_account")
|
|
.unwrap();
|
|
|
|
// attempt to verify (good pw, should fail, anon-conn)
|
|
let r3 = anon_rsclient.idm_account_unix_cred_verify("posix_account", UNIX_TEST_PASSWORD);
|
|
match r3 {
|
|
Ok(None) => {}
|
|
_ => assert!(false),
|
|
};
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_recycle_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Setup a unix user
|
|
rsclient
|
|
.idm_account_create("recycle_account", "Recycle Demo Account")
|
|
.unwrap();
|
|
|
|
// delete them
|
|
rsclient.idm_account_delete("recycle_account").unwrap();
|
|
|
|
// not there
|
|
let acc = rsclient.idm_account_get("recycle_account").unwrap();
|
|
assert!(acc.is_none());
|
|
|
|
// list the recycle bin
|
|
let r_list = rsclient.recycle_bin_list().unwrap();
|
|
|
|
assert!(r_list.len() == 1);
|
|
// get the user in recycle bin
|
|
let r_user = rsclient.recycle_bin_get("recycle_account").unwrap();
|
|
assert!(r_user.is_some());
|
|
|
|
// revive
|
|
rsclient.recycle_bin_revive("recycle_account").unwrap();
|
|
|
|
// they are there!
|
|
let acc = rsclient.idm_account_get("recycle_account").unwrap();
|
|
assert!(acc.is_some());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_account_import_password() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
// To enable the admin to actually make some of these changes, we have
|
|
// to make them a password import admin. NOT recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_people_account_password_import_priv", &["admin"])
|
|
.unwrap();
|
|
rsclient
|
|
.idm_group_add_members("idm_people_extend_priv", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("demo_account", "Deeeeemo")
|
|
.unwrap();
|
|
|
|
// Make them a person, so we can import the password
|
|
rsclient.idm_account_person_extend("demo_account").unwrap();
|
|
|
|
// Attempt to import a bad password
|
|
let r = rsclient.idm_account_primary_credential_import_password("demo_account", "password");
|
|
assert!(r.is_err());
|
|
|
|
// Import a good password
|
|
// eicieY7ahchaoCh0eeTa
|
|
// pbkdf2_sha256$36000$xIEozuZVAoYm$uW1b35DUKyhvQAf1mBqMvoBDcqSD06juzyO/nmyV0+w=
|
|
rsclient
|
|
.idm_account_primary_credential_import_password(
|
|
"demo_account",
|
|
"pbkdf2_sha256$36000$xIEozuZVAoYm$uW1b35DUKyhvQAf1mBqMvoBDcqSD06juzyO/nmyV0+w=",
|
|
)
|
|
.unwrap();
|
|
|
|
// Now show we can auth with it
|
|
// "reset" the client.
|
|
let _ = rsclient.logout();
|
|
let res = rsclient.auth_simple_password("demo_account", "eicieY7ahchaoCh0eeTa");
|
|
assert!(res.is_ok());
|
|
|
|
// And that the account can self read the cred status.
|
|
let cred_state = rsclient
|
|
.idm_account_get_credential_status("demo_account")
|
|
.unwrap();
|
|
|
|
if let Some(cred) = cred_state.creds.get(0) {
|
|
assert!(cred.type_ == CredentialDetailType::Password)
|
|
} else {
|
|
assert!(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_totp_auth_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("demo_account", "Deeeeemo")
|
|
.unwrap();
|
|
|
|
// Enroll a totp to the account
|
|
assert!(rsclient
|
|
.idm_account_primary_credential_set_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
|
.is_ok());
|
|
let (sessionid, tok) = rsclient
|
|
.idm_account_primary_credential_generate_totp("demo_account")
|
|
.unwrap();
|
|
|
|
let r_tok: Totp = tok.into();
|
|
let totp = r_tok
|
|
.do_totp_duration_from_epoch(
|
|
&SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.unwrap(),
|
|
)
|
|
.expect("Failed to do totp?");
|
|
|
|
rsclient
|
|
.idm_account_primary_credential_verify_totp("demo_account", totp, sessionid)
|
|
.unwrap(); // the result
|
|
|
|
// Check a good auth
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
let totp = r_tok
|
|
.do_totp_duration_from_epoch(
|
|
&SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.unwrap(),
|
|
)
|
|
.expect("Failed to do totp?");
|
|
// TODO: It's extremely rare, but it's happened ONCE where, the time window
|
|
// elapsed DURING this test, so there is a minor possibility of this actually
|
|
// having a false negative. Is it possible to prevent this?
|
|
assert!(rsclient_good
|
|
.auth_password_totp("demo_account", "sohdi3iuHo6mai7noh0a", totp)
|
|
.is_ok());
|
|
|
|
// Check a bad auth - needs to be second as we are going to trigger the slock.
|
|
// Get a new connection
|
|
let rsclient_bad = rsclient.new_session().unwrap();
|
|
assert!(rsclient_bad
|
|
.auth_password_totp("demo_account", "sohdi3iuHo6mai7noh0a", 0)
|
|
.is_err());
|
|
// Delay by one second to allow the account to recover from the softlock.
|
|
std::thread::sleep(std::time::Duration::from_millis(1100));
|
|
|
|
// Remove TOTP on the account.
|
|
rsclient
|
|
.idm_account_primary_credential_remove_totp("demo_account")
|
|
.unwrap();
|
|
// Check password auth.
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
assert!(rsclient_good
|
|
.auth_simple_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
|
.is_ok());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_webauthn_auth_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("demo_account", "Deeeeemo")
|
|
.unwrap();
|
|
|
|
// Enroll a soft token to the account webauthn.
|
|
let mut wa_softtok = WebauthnAuthenticator::new(U2FSoft::new());
|
|
|
|
// Do the challenge
|
|
let (sessionid, regchal) = rsclient
|
|
.idm_account_primary_credential_register_webauthn("demo_account", "softtok")
|
|
.unwrap();
|
|
|
|
let rego = wa_softtok
|
|
.do_registration("https://idm.example.com", regchal)
|
|
.expect("Failed to register to softtoken");
|
|
|
|
// Enroll the cred after signing.
|
|
rsclient
|
|
.idm_account_primary_credential_complete_webuthn_registration(
|
|
"demo_account",
|
|
rego,
|
|
sessionid,
|
|
)
|
|
.unwrap();
|
|
|
|
// ====== Reg a second token.
|
|
let mut wa_softtok_2 = WebauthnAuthenticator::new(U2FSoft::new());
|
|
|
|
// Do the challenge
|
|
let (sessionid, regchal) = rsclient
|
|
.idm_account_primary_credential_register_webauthn("demo_account", "softtok_2")
|
|
.unwrap();
|
|
|
|
let rego = wa_softtok_2
|
|
.do_registration("https://idm.example.com", regchal)
|
|
.expect("Failed to register to softtoken");
|
|
|
|
// Enroll the cred after signing.
|
|
rsclient
|
|
.idm_account_primary_credential_complete_webuthn_registration(
|
|
"demo_account",
|
|
rego,
|
|
sessionid,
|
|
)
|
|
.unwrap();
|
|
|
|
// Now do an auth
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
|
|
let pkr = rsclient_good.auth_webauthn_begin("demo_account").unwrap();
|
|
|
|
// Get the auth chal.
|
|
let auth = wa_softtok_2
|
|
.do_authentication("https://idm.example.com", pkr)
|
|
.expect("Failed to auth to softtoken");
|
|
|
|
// Submit the webauthn auth.
|
|
rsclient_good
|
|
.auth_webauthn_complete(auth)
|
|
.expect("Failed to authenticate");
|
|
|
|
// ======== remove the second softtok.
|
|
|
|
rsclient
|
|
.idm_account_primary_credential_remove_webauthn("demo_account", "softtok_2")
|
|
.expect("failed to remove softtoken");
|
|
|
|
// All good, check first tok auth.
|
|
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
|
|
let pkr = rsclient_good.auth_webauthn_begin("demo_account").unwrap();
|
|
|
|
// Get the auth chal.
|
|
let auth = wa_softtok
|
|
.do_authentication("https://idm.example.com", pkr)
|
|
.expect("Failed to auth to softtoken");
|
|
|
|
// Submit the webauthn auth.
|
|
rsclient_good
|
|
.auth_webauthn_complete(auth)
|
|
.expect("Failed to authenticate");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_rest_webauthn_mfa_auth_lifecycle() {
|
|
run_test(|rsclient: KanidmClient| {
|
|
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
|
assert!(res.is_ok());
|
|
|
|
// Not recommended in production!
|
|
rsclient
|
|
.idm_group_add_members("idm_admins", &["admin"])
|
|
.unwrap();
|
|
|
|
// Create a new account
|
|
rsclient
|
|
.idm_account_create("demo_account", "Deeeeemo")
|
|
.unwrap();
|
|
|
|
// Enroll a soft token to the account webauthn.
|
|
let mut wa_softtok = WebauthnAuthenticator::new(U2FSoft::new());
|
|
|
|
// Do the challenge
|
|
let (sessionid, regchal) = rsclient
|
|
.idm_account_primary_credential_register_webauthn("demo_account", "softtok")
|
|
.unwrap();
|
|
|
|
let rego = wa_softtok
|
|
.do_registration("https://idm.example.com", regchal)
|
|
.expect("Failed to register to softtoken");
|
|
|
|
// Enroll the cred after signing.
|
|
rsclient
|
|
.idm_account_primary_credential_complete_webuthn_registration(
|
|
"demo_account",
|
|
rego,
|
|
sessionid,
|
|
)
|
|
.unwrap();
|
|
|
|
// Now do an auth
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
|
|
let pkr = rsclient_good.auth_webauthn_begin("demo_account").unwrap();
|
|
|
|
// Get the auth chal.
|
|
let auth = wa_softtok
|
|
.do_authentication("https://idm.example.com", pkr)
|
|
.expect("Failed to auth to softtoken");
|
|
|
|
// Submit the webauthn auth.
|
|
rsclient_good
|
|
.auth_webauthn_complete(auth)
|
|
.expect("Failed to authenticate");
|
|
|
|
// Set a password to cause the state to change to PasswordMfa
|
|
assert!(rsclient
|
|
.idm_account_primary_credential_set_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
|
.is_ok());
|
|
|
|
// Now remove Webauthn ...
|
|
rsclient
|
|
.idm_account_primary_credential_remove_webauthn("demo_account", "softtok")
|
|
.expect("failed to remove softtoken");
|
|
|
|
// Check pw only
|
|
let rsclient_good = rsclient.new_session().unwrap();
|
|
assert!(rsclient_good
|
|
.auth_simple_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
|
.is_ok());
|
|
});
|
|
}
|
|
|
|
// Test setting account expiry
|
|
|
|
// Test the self version of the radius path.
|
|
|
|
// Test hitting all auth-required endpoints and assert they give unauthorized.
|