mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Extend testing of default entries
Add default entries test to apply behaviours according to `designs/default_idm_layout.rst`. Add expected behaviours for: - Users - Account managers - Group managers - Admins - People Managers - Anonymous clients - Radius servers Also, refactor `kanidmd_client` tests to separate into different files and fix some documentation typos Resolves: #108
This commit is contained in:
parent
fa43ac2110
commit
4dcad60374
|
@ -150,7 +150,7 @@ requirements.
|
||||||
An example is user Alice should only be able to create objects where the class is group, and can
|
An example is user Alice should only be able to create objects where the class is group, and can
|
||||||
only name the group - they can not add members to the group.
|
only name the group - they can not add members to the group.
|
||||||
|
|
||||||
A content requriemnt could be something such as the value an attribute can contain must conform to a
|
A content requirement could be something such as the value an attribute can contain must conform to a
|
||||||
regex, IE, you can create a group of any name, except where the name contains "admin" somewhere
|
regex, IE, you can create a group of any name, except where the name contains "admin" somewhere
|
||||||
in it's name. Arguable, this is partially possible with filtering.
|
in it's name. Arguable, this is partially possible with filtering.
|
||||||
|
|
||||||
|
|
|
@ -65,16 +65,16 @@ account.
|
||||||
As a result, this is high access. This role importantly should NOT be able to lock or alter
|
As a result, this is high access. This role importantly should NOT be able to lock or alter
|
||||||
credentials of high access granted accounts. That must be performed by a higher privilege.
|
credentials of high access granted accounts. That must be performed by a higher privilege.
|
||||||
|
|
||||||
* read and write to accounts, including credentials but NOT private data (see people manager)
|
* read and write to accounts, including write credentials but NOT private data (see people manager)
|
||||||
* ability to lock and unlock accounts, excluding high access members.
|
* ability to lock and unlock accounts, excluding high access members.
|
||||||
|
|
||||||
|
|
||||||
Group Manager
|
Group Managers
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is a role who is able to manage and create groups on the system. Note this does not include
|
This is a role who is able to manage and create groups on the system. Note this does not include
|
||||||
high access groups. This is intended to be for support (ie service desk) staff to help users
|
high access groups. This is intended to be for support (ie service desk) staff to help users
|
||||||
be added to the necesary security groups within reason.
|
be added to the necessary security groups within reason.
|
||||||
|
|
||||||
* read all groups
|
* read all groups
|
||||||
* write group but not high access
|
* write group but not high access
|
||||||
|
@ -109,7 +109,7 @@ for write.
|
||||||
Due to dealing with potentially private or sensitive information, this is a "high access" account.
|
Due to dealing with potentially private or sensitive information, this is a "high access" account.
|
||||||
|
|
||||||
* read private or sensitive data of persons, IE legalName
|
* read private or sensitive data of persons, IE legalName
|
||||||
* write privare or sensitive data of persons, IE legalName
|
* write private or sensitive data of persons, IE legalName
|
||||||
|
|
||||||
Remember, this role does NOT allow technical changes, IE password changes or normal technical changes.
|
Remember, this role does NOT allow technical changes, IE password changes or normal technical changes.
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ should exist by default.
|
||||||
Due to the handling of credentials, this is a "high access" group.
|
Due to the handling of credentials, this is a "high access" group.
|
||||||
|
|
||||||
* Read radius credentials
|
* Read radius credentials
|
||||||
* Read other needed attributes to fufil radius functions.
|
* Read other needed attributes to fulfil radius functions.
|
||||||
|
|
||||||
External Account Systems
|
External Account Systems
|
||||||
========================
|
========================
|
||||||
|
|
|
@ -10,12 +10,12 @@ box experience possible, as well as supplying best practice examples related to
|
||||||
systems.
|
systems.
|
||||||
|
|
||||||
The system admin account (the account you recovered in the setup) has limited privileges - only to
|
The system admin account (the account you recovered in the setup) has limited privileges - only to
|
||||||
manage high-privilege accounts and services. This is to help seperate system administration
|
manage high-privilege accounts and services. This is to help separate system administration
|
||||||
from identity administration actions. An idm_admin is also provided that is only for management
|
from identity administration actions. An idm_admin is also provided that is only for management
|
||||||
of accounts and groups.
|
of accounts and groups.
|
||||||
|
|
||||||
Both admin and idm_admin should *NOT* be used for daily activities - they exist for initial
|
Both admin and idm_admin should *NOT* be used for daily activities - they exist for initial
|
||||||
system configuration, and for disaster recovery scenarioes. You should delegate permissions
|
system configuration, and for disaster recovery scenarios. You should delegate permissions
|
||||||
as required to named user accounts instead.
|
as required to named user accounts instead.
|
||||||
|
|
||||||
The majority of the provided content is privilege groups that provide rights over Kanidm
|
The majority of the provided content is privilege groups that provide rights over Kanidm
|
||||||
|
@ -42,7 +42,7 @@ We can now use the idm_admin to create initial groups and accounts.
|
||||||
You can also use anonymous to view users and groups - note that you won't see as many fields due
|
You can also use anonymous to view users and groups - note that you won't see as many fields due
|
||||||
to the different anonymous access profile limits!
|
to the different anonymous access profile limits!
|
||||||
|
|
||||||
kanidm account get demo_user --name anonymous
|
kanidm account get demo_user --name anonymous
|
||||||
|
|
||||||
## Viewing Default Groups
|
## Viewing Default Groups
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ accounts security and login aspects. This includes resetting account credentials
|
||||||
We can perform a password reset on the demo_user for example as idm_admin, who is
|
We can perform a password reset on the demo_user for example as idm_admin, who is
|
||||||
a default member of this group.
|
a default member of this group.
|
||||||
|
|
||||||
kanidm account credential set_password demo_user --name idm_admin
|
kanidm account credential set_password demo_user --name idm_admin
|
||||||
kanidm self whoami --name demo_user
|
kanidm self whoami --name demo_user
|
||||||
|
|
||||||
## Nested Groups
|
## Nested Groups
|
||||||
|
|
||||||
|
@ -116,5 +116,3 @@ resources that trust Kanidm.
|
||||||
|
|
||||||
All groups that are flagged as "idm_high_privilege" should be audited and
|
All groups that are flagged as "idm_high_privilege" should be audited and
|
||||||
monitored to ensure that they are not altered.
|
monitored to ensure that they are not altered.
|
||||||
|
|
||||||
|
|
||||||
|
|
63
kanidm_client/tests/common.rs
Normal file
63
kanidm_client/tests/common.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use kanidm::config::{Configuration, IntegrationTestConfig};
|
||||||
|
use kanidm::core::create_server_core;
|
||||||
|
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||||
|
|
||||||
|
use actix::prelude::*;
|
||||||
|
|
||||||
|
pub const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
||||||
|
static PORT_ALLOC: AtomicUsize = AtomicUsize::new(8080);
|
||||||
|
|
||||||
|
// Test external behaviours of the service.
|
||||||
|
|
||||||
|
pub fn run_test(test_fn: fn(KanidmClient) -> ()) {
|
||||||
|
// ::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug");
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let port = PORT_ALLOC.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let int_config = Box::new(IntegrationTestConfig {
|
||||||
|
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut config = Configuration::new();
|
||||||
|
config.address = format!("127.0.0.1:{}", port);
|
||||||
|
config.secure_cookies = false;
|
||||||
|
config.integration_test_config = Some(int_config);
|
||||||
|
// Setup the config ...
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
// Spawn a thread for the test runner, this should have a unique
|
||||||
|
// port....
|
||||||
|
System::run(move || {
|
||||||
|
create_server_core(config);
|
||||||
|
|
||||||
|
// This appears to be bind random ...
|
||||||
|
// let srv = srv.bind("127.0.0.1:0").unwrap();
|
||||||
|
let _ = tx.send(System::current());
|
||||||
|
})
|
||||||
|
.expect("unable to start system");
|
||||||
|
});
|
||||||
|
let sys = rx.recv().unwrap();
|
||||||
|
System::set_current(sys.clone());
|
||||||
|
|
||||||
|
// Do we need any fixtures?
|
||||||
|
// Yes probably, but they'll need to be futures as well ...
|
||||||
|
// later we could accept fixture as it's own future for re-use
|
||||||
|
|
||||||
|
// Setup the client, and the address we selected.
|
||||||
|
let addr = format!("http://127.0.0.1:{}", port);
|
||||||
|
let rsclient = KanidmClientBuilder::new()
|
||||||
|
.address(addr)
|
||||||
|
.build()
|
||||||
|
.expect("Failed to build client");
|
||||||
|
|
||||||
|
test_fn(rsclient);
|
||||||
|
|
||||||
|
// We DO NOT need teardown, as sqlite is in mem
|
||||||
|
// let the tables hit the floor
|
||||||
|
sys.stop();
|
||||||
|
}
|
622
kanidm_client/tests/default_entries.rs
Normal file
622
kanidm_client/tests/default_entries.rs
Normal file
|
@ -0,0 +1,622 @@
|
||||||
|
#![deny(warnings)]
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use kanidm_client::KanidmClient;
|
||||||
|
use kanidm_proto::v1::{Filter, Modify, ModifyList};
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
use crate::common::{run_test, ADMIN_TEST_PASSWORD};
|
||||||
|
|
||||||
|
static USER_READABLE_ATTRS: [&str; 9] = [
|
||||||
|
"name",
|
||||||
|
"spn",
|
||||||
|
"displayname",
|
||||||
|
"class",
|
||||||
|
"memberof",
|
||||||
|
"uuid",
|
||||||
|
"gidnumber",
|
||||||
|
"loginshell",
|
||||||
|
"ssh_publickey",
|
||||||
|
];
|
||||||
|
static SELF_WRITEABLE_ATTRS: [&str; 7] = [
|
||||||
|
"name",
|
||||||
|
"displayname",
|
||||||
|
"legalname",
|
||||||
|
"radius_secret",
|
||||||
|
"primary_credential",
|
||||||
|
"ssh_publickey",
|
||||||
|
"unix_password",
|
||||||
|
];
|
||||||
|
static DEFAULT_HP_GROUP_NAMES: [&str; 22] = [
|
||||||
|
"idm_admins",
|
||||||
|
"system_admins",
|
||||||
|
"idm_people_manage_priv",
|
||||||
|
"idm_people_account_password_import_priv",
|
||||||
|
"idm_people_extend_priv",
|
||||||
|
"idm_people_write_priv",
|
||||||
|
"idm_people_read_priv",
|
||||||
|
"idm_group_manage_priv",
|
||||||
|
"idm_group_write_priv",
|
||||||
|
"idm_account_manage_priv",
|
||||||
|
"idm_account_write_priv",
|
||||||
|
"idm_account_read_priv",
|
||||||
|
"idm_radius_servers",
|
||||||
|
"idm_hp_account_manage_priv",
|
||||||
|
"idm_hp_account_write_priv",
|
||||||
|
"idm_hp_account_read_priv",
|
||||||
|
"idm_schema_manage_priv",
|
||||||
|
"idm_hp_group_manage_priv",
|
||||||
|
"idm_hp_group_write_priv",
|
||||||
|
"idm_acp_manage_priv",
|
||||||
|
"domain_admins",
|
||||||
|
"idm_high_privilege",
|
||||||
|
];
|
||||||
|
static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
|
||||||
|
["idm_account_unix_extend_priv", "idm_group_unix_extend_priv"];
|
||||||
|
|
||||||
|
fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) -> () {
|
||||||
|
rsclient.idm_account_create(id, "Deeeeemo").unwrap();
|
||||||
|
|
||||||
|
// Create group and add to user to test read attr: member_of
|
||||||
|
match rsclient.idm_group_get(&group_name).unwrap() {
|
||||||
|
Some(_) => (),
|
||||||
|
None => rsclient.idm_group_create(&group_name).unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members(&group_name, vec![id])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"primary_credential" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_account_primary_credential_set_password(id, "dsadjasiodqwjk12asdl")
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"ssh_publickey" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_account_post_ssh_pubkey(
|
||||||
|
id,
|
||||||
|
"k1",
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0\
|
||||||
|
L1EyR30CwoP william@amethyst",
|
||||||
|
)
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
"unix_password" => Some(
|
||||||
|
rsclient
|
||||||
|
.idm_account_unix_cred_put(id, "dsadjasiodqwjk12asdl")
|
||||||
|
.is_ok(),
|
||||||
|
),
|
||||||
|
entry => {
|
||||||
|
let new_value = match entry {
|
||||||
|
"acp_receiver" => "{\"Eq\":[\"memberof\",\"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()).is_ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_all_attrs(mut rsclient: &mut KanidmClient, id: &str, group_name: &str) {
|
||||||
|
// Extend with posix attrs to test read attr: gidnumber and loginshell
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("idm_admins", vec!["admin"])
|
||||||
|
.unwrap();
|
||||||
|
rsclient
|
||||||
|
.idm_account_unix_extend(id, None, Some(&"/bin/bash"))
|
||||||
|
.unwrap();
|
||||||
|
rsclient.idm_group_unix_extend(&group_name, None).unwrap();
|
||||||
|
|
||||||
|
// Extend with person to allow legalname
|
||||||
|
rsclient.idm_account_person_extend(id).unwrap();
|
||||||
|
|
||||||
|
["ssh_publickey", "legalname", "mail"]
|
||||||
|
.iter()
|
||||||
|
.for_each(|attr| {
|
||||||
|
assert!(is_attr_writable(&rsclient, id, attr).unwrap());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write radius credentials
|
||||||
|
if id != "anonymous" {
|
||||||
|
login_account(&mut rsclient, id);
|
||||||
|
let _ = rsclient
|
||||||
|
.idm_account_radius_credential_regenerate(id)
|
||||||
|
.unwrap();
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_user_with_all_attrs(
|
||||||
|
mut rsclient: &mut 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);
|
||||||
|
add_all_attrs(&mut rsclient, id, group_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn login_account(rsclient: &mut KanidmClient, id: &str) -> () {
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("idm_people_account_password_import_priv", vec!["admin"])
|
||||||
|
.unwrap();
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("idm_people_extend_priv", vec!["admin"])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
rsclient
|
||||||
|
.idm_account_primary_credential_set_password(id, "eicieY7ahchaoCh0eeTa")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _ = rsclient.logout();
|
||||||
|
let res = rsclient.auth_simple_password(id, "eicieY7ahchaoCh0eeTa");
|
||||||
|
println!("{} logged in", id);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
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()))
|
||||||
|
.unwrap();
|
||||||
|
let e = rset.first().unwrap();
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.map(|attr| {
|
||||||
|
println!("Reading {}", attr);
|
||||||
|
match *attr {
|
||||||
|
"radius_secret" => match rsclient.idm_account_radius_credential_get(id).unwrap() {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false,
|
||||||
|
},
|
||||||
|
_ => match e.attrs.get(*attr) {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|is_ok| assert!(is_ok == is_readable));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_write_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str], is_writeable: bool) -> () {
|
||||||
|
println!("Test write to {}, is writeable: {}", id, is_writeable);
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.map(|attr| {
|
||||||
|
println!("Writing to {}", attr);
|
||||||
|
is_attr_writable(&rsclient, id, attr).unwrap()
|
||||||
|
})
|
||||||
|
.for_each(|is_ok| assert!(is_ok == is_writeable));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_modify_group(rsclient: &KanidmClient, group_names: &[&str], is_modificable: bool) -> () {
|
||||||
|
// need user test created to be added as test part
|
||||||
|
group_names.iter().for_each(|group| {
|
||||||
|
println!("Testing group: {}", group);
|
||||||
|
["description", "name"].iter().for_each(|attr| {
|
||||||
|
assert!(is_attr_writable(&rsclient, group, attr).unwrap() == is_modificable)
|
||||||
|
});
|
||||||
|
assert!(rsclient.idm_group_add_members(group, vec!["test"]).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.
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_users() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "self_account", Some("self_group"));
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "other_account", Some("other_group"));
|
||||||
|
|
||||||
|
login_account(&mut rsclient, "self_account");
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "self_account", &USER_READABLE_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "other_account", &USER_READABLE_ATTRS, true);
|
||||||
|
|
||||||
|
static GROUP_READABLE_ATTRS: [&str; 5] = ["class", "name", "spn", "uuid", "member"];
|
||||||
|
test_read_attrs(&rsclient, "self_group", &GROUP_READABLE_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "other_group", &GROUP_READABLE_ATTRS, true);
|
||||||
|
|
||||||
|
static USER_SENSITIVE_ATTRS: [&str; 2] = ["legalname", "mail"];
|
||||||
|
test_read_attrs(&rsclient, "other_account", &USER_SENSITIVE_ATTRS, false);
|
||||||
|
|
||||||
|
static SELF_READABLE_ATTRS: [&str; 1] = ["radius_secret"];
|
||||||
|
test_read_attrs(&rsclient, "self_account", &SELF_READABLE_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "other_account", &SELF_READABLE_ATTRS, false);
|
||||||
|
|
||||||
|
test_write_attrs(&rsclient, "self_account", &SELF_WRITEABLE_ATTRS, true);
|
||||||
|
test_write_attrs(&rsclient, "other_account", &SELF_WRITEABLE_ATTRS, false);
|
||||||
|
|
||||||
|
static NON_SELF_WRITEABLE_ATTRS: [&str; 5] =
|
||||||
|
["spn", "class", "memberof", "gidnumber", "uuid"];
|
||||||
|
test_write_attrs(&rsclient, "self_account", &NON_SELF_WRITEABLE_ATTRS, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account Managers
|
||||||
|
// read and write to accounts, including write credentials but NOT private data (see people manager)
|
||||||
|
// ability to lock and unlock accounts, excluding high access members.
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_account_managers() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_user(&rsclient, "account_manager", "idm_account_manage_priv");
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||||
|
|
||||||
|
login_account(&mut rsclient, "account_manager");
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true);
|
||||||
|
static ACCOUNT_MANAGER_ATTRS: [&str; 5] = [
|
||||||
|
"name",
|
||||||
|
"displayname",
|
||||||
|
"primary_credential",
|
||||||
|
"ssh_publickey",
|
||||||
|
"mail",
|
||||||
|
];
|
||||||
|
test_write_attrs(&rsclient, "test", &ACCOUNT_MANAGER_ATTRS, true);
|
||||||
|
|
||||||
|
static PRIVATE_DATA_ATTRS: [&str; 1] = ["legalname"];
|
||||||
|
test_read_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false);
|
||||||
|
test_write_attrs(&rsclient, "test", &PRIVATE_DATA_ATTRS, false);
|
||||||
|
// TODO: lock and _unlock, except high access members
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group Managers
|
||||||
|
// read all groups
|
||||||
|
// write group but not high access
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_group_managers() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_user(&rsclient, "group_manager", "idm_group_manage_priv");
|
||||||
|
// create test user without creating new groups
|
||||||
|
create_user(&rsclient, "test", "idm_admins");
|
||||||
|
|
||||||
|
login_account(&mut rsclient, "group_manager");
|
||||||
|
|
||||||
|
let default_group_names: HashSet<String> =
|
||||||
|
[&DEFAULT_HP_GROUP_NAMES[..], &DEFAULT_NOT_HP_GROUP_NAMES[..]]
|
||||||
|
.concat()
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let groups = rsclient.idm_group_list().unwrap();
|
||||||
|
let group_names: HashSet<String> = groups
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.attrs.get("name").unwrap().first().unwrap())
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
assert_eq!(default_group_names, group_names);
|
||||||
|
|
||||||
|
test_modify_group(&rsclient, &DEFAULT_HP_GROUP_NAMES, false);
|
||||||
|
test_modify_group(&rsclient, &DEFAULT_NOT_HP_GROUP_NAMES, true);
|
||||||
|
|
||||||
|
rsclient.idm_group_create("test_group").unwrap();
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("test_group", vec!["test"])
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_attr_writable(&rsclient, "test_group", "description").unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admins
|
||||||
|
// read and write access control entries.
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_admins_access_control_entries() {
|
||||||
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
static ACP_COMMON_ATTRS: [&str; 4] =
|
||||||
|
["name", "description", "acp_receiver", "acp_targetscope"];
|
||||||
|
static ACP_ENTRIES: [&str; 28] = [
|
||||||
|
"idm_admins_acp_recycle_search",
|
||||||
|
"idm_admins_acp_revive",
|
||||||
|
"idm_self_acp_read",
|
||||||
|
"idm_self_acp_write",
|
||||||
|
"idm_all_acp_read",
|
||||||
|
"idm_acp_people_read_priv",
|
||||||
|
"idm_acp_people_write_priv",
|
||||||
|
"idm_acp_people_manage",
|
||||||
|
"idm_acp_people_account_password_import_priv",
|
||||||
|
"idm_acp_people_extend_priv",
|
||||||
|
"idm_acp_group_write_priv",
|
||||||
|
"idm_acp_account_read_priv",
|
||||||
|
"idm_acp_account_write_priv",
|
||||||
|
"idm_acp_account_manage",
|
||||||
|
"idm_acp_radius_servers",
|
||||||
|
"idm_acp_hp_account_read_priv",
|
||||||
|
"idm_acp_hp_account_write_priv",
|
||||||
|
"idm_acp_hp_group_write_priv",
|
||||||
|
"idm_acp_schema_write_attrs_priv",
|
||||||
|
"idm_acp_acp_manage_priv",
|
||||||
|
"idm_acp_schema_write_classes_priv",
|
||||||
|
"idm_acp_group_manage",
|
||||||
|
"idm_acp_hp_account_manage",
|
||||||
|
"idm_acp_hp_group_manage",
|
||||||
|
"idm_acp_domain_admin_priv",
|
||||||
|
"idm_acp_system_config_priv",
|
||||||
|
"idm_acp_account_unix_extend_priv",
|
||||||
|
"idm_acp_group_unix_extend_priv",
|
||||||
|
];
|
||||||
|
|
||||||
|
ACP_ENTRIES.iter().for_each(|entry| {
|
||||||
|
test_read_attrs(&rsclient, entry, &ACP_COMMON_ATTRS, true);
|
||||||
|
test_write_attrs(&rsclient, entry, &ACP_COMMON_ATTRS, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// read schema entries.
|
||||||
|
// TODO: write schema entries
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_admins_schema_entries() {
|
||||||
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
let default_classnames: HashSet<String> = [
|
||||||
|
"access_control_create",
|
||||||
|
"access_control_delete",
|
||||||
|
"access_control_modify",
|
||||||
|
"access_control_profile",
|
||||||
|
"access_control_search",
|
||||||
|
"attributetype",
|
||||||
|
"classtype",
|
||||||
|
"extensibleobject",
|
||||||
|
"memberof",
|
||||||
|
"object",
|
||||||
|
"recycled",
|
||||||
|
"system",
|
||||||
|
"system_info",
|
||||||
|
"tombstone",
|
||||||
|
"person",
|
||||||
|
"group",
|
||||||
|
"account",
|
||||||
|
"domain_info",
|
||||||
|
"posixaccount",
|
||||||
|
"posixgroup",
|
||||||
|
"system_config",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let classtype_entries = rsclient.idm_schema_classtype_list().unwrap();
|
||||||
|
let classnames: HashSet<String> = classtype_entries
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.attrs.get("classname").unwrap().first().unwrap())
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
println!("{:?}", classnames);
|
||||||
|
|
||||||
|
assert_eq!(default_classnames, classnames);
|
||||||
|
|
||||||
|
let default_attributenames: HashSet<String> = [
|
||||||
|
"acp_create_attr",
|
||||||
|
"acp_create_class",
|
||||||
|
"acp_enable",
|
||||||
|
"acp_modify_class",
|
||||||
|
"acp_modify_presentattr",
|
||||||
|
"acp_modify_removedattr",
|
||||||
|
"acp_receiver",
|
||||||
|
"acp_search_attr",
|
||||||
|
"acp_targetscope",
|
||||||
|
"attributename",
|
||||||
|
"claim",
|
||||||
|
"class",
|
||||||
|
"classname",
|
||||||
|
"description",
|
||||||
|
"directmemberof",
|
||||||
|
"domain",
|
||||||
|
"index",
|
||||||
|
"last_modified_cid",
|
||||||
|
"may",
|
||||||
|
"member",
|
||||||
|
"memberof",
|
||||||
|
"multivalue",
|
||||||
|
"must",
|
||||||
|
"name",
|
||||||
|
"password_import",
|
||||||
|
"phantom",
|
||||||
|
"spn",
|
||||||
|
"syntax",
|
||||||
|
"systemmay",
|
||||||
|
"systemmust",
|
||||||
|
"unique",
|
||||||
|
"uuid",
|
||||||
|
"version",
|
||||||
|
"displayname",
|
||||||
|
"legalname",
|
||||||
|
"mail",
|
||||||
|
"ssh_publickey",
|
||||||
|
"primary_credential",
|
||||||
|
"radius_secret",
|
||||||
|
"domain_name",
|
||||||
|
"domain_uuid",
|
||||||
|
"domain_ssid",
|
||||||
|
"gidnumber",
|
||||||
|
"badlist_password",
|
||||||
|
"loginshell",
|
||||||
|
"unix_password",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let attributename_entries = rsclient.idm_schema_attributetype_list().unwrap();
|
||||||
|
println!("{:?}", attributename_entries);
|
||||||
|
let attributenames = attributename_entries
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.attrs.get("attributename").unwrap().first().unwrap())
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(default_attributenames, attributenames);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// modify all groups including high access groups.
|
||||||
|
// create new accounts (to bootstrap the system).
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_admins_group_entries() {
|
||||||
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
create_user(&rsclient, "test", "test_group");
|
||||||
|
|
||||||
|
let default_group_names =
|
||||||
|
[&DEFAULT_HP_GROUP_NAMES[..], &DEFAULT_NOT_HP_GROUP_NAMES[..]].concat();
|
||||||
|
|
||||||
|
test_modify_group(&rsclient, &default_group_names, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// modify high access accounts as an escalation for security sensitive accounts.
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_admins_ha_accounts() {
|
||||||
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
|
||||||
|
test_write_attrs(&rsclient, "idm_admin", &MAIN_ATTRS, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover from the recycle bin
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_admins_recycle_accounts() {
|
||||||
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
create_user(&rsclient, "test", "test_group");
|
||||||
|
|
||||||
|
rsclient.idm_account_delete("test").unwrap();
|
||||||
|
rsclient.recycle_bin_revive("test").unwrap();
|
||||||
|
|
||||||
|
let acc = rsclient.idm_account_get("test").unwrap();
|
||||||
|
assert!(acc.is_some());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// People Managers
|
||||||
|
// read private or sensitive data of persons, IE legalName
|
||||||
|
// write private or sensitive data of persons, IE legalName
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_people_managers() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_user(&rsclient, "read_people_manager", "idm_people_read_priv");
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
login_account(&mut rsclient, "read_people_manager");
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false);
|
||||||
|
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, false);
|
||||||
|
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false);
|
||||||
|
|
||||||
|
let _ = rsclient.logout();
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
create_user(&rsclient, "write_people_manager", "idm_people_write_priv");
|
||||||
|
login_account(&mut rsclient, "write_people_manager");
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false);
|
||||||
|
test_write_attrs(&rsclient, "test", &PEOPLE_MANAGER_ATTRS, true);
|
||||||
|
test_write_attrs(&rsclient, "test", &TECHNICAL_ATTRS, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anonymous Clients + Everyone Else
|
||||||
|
// read memberof, unix attrs, name, displayname, class
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_anonymous_entry() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||||
|
rsclient
|
||||||
|
.idm_group_add_members("test_group", vec!["anonymous"])
|
||||||
|
.unwrap();
|
||||||
|
add_all_attrs(&mut rsclient, "anonymous", "test_group");
|
||||||
|
|
||||||
|
let _ = rsclient.logout();
|
||||||
|
rsclient.auth_anonymous().unwrap();
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "anonymous", &USER_READABLE_ATTRS, true);
|
||||||
|
test_write_attrs(&rsclient, "test", &SELF_WRITEABLE_ATTRS, false);
|
||||||
|
test_write_attrs(&rsclient, "anonymous", &SELF_WRITEABLE_ATTRS, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// RADIUS Servers
|
||||||
|
// Read radius credentials
|
||||||
|
// Read other needed attributes to fulfil radius functions.
|
||||||
|
#[test]
|
||||||
|
fn test_default_entries_rbac_radius_servers() {
|
||||||
|
run_test(|mut rsclient: KanidmClient| {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.unwrap();
|
||||||
|
create_user(&rsclient, "radius_server", "idm_radius_servers");
|
||||||
|
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||||
|
|
||||||
|
login_account(&mut rsclient, "radius_server");
|
||||||
|
static RADIUS_NECESSARY_ATTRS: [&str; 4] = ["name", "spn", "uuid", "radius_secret"];
|
||||||
|
|
||||||
|
test_read_attrs(&rsclient, "test", &USER_READABLE_ATTRS, true);
|
||||||
|
test_read_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, true);
|
||||||
|
test_write_attrs(&rsclient, "test", &RADIUS_NECESSARY_ATTRS, false);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,75 +1,18 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use kanidm::config::{Configuration, IntegrationTestConfig};
|
|
||||||
use kanidm::core::create_server_core;
|
|
||||||
use kanidm::credential::totp::TOTP;
|
|
||||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
|
||||||
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
|
|
||||||
|
|
||||||
use actix::prelude::*;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
static PORT_ALLOC: AtomicUsize = AtomicUsize::new(8080);
|
use kanidm::credential::totp::TOTP;
|
||||||
const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
use kanidm_client::KanidmClient;
|
||||||
|
use kanidm_proto::v1::{Entry, Filter, Modify, ModifyList};
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
use crate::common::{run_test, ADMIN_TEST_PASSWORD};
|
||||||
|
|
||||||
const ADMIN_TEST_PASSWORD_CHANGE: &str = "integration test admin new🎉";
|
const ADMIN_TEST_PASSWORD_CHANGE: &str = "integration test admin new🎉";
|
||||||
const UNIX_TEST_PASSWORD: &str = "unix test user password";
|
const UNIX_TEST_PASSWORD: &str = "unix test user password";
|
||||||
|
|
||||||
// Test external behaviorus of the service.
|
|
||||||
|
|
||||||
fn run_test(test_fn: fn(KanidmClient) -> ()) {
|
|
||||||
// ::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug");
|
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let port = PORT_ALLOC.fetch_add(1, Ordering::SeqCst);
|
|
||||||
|
|
||||||
let int_config = Box::new(IntegrationTestConfig {
|
|
||||||
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut config = Configuration::new();
|
|
||||||
config.address = format!("127.0.0.1:{}", port);
|
|
||||||
config.secure_cookies = false;
|
|
||||||
config.integration_test_config = Some(int_config);
|
|
||||||
// Setup the config ...
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
// Spawn a thread for the test runner, this should have a unique
|
|
||||||
// port....
|
|
||||||
System::run(move || {
|
|
||||||
create_server_core(config);
|
|
||||||
|
|
||||||
// This appears to be bind random ...
|
|
||||||
// let srv = srv.bind("127.0.0.1:0").unwrap();
|
|
||||||
let _ = tx.send(System::current());
|
|
||||||
})
|
|
||||||
.expect("unable to start system");
|
|
||||||
});
|
|
||||||
let sys = rx.recv().unwrap();
|
|
||||||
System::set_current(sys.clone());
|
|
||||||
|
|
||||||
// Do we need any fixtures?
|
|
||||||
// Yes probably, but they'll need to be futures as well ...
|
|
||||||
// later we could accept fixture as it's own future for re-use
|
|
||||||
|
|
||||||
// Setup the client, and the address we selected.
|
|
||||||
let addr = format!("http://127.0.0.1:{}", port);
|
|
||||||
let rsclient = KanidmClientBuilder::new()
|
|
||||||
.address(addr)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to build client");
|
|
||||||
|
|
||||||
test_fn(rsclient);
|
|
||||||
|
|
||||||
// We DO NOT need teardown, as sqlite is in mem
|
|
||||||
// let the tables hit the floor
|
|
||||||
sys.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_server_create() {
|
fn test_server_create() {
|
||||||
run_test(|rsclient: KanidmClient| {
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
@ -213,7 +156,7 @@ fn test_server_admin_change_simple_password() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a test for reseting another accounts pws via the rest api
|
// Add a test for resetting another accounts pws via the rest api
|
||||||
#[test]
|
#[test]
|
||||||
fn test_server_admin_reset_simple_password() {
|
fn test_server_admin_reset_simple_password() {
|
||||||
run_test(|rsclient: KanidmClient| {
|
run_test(|rsclient: KanidmClient| {
|
||||||
|
|
|
@ -385,7 +385,8 @@ pub const JSON_IDM_ACP_ACCOUNT_MANAGE_PRIV_V1: &str = r#"{
|
||||||
"displayname",
|
"displayname",
|
||||||
"description",
|
"description",
|
||||||
"primary_credential",
|
"primary_credential",
|
||||||
"ssh_publickey"
|
"ssh_publickey",
|
||||||
|
"mail"
|
||||||
],
|
],
|
||||||
"acp_create_class": [
|
"acp_create_class": [
|
||||||
"object", "account"
|
"object", "account"
|
||||||
|
|
Loading…
Reference in a new issue