kanidm/kanidmd/score/tests/proto_v1_test.rs

1204 lines
40 KiB
Rust
Raw Normal View History

2019-07-29 09:09:09 +02:00
#![deny(warnings)]
use std::time::SystemTime;
2021-12-17 04:54:13 +01:00
use tracing::debug;
2021-04-25 03:35:56 +02:00
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";
2018-09-29 09:54:16 +02:00
#[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());
2018-11-26 07:13:22 +01:00
let a_res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
assert!(a_res.is_ok());
2018-11-26 07:13:22 +01:00
let res = rsclient.create(vec![e]);
assert!(res.is_ok());
2018-11-26 07:13:22 +01:00
});
}
#[test]
fn test_server_modify() {
run_test(|rsclient: KanidmClient| {
// Build a self mod.
2021-04-25 03:35:56 +02:00
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.
2019-09-11 04:21:15 +02:00
let (_e, uat) = match rsclient.whoami().unwrap() {
Some((e, uat)) => (e, uat),
None => panic!(),
};
debug!("{}", uat);
assert!(uat.spn == "anonymous@idm.example.com");
2021-07-25 02:51:37 +02:00
// Do a check of the auth/valid endpoint, tells us if our token
// is okay.
let res = rsclient.auth_valid();
assert!(res.is_ok());
});
}
#[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.
2019-09-11 04:21:15 +02:00
let (_e, uat) = match rsclient.whoami().unwrap() {
Some((e, uat)) => (e, uat),
None => panic!(),
};
debug!("{}", uat);
assert!(uat.spn == "admin@idm.example.com");
});
}
#[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());
2018-11-07 08:27:11 +01:00
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
assert!(res.is_ok());
2018-11-07 08:27:11 +01:00
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()]);
2018-09-29 09:54:16 +02:00
});
}
#[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@idm.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@idm.example.com".to_string(),
"demo_group@idm.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@idm.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@idm.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());
// self management of credentials is only for persons.
rsclient
.idm_account_person_extend("admin", None, None)
.unwrap();
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());
rsclient
.idm_account_create("demo_account", "Deeeeemo")
.unwrap();
// Should have no radius secret
let n_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.unwrap();
assert!(n_sec.is_none());
// Set one
let sec1 = rsclient
.idm_account_radius_credential_regenerate("demo_account")
.unwrap();
// Should be able to get it.
let r_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.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("demo_account")
.unwrap();
assert!(sec1 == r_tok.secret);
assert!(r_tok.name == "demo_account");
// Reset it
let sec2 = rsclient
.idm_account_radius_credential_regenerate("demo_account")
.unwrap();
// Should be different
println!("s1 {} != s2 {}", sec1, sec2);
assert!(sec1 != sec2);
// Delete it
let res = rsclient.idm_account_radius_credential_delete("demo_account");
assert!(res.is_ok());
// No secret
let n_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.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@idm.example.com"])
.unwrap();
let r = rsclient
.idm_account_get_attr("demo_account", "mail")
.unwrap();
assert!(r == Some(vec!["demo@idm.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 _dlocal = rsclient.idm_domain_get().unwrap();
// Change the ssid
rsclient.idm_domain_set_ssid("new_ssid").unwrap();
// check get and get the ssid and domain info
let nssid = rsclient.idm_domain_get_ssid().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
2021-12-25 00:47:14 +01:00
rsclient
.idm_account_person_extend("demo_account", None, None)
.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
2021-06-02 01:30:37 +02:00
.idm_account_primary_credential_generate_totp("demo_account")
.unwrap();
2021-04-25 03:35:56 +02:00
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_backup_code_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
// Generate backup codes
let backup_codes = rsclient
.idm_account_primary_credential_generate_backup_code("demo_account")
.expect("Failed to generate backup codes?");
// Check a good auth using a backup code
let rsclient_good = rsclient.new_session().unwrap();
assert!(rsclient_good
.auth_password_backup_code(
"demo_account",
"sohdi3iuHo6mai7noh0a",
backup_codes[0].as_str()
)
.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_backup_code("demo_account", "sohdi3iuHo6mai7noh0a", "wrong-backup-code")
.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 and backup codes 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");
2021-04-25 03:35:56 +02:00
// 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]
fn test_server_rest_oauth2_basic_lifecycle() {
run_test(|rsclient: KanidmClient| {
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
assert!(res.is_ok());
// List, there are non.
let initial_configs = rsclient
.idm_oauth2_rs_list()
.expect("Failed to retrieve oauth2 configs");
assert!(initial_configs.is_empty());
// Create a new oauth2 config
rsclient
2021-10-07 10:31:48 +02:00
.idm_oauth2_rs_basic_create(
"test_integration",
"Test Integration",
"https://demo.example.com",
)
.expect("Failed to create oauth2 config");
// List, there is what we created.
let initial_configs = rsclient
.idm_oauth2_rs_list()
.expect("Failed to retrieve oauth2 configs");
assert!(initial_configs.len() == 1);
// Get the value. Assert we have oauth2_rs_basic_secret,
// but can NOT see the token_secret.
let oauth2_config = rsclient
.idm_oauth2_rs_get("test_integration")
.ok()
.flatten()
.expect("Failed to retrieve test_integration config");
2021-10-07 10:31:48 +02:00
eprintln!("{:?}", oauth2_config);
// What can we see?
assert!(oauth2_config.attrs.contains_key("oauth2_rs_basic_secret"));
// This is present, but redacted.
2021-10-07 10:31:48 +02:00
assert!(oauth2_config.attrs.contains_key("oauth2_rs_token_key"));
// Mod delete the secret/key and check them again.
// Check we can patch the oauth2_rs_name / oauth2_rs_origin
rsclient
.idm_oauth2_rs_update(
"test_integration",
None,
2021-10-07 10:31:48 +02:00
Some("Test Integration"),
Some("https://new_demo.example.com"),
2021-10-07 10:31:48 +02:00
Some(vec!["read", "email"]),
true,
true,
2021-11-21 07:41:49 +01:00
true,
)
.expect("Failed to update config");
let oauth2_config_updated = rsclient
.idm_oauth2_rs_get("test_integration")
.ok()
.flatten()
.expect("Failed to retrieve test_integration config");
assert!(oauth2_config_updated != oauth2_config);
2021-10-07 10:31:48 +02:00
// Check that we can add scope maps and delete them.
rsclient
.idm_oauth2_rs_create_scope_map("test_integration", "system_admins", vec!["a", "b"])
.expect("Failed to create scope map");
let oauth2_config_updated2 = rsclient
.idm_oauth2_rs_get("test_integration")
.ok()
.flatten()
.expect("Failed to retrieve test_integration config");
assert!(oauth2_config_updated != oauth2_config_updated2);
rsclient
.idm_oauth2_rs_delete_scope_map("test_integration", "system_admins")
.expect("Failed to delete scope map");
let oauth2_config_updated3 = rsclient
.idm_oauth2_rs_get("test_integration")
.ok()
.flatten()
.expect("Failed to retrieve test_integration config");
eprintln!("{:?}", oauth2_config_updated);
eprintln!("{:?}", oauth2_config_updated3);
assert!(oauth2_config_updated == oauth2_config_updated3);
// Delete the config
rsclient
.idm_oauth2_rs_delete("test_integration")
.expect("Failed to delete test_integration");
// List, there are none.
let final_configs = rsclient
.idm_oauth2_rs_list()
.expect("Failed to retrieve oauth2 configs");
assert!(final_configs.is_empty());
});
}
// Test setting account expiry
// Test the self version of the radius path.
// Test hitting all auth-required endpoints and assert they give unauthorized.
2021-06-25 04:39:05 +02:00
// WIP_TODO: Test create/retrieve then use Backup Code, via API interactions.